0
点赞
收藏
分享

微信扫一扫

【FFmpeg】FFmpeg 函数简介 ④ ( FFmpeg 组件注册简介 | 组件 静态注册 | 组件 动态注册 | 编解码器 动态注册 分析 | 复用器 / 解复用器 动态注册 分析 )



文章目录

  • 一、FFmpeg 组件注册
  • 1、FFmpeg 组件简介
  • 2、FFmpeg 组件注册简介
  • 3、组件静态注册
  • 4、组件动态注册
  • 二、FFmpeg 组件动态注册分析
  • 1、组件动态注册示例 - 编解码器动态注册
  • 2、组件动态注册示例 - 复用器 / 解复用器 动态注册




FFmpeg 4.0 版本源码地址 :

  • GitHub : https://github.com/FFmpeg/FFmpeg/tree/release/4.0
  • GitCode : https://gitcode.com/gh_mirrors/ff/FFmpeg/tree/release/4.0





一、FFmpeg 组件注册



1、FFmpeg 组件简介



FFmpeg 在使用时 , 需要 提前将需要用到的组件进行注册

  • 复用器 : 将 编码压缩后的 媒体数据 打包成特定格式 , 如 MP4、MKV 容器格式 ;
  • 解复用器 : 从 打包格式 中 提取出 编码后的数据 , 此时该数据未解码 , 不能播放 ;
  • 编解码器 Codecs : 视频编解码器 ( 如 : H.264、MPEG-4 ) , 音频编解码器 ( 如 : AAC、MP3 ) 等 ;
  • 硬件加速器
  • 解析器
  • 协议
  • 滤镜


2、FFmpeg 组件注册简介



注册的过程是 将上述 组件 对应的结构体 , 注册到 各自对应的 全局对象链表 中 ;

注册的 流程 中 调用的 相关函数 如下图所示 :

【FFmpeg】FFmpeg 函数简介 ④ ( FFmpeg 组件注册简介 | 组件 静态注册 | 组件 动态注册 | 编解码器 动态注册 分析 | 复用器 / 解复用器 动态注册 分析 )_静态注册



FFmpeg 组件注册方式有两种 , 分别是 静态注册 和 动态注册 ;

  • 在 4.x 及之前的版本 , 使用 静态注册 ;
  • 在 4.x 及之后的版本 , 使用 动态注册 ;
  • 4.x 版本既可以使用 静态注册 , 也可以进行 动态注册 ;


3、组件静态注册



在 4.x 及之前的版本 , 使用 静态注册 的方法 , 需要 显式 调用 av_register_all 注册函数 来初始化 复用器、解复用器、编码器、解码器等 组件 ;

FFmpeg 4.x 版本 既可以使用 静态注册 , 又可以使用 动态注册 ;

这里特别注意 , 在 FFmpeg 4.x 版本中 , av_register_all 函数 和 avcodec_register_all 函数仍然存在 , 但它们已不再是必须的 , 即使不调用这两个函数 , 也能自动注册组件 , 这些注册函数在该版本中 仅起到兼容旧代码的作用 ;



可参考 【FFmpeg】FFmpeg 函数简介 ① ( 注册和初始化函数 | avformat_network_init 函数 | avdevice_register_all 函数 ) 博客 中详细介绍了 avdevice_register_all 函数 ;



代码示例 :

#include <libavformat/avformat.h>

int main() {
    // 注册所有组件
    av_register_all();
    return 0;
}



4、组件动态注册



在 FFmpeg 5.x 及之后的版本 , 不再需要 显式 调用 av_register_all 函数 进行注册 , FFmpeg 会自动注册组件 , 所有组件在 首次使用 时 自动注册 ;

av_register_all 函数 和 avcodec_register_all 函数 在 5.x 版本 被彻底移除 , 再调用就会报错 ;

FFmpeg 4.x 版本 既可以使用 静态注册 , 又可以使用 动态注册 ;

实际开发中 , 推荐在 4.x 版本中 , 就可以不再使用 手动注册组件 代码 , 推荐不进行 组件 的 静态注册 ;






二、FFmpeg 组件动态注册分析



1、组件动态注册示例 - 编解码器动态注册



编译 FFmpeg 源码的时候 , 执行 configure 命令时 , 会生成要注册的组件 ;

参考 【FFmpeg】编译不同平台的 FFmpeg 源码 ( 本地编译 与 交叉编译 | FFmpeg 指定目标平台的编译配置参数 | 配置目标 CPU 架构 | 配置目标操作系统 ) 博客 , 其中

其中在编译前要 执行 configure 脚本生成 Makefile 构建脚本 ,

# 执行 configure 脚本生成 Makefile 构建脚本
./configure \
--prefix=$PREFIX \
--enable-small \		// 启用精简模式,减小生成的库的体积
--disable-programs \	// 禁用生成可执行程序
--disable-avdevice \	// 禁用音视频设备支持
--disable-encoders \	// 禁用编码器
--disable-muxers \		// 禁用复用器
--disable-filters \		// 禁用滤镜
--enable-cross-compile \	// 启用交叉编译
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \	// 指定交叉编译工具链的前缀
--sysroot=$NDK_ROOT/platforms/android-21/arch-arm \		// 指定 Android NDK 中 Android 21 版
--extra-cflags="$FLAGS" \	// 额外的编译选项 , 用于设定 gcc 编译器的其它编译选项
--arch=arm \				// 指定目标体系结构为 ARM
--target-os=android			// 指定目标操作系统为 Android

