avformat_seek_file函数介绍

在做音视频数据分析的时候,经常会遇到这样的需求,每隔5分钟抽取一帧数据进行分析。

在做播放器开发的时候,也会遇到这种情况,就是拖动进度条跳转到某个位置进行播放。

如果直接用 av_read_frame() 不断读数据,读到第 5 分钟的 AVPacket 才开始处理,其他读出来的 AVPacket 丢弃,这样做会带来非常大的磁盘IO。

其实上面两种场景,都可以用同一个函数解决,那就是 avformat_seek_file(),这个函数类似于 Linux 的 lseek() ,设置文件的读取位置

只不过 avformat_seek_file() 是用于音视频文件的。


avformat_seek_file() 函数的定义如下:

int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);

参数解释如下:

1,AVFormatContext *s,已经打开的容器示例。

2,int stream_index,流索引,但是只有在 flags 包含 AVSEEK_FLAG_FRAME 的时候才是 设置某个流的读取位置。其他情况都只是把这个流的 time_base (时间基)作为参考。

3,int64_t min_ts,跳转到的最小的时间,但是这个变量不一定是时间单位,也有可能是字节单位,也可能是帧数单位(第几帧)。

4,int64_t ts,要跳转到的读取位置,单位同上。

5,int64_t max_ts,跳转到的最大的时间,单位同上,通常填 INT64_MAX 即可。

6,int flags,跳转的方式,有 4 个 flags,如下:

  • AVSEEK_FLAG_BYTE,按字节大小进行跳转。
  • AVSEEK_FLAG_FRAME,按帧数大小进行跳转。
  • AVSEEK_FLAG_ANY,可以跳转到非关键帧的读取位置,但是解码会出现马赛克。
  • AVSEEK_FLAG_BACKWARD,往 ts 的后面找关键帧,默认是往 ts 的前面找关键帧。

avformat_seek_file() 函数默认是把文件的读取位置,设置到离 ts 参数最近的关键帧的地方。

而且默认情况,是容器里面所有流的读取位置都会被设置,包括 音频流,视频流,字幕流。

只要流的 discard 属性小于 AVDISCARD_ALL 就会被设置。

AVStream.discard < AVDISCARD_ALL

min_ts 跟 max_ts 变量有一些设置的技巧。

如果是快进的时候min_ts 可以设置得比 当前位置 大一点,例如加 2。 而 max_ts 可以填 INT64_MAX

min_ts = 当前位置 + 2
max_ts = INT64_MAX

+2 是为了防止某些情况,avformat_seek_file() 会把读取位置往后挪一点

如果是后退的时候min_ts 可以填 INT64_MIN,max_ts 可以设置得比 当前位置 小一点,例如减 2。

min_ts = INT64_MIN
max_ts = 当前位置 - 2

-2 是为了防止某些情况,avformat_seek_file() 会把读取位置往前挪一点


当 flags 为 0 的时候,默认情况,是按时间来 seek 的,而时间基是根据 stream_index 来确定的。

如果 stream_index 为 -1 ,那 ts 的时间基就是 AV_TIME_BASE

如果stream_index 不等于 -1 ,那 ts 的时间基就是 stream_index 对应的流的时间基。

这种情况,avformat_seek_file() 会导致容器里面所有流的读取位置都发生跳转,包括音频流,视频流,字幕流。


当 flags 包含 AVSEEK_FLAG_BYTEts 参数就是字节大小,代表 avformat_seek_file() 会把读取位置设置到第几个字节。用 av_read_frame() 读出来的 pkt 里面有一个字段 pos,代表当前读取的字节位置。可以用pkt->pos 辅助设置 ts 参数,

AVSEEK_FLAG_BYTE 是否是对所有流都生效,我后面测试一下再补充。


当 flags 包含 AVSEEK_FLAG_FRAMEts 参数就是帧数大小,代表 avformat_seek_file() 会把读取位置设置到第几帧。这时候 stream_index 可以指定只设置某个流的读取位置,如果 stream_index 为 -1 ,代表设置所有的流。


当 flags 包含 AVSEEK_FLAG_ANY,那就代表 seek 可以跳转到非关键帧的位置,但是非关键帧解码会出现马赛克。如果不设置 AVSEEK_FLAG_ANY, 默认是跳转到离 ts 最近的关键帧的位置的。


当 flags 包含 AVSEEK_FLAG_BACKWARD,代表 avformat_seek_file() 在查找里 ts 最近的关键帧的时候,会往 ts 的后面找,默认是往 ts 的前面找关键帧。

提醒:AVSEEK_FLAG_BYTE ,AVSEEK_FLAG_FRAMEAVSEEK_FLAG_ANY 这 3 种方式,有些封装格式是不支持的。


下面通过一个例子来演示 avformat_seek_file() 函数的用法。代码下载地址:GitHb,编译环境是 Qt 5.15.2 跟 MSVC2019_64bit 。

avformat_seek_file函数介绍_第1张图片

avformat_seek_file函数介绍_第2张图片

运行结果如下:

avformat_seek_file函数介绍_第3张图片

可以看到,跳转之后,后面 av_read_frame() 读取到的 AVPacket 的 pts 跟 pos 都有很大的偏移了。

avformat_seek_file() 函数介绍完毕。

扩展知识:avformat_seek_file() 对应的旧版函数是 av_seek_frame()


推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:

Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

你可能感兴趣的:(FFmpeg实战之路,数据库,java,服务器)