【FFmpeg】如何通过字符串到对应的封装器,以flv为例

1、简述

使用avformat_alloc_output_context2创建封装器上下文AVFormatContext时,只需将封装器的名字传递给形参format_name,就可以获取对应的封装器。这篇博客就是追寻avformat_alloc_output_context2的调用关系,探明原因。
函数原型如下:

int avformat_alloc_output_context2(AVFormatContext **ctx, ff_const59 AVOutputFormat *oformat,
                                   const char *format_name, const char *filename);
2、FFmpeg对flv封装格式的支持

flv封装格式的实现源码在libavformat/flvenc.c中,如果将 AVClass 和 AVOutputFormat结构体看作调用封装的抽象类或者接口类,要实现对flv的支持,就要实现这些接口。flvenc.c中实现了这些接口,并初始化了两个结构体 AVClass flv_muxer_classAVOutputFormat ff_flv_muxer,源码下

static const AVClass flv_muxer_class = {
    .class_name = "flv muxer",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

AVOutputFormat ff_flv_muxer = {
    .name           = "flv",
    .long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
    .mime_type      = "video/x-flv",
    .extensions     = "flv",
    .priv_data_size = sizeof(FLVContext),
    .audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,
    .video_codec    = AV_CODEC_ID_FLV1,
    .init           = flv_init,
    .write_header   = flv_write_header,
    .write_packet   = flv_write_packet,
    .write_trailer  = flv_write_trailer,
    .check_bitstream= flv_check_bitstream,
    .codec_tag      = (const AVCodecTag* const []) {
                          flv_video_codec_ids, flv_audio_codec_ids, 0
                      },
    .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
                      AVFMT_TS_NONSTRICT,
    .priv_class     = &flv_muxer_class,
};

当加载libavformat库时,flv相关功能已经初始化好,静等调用。

3、通过字符串flv找到对应的封装器

将包含封装器的封装器上下文AVFormatContext和字符串flv传递给函数avformat_alloc_output_context2,该函数中通过调用av_guess_format来获取封装器AVOutputFormat,函数原型如下:

AVOutputFormat *av_guess_format(const char *short_name,
                                const char *filename,
                                const char *mime_type);

通过参数可以判读,获取封装器有三种方法,分别是:

  1. 使用封装器名字“short_name”和AVOutputFormat.name作比较,本例中是“flv”;
  2. 使用文件名“filename”的后缀AVOutputFormat.extensions作比较,本例中是“flv”;
  3. 使用媒体类型“mime_type”和AVOutputFormat.mime_type作比较,本例中是"video/x-flv"

正常情况下只需传递一个参数,其它两个传空NULL;如果同时传递三个,优先级为:short_name > mime_type > filename。

当然了,在作比较之前,要获取封装器列表,依次循环比较。
av_guess_format通过循环调用av_muxer_iterate来获取封装器,每调用时,传入一个索引,返回这个索引对应的封装器。索引从0开始,索引的累加在av_muxer_iterate内部实现。
av_muxer_iterate中调用一个封装器指针数组muxer_list,并根据传入的索引,返回封装器数组中指定的封装器指针AVOutputFormat*
封装器指针数组muxer_list在libavformat/muxer_list.c中初始化,其中就包含本例中用到的flv封装器ff_flv_muxer的指针。
找到所需封装器指针后,最终它赋值给AVFormatContext.oformat

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章