ffmpeg源码笔记-查找编解码器(一)

目录

1. 查找编码器的方法

2. 查找解码器的方法

3. 源码解读avcodec_find_encoder_by_name调用

 4. 源码解读avcodec_find_encoder调用流程

5. 源码解读avcodec_register作用

6. 总结


ffmpeg版本:ffmpeg-4.3.4

 AVCodec类型的结构体包含了对一个编码器底层实现的封装;定义如下:

typedef struct AVCodec {
    //编码器名,在编码器和解码器两个类别中分别具有唯一性;
    //如:libx264
    const char *name;

    //编码器实例的完整名称;
    //如:libx264 H.264/AVC/MPEG-4 AVC/MPEG-4 part 10
    const char *long_name;

    //当前编码器处理的媒体类型;
    enum AVMediaType type;

    //编码类型ID;
    enum AVCodecID id;

    //当前编码器所支持的能力;
    int capabilities;

    //支持的帧率
    const AVRational *supported_framerates; 
    //支持的图像像素格式;
    const enum AVPixelFormat *pix_fmts;    

    //支持的音频采样率 
    const int *supported_samplerates;  
    //支持的音频采样格式    
    const enum AVSampleFormat *sample_fmts; 
    //支持的声道布局
    const uint64_t *channel_layouts;      
    //支持的降分辨率解码;  
    uint8_t max_lowres;                     
    const AVClass *priv_class;  

    //支持的编码档次;      
    const AVProfile *profiles;              

    /*编码器实现的组件或封装名称,主要用于标识该编码器的外部实现者;
      当该字段为空时,该编码器有libavcodec库内部实现;当该字段不为空时,该编码器由硬件或操作系统
      等外部实现,并在字段保存AVCodec.nam的缩写;    
    */
    const char *wrapper_name;

    int priv_data_size;

    //实现链表
    struct AVCodec *next;
   
    int (*update_thread_context)(struct AVCodecContext *dst, const struct AVCodecContext *src);
  
    const AVCodecDefault *defaults;

    void (*init_static_data)(struct AVCodec *codec);

    int (*init)(struct AVCodecContext *);
    int (*encode_sub)(struct AVCodecContext *, uint8_t *buf, int buf_size,
                      const struct AVSubtitle *sub);
    int (*encode2)(struct AVCodecContext *avctx, struct AVPacket *avpkt,
                   const struct AVFrame *frame, int *got_packet_ptr);
    int (*decode)(struct AVCodecContext *, void *outdata, int *outdata_size, struct AVPacket *avpkt);
    int (*close)(struct AVCodecContext *);
   
    int (*send_frame)(struct AVCodecContext *avctx, const struct AVFrame *frame);
    int (*receive_packet)(struct AVCodecContext *avctx, struct AVPacket *avpkt);
   
    int (*receive_frame)(struct AVCodecContext *avctx, struct AVFrame *frame);
    void (*flush)(struct AVCodecContext *);
    
    int caps_internal;
    const char *bsfs;

    const struct AVCodecHWConfigInternal **hw_configs;

    const uint32_t *codec_tags;
} AVCodec;

1. 查找编码器的方法

//通过指定名称查找编码器实例; 
AVCodec *avcodec_find_encoder_by_name(const char *name);

//通过指定编码器ID查找编码器实例;
AVCodec *avcodec_find_encoder(enum AVCodecID id);

(a)其中的name为编解码器名,不知道如何填写可以看文件开头定义的很多extern结构体;

最常见的为 extern AVCodec ff_libx264_encoder; 

点击去后为:

AVCodec ff_libx264_encoder = {
    .name             = "libx264",
    .long_name        = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
    .type             = AVMEDIA_TYPE_VIDEO,
    .id               = AV_CODEC_ID_H264,
    .priv_data_size   = sizeof(X264Context),
    .init             = X264_init,
    .encode2          = X264_frame,
    .close            = X264_close,
    .capabilities     = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS |
                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
    .priv_class       = &x264_class,
    .defaults         = x264_defaults,
    .init_static_data = X264_init_static,
#if X264_BUILD >= 158
    .caps_internal    = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_INIT_THREADSAFE,
#else
    .caps_internal    = FF_CODEC_CAP_INIT_CLEANUP,
#endif
    .wrapper_name     = "libx264",
};

 可知对应的名字可以填 libx264;

而要支持x264需要configue时--enable-x264等字样去配置;

(b) enum AVCodecID id;对应codec_id.h;

2. 查找解码器的方法

//通过名字查找指定解码器
AVCodec *avcodec_find_decoder_by_name(const char *name);

//通过ID查找指定解码器
AVCodec *avcodec_find_decoder(enum AVCodecID id);

libavcodec/allcodecs.c中开头定义了很多支编解码结构体;如:

extern AVCodec ff_h264_decoder;
...

//音频编解码器
extern AVCodec ff_aac_encoder;
extern AVCodec ff_aac_decoder;
...

//PCM编解码器
extern AVCodec ff_pcm_alaw_encoder;
extern AVCodec ff_pcm_alaw_decoder;
...

