ffmpeg新手成长之路——使用av_seek_frame做seek定位

av_seek_frame函数

/**
 * Seek to the keyframe at timestamp.
 * 'timestamp' in 'stream_index'.
 *
 * @param s media file handle
 * @param stream_index If stream_index is (-1), a default
 * stream is selected, and timestamp is automatically converted
 * from AV_TIME_BASE units to the stream specific time_base.
 * @param timestamp Timestamp in AVStream.time_base units
 *        or, if no stream is specified, in AV_TIME_BASE units.
 * @param flags flags which select direction and seeking mode
 * @return >= 0 on success
 */
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);

stream_index:用于seek的流索引,-1为默认,一般0为视频流索引,1为音频流索引
timestamp:定位的时间戳,单位以流的time_base为单位,并非毫秒单位的时间戳,因此如果seek,需要结合具体的流的time_base计算对应单位的时间戳
flags:可以设置为按字节,在按时间seek时取该点之前还是之后的关键帧,以及不按关键帧seek等。具体的值有如下几个,可以通过运算符来进行组合
#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward
#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes
#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes
#define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number
ts一般使用AVSEEK_FLAG_BYTE,mp4一般使用AVSEEK_FLAG_ANY、AVSEEK_FLAG_BACKWARD(seek到对应时间戳之前的最后一个关键帧)

下面给出一段实例代码,代码中是对mp4文件按照音频流进行seek,seek的时间点为6秒。

	av_register_all();
	std::string path = "E:\\media\\test.mp4";

	const char *in_filename = path.c_str();

	//设置日志级别
	av_log_set_level(AV_LOG_INFO);

	//通过日志的形式打印
	av_log(NULL, AV_LOG_INFO, "\n in_filename = %s\n", in_filename);

	//AVFormatContext是描述一个媒体文件或媒体流构成和基本信息的结构体
	AVFormatContext *ifmt_ctx = NULL;   // 输入文件的demux(解复用)

	int videoindex = -1;  //视频索引
	int audioindex = -1;  //音频索引

	//1 打开文件,主要探测协议类型,如果是网络文件需要插件网络连接
	int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
	if (ret < 0)
	{
		//打印失败原因
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		av_log(NULL, AV_LOG_ERROR, "open %s failed:%s\n", in_filename, buf);
		//open believe.mp41 failed:No such file or directory

//        goto failed;  //error
		return;
	}

	//2 读取媒体的码流信息
	ret = avformat_find_stream_info(ifmt_ctx, NULL);
	if (ret < 0)
	{
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		av_log(NULL, AV_LOG_ERROR, "avformat_find_stream_info %s failed:%s\n", in_filename, buf);
		//        goto filed;  //error ??
		return;
	}

	av_log(NULL, AV_LOG_INFO, "\n==== av_dump_format in_filename:%s ===\n", in_filename);
	//    打印输入或输出格式的详细信息
	//    void av_dump_format(AVFormatContext *ic, int index, const char *url,int is_output);
	av_dump_format(ifmt_ctx, 0, in_filename, 0);
	av_log(NULL, AV_LOG_INFO, "\n==== av_dump_format finish =======\n\n");

	//查看媒体文件的一些信息
	 //打印媒体的路径
	av_log(NULL, AV_LOG_INFO, "media name: %s\n", ifmt_ctx->filename);
	//nb_streams媒体流数量
	printf("media stream number: %d\n", ifmt_ctx->nb_streams); //media stream number: 2
	//bit_rate: 媒体文件的码率,单位为bps
	printf("media average ratio:%11d\n", (int64_t)(ifmt_ctx->bit_rate / 1024));
	//时间
	int total_seconds, hours, minute, second;
	//duration: 媒体文件时长,单位微秒
	total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE;  // 1000us = 1ms, 1000ms = 1秒
	hours = total_seconds / 3600;
	minute = (total_seconds % 3600) / 60;
	second = (total_seconds % 60);
	printf("total duration: %02d:%02d:%02d\n", hours, minute, second);
	printf("\n");
	AVRational in_timebase_v;
	AVRational in_timebase_a;
	/*
	 * 老版本通过遍历的方式读取媒体文件视频和音频的信息
	 * 新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果
	 */
	for (uint32_t i = 0;i < ifmt_ctx->nb_streams;++i)
	{
		AVStream *in_stream = ifmt_ctx->streams[i];  //音频流,视频流,字幕流
		//如果是音频流,则打印音频的信息
		if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
		{
			in_timebase_a = in_stream->time_base;
			printf("----- Audio info timebase:%d/%d\n", in_timebase_a.num, in_timebase_a.den);
			//index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标书
			printf("index:%d\n", in_stream->index);
			//sample_rate: 音频编码解码器的采样率,单位为HZ
			printf("samplerate:%dnHZ\n", in_stream->codecpar->sample_rate);
			// codecpar->format:音频采样格式
			if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format)
			{
				printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");
			}
			else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format)
			{
				printf("sampleformat:AV_SAMPLE_FMT_S16P\n");
			}
			//channels:音频信道数目
			printf("channels number:%d\n", in_stream->codecpar->channels);
			//codec_id: 音频压缩编码格式
			if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id)
				printf("audio codec:AAC\n");
			else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id)
				printf("audio codec:MP3\n");
			else
				printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);

			// 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
			if (in_stream->duration != AV_NOPTS_VALUE)
			{
				int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
				//将音频总时长转换为时分秒的格式打印到控制台上
				printf("audio duration: %02d:%02d:%02d\n",
					duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));
			}
			else  //如果无效
			{
				printf("audio duration unknown");
			}
			printf("\n");

			audioindex = i;

		}
		else if (AVMEDIA_TYPE_VIDEO == in_stream->codec->codec_type)
		{
			in_timebase_v = in_stream->time_base;
			printf("----- Video info:timebase:%d/%d\n", in_timebase_v.num, in_timebase_v.den);
			printf("index:%d\n", in_stream->index);
			// avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧
			printf("fps:%lf\n", av_q2d(in_stream->avg_frame_rate));
			if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式
			{
				printf("video codec:MPEG4\n");
			}
			else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式
			{
				printf("video codec:H264\n");
			}
			else
			{
				printf("video codec_id:%d\n", in_stream->codecpar->codec_id);
			}
			// 视频帧宽度和帧高度
			printf("width:%d height:%d\n", in_stream->codecpar->width,
				in_stream->codecpar->height);
			//视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
			if (in_stream->duration != AV_NOPTS_VALUE)
			{
				int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);
				printf("video duration: %02d:%02d:%02d\n",
					duration_video / 3600,
					(duration_video % 3600) / 60,
					(duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上
			}
			else
			{
				printf("video duration unknown");
			}

			printf("\n");
			videoindex = i;
		}
	}

	//3 从文件中读取数据包
	AVPacket *pkt = av_packet_alloc();

	int pkt_count = 0;
	int print_max_count = 1000;
	printf("\n-----av_read_frame start\n");
	zint64 seekSecValue = 6;
	int64_t startTime = seekSecValue / av_q2d(in_timebase_a);
	ret = av_seek_frame(ifmt_ctx, audioindex , startTime, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_ANY);
	while (1)
	{
		//读取音视频包
		ret = av_read_frame(ifmt_ctx, pkt);
		if (ret < 0)
		{
			printf("-----av_read_frame end\n");
			break;
		}
		if (pkt_count++ < print_max_count)
		{
			if (pkt->stream_index == audioindex)
			{
				zint64 pts = pkt->pts*1000.0*in_timebase_a.num / in_timebase_a.den;
				printf("audio pts: %lld(%lld)\n", pts, pkt->pts);
				printf("audio dts: %lld\n", pkt->dts);
				printf("audio size: %d\n", pkt->size);
				printf("audio pos: %lld\n", pkt->pos);
				printf("audio duration: %lf\n\n",
					pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));
			}
			else if (pkt->stream_index == videoindex)
			{
				zint64 pts = pkt->pts*1000.0*in_timebase_v.num / in_timebase_v.den;
				printf("video pts: %lld(%lld)\n", pts, pkt->pts);
				printf("video dts: %lld\n", pkt->dts);
				printf("video size: %d\n", pkt->size);
				printf("video pos: %lld\n", pkt->pos);
				printf("video duration: %lf\n\n",
					pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));
			}
			else {
				printf("unknown stream_index:\n", pkt->stream_index);
			}
		}
		av_packet_unref(pkt);
	}
	if (pkt)
		av_packet_free(&pkt);

	//4 关闭复用器
