前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ffmpeg4.x为什么不再需要调用av_register_all呢

ffmpeg4.x为什么不再需要调用av_register_all呢

原创
作者头像
客行天涯
修改2021-12-04 18:31:54
6.2K0
修改2021-12-04 18:31:54
举报
文章被收录于专栏:音视频处理音视频处理

旧版本的ffmpeg程序, 程序开头处, 一般总是av_register_all. 4.x之后, 该函数已经废弃,不需要调用了. 我们以ffmpeg4.4的为例. 首先看看官方的版本特性(doc\APIchanges)变更说明:

代码语言:javascript
复制
2018-02-06 - 0694d87024 - lavf 58.9.100 - avformat.h
  Deprecate use of av_register_input_format(), av_register_output_format(),
  av_register_all(), av_iformat_next(), av_oformat_next().
  Add av_demuxer_iterate(), and av_muxer_iterate().

可以看到av_register_all已经被标志为废弃.

那么新版ffmpeg是怎么做到可以省去调用这个函数的呢.

先从av_register_all的历史上曾经的作用说起. 这个函数就是注册各种 复用器(muxer),解复用器(demuxer),编码器(encoder),解码器(decoder)等, 将各个类别串成一个链表. 该函数的分析, 可以参考雷博士的文章: ffmpeg 源代码简单分析 : av_register_all() .

我们以复用器为例, 新版中,所有的复用器被组织为一个全局静态数组. 位于 libavformat\format.c, 如下:

代码语言:javascript
复制
libavformat\format.c:

 static const AVOutputFormat * const muxer_list[] = {
    &ff_a64_muxer,
    &ff_ac3_muxer,
    &ff_adts_muxer,
    &ff_adx_muxer,
    &ff_aiff_muxer,
    &ff_alp_muxer,
    &ff_amr_muxer,
    ......,
    }

有的同学会发现自己的源代码并没有libavformat\muxer_list.c文件. 这是因为此文件是执行configure命令时, 根据实际配置生成的. 如下,执行完configure命令后,会自动生成这个文件.

代码语言:javascript
复制
configure:

# generate the lists of enabled components
print_enabled_components(){
    file=$1
    struct_name=$2
    name=$3
    shift 3
    echo "static const $struct_name * const $name[] = {" > $TMPH
    for c in $*; do
        if enabled $c; then
            case $name in
                filter_list)
                    eval c=\$full_filter_name_${c%_filter}
                ;;
                indev_list)
                    c=${c%_indev}_demuxer
                ;;
                outdev_list)
                    c=${c%_outdev}_muxer
                ;;
            esac
            printf "    &ff_%s,\n" $c >> $TMPH
        fi
    done
    if [ "$name" = "filter_list" ]; then
        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
            printf "    &ff_%s,\n" $c >> $TMPH
        done
    fi
    echo "    NULL };" >> $TMPH
    cp_if_changed $TMPH $file
}
......
print_enabled_components libavformat/muxer_list.c AVOutputFormat muxer_list $MUXER_LIST
......

为了兼容老代码,新版的av_register_all函数还在保留下来了.

代码语言:javascript
复制
libavformat\allformats.c:

void av_register_all(void)
{
    ff_thread_once(&av_format_next_init, av_format_init_next);
}

static void av_format_init_next(void)
{
......

    for (int i = 0; (out = (AVOutputFormat*)muxer_list[i]); i++) {
        if (prevout)
            prevout->next = out;
        prevout = out;
    }

......
}

可以看到, 调用av_register_all可以把所有的复用器通过next指针串联成一个链表. 那用next串起来的链条还有用吗, next指针还有作用吗, 我们不调用av_register_all的话, 又是哪个逻辑将所有的复用器串成一个链表的呢?

要解开这些谜团, 要从复用器的遍历入手. 老版本ffmpeg的复用器的遍历是通过av_oformat_next实现的. 新版ffmpeg这个函数也是废弃了, 遍历复用器用 av_muxer_iterate.

代码语言:javascript
复制
libavformat\allformats.c:

const AVOutputFormat *av_muxer_iterate(void **opaque)
{
    static const uintptr_t size = sizeof(muxer_list)/sizeof(muxer_list[0]) - 1;
    uintptr_t i = (uintptr_t)*opaque;
    const AVOutputFormat *f = NULL;

    if (i < size) {
        f = muxer_list[i];
    } else if (outdev_list) {
        f = outdev_list[i - size];
    }

    if (f)
        *opaque = (void*)(i + 1);
    return f;
}

可以看到, av_muxer_iterate的遍历并不依赖next指针. 而是通过数组下标来完成的. 我们可以通过下面代码遍历所有的复用器.

代码语言:javascript
复制
#include <stdio.h>
#include "libavformat/avformat.h"

int main()
{
  void *ofmt_opaque = NULL;
  const AVOutputFormat *ofmt = NULL;
  while ((ofmt = av_muxer_iterate(&ofmt_opaque)))
  {
    printf("short name:%-30s  long name:%-50s next point:0x%llx \n",ofmt->name,ofmt->long_name,ofmt->next);
  }
}

输出如下:

代码语言:javascript
复制
short name:a64                             long name:a64 - video for Commodore 64                       next point:0x0
short name:ac3                             long name:raw AC-3                                           next point:0x0
short name:adts                            long name:ADTS AAC (Advanced Audio Coding)                   next point:0x0
short name:adx                             long name:CRI ADX                                            next point:0x0
short name:aiff                            long name:Audio IFF                                          next point:0x0
short name:alp                             long name:LEGO Racers ALP                                    next point:0x0
short name:amr                             long name:3GPP AMR                                           next point:0x0
short name:amv                             long name:AMV                                                next point:0x0

......

我专门把next指针的值打印了下, 为的就是说明遍历时, 实际不依赖next. next是个空指针.

那还有个疑问, 老版本的遍历函数av_oformat_next还可以用吗. 答案是可以的. 而且在不需要调用av_register_all的情况下就可以用.

代码语言:javascript
复制
libavformat\allformats.c

AVOutputFormat *av_oformat_next(const AVOutputFormat *f)
{
    ff_thread_once(&av_format_next_init, av_format_init_next);

    if (f)
......
        return f->next;
......        
    else {
        void *opaque = NULL;
        return (AVOutputFormat *)av_muxer_iterate(&opaque);
    }
}

大家应该注意到av_oformat_next函数的第一行代码了, 第一行代码和av_register_all的逻辑是一样的. 就是说, 如果使用老的遍历方法的话, 会再次通过next指针将所有复用器串联成一个链表.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com