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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用FFmpeg库进行推流和跳转功能,需要按照以下步骤进行操作: 1. 初始化FFmpeg库 在使用FFmpeg库之前,需要先初始化FFmpeg库。可以使用av_register_all()函数进行初始化。 ```c av_register_all(); ``` 2. 打开输入文件 使用avformat_open_input()函数打开输入文件,然后使用avformat_find_stream_info()函数查找文件中的流信息。 ```c AVFormatContext *formatCtx = NULL; avformat_open_input(&formatCtx, inputFile, NULL, NULL); avformat_find_stream_info(formatCtx, NULL); ``` 3. 打开输出文件 使用avformat_alloc_output_context2()函数创建输出文件的AVFormatContext,并使用avio_open()函数打开输出文件。 ```c AVFormatContext *outFormatCtx = NULL; avformat_alloc_output_context2(&outFormatCtx, NULL, NULL, outputFile); AVIOContext *outAVIOContext = NULL; avio_open(&outAVIOContext, outputFile, AVIO_FLAG_WRITE); outFormatCtx->pb = outAVIOContext; ``` 4. 为输出文件添加流 使用avformat_new_stream()函数为输出文件添加音频或视频流,并设置流的编码格式和参数。 ```c AVStream *outStream = avformat_new_stream(outFormatCtx, NULL); outStream->codecpar->codec_id = codecId; outStream->codecpar->codec_type = codecType; outStream->codecpar->width = width; outStream->codecpar->height = height; outStream->codecpar->sample_rate = sampleRate; outStream->codecpar->channels = channels; outStream->codecpar->format = AV_SAMPLE_FMT_FLTP; ``` 5. 打开编码器 使用avcodec_find_encoder()函数查找流的编码器,并使用avcodec_open2()函数打开编码器。 ```c AVCodec *encoder = avcodec_find_encoder(outStream->codecpar->codec_id); AVCodecContext *encoderCtx = avcodec_alloc_context3(encoder); avcodec_parameters_to_context(encoderCtx, outStream->codecpar); avcodec_open2(encoderCtx, encoder, NULL); ``` 6. 写入文件头 使用avformat_write_header()函数写入输出文件的文件头。 ```c avformat_write_header(outFormatCtx, NULL); ``` 7. 读取和写入数据 使用av_read_frame()函数读取输入文件中的数据,并使用av_write_frame()函数将数据写入输出文件。如果需要跳转到指定时间点,可以使用av_seek_frame()函数进行跳转。 ```c while (av_read_frame(formatCtx, &packet) == 0) { if (packet.stream_index == streamIndex) { if (av_seek_frame(formatCtx, streamIndex, timestamp, AVSEEK_FLAG_BACKWARD) >= 0) { avcodec_flush_buffers(decoderCtx); continue; } avcodec_send_packet(decoderCtx, &packet); while (avcodec_receive_frame(decoderCtx, frame) == 0) { // 对音视频数据进行处理 avcodec_send_frame(encoderCtx, frame); while (avcodec_receive_packet(encoderCtx, &outPacket) == 0) { outPacket.stream_index = outStream->index; av_interleaved_write_frame(outFormatCtx, &outPacket); av_packet_unref(&outPacket); } } } av_packet_unref(&packet); } ``` 8. 关闭和释放资源 使用av_write_trailer()函数写入输出文件的文件尾,并使用avformat_close_input()、avformat_close_output()等函数关闭输入输出文件,并释放相应的资源。 ```c av_write_trailer(outFormatCtx); avformat_close_input(&formatCtx); avformat_close_input(&outFormatCtx); avcodec_close(decoderCtx); avcodec_close(encoderCtx); avformat_free_context(formatCtx); avformat_free_context(outFormatCtx); av_frame_free(&frame); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值