FFmpeg源码分析。

前言

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函数的具体做了啥。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值