懒人李冰

记录我的生活、学习

FFmpeg 学习之 FFplay 源码分析

FFplay 是一个简单便携的媒体播放器,它使用了 FFmpeg 和 SDL 库。

FFplay 的使用

首先看一下 FFplay 的使用:安装完 FFmpeg 后,直接在命令行中输入

1
ffplay [options] bitstream_file

其中更详细的使用说明,可以使用man ffplay来查看。

其次我们可以通过使用Escq来推出播放,可以使用空格来暂停播放,可以使用s来执行逐帧播放视频等等操作。

FFplay 源码分析

ffplay 里面最主要的函数时:av_register_all()SDL_Init(flags)av_init_packetstream_openevent_loop。下面逐个分析这几个函数主要完成的功能。

av_register_all()

av_register_all实现注册codecsdemuxprotocols。其中注册的codecs时通过avcodec_reigster_all()函数来实现的;注册demux的方法,以 HEVC 为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define REGISTER_MUXER(X, x)                                            \
    {                                                                   \
        extern AVOutputFormat ff_##x##_muxer;                           \
        if (CONFIG_##X##_MUXER)                                         \
            av_register_output_format(&ff_##x##_muxer);                 \
    }

#define REGISTER_DEMUXER(X, x)                                          \
    {                                                                   \
        extern AVInputFormat ff_##x##_demuxer;                          \
        if (CONFIG_##X##_DEMUXER)                                       \
            av_register_input_format(&ff_##x##_demuxer);                \
    }

#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)

REGISTER_MUXDEMUX(HEVC,hevc);

将上面的宏展开即为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void av_register_input_format(AVInputFormat *format)
{
    AVInputFormat **p = last_iformat;

    format->next = NULL;
    while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
        p = &(*p)->next;
    last_iformat = &format->next;
}

void av_register_output_format(AVOutputFormat *format)
{
    AVOutputFormat **p = last_oformat;

    format->next = NULL;
    while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
        p = &(*p)->next;
    last_oformat = &format->next;
}
1
2
3
4
5
6
7
8
9
AVOutputFormat ff_hevc_muxer = {
    .name              = "hevc",
    .long_name         = NULL_IF_CONFIG_SMALL("raw HEVC video"),
    .extensions        = "hevc",
    .audio_codec       = AV_CODEC_ID_NONE,
    .video_codec       = AV_CODEC_ID_HEVC,
    .write_packet      = ff_raw_write_packet,
    .flags             = AVFMT_NOTIMESTAMPS,
};