摘要:本文主要描述了FFmpeg中用于打开编解码器接口av_read_frame
的具体调用流程,详细描述了该接口被调用时所作的具体工作。
关键字:ffmpeg
、av_read_frame
读者须知:读者需要了解FFmpeg的基本使用流程,以及一些FFmpeg的基本常识,了解FFmpegIO相关的内容,以及大致的解码流程。
使用FFmpeg解码视频时需要主动打开解码器获得解码器相关的Context,然后直接调用av_read_frame
读取AVPacket
码流数据送到FFmpeg中进行解码即可。本文主要通过源码了解FFmpeg搜索和打开解码器的基本实现原理和FFmpeg具体的解码流程。
av_read_frame
用于从已经打开的文件中读取未经过解码的码流AVPacket
,对于视频帧就是一帧的压缩帧,对于音频帧如果音频是固定大小的话则可以是多帧,否则也是一帧。av_read_frame
内部读取码流时调用avpriv_packet_list_get
和av_read_frame_internal
。
avpriv_packet_list_get
比较简单就是从当前媒体的PackList中取出一帧。av_read_frame
的函数实现比较长,其大致流程为:
ff_read_packet
读取一帧码流;parse_packet
刷新解析器,否则继续到步骤3; ff_read_packet
会先检查缓冲区是否有帧没有的话就会调用s->iformat->read_packet
即对应个是的解析码流的函数进行解码。每个FFmpeg支持的格式都有一个FormatContext描述对应格式的信息以及解析对应格式的函数指针,比如下面是mov的格式描述:
static const AVClass mov_class = {
.class_name = "mov,mp4,m4a,3gp,3g2,mj2",
.item_name = av_default_item_name,
.option = mov_options,
.version = LIBAVUTIL_VERSION_INT,
};
const AVInputFormat ff_mov_demuxer = {
.name = "mov,mp4,m4a,3gp,3g2,mj2",
.long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
.priv_class = &mov_class,
.priv_data_size = sizeof(MOVContext),
.extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v",
.flags_internal = FF_FMT_INIT_CLEANUP,
.read_probe = mov_probe,
.read_header = mov_read_header,
.read_packet = mov_read_packet,
.read_close = mov_read_close,
.read_seek = mov_read_seek,
.flags = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS,
};
从上面的过程能够看出av_read_frame
完全是同步的操作,可能是比较耗时的,因为如果一直拿不到帧就会一直遍历当前媒体文件的buffer,因此一般建议开一个线程读取Packet。