FFmpeg源码分析。

来源:互联网 发布:linux安装软件包命令 编辑:程序博客网 时间:2024/06/05 09:23

前言

ffmeg源码讲解的博客很多,可是完全讲解清楚原理的,觉得还不太多,这里就稍微分析一下,无论是编码器,和解码器,都是注册一下,然后在特定的流,对应特定的编解码器。具体我们借助官方的提供的ffpalyer,稍微分析下。

正文

首先是使用,这里超级简单

ffplayer.exe +"文件名"

这是最简单的方法,这里我们目光不是控制,仅仅大概了解ffpeg的流程,这里我们就不详细介绍。

下面我们开始代码阅读

int main(int argc, char **argv){    ......#if CONFIG_AVDEVICE    avdevice_register_all();#endif#if CONFIG_AVFILTER    avfilter_register_all();#endif    av_register_all();    avformat_network_init();    ......    is = stream_open(input_filename, file_iformat);    ......    event_loop(is);    ......}

这里显示用的SDL,这是一个开源的可以用来渲染支持surface的一个多平台的玩意,代码量比较小,如果有机会可以研究下,不过这里暂时不再纠结这个问题,反正,得到的每一帧数据,都是用SDL来渲染的,知道这些就够了。我们关注我们的重点。
前边几个是条件编译,我做过详细阅读。这些其实不是关键,最核心的几个函数是

av_register_all();  //这是吧所有的编解码结构体给注册完成is = stream_open(input_filename, file_iformat);//开启四个线程,完成视频音频,和读取文件的工作event_loop(is); //更新页面相应键盘。

首先看下注册的函数

void av_register_all(void){    static AVOnce control = AV_ONCE_INIT;    ff_thread_once(&control, register_all);}static void register_all(void){    avcodec_register_all();    /* (de)muxers */    REGISTER_MUXER   (A64,              a64);    REGISTER_DEMUXER (AA,               aa);    REGISTER_DEMUXER (AAC,              aac);    REGISTER_MUXDEMUX(AC3,              ac3);    ......}void avcodec_register_all(void){    static AVOnce control = AV_ONCE_INIT;    ff_thread_once(&control, register_all);}static void register_all(void){    /* hardware accelerators */    REGISTER_HWACCEL(H263_VAAPI,        h263_vaapi);    ......     REGISTER_ENCODER(A64MULTI5,         a64multi5);    REGISTER_DECODER(AASC,              aasc);    }

看到代码的第一反应,着都是干啥的呢,乌七八糟的,不过耐心看,其实这些整整齐齐的东西,会觉得很可爱。

#define REGISTER_MUXER(X, x)                                            \    {                                                                   \        extern AVOutputFormat ff_##x##_muxer;                           \        if (CONFIG_##X##_MUXER)                                         \            av_register_output_format(&ff_##x##_muxer);                 \    }void av_register_output_format(AVOutputFormat *format){    AVOutputFormat **p = last_oformat;    while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))        p = &(*p)->next;    if (!format->next)        last_oformat = &format->next;}

这些结构非常简单,也就是把extern AVOutputFormat ff_##x##_muxer;这个变量给注册到一个叫做last_oformat中。
其他都是同理,这些触发器,这里暂时不说,关键看下我们的编解码器。

#define REGISTER_DECODER(X, x)                                          \    {                                                                   \        extern AVCodec ff_##x##_decoder;                                \        if (CONFIG_##X##_DECODER)                                       \            avcodec_register(&ff_##x##_decoder);                        \    }

其实这里很容易理解,还是注册到一个全局的链表,这里暂时不要纠结到底在哪里,不用纠结,可是这个变量到底是啥呢?我们试着找一找,毕竟假如这个是h264,我应该定义一个exterel的ff_h264_decoder

AVCodec ff_h264_decoder = {    .name                  = "h264",    .long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),    .type                  = AVMEDIA_TYPE_VIDEO,    .id                    = AV_CODEC_ID_H264,    .priv_data_size        = sizeof(H264Context),    .init                  = h264_decode_init,    .close                 = h264_decode_end,    .decode                = h264_decode_frame,    .capabilities          = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |                             AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |                             AV_CODEC_CAP_FRAME_THREADS,    .caps_internal         = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING,    .flush                 = flush_dpb,    .init_thread_copy      = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),    .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),    .profiles              = NULL_IF_CONFIG_SMALL(ff_h264_profiles),    .priv_class            = &h264_class,};

这里解码也是这个东东,总之,就是吧全局的解码器,集中到一个列表中,便于查找。

is = stream_open(input_filename, file_iformat)

这个是控制开启解码库,等等读取文件等操作,限于篇幅,暂时不做详细解析。

static void event_loop(VideoState *cur_stream){    SDL_Event event;    double incr, pos, frac;    for (;;) {        double x;        //这里一直刷新,用来更新通过stream_open函数打开的线程更新出来的视频帧。        refresh_loop_wait_event(cur_stream, &event);        switch (event.type) {        case SDL_KEYDOWN:        ......//这里是处理键盘操作的。        }     }  }

这里代码逻辑比较复杂,就不追踪,有机会好好分析这里。

后记

这里其实没有过多的介绍,不过对于整个代码架构稍微有些了解,下一篇我们研究一些stream_open函数的具体做了啥。

原创粉丝点击