【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
ffmpeg4.2.2 av_register_all()的分析
1. av_register_all()介绍
av_register_all()是所有基于ffmpeg的应用程序中第一个被调用的函数, 只有调用了该函数,才能正常使用ffmpeg的各项功能, 如复用/解复用器,编码/解码器, 以及各种协议等等.时过境迁, 现在的av_register_all()是ffmpeg的过时函数(deprecated). 当我们使用的时候, 编译器会提示如下的警告: (尽管这个函数已经过时了, 但是我们依旧可以正常使用, 只是多了一个警告)
warning: 'void av_register_all()' is deprecated [-Wdeprecated-declarations]
av_register_all();
^
如何解决呢? 我们接着来看.
2. av_register_all()的函数声明
/**
* 初始化libavformat和注册所有的复用/解复用器和协议.
* 如果你没有调用这个函数, 那么你可以指定选择你想要支持
* 格式. 具体看如下的两个函数
*
* @see av_register_input_format()
* @see av_register_output_format()
*/
attribute_deprecated
void av_register_all(void);
attribute_deprecated
void av_register_input_format(AVInputFormat *format);
attribute_deprecated
void av_register_output_format(AVOutputFormat *format);
在版本稍早的ffmpeg中, 其实av_register_input_format()和av_register_output_format()才是真正执行注册的函数(av_register_all()里面也是调用了这两个函数), 只不过av_register_all()函数把所有能注册的都注册上去了, 而av_register_input_format()和av_register_output_format()函数可以让我们选择性地把程序用到的才注册上去.
但是如上所示, av_register_input_format()和av_register_output_format()这两个函数也过时了!!! 那么, ffmpeg取代的手段是什么呢? 我们接着来看.
3. av_register_all()的源码分析
目录: ffmpeg-4.2.2\libavformat\allformats.cvoid av_register_all(void)
{
ff_thread_once(&av_format_next_init, av_format_init_next);
}
void av_register_input_format(AVInputFormat *format)
{
ff_thread_once(&av_format_next_init, av_format_init_next);
}
void av_register_output_format(AVOutputFormat *format)
{
ff_thread_once(&av_format_next_init, av_format_init_next);
}
由上可见, 新版本的ffmpeg的三个函数是等效的! 也就是说, ffmpeg的维护者觉得开头使用av_register_all()注册所有需要的复用/解复用器是可行的(或者说是妥协? 还是av_register_all()函数的内部实现被改了?).
#if HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS2THREADS // 支持线程的平台
#define AVOnce pthread_once_t
#define AV_ONCE_INIT PTHREAD_ONCE_INIT
#define ff_thread_once(control, routine) pthread_once(control, routine)
....
#else
#define AVOnce char
#define AV_ONCE_INIT 0
static inline int ff_thread_once(char *control, void (*routine)(void))
{
if (!*control) {
routine();
*control = 1;
}
return 0;
}
#endif
static AVOnce av_format_next_init = AV_ONCE_INIT;
ff_thread_once(&av_format_next_init, av_format_init_next)
上面的宏HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS2THREADS 指的是那些支持线程的平台跑av_register_all()的流程, 我看了一下, 相当复杂; 相对于不支持线程的分支来说, 我们就可以很容易看明白.(两者想要达成的目的是一样的) 也就是保证av_register_all()里面的注册流程只会被跑一遍! 也就是av_format_init_next()只会被调用一次!
av_format_init_next()所做的事情如下:
static void av_format_init_next(void)
{
AVOutputFormat *prevout = NULL, *out;
AVInputFormat *previn = NULL, *in;
ff_mutex_lock(&avpriv_register_devices_mutex);
for (int i = 0; (out = (AVOutputFormat*)muxer_list[i]); i++) {
if (prevout)
prevout->next = out;
prevout = out;
}
if (outdev_list) {
for (int i = 0; (out = (AVOutputFormat*)outdev_list[i]); i++) {
if (prevout)
prevout->next = out;
prevout = out;
}
}
for (int i = 0; (in = (AVInputFormat*)demuxer_list[i]); i++) {
if (previn)
previn->next = in;
previn = in;
}
if (indev_list) {
for (int i = 0; (in = (AVInputFormat*)indev_list[i]); i++) {
if (previn)
previn->next = in;
previn = in;
}
}
ff_mutex_unlock(&avpriv_register_devices_mutex);
}
上面的代码只是遍历了几个数组(muxer_list[], outdev_list[], demuxer_list[], indev_list[]), 并没有实际的操作! 也就是说, 我现在完全不调用av_register_all()也是可以成功运行ffmpeg应用的.
4. av_register_all()的分析结果
在新版本的ffmpeg4.2.2中, av_register_all()函数中已经不做实际的工作, 留在这里是做兼容性考虑!