看这篇文章你需要对ffmpeg已经有一定的使用经验,知道如何read帧,解帧,或者write等。
ffmpeg内部使用跑结构体表函数指针的方式,实现了类似C++的多态性,,我们来简析一下。
【注册表】
我们使用ffmpeg,首先要执行av_register_all,这个玩意儿把全局的解码器、编码器等结构体注册到一些全局的对象表里,以便后面跑表调用。
注册的类型有:解复用器、重复用器、解码器、编码器、包解析器、BitStreamFilter(位流处理器)。
【解封装(解复用器)】
解复用的工作主要有:
1、判断流格式是否支持(正确)。
2、提取流的头信息,比如视频的长宽、音频的声道样本数等。
3、读取压缩的数据流,用于被解码器解码。
这3个步骤,就是我们使用的抽象化的3个函数:
avformat_open_input->av_find_stream_info->av_read_frame。
我们来看一下ffmpeg中解复用器的核心结构(以FLAC音频为例):
而avformat_open_input的工作很简单,其通过avio从来源读取字节流头,然后跑这个全局对象表,一个个去执行read_probe函数,如果哪个read_probe认了就行了。
我们看flac的read_probe实现:
如果全部的probe函数都不认,则open失败。
如果有一个probe认了,本来按照理论上来说,read_header应该是在av_find_stream_info执行的,但是ffmpeg还是将其放在了avformat_open_input,其实效果也一样,我们来看看flac的read_header:
ffmpeg内部使用跑结构体表函数指针的方式,实现了类似C++的多态性,,我们来简析一下。
【注册表】
我们使用ffmpeg,首先要执行av_register_all,这个玩意儿把全局的解码器、编码器等结构体注册到一些全局的对象表里,以便后面跑表调用。
注册的类型有:解复用器、重复用器、解码器、编码器、包解析器、BitStreamFilter(位流处理器)。
【解封装(解复用器)】
解复用的工作主要有:
1、判断流格式是否支持(正确)。
2、提取流的头信息,比如视频的长宽、音频的声道样本数等。
3、读取压缩的数据流,用于被解码器解码。
这3个步骤,就是我们使用的抽象化的3个函数:
avformat_open_input->av_find_stream_info->av_read_frame。
我们来看一下ffmpeg中解复用器的核心结构(以FLAC音频为例):
AVInputFormat ff_flac_demuxer = {
.name = "flac",
.long_name = NULL_IF_CONFIG_SMALL("raw FLAC"),
.read_probe = flac_probe,
.read_header = flac_read_header,
.read_packet = ff_raw_read_partial_packet,
.flags = AVFMT_GENERIC_INDEX,
.extensions = "flac",
.raw_codec_id = AV_CODEC_ID_FLAC,
};
在我们执行av_register_all后,这个flac的AVInputFormat结构体指针会被注册到一个全局对象表里。
而avformat_open_input的工作很简单,其通过avio从来源读取字节流头,然后跑这个全局对象表,一个个去执行read_probe函数,如果哪个read_probe认了就行了。
我们看flac的read_probe实现:
static int flac_probe(AVProbeData *p)
{
if (p->buf_size < 4 || memcmp(p->buf, "fLaC", 4))
return 0;
return AVPROBE_SCORE_EXTENSION;
}
这个probe仅仅只是判断了头是不是fLaC这个字符,是就是ok了,我认了,不是,让avformat_open_input继续匹配文件扩展名(如果有的话)。
如果全部的probe函数都不认,则open失败。
如果有一个probe认了,本来按照理论上来说,read_header应该是在av_find_stream_info执行的,但是ffmpeg还是将其放在了avformat_open_input,其实效果也一样,我们来看看flac的read_header:
static int flac_read_header(AVFormatContext *s)
{
int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0;
uint8_t header[4];
uint8_t *buffer=NULL;
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_FLAC;
st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
/* the parameters will be extracted from the compressed bitstream */
/* if fLaC marker is not found, assume there is no header */
if (avio_rl32(s->pb) !=