ffmpefg源码分析

来源:互联网 发布:淘宝网棉麻布鞋 编辑:程序博客网 时间:2024/06/06 00:03

以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     } else00139     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的名字

00144    intflags;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双精度的标志

00165     union {

00166        void *dst_ptr;

00167         int(*func_arg)(void *,constchar *,constchar *);

00168        size_toff;

00169    } u;共用体

00170    constchar *help;参数选项的用处

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},

04680 };


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);

03129         argc--;

03130         argv++;

03131    }


进入av_log_set_callback()函数,可以看到

00277voidav_log_set_callback(void (*callback)(void*,int,constchar*, va_list))

00278 {

00279    av_log_callback=callback;av_log_callback是函数指针,通过指向callback,来调用callback函数

00280 }

 

那再看一下具体的调用函数log_callback_null()函数,

03105staticvoidlog_callback_null(void *ptr,intlevel,constchar *fmt,va_list vl)

03106 {

03107 }

是个空函数,也就是说什么都不干;

 

总之,这段程序就是判断一下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);


这是REGISTER_HWACCEL()定义:
30 #define REGISTER_HWACCEL(X,x) { \
31  extern AVHWAccel ff_##x##_hwaccel; \
32  if(CONFIG_##X##_HWACCEL) av_register_hwaccel(&ff_##x##_hwaccel); }

这是



(2)注册编解码器
00136  00136REGISTER_DECODER (H264, h264);00137     REGISTER_DECODER (H264_CRYSTALHD, h264_crystalhd);00138     REGISTER_DECODER (H264_VDA, h264_vda);00139     REGISTER_DECODER (H264_VDPAU, h264_vdpau);

REGISTER_DECODER()定义为:
00037 #define REGISTER_DECODER(X,x) { \00038           extern AVCodec ff_##x##_decoder; \00039           if(CONFIG_##X##_DECODER)  avcodec_register(&ff_##x##_decoder); }


这是解码器的宏定义,先是声明AVCodec类型的编解码器为外部变量,然后调用avcodec_register()函数进行注册,看完avcodec_register()函数之

后,我们就知道,所谓的注册,就是将表示该编解码器的结构体加入到编解码器的列表,所以进行函数优化时,用不到的那些注册尽管删掉就是。

宏定义展开后:

编码器的注册实际为:

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);





void avcodec_register(AVCodec* codec):util.c文件中


Register the codec codec and initialize libavcodec.

Warning:
either this function or avcodec_register_all() must be called before any other libavcodec functions.




此函数功能是注册编解码器codec,并且初始化libavcodec

将编解码器保存在静态全局变量first_avcodec中,这是一个链表结构体,当用到时,遍历这个链表即可

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()函数:

来看一下avcodec_init()函数是怎样进行初始化的。

00130staticvoidavcodec_init(void)

00131 {

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)

02783 {

02784    int i;

02785

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, 是将-10241024之间的任意数转化为0~255的数,1024就是MAX_NEG_CROP的值。这样设计的实质是用查表的方式来减少所需执行的指令数量,是用空间上的代价来换取速度的提升,这是一种典型的方式。

 

02791

02792    for(i=0;i<512;i++) {

02793         ff_squareTbl[i]= (i - 256) * (i - 256);

02794    }

02795

02796    for(i=0; i<64; i++)ff_inv_zigzag_direct16[ff_zigzag_direct[i]]=i+1;

02797 }

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指针还没指向某个函数,应该是空;

 



(3)注册音频编解码器
00310     REGISTER_DECODER (MP3, mp3);
这个先不看,以后有时间再看音频解码器的注册。



(4)外部的库文件,以H264和mp3为例
00450     REGISTER_ENCODER (LIBX264, libx264);
00435     REGISTER_ENCODER (LIBMP3LAME, libmp3lame);

这里主要看libx264库的注册:

这个库文件的注册和上边vcodec的注册是一样的,都是调用avcodec_register()函数完成,同样也是讲库文件添加到AVCodec链表中去



(5)注册解析器
00478     REGISTER_PARSER  (H264, h264);

这是REGISTER_PARSER(X,x)的定义:

00042 #define REGISTER_PARSER(X,x) { \00043           extern AVCodecParser ff_##x##_parser; \00044           if(CONFIG_##X##_PARSER)  av_register_codec_parser(&ff_##x##_parser); }

宏定义的功能是:

先将当前编解码器的解析器声明为外部函数,以方便调用;

调用av_register_codec_parser()函数进行注册此种编解码器的解析器;


av_register_codec_parser():parser.c文件中


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指向当前的解析器,也就是把当前的解析器连接到链表的尾部。


(6)还有其他的一些注册,如数据流滤波器的注册等,方式和上边两种注册方式基本一致。

void  av_register_bitstream_filter(AVBitStreamFilter* bsf)

下面是具体代码:
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)

00033 {

00034    staticint initialized;

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下的一种屏幕录像工具

00059

00060    /* externallibraries */

00061    REGISTER_INDEV    (LIBCDIO, libcdio);

00062    REGISTER_INDEV    (LIBDC1394, libdc1394);

00063 }


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);


在编译阶段,会提示如果不安装SDL,不会生成ffplay,不知道这个SDL是不是生成ffplay的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)); }


先将URLProtocol型的协议声明为外部变量,URLProtocol是对协议的抽象

然后调用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 }


原创粉丝点击