以FFMPEG 1.0为参考,对FFMPEG源码分析,其中调用以H264为例
一、main()中;在ffmpeg.c文件中
1、OptionsContext o ={ 0 }:
初始化结构体变量o,这个结构体主要是一些参数选项;
初始化的结果是:整型和浮点型都为0,指针型成员都为NULL
疑问是,这种初始化方式到底是:
(1)初始化结构体变量的第一个成员,其他成员变量由系统采用缺省值初始化
(2)初始化所有的结构体成员
2、reset_options(&o,0):在ffmpeg_opt.c.
这是重新设置结构体体变量o,前面只是初始化,估计是以防参数选项的结构体o的某些成员变量在以往的调试过程中保留了一些参数值或者是初始化时的一些随机值,因此要将这个结构体重置,这种思想值得学习,因为我们进行反复调试的时候,可能中间强行退出,所以在退出时没有将这个参数选项的结构体释放,所以会有某些值被保留下来,会影响以后的调试或者编解码器,或者是初始化时的一些随机值恰好是参数选项的有效值,那样也会影响程序的运行结果,所以要想消除影响,只有重置这个结构体,具体如下:
下面列出一段reset_options(&o,0)的内容:
00099 void reset_options(OptionsContext *o, int is_input) 00100 { 00101 const OptionDef *po = options; 00102 OptionsContext bak= *o; 00103 int i; 00104 00105 /* all OPT_SPEC and OPT_STRING can be freed in generic way */所有这种参数选项值都可以使用这种方式释放 00106 while (po->name) { 00107 void *dst = (uint8_t*)o + po->u.off; 00108 00109 if (po->flags & OPT_SPEC) { 00110 SpecifierOpt **so = dst; 00111 int i, *count = (int*)(so + 1); 00112 for (i = 0; i < *count; i++) { 00113 av_freep(&(*so)[i].specifier); 00114 if (po->flags & OPT_STRING) 00115 av_freep(&(*so)[i].u.str); 00116 } 00117 av_freep(so); 00118 *count = 0; 00119 } else if (po->flags & OPT_OFFSET && po->flags & OPT_STRING) 00120 av_freep(dst); 00121 po++; 00122 } 00123 00124 for (i = 0; i < o->nb_stream_maps; i++) 00125 av_freep(&o->stream_maps[i].linklabel); 00126 av_freep(&o->stream_maps); 00127 av_freep(&o->audio_channel_maps); 00128 av_freep(&o->streamid_map); 00129 00130 memset(o, 0, sizeof(*o));使用memset函数重置结构体O
00131 00132 if (is_input) { 00133 o->recording_time = bak.recording_time; 00134 if (o->recording_time != INT64_MAX) 00135 av_log(NULL, AV_LOG_WARNING, 00136 "-t is not an input option, keeping it for the next output;" 00137 " consider fixing your command line.\n"); 00138 } else 00139 o->recording_time = INT64_MAX; 00140 o->mux_max_delay = 0.7; 00141 o->limit_filesize = UINT64_MAX; 00142 o->chapters_input_file = INT_MAX; 00143 00144 uninit_opts(); 00145 init_opts(); 00146 }
具体分析如下:
options是一个静态恒定的OptionDef型的数组,
00142typedefstruct{
00143 constchar *name;option的名字
00145#defineHAS_ARG 0x0001即命令行含有参数选项的标志
00146#defineOPT_BOOL 0x0002布尔型数据的标志
00147#defineOPT_EXPERT 0x0004不知什么意思
00148#defineOPT_STRING 0x0008字符串的标志
00149#defineOPT_VIDEO 0x0010视频的标志
00150#defineOPT_AUDIO 0x0020音频的标志
00151#define OPT_INT 0x0080输入的标志
00152#defineOPT_FLOAT 0x0100浮点型的标志
00153#defineOPT_SUBTITLE 0x0200字幕的标志
00154#defineOPT_INT64 0x040064位int型的标志
00155#defineOPT_EXIT 0x0800退出的标志
00156#defineOPT_DATA 0x1000数据的标志
00157#defineOPT_PERFILE 0x2000 /* the option is per-file (currently ffmpeg-only).
00158 implied byOPT_OFFSET or OPT_SPEC */
00159#defineOPT_OFFSET 0x4000 /* option is specified as an offset in apassed optctx */
00160#defineOPT_SPEC 0x8000 /* option is to be stored in an array ofSpecifierOpt.
00161 ImpliesOPT_OFFSET. Next element after the offset is
00162 an intcontaining element count in the array. */
00163#defineOPT_TIME 0x10000时间的标志
00164#defineOPT_DOUBLE 0x20000双精度的标志
00166 void *dst_ptr;
00167 int(*func_arg)(void *,constchar *,constchar *);
00171 constchar *argname;参数选项的名字
00172 } OptionDef;
options已经在ffmpeg.c中已经定义好了,可以根据上述定义对照下面的options数组:
04674staticconstOptionDefoptions[] = {
04675 #include "cmdutils_common_opts.h"
04676 { "n",OPT_BOOL,{(void *)&no_launch},"enable no-launchmode" },
04677 { "d", 0, {(void*)opt_debug},"enabledebug mode" },
04678 { "f",HAS_ARG|OPT_STRING,{(void*)&config_filename},"useconfigfile instead of /etc/ffserver.conf","configfile" },
04679 { NULL},
00144 uninit_opts();Uninitialize the cmdutils option system, inparticular free the *_opts contexts and their contents.通过调用av_dict_free(&format_opts);av_dict_free(&codec_opts);即释放原来可能有的初始化,从进行复位
00145 init_opts();Initialize the cmdutils option system, inparticular allocate the *_opts contexts.通过调用,但是如果你想初始化,必须在configure时进行相应的配置,才可以if(CONFIG_SWSCALE)sws_opts =sws_getContext(16, 16, 0, 16, 16, 0, SWS_BICUBIC,NULL, NULL,NULL);if(CONFIG_SWRESAMPLE) swr_opts = swr_alloc();
3、av_log_set_flags(AV_LOG_SKIP_REPEATED):在log.c.
03123 av_log_set_flags(AV_LOG_SKIP_REPEATED);
AV_LOG_SKIP_REPEATED这个宏定义的含义:
Skip repeated messages, this requires the user app to use av_log() insteadof (f)printf as the 2 would otherwise interfere and lead to "Last message repeatedx times" messages below (f)printf messages with some bad luck.
跳过重复的消息;这就要求用户应用程序使用av_log()日志函数,而不是printf()函数;
4、parse_loglevel(argc,argv,options):cmdutils.c.
Find the '-loglevel' option in the command line args and apply it.
在参数命令行args中,找到’-loglevel’这个参数选项,并应用它
具体的函数内容为:
int idx = locate_option(argc,argv, options, "loglevel");
调用locate_options()函数来找loglevel这个参数选项;成功则返回loglevel所在位置的索引号,没有的话就返回0;
5、av_log_set_callback(log_callback_null);:log.c.
03126 if(argc>1 && !strcmp(argv[1],"-d")){
03127 run_as_daemon=1;守护进程标志位置1
03128 av_log_set_callback(log_callback_null);
进入av_log_set_callback()函数,可以看到
00277voidav_log_set_callback(void (*callback)(void*,int,constchar*, va_list))
00279 av_log_callback=callback;av_log_callback是函数指针,通过指向callback,来调用callback函数
那再看一下具体的调用函数log_callback_null()函数,
是个空函数,也就是说什么都不干;
总之,这段程序就是判断一下argv中有没有’-d’参数选项,若有,则守护进程标志位置1;守护进程标志位初始置0:
00114staticintrun_as_daemon = 0;
下面是一些注册函数
6、avcodec_register_all():allcodecs.c.
下面是libavcodec/allcodecs.c文件开头的一句话
Provide registration of all codecs, parsersand bitstream filters for libavcodec.
函数作用:
Register all the codecs,parsers and bitstream filters which were enabled at configuration time.
If you do not call thisfunction you can select exactly which formats you want to support, by using theindividual registration functions.
即:注册所有的编解码器、参数以及比特流滤波器,这些都是在配置阶段就启用了;
如果你不想调用这个函数,你可以准确的悬着你想要支持的格式,当然这得通过你自己的注册函数;
各位,这就是说在我们实际应用的时候,没必要非得把所有的编解码器格式都注册一遍,可以选择自己能用到的,其他的,嘿嘿,就让他们玩去吧
avcodec_register_all()函数主要调用三个函数来完成编解码器、参数以及比特流滤波器的注册。这三个函数是:
avcodec_register音频视频字幕编解码器的注册
av_register_codec_parser编解码器解析器的注册
av_register_bitstream_filter数据流的滤波器的注册
注册流程是:
(1)avcodec_register_all()函数调用宏定义
(2)宏定义调用具体的注册函数完成注册,就是指上面的三个函数
下面具体分析一下某些格式的注册问题,例如FFMPEG和H264的注册:
(1)硬件加速:
00059 REGISTER_HWACCEL (H264_DXVA2, h264_dxva2); 00060 REGISTER_HWACCEL (H264_VAAPI, h264_vaapi); 00061 REGISTER_HWACCEL (H264_VDA, h264_vda);
00136 00136 REGISTER_DECODER (H264, h264); 00137 REGISTER_DECODER (H264_CRYSTALHD, h264_crystalhd); 00138 REGISTER_DECODER (H264_VDA, h264_vda); 00139 REGISTER_DECODER (H264_VDPAU, h264_vdpau);
00037 #define REGISTER_DECODER(X,x) { \ 00038 extern AVCodec ff_##x##_decoder; \ 00039 if(CONFIG_##X##_DECODER) avcodec_register(&ff_##x##_decoder); }
编码器的注册实际为:
extern AVCodec ff_h264_encoder;
if(CONFIG_H264_ENCODER)
avcodec_register(&ff_h264_encoder);
解码器的注册实际为:
extern AVCodec ff_h264_decoder;
if(CONFIG_H264_DECODER)
avcodec_register(&ff_h264_decoder);
解析器的注册实际为:
extern AVCodecParser ff_h264_parser;
if(CONFIG_H264_PARSER)
avcodec_register(&ff_h264_parser);
either this function or avcodec_register_all() must be called before any other libavcodec functions.
00150 00151 void avcodec_register(AVCodec *codec) 00152 { 00153 AVCodec **p;将p定义成二级指针,估计是为了下面将编解码器连成编解码器链表的方便;p指向整个链表的首地址,使用*p存放每一个编解码器的首地址,**p就是表示编解码器的具体的字符串形式; 00154 avcodec_init();编解码器的初始化,且只能初始化一次。其中负责静态查找表结构的初始化的函数:ff_dsputil_static_init(),主要是两个表结构的初始化,ff_cropTbl和ff_squareTbl的初始化;其中ff_cropTble[i]实现的功能实际上是a = i<0 ? 0 : (i>255 ? 255 : i);这个表的大小是2*MAX_NEG_CROP+256, 是将-1024到1024之间的任意数转化为0~255的数,1024就是MAX_NEG_CROP的值。这样设计的实质是用查表的方式来减少所需执行的指令数量,是用空间上的代价来换取速度的提升,这是一种典型的方式。 00155 p = &first_avcodec;前面有first_avcodec的静态全局变量初始化,即static AVCodec* first_avcodec = NULL;除了第一次添加avcodec时p会指向NULL,其他时候都是指向链表的首地址,实际上first_avcode存放的就是avcodec链表的首地址;不过感觉每一次调用avcodec_register()函数都要从开始遍历编解码器链表,这样不是很浪费时间么 00156 while (*p != NULL) 00157 p = &(*p)->next; 00158 *p = codec; 00159 codec->next = NULL;上边几行代码一块来看,就是将编解码器连接成链表的过程;先遍历已经存在的编解码器链表,然后将当前的编解码器插入到链表的结尾 00160 00161 if (codec->init_static_data)这个函数指针主要完成编解码器静态数据的初始化 00162 codec->init_static_data(codec);想进入看看这个函数,但是没进去。 00163 }
来看一下avcodec_init()函数是怎样进行初始化的。
00130staticvoidavcodec_init(void)
00132 static int initialized = 0;
00133
00134 if (initialized != 0)
00135 return;
00136 initialized = 1;
00137
00138 ff_dsputil_static_init();
00139 }
调用了ff_dsputil_static_init()函数,那就继续跟进看一下:
这个函数主要是初始化avcodec,需要保证在编解码之前完成ff_dsputil_static_init()函数,但是该函数只能初始化一次,保证其初始化一次的方式就是:使用initialized变量,当initialied=1时,就返回,所以只有第一次注册编解码器才执行ff_dsputil_static_init()函数,这样就保证了ff_dsputil_static_init()函数只执行一次。
ff_dsputil_static_init()函数主要是对一些静态查找表结构的初始化:
02782av_coldvoidff_dsputil_static_init(void)
02786 for(i=0;i<256;i++)ff_cropTbl[i+MAX_NEG_CROP]= i;
02787 for(i=0;i<MAX_NEG_CROP;i++){
02788 ff_cropTbl[i]= 0;
02789 ff_cropTbl[i+ MAX_NEG_CROP + 256] = 255;
02790 }其中ff_cropTble[i]实现的功能实际上是a = i<0 ? 0 : (i>255 ? 255 : i);这个表的大小是2*MAX_NEG_CROP+256, 是将-1024到1024之间的任意数转化为0~255的数,1024就是MAX_NEG_CROP的值。这样设计的实质是用查表的方式来减少所需执行的指令数量,是用空间上的代价来换取速度的提升,这是一种典型的方式。
02793 ff_squareTbl[i]= (i - 256) * (i - 256);
02796 for(i=0; i<64; i++)ff_inv_zigzag_direct16[ff_zigzag_direct[i]]=i+1;
MAX_NEG_CROP的值,用作pixel opration:
00087 /* pixel operations */
00088#defineMAX_NEG_CROP 1024
但是最后一点有点不明白:
00161 if (codec->init_static_data)
00162 codec->init_static_data(codec);
此时codec还没初始化,这里做一个判断codec->init_static_data这个函数指针是否为NULL,若是不为空指针,则初始化该codec的静态数据;但是这里就是做codec的初始化,估计这个codec->init_static_data指针还没指向某个函数,应该是空;
00310 REGISTER_DECODER (MP3, mp3);这个先不看,以后有时间再看音频解码器的注册。
00478 REGISTER_PARSER (H264, h264);
00042 #define REGISTER_PARSER(X,x) { \ 00043 extern AVCodecParser ff_##x##_parser; \ 00044 if(CONFIG_##X##_PARSER) av_register_codec_parser(&ff_##x##_parser); }
00035 void av_register_codec_parser(AVCodecParser *parser) 00036 { 00037 parser->next = av_first_parser;av_first_parser也是在前面已经定义好的静态全局变量:static AVCodecParser *av_first_parser = NULL; 00038 av_first_parser = parser; 00039 }本函数完成功能也是讲解析器连接成解析器链表;先让parser->next指向NULL指针av_first_parser,然后再将av_first_parser指向当前的解析器,也就是把当前的解析器连接到链表的尾部。
00033 void av_register_bitstream_filter(AVBitStreamFilter *bsf){ 00034 bsf->next = first_bitstream_filter; 00035 first_bitstream_filter= bsf; 00036 }此函数的注册方式和上面codec_parser的注册方式一致
7、avdevice_regiser_all():alldevices.c.
官网上的解释:
Initializelibavdevice and register all the input and output devices.
Warning:
This function is not thread safe.
也就是说初始化库libavdevice,并且注册所有的输入和输出设备。
Register all the grabbing devices
这个是libavdevice/alldevices.c文件开头的一句话,明白了,原来是注册所有的多媒体捕捉设备,包括输入和输出设备,这些设备可以是硬件实现,也可以是软件实现。
avdevice_register_all()函数的具体内容是:
00032voidavdevice_register_all(void)
00035
00036 if (initialized)
00037 return;
00038 initialized = 1;通过initialized变量的设置,来说明该函数avdevice_register_all()只能被有效调用一次,即所有的device只能注册一次。
00039
00040 /* devices */
00041 REGISTER_INOUTDEV(ALSA, alsa);Advanced Linux Sound Architecture,一种linux下的音频架构,它在linux上提供音频和MIDI(Musical Instrument Digital Interface,音乐设备数字化接口)的支持,在2.6系列的内核中ALSA已经成为默认的声音子系统,来替换2.4内核系列的OSS(OPEN SOUND SYSTEM,开放声音系统)
00042 REGISTER_INDEV (BKTR, bktr);
00043 REGISTER_OUTDEV (CACA, caca);
00044 REGISTER_INDEV (DSHOW, dshow);即DirectShow,微软开发基于COM的流媒体处理的开发包,广泛的支持各种多媒体格式,包括asf、mpeg、avi、dv、mp3、wave等,为多媒体流的捕捉和回放提供强有力的支持。
00045 REGISTER_INDEV (DV1394, dv1394);高速处理的视频采集卡,分为软件和硬件实现两种。
00046 REGISTER_INDEV (FBDEV, fbdev);
00047 REGISTER_INDEV (IEC61883, iec61883);
00048 REGISTER_INDEV (JACK, jack);
00049 REGISTER_INDEV (LAVFI, lavfi);
00050 REGISTER_INDEV (OPENAL, openal);
00051 REGISTER_INOUTDEV(OSS, oss);
00052 REGISTER_INDEV (PULSE, pulse);
00053 REGISTER_OUTDEV (SDL, sdl);开放源码的跨平台多媒体开发库,提供了数种控制图像、声音、输入输出的函数,让开发者只要使用相同或者相似的代码就可以开放出跨平台的应用软件。Simple DirectMedia Layer
00054 REGISTER_INOUTDEV(SNDIO, sndio);
00055 REGISTER_INDEV (V4L2, v4l2);Video 4 for linux 2;针对于uvc免驱usb设备的编程框架,主要用于采集usb摄像头等。只能用于linux;包含了
1、视频采集接口:可以是高频头或者摄像头
2、视频输出接口:可以驱动计算机的外围视频图像设备—像可以输出电视信号格式的设备
3、直接传输视频接口:它的工作主要是把视频采集设备采集过来的信号直接输出到输出设备之上,而不用经过系统的CPU。
4、视频间隔消隐信号接口:他能使应用可以访问传输消隐期的视频信号
5、收音机接口:可用来处理从AM或FM高频头设备接受来的音频流。
00056 // REGISTER_INDEV (V4L, v4l
00057 REGISTER_INDEV (VFWCAP, vfwcap);
00058 REGISTER_INDEV (X11GRAB,x11grab);windows下的一种屏幕录像工具
00061 REGISTER_INDEV (LIBCDIO, libcdio);
00062 REGISTER_INDEV (LIBDC1394, libdc1394);
avdevice_register_all()函数主要是通过调用两个函数具体实现注册输入输出设备:
av_register_input_format():utils.c文件中
av_register_output_format():utils.c文件中
注册流程:
(1)avdevice_register_all()调用宏定义REGISTER_*
(2)REGISTER_*调用具体的注册函数:av_register_input_format()、av_register_output_format()。
(1)输出设备的注册:
下面是avdevice_register_all()函数调用的SDL的宏定义:
00053 REGISTER_OUTDEV (SDL, sdl);
这个是REGISTER_OUTDEV的宏定义:
00024 #define REGISTER_OUTDEV(X,x) { \ 00025 extern AVOutputFormat ff_##x##_muxer; \ 00026 if(CONFIG_##X##_OUTDEV) av_register_output_format(&ff_##x##_muxer); }
上面的宏定义先声明AVOutputFormat类型的muxer(复用器或者说混流器)
然后调用av_register_output_foramt()函数进行注册
拿SDL来说吧:
00053 REGISTER_OUTDEV (SDL, sdl);
可以转化成:
externAVOutputFormat ff_sdl_muxer;
if(CONFIG_SDL_OUTDEV)
av_register_output_format(&ff_sdl_muxer);
av_register_output_format():utils.c文件中
将muxer保存在静态全局变量first_oformat中,这也是一个链表,当用到muxer时,通过遍历的方式在first_oformat中查找所需要muxer即可;这一点和codec的注册方式相同
00157 void av_register_output_format(AVOutputFormat *format) 00158 { 00159 AVOutputFormat **p;和上面avcodec_register()的用意一样,都是定义成二级指针,方便处理;p指向复用器muxer链表的首地址,*p指向每一个muxer的首地址,**p是指表示muxer名字 00160 p = &first_oformat;这是本程序前面的声明:static AVOutputFormat *first_oformat = NULL;注册之前先让二级指针p指向NULL型指针,当然除了第一次注册是指向NULL指针之外,后面的注册都不再指向NULL指针了,因为first_oformat不再为空,它其实指向muxer的链表的首地址。 00161 while (*p != NULL) p = &(*p)->next; 00162 *p = format; 00163 format->next = NULL;和前面一样,先遍历一遍muxer的链表,然后将当前的muxer加到链表的尾部; 00164 }
(2)输入设备的注册和输出设备的注册一致:
00055 REGISTER_INDEV (V4L2, v4l2);
宏定义具体为:
00027 #define REGISTER_INDEV(X,x) { \ 00028 extern AVInputFormat ff_##x##_demuxer; \ 00029 if(CONFIG_##X##_INDEV) av_register_input_format(&ff_##x##_demuxer); }
也是先做一个外部变量的声明,即声明某种demuxer为全局变量,方便后面程序调用这个demuxer;
然后调用av_register_input_format()函数进行具体的注册;
av_register_input_format():utils.c文件中
将demuxer保存在静态全局变量first_iformat中,这也是一个链表,当用到demuxer时,通过遍历的方式在first_iformat中查找所需要demuxer即可;这一点和muxer的注册方式相同
00148 void av_register_input_format(AVInputFormat *format) 00149 { 00150 AVInputFormat **p;和上面av_register_input_format()的用意一样,都是定义成二级指针,方便处理;p指向复用器demuxer链表的首地址,*p指向每一个demuxer的首地址,**p是指表示demuxer名字 00151 p = &first_iformat;这是本程序前面的声明:static AVInputFormat *first_iformat = NULL;注册之前先让二级指针p指向NULL型指针,当然除了第一次注册是指向NULL指针之外,后面的注册都不再指向NULL指针了,因为first_iformat不再为空,它其实指向demuxer的链表的首地址。 00152 while (*p != NULL) p = &(*p)->next; 00153 *p = format; 00154 format->next = NULL;和前面一样,先遍历一遍demuxer的链表,然后将当前的muxer加到链表的尾部; 00155 }
(3)还有一种是同时具备输入和输出功能的设备,这种设备的注册就是将上面两种注册同时进行即可。
00051 REGISTER_INOUTDEV (OSS, oss);
00030 #define REGISTER_INOUTDEV(X,x) REGISTER_OUTDEV(X,x); REGISTER_INDEV(X,x)
同时注册输入和输出设备;具体过程不再分析,和上面输入输出的注册相同。
8、avfilter_register_all():allfilters.c.
初始化滤波器系统,注册所有的内置滤波器;
滤波器的注册和上边的注册大同小异,因不是关注的重点,暂时就不写源码分析了。
(1)先调用宏定义,
(2)宏定义中调用函数avfilter_register_all()函数具体实现滤波器的注册;
但是要注意的是:一种滤波器是FFMPEG调用外部的滤波器;另一种滤波器是FFMPEG内置的滤波器。
具体注册函数为
avfilter_register():在avfilters.c文件中
函数功能是:注册滤波器;仅当你在后面打算通过avfilter_get_by_name()函数使用名字去寻找AVFilter结构时,这个函数才会用到;即使你没有注册滤波器,那后面的avfilter_open()函数依然可以为你实现滤波器。
9、av_register_all():allformats.c.
官网上给出的解释是:
Initializelibavformat and register all the muxers, demuxers and protocols.
If you do not callthis function, then you can select exactly which formats you want to support.
初始化libavformat,并且注册所有的复用器、解复用器和网络协议
如果你不想调用这个函数,你可以选择使用你想支持的格式;
这个和上面编解码器的注册一样,都可以自己编写,没必要啰啰嗦嗦一大堆东西
注册完编解码器、输入输出设备以及滤波器,终于轮到demuxer和muxer的注册了,其实。
av_register_all()函数主要通过调用下面三个函数实现具体的注册活动:
(1)av_register_input_format():utils.c文件中
(2)av_register_output_format():utils.c文件中
(3)ffurl_register_protocol():avio.c文件中;原来的av_register_protocol()函数已经不再用了
注册过程:
(1)先调用宏定义,例如H264调用为:REGISTER_MUXDEMUX(H264,h264)
(2)然后通过宏定义调用muxer和demuxer的注册函数
00002 *Register all the formats and protocols
00041voidav_register_all(void)
00042 {
00043 static int initialized;
00044
00045 if (initialized)
00046 return;
00047 initialized = 1;
00048
00049 avcodec_register_all();
这个函数也是使用initialized变量来控制本函数只能被注册一次。
在这函数中,调用了前面的avcodec_register_all()函数,但是由于前面注册过了,所以这里不会重新注册,估计是预备前面没有注册的。
具体实现过程为:
(1)先调用宏定义:
00114 REGISTER_MUXDEMUX (H264, h264);
(2)宏定义的内容为:因为H264同时为muxer和demuxer,所以将三个宏定义都列上:
00027 #define REGISTER_MUXER(X,x) { \ 00028 extern AVOutputFormat ff_##x##_muxer; \ 00029 if(CONFIG_##X##_MUXER) av_register_output_format(&ff_##x##_muxer); } 00030 00031 #define REGISTER_DEMUXER(X,x) { \ 00032 extern AVInputFormat ff_##x##_demuxer; \ 00033 if(CONFIG_##X##_DEMUXER) av_register_input_format(&ff_##x##_demuxer); } 00034 00035 #define REGISTER_MUXDEMUX(X,x) REGISTER_MUXER(X,x); REGISTER_DEMUXER(X,x)
宏定义先声明muxer和demuxer的外部变量,在ffmpeg中muxer和demuxer分别抽象为:AVOutputFormat和AVInputFormat,muxer为混流器,demuxer为分流器
然后调用av_register_output_format()和av_register_input_format()分别完成muxer和demuxer的注册
muxer和demuxer的注册过程在前面avdevice_register_all()中已经讲过,这里就不再重复了。
宏定义替换后为:
extern AVOutputFormat ff_h264_muxer;
if(CONFIG_H264_MUXER)
av_register_output_format(&ff_h264_muxer);
extern AVInputFormat ff_h264_demuxer;
if(CONFIG_H264_DEMUXER)
av_register_input_foramt(&ff_h264_demuxer);
这就把H264的复用器和解复用器都注册上了
协议的注册:
以RTP的实现为例:
00297 REGISTER_PROTOCOL (RTP, rtp);下面是宏定义的具体内容:
00037 #define REGISTER_PROTOCOL(X,x) { \ 00038 extern URLProtocol ff_##x##_protocol; \ 00039 if(CONFIG_##X##_PROTOCOL) ffurl_register_protocol(&ff_##x##_protocol, sizeof(ff_##x##_protocol)); }
然后调用ffurl_register_protocol()实现具体的注册活动;
int ffurl_register_protocol(URLProtocol * protocol,int size):avio.c文件中
功能是注册URLProtocol类型的协议,其中size是URLProtocol结构体类型的尺寸,可以过滤掉协议长度太长的协议;
00096 int ffurl_register_protocol(URLProtocol *protocol, int size)协议抽象为URLProtocol类型的结构体 00097 { 00098 URLProtocol **p;定义二级指针是为了方便后面协议查找,其中p指向协议链表的首地址,*p指向每一个协议的首地址,**p指向具体协议内容 00099 if (size < sizeof(URLProtocol)) {先判断一下size尺寸是否满足protocol的需要,不能的话就从新分配空间,将协议复制到新分配的空间中00100 URLProtocol* temp = av_mallocz(sizeof(URLProtocol)); 00101 memcpy(temp, protocol, size); 00102 protocol = temp; 00103 }上面这几句可以看成是过滤掉长度大于FFMPEG所支持的协议长度的一些协议 00104 p = &first_protocol;first_protocol是在这个函数之前声明的静态全局变量: static URLProtocol *first_protocol = NULL;除了第一次注册时p为NULL指针外,其余时候均不为NULL指针,因为一旦注册开始,first_protocol其实就是整个协议注册链表的首地址。 00105 while (*p != NULL) p = &(*p)->next; 00106 *p = protocol; 00107 protocol->next = NULL;上边这几句和前面编解码器、muxer、demuxer的注册方式一致,都是先遍历整个注册链表,然后将当前要注册的协议添加到协议链表的末尾。 00108 return 0; 00109 }