//failed:
	if (ifmt_ctx)
		avformat_close_input(&ifmt_ctx);

输出信息如下图所示:

media stream number: 2
media average ratio:       2155
total duration: 00:19:47

----- Video info:timebase:1/10240
index:0
fps:20.000000
video codec:H264
width:1920 height:1080
video duration: 00:19:47

----- Audio info timebase:1/32000
index:1
samplerate:32000nHZ
sampleformat:AV_SAMPLE_FMT_FLTP
channels number:1
audio codec:AAC
audio duration: 00:19:47


-----av_read_frame start
video pts: 5950(60928)
video dts: 60928
video size: 4136
video pos: 5898643
video duration: 0.050000

audio pts: 5984(191488)
audio dts: 191488
audio size: 395
audio pos: 5903014
audio duration: 0.032000

video pts: 6000(61440)
video dts: 61440
video size: 4319
video pos: 5903409
video duration: 0.050000

audio pts: 6016(192512)
audio dts: 192512
audio size: 275
audio pos: 5907728
audio duration: 0.032000

audio pts: 6048(193536)
audio dts: 193536
audio size: 260
audio pos: 5908003
audio duration: 0.032000

video pts: 6050(61952)
video dts: 61952
video size: 2956
video pos: 5908263
video duration: 0.050000

audio pts: 6080(194560)
audio dts: 194560
audio size: 283
audio pos: 5911219
audio duration: 0.032000

video pts: 6100(62464)
video dts: 62464
video size: 4262
video pos: 5911502
video duration: 0.050000

audio pts: 6112(195584)
audio dts: 195584
audio size: 266
audio pos: 5915764
audio duration: 0.032000

audio pts: 6144(196608)
audio dts: 196608
audio size: 327
audio pos: 5916030
audio duration: 0.032000

你可能感兴趣的:(ffmpeg,c++)