执行 configure 脚本命令时 , 还会 生成 codec_list.c 文件 , 该文件中只包含一个 static const AVCodec * const codec_list[] 数组 , 其中 列出了 FFmpeg 编译时所选择支持的编解码器 ,

下面是 codec_list.c 文件的示例 :

#include "libavcodec/avcodec.h"

static const AVCodec * const codec_list[] = {
    &ff_h264_decoder,
    &ff_h264_encoder,
    &ff_mpeg4_decoder,
    &ff_mpeg4_encoder,
    &ff_aac_decoder,
    &ff_aac_encoder,
    &ff_mp3_decoder,
    &ff_vp9_decoder,
    &ff_flac_encoder,
    &ff_vorbis_decoder,
    ...
    NULL
};

注意 : FFmpeg 源码中是没有 libavcodec/codec_list.c文件的 , 这是再调用 configure 命令后 , 根据用户配置的 configure 参数生成的源码 ;

上述代码中 , 每个 &ff_* 数组成员 , 都代表 FFmpeg 支持的一个具体编解码器 ;



codec_list 数组 为 FFmpeg 提供了一种方式来遍历所有支持的编解码器 , 可以在运行时 获取 并 识别哪些编解码器被编译进了当前的 FFmpeg 库 ;



codec_list.c 文件 中的 static const AVCodec * const codec_list[] 数组在 libavcodec/allcodecs.c 源码中进行了使用 , 下面是 libavcodec/allcodecs.c 源码中截取的内容 , 并添加了中文注释 :

#include "libavcodec/codec_list.c" // 包含 codec_list.c 文件,它定义了编解码器列表 codec_list

// 静态变量 av_codec_static_init,用于确保 av_codec_init_static 函数只初始化一次
static AVOnce av_codec_static_init = AV_ONCE_INIT;

// 静态初始化函数,用于遍历 codec_list 中的编解码器,并执行各个编解码器的静态数据初始化函数(如果存在)
static void av_codec_init_static(void)
{
    for (int i = 0; codec_list[i]; i++) { // 遍历 codec_list 数组中的编解码器,直到列表末尾(NULL)为止
        if (codec_list[i]->init_static_data) // 如果当前编解码器有静态数据初始化函数
            codec_list[i]->init_static_data((AVCodec*)codec_list[i]); // 调用静态数据初始化函数
    }
}

// 编解码器迭代函数,用于逐个遍历和返回 codec_list 中的编解码器
const AVCodec *av_codec_iterate(void **opaque)
{
    uintptr_t i = (uintptr_t)*opaque; // 获取当前的迭代位置(由 opaque 指针提供)
    const AVCodec *c = codec_list[i]; // 从 codec_list 数组中获取当前位置的编解码器

    // 确保 av_codec_init_static 只会在首次调用时执行一次
    ff_thread_once(&av_codec_static_init, av_codec_init_static);

    if (c) // 如果当前位置的编解码器存在
        *opaque = (void*)(i + 1); // 更新迭代位置指针,指向下一个编解码器

    return c; // 返回当前的编解码器指针
}

【FFmpeg】FFmpeg 函数简介 ④ ( FFmpeg 组件注册简介 | 组件 静态注册 | 组件 动态注册 | 编解码器 动态注册 分析 | 复用器 / 解复用器 动态注册 分析 )_动态注册_02

参考源码路径 : https://gitcode.com/gh_mirrors/ff/FFmpeg/blob/release/4.0/libavcodec/allcodecs.c



2、组件动态注册示例 - 复用器 / 解复用器 动态注册



执行 ./configure 命令时 , 会生成 libavformat/muxer_list.c 文件 , 其中只有一个 static const AVOutputFormat * const muxer_list[] 数组 , 列举了支持的 复用器 ,

该数组根据用户的 configure 配置生成 ,

其中列举了 当前编译的 FFmpeg 支持的容器格式 , 如果支持全部格式 , FFmpeg 编译后将会很大 , 有时为了尽可能精简 FFmpeg , 会根据需求只支持一种或少数的容器格式 ;

源码内容如下 :

#include "libavformat/avformat.h"

static const AVOutputFormat * const muxer_list[] = {
    &ff_mp4_muxer,
    &ff_mkv_muxer,
    &ff_flv_muxer,
    &ff_mov_muxer,
    &ff_webm_muxer,
    &ff_avi_muxer,
    &ff_mpegts_muxer,
    &ff_mp3_muxer,
    &ff_wav_muxer,
    ...
    NULL
};

同理 , 执行 ./configure 命令时 , 会生成 libavformat/demuxer_list.c 文件 , 源码内容如下 :

#include "libavformat/avformat.h"

static const AVInputFormat * const demuxer_list[] = {
    &ff_mp4_demuxer,
    &ff_mkv_demuxer,
    &ff_flv_demuxer,
    &ff_mov_demuxer,
    &ff_avi_demuxer,
    &ff_mpegts_demuxer,
    &ff_mp3_demuxer,
    &ff_wav_demuxer,
    &ff_ogg_demuxer,
    ...
    NULL
};

上述 编译时 生成的 复用器 和 解复用器 列表 , 在 FFmpeg 启动时 , 会自动注册 , 不需要用户再额外手动进行注册 ;


举报

相关推荐

0 条评论