//DPCM编解码器
extern AVCodec ff_derf_dpcm_decoder;
extern AVCodec ff_gremlin_dpcm_decoder;
...

//ADPCM编解码器
extern AVCodec ff_adpcm_4xm_decoder;
extern AVCodec ff_adpcm_adx_encoder;
...

//字幕编解码器
extern AVCodec ff_ssa_encoder;
extern AVCodec ff_ssa_decoder;
...

//外部库、
extern AVCodec ff_aac_at_encoder;
extern AVCodec ff_aac_at_decoder;
...
//优于libwebp
extern AVCodec ff_libx264_encoder;


//文本
extern AVCodec ff_bintext_decoder;
extern AVCodec ff_xbin_decoder;
extern AVCodec ff_idf_decoder;

//其他外部库
extern AVCodec ff_aac_mf_encoder;
extern AVCodec ff_ac3_mf_encoder;
...

3. 源码解读avcodec_find_encoder_by_name调用

 avcodec_find_encoder_by_name调用了find_codec_by_name;

//参数1,需要查找的编解码器名  参数2:回调函数指针,用于判断下面查找出来的AVCodec是否为编/解码器
static AVCodec *find_codec_by_name(const char *name, int (*x)(const AVCodec *))
{
    void *i = 0;
    const AVCodec *p;

    if (!name)
        return NULL;

    //av_codec_iterate相当于迭代器遍历所有的codec_list数组,返回值p为遍历到的编/解码器
    while ((p = av_codec_iterate(&i))) {
        if (!x(p)) //既不是编码器也不是解码器,滤过;
            continue;
        if (strcmp(name, p->name) == 0) //比较名字
            return (AVCodec*)p;
    }

    return NULL;//没有找到
}

其中调用的av_codec_iterate如下:

//传入的是上面的整形值i的地址,二级指针,可以改传入的地址值;
const AVCodec *av_codec_iterate(void **opaque)
{
    uintptr_t i = (uintptr_t)*opaque;
    const AVCodec *c = codec_list[i];

    ff_thread_once(&av_codec_static_init, av_codec_init_static);

    if (c) //若codec_list不为NULL,继续下一个
        *opaque = (void*)(i + 1); //相当于i++

    return c; //返回AVCodec,可能为NULL;
}

其中又调用ff_thread_once;

 4. 源码解读avcodec_find_encoder调用流程

avcodec_find_encoder调用了find_codec

static AVCodec *find_codec(enum AVCodecID id, int (*x)(const AVCodec *))
{
    const AVCodec *p, *experimental = NULL;
    void *i = 0;

    //看名字是重映射,实际啥都没干;
    id = remap_deprecated_codec_id(id);

    while ((p = av_codec_iterate(&i))) { //通过codec_list数组获取AVCodec
        if (!x(p)) //判断是否为编/解码器
            continue;
        if (p->id == id) { //比较ID

            //判断编/解码器是否是实验性的,优先选择非实验性的;
            if (p->capabilities & AV_CODEC_CAP_EXPERIMENTAL && !experimental) {
                experimental = p;
            } else
                return (AVCodec*)p;
        }
    }

    return (AVCodec*)experimental;
}

5. 源码解读avcodec_register作用

调用了 ff_thread_once(&av_codec_next_init, av_codec_init_next);

static AVOnce av_codec_next_init = AV_ONCE_INIT; //标识只初始化一次

static void av_codec_init_next(void)
{
    AVCodec *prev = NULL, *p;
    void *i = 0;
    while ((p = (AVCodec*)av_codec_iterate(&i))) {
        if (prev)
            prev->next = p; //把所有编/解码器连接起来
        prev = p;
    }
}

6. 总结

avcodec_find_encoder_by_name查找编码器可以使开发者对系统的控制性更强,但整体兼容性较弱,因为一旦当前使用的FFmpeg不支持指定的编码器,则整个流程将以错误结束;

若使用avcodec_find_encoder,则调用者无法指定特定的编码器进行编码,只能由系统根据优先级自动选择,因此兼容性更好; 

实际开发时根据需求选择;

补充说明:

#if CONFIG_OSSFUZZ
AVCodec * codec_list[] = {
    NULL,
    NULL,
    NULL
};
#else
#include "libavcodec/codec_list.c"
#endif

源码中有codec_list数组,其内容在#include "libavcodec/codec_list.c"当中,但是源码中并没有该文件; 而在编译后的源码中有此文件;所以该文件是编译时根据版本等来生成的;其内容部分如下:

static const AVCodec * const codec_list[] = {
    &ff_a64multi_encoder,
    &ff_a64multi5_encoder,
    &ff_alias_pix_encoder,
    &ff_amv_encoder,
    &ff_asv1_encoder,
    &ff_asv2_encoder,
    &ff_avrp_encoder,
    &ff_avui_encoder,
    ...
    ...
};

下一篇: https://blog.csdn.net/qq_39048131/article/details/125707330

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天未及海宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值