旧版本的ffmpeg程序, 程序开头处, 一般总是av_register_all. 4.x之后, 该函数已经废弃,不需要调用了. 我们以ffmpeg4.4的为例. 首先看看官方的版本特性(doc\APIchanges)变更说明:
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, 如下:
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命令后,会自动生成这个文件.
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函数还在保留下来了.
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.
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指针. 而是通过数组下标来完成的. 我们可以通过下面代码遍历所有的复用器.
#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);
}
}
输出如下:
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的情况下就可以用.
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 删除。