当前位置:   article > 正文

ffmpeg新手成长之路——使用av_seek_frame做seek定位_ffmpeg 如何精准定位到某个播放位置

ffmpeg 如何精准定位到某个播放位置

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212

输出信息如下图所示:

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/647067
推荐阅读
相关标签
  

闽ICP备14008679号