FFmpeg 源代码结构(编解码解析)
特别说明,此文参考至雷神笔记,做一个备忘录。
1. FFmpeg源代码结构图 - 解码
下图表明了 FFmpeg 在解码一个视频的时候的函数调用流程。为了保证结构清晰,其中仅列出了最关键的函数,剔除了其它不是特别重要的函数。
下面解释一下图中关键标记的含义。
1.1 函数背景色
函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:
- 粉红色背景函数:FFmpeg 的 API函数。
- 白色背景的函数:FFmpeg 的内部函数。
- 黄色背景的函数:URLProtocol 结构体中的函数,包含处理协议(Protocol)的功能。
- 绿色背景的函数:AVInputFormat 结构体中的函数,包含处理封装格式(Format)的功能。
- 蓝色背景的函数:AVCodec 结构体中的函数,包含了编解码器(Codec)的功能。
PS:URLProtocol,AVInputFormat,AVCodec在FFmpeg开始运行并且注册完组件之后,都会分别被连接成一个个的链表。因此实际上是有很多的URLProtocol,AVInputFormat,AVCodec的。图中画出了解码一个输入协议是“文件”(其实就是打开一个文件。“文件”也被当做是一种广义的协议),封装格式为FLV,视频编码格式是H.264的数据的函数调用关系。
1.2 区域
整个架构图可以分为以下几个区域:
- 左边区域——架构函数区域:这些函数并不针对某一特定的视频格式。
- 右上方黄色区域——协议处理函数区域:不同的协议(RTP,RTMP,FILE)会调用不同的协议处理函数。
- 右边中间绿色区域——封装格式处理函数区域:不同的封装格式(MKV,FLV,MPEGTS,AVI)会调用不同的封装格式处理函数。
- 右边下方蓝色区域——编解码函数区域:不同的编码标准(HEVC,H.264,MPEG2)会调用不同的编解码函数。
1.3 箭头线
为了把调用关系表示的更明显,图中的箭头线也使用了不同的颜色:
黑色箭头线:标志了函数之间的调用关系。
红色的箭头线:标志了解码的流程。
其他颜色的箭头线:标志了函数之间的调用关系。其中:
- 调用 URLProtocol 结构体中的函数用黄色箭头线标识;
- 调用 AVInputFormat 结构体中的函数用绿色箭头线标识;
- 调用 AVCodec 结构体中的函数用蓝色箭头线标识。
1.4 函数所在的文件
每个函数旁边标识了它所在的文件的路径。
此外,还有一点需要注意的是,一些 API 函数内部也调用了另一些API函数。也就是说,API函数并不一定全部都调用FFmpeg的内部函数,他也有可能调用其他的API函数。例如从图中可以看出来, avformat_close_input()
调用了 avformat_free_context()
和 avio_close()
。这些在内部代码中被调用的API函数也标记为粉红色。
1.5 函数调用关系
下面简单列出几个区域中函数之间的调用关系(函数之间的调用关系使用缩进的方式表现出来)。详细的函数分析可以参考相关的《FFmpeg源代码分析》系列文章。
1.5.1 左边区域(FFmpeg架构函数)
1. av_register_all()【函数简单分析】>
- 1) avcodec_register_all()
- (a) REGISTER_HWACCEL()
- (b) REGISTER_ENCODER()
- (c) REGISTER_DECODER()
- (d) REGISTER_PARSER()
- (e) REGISTER_BSF()
- 2) REGISTER_MUXER()
- 3) REGISTER_DEMUXER()
- 4) REGISTER_PROTOCOL()
2. avformat_alloc_context()【函数简单分析】
1) av_malloc(sizeof(AVFormatContext))
2) avformat_get_context_defaults()
- (a) av_opt_set_defaults()
3. avformat_open_input()【函数简单分析】
- 1) init_input()
- (a) avio_open2()【函数简单分析】
- a) ffurl_open()
- i. ffurl_alloc()
- url_find_protocol()
- url_alloc_for_protocol()
- ii. ffurl_connect()
- URLProtocol->url_open()
- i. ffurl_alloc()
- b) ffio_fdopen()
- i. av_malloc(buffer_size)
- ii. avio_alloc_context()
- av_mallocz(sizeof(AVIOContext))
- ffio_init_context()
- a) ffurl_open()
- (b) av_probe_input_buffer2()
- a) avio_read()
- i. AVInputFormat->read_packet()
- b) av_probe_input_format2()
- c) av_probe_input_format3()
- i. av_iformat_next()
- ii. av_match_name()
- iii. av_match_ext()
- iv. AVInputFormat->read_probe()
- a) avio_read()
- (a) avio_open2()【函数简单分析】
- 2) AVInputFormat->read_header()
4. avformat_find_stream_info()【函数简单分析】
- 1) find_decoder()
- (a) avcodec_find_decoder()
- 2) avcodec_open2()
- 3) read_frame_internal()
- 4) try_decode_frame()
- (a) avcodec_decode_video2()
- 5) avcodec_close()
- 6) estimate_timings()
- (a) estimate_timings_from_pts()
- (b) estimate_timings_from_bit_rate()
- (c) update_stream_timings()
5. avcodec_find_decoder()【函数简单分析】
- 1) find_encdec()
6. avcodec_open2()【函数简单分析】
- 1) AVCodec->init()
7. av_read_frame()【函数简单分析】
1) read_from_packet_buffer()
2) read_frame_internal()
- (a) ff_read_packet()
- a) AVInputFormat->read_packet()
- (b) parse_packet()
- a) av_parser_parse2()
- (a) ff_read_packet()
8. avcodec_decode_video2()【函数简单分析】
1) av_packet_split_side_data()
2) AVCodec-> decode()
3) av_frame_set_pkt_pos()
4) av_frame_set_best_effort_timestamp()
9. avcodec_close()【函数简单分析】
- 1) AVCodec->close()
10. avformat_close_input()【函数简单分析】
1) AVInputFormat->read_close()
2) avformat_free_context()
- (a) ff_free_stream()
3) avio_close()
- (a) avio_flush()
- a) flush_buffer()
- (b) ffurl_close()
- a) ffurl_closep()
- URLProtocol->url_close()
- a) ffurl_closep()
- (a) avio_flush()
1.5.2 右上区域(URLProtocol协议处理函数)
URLProtocol结构体包含如下协议处理函数指针:
- url_open():打开
- url_read():读取
- url_write():写入
- url_seek():调整进度
- url_close():关闭
【例子】不同的协议对应着上述接口有不同的实现函数,举几个例子:
File协议(即文件)对应的URLProtocol结构体 ff_file_protocol
:
1 | url_open() -> file_open() -> open() |
RTMP协议(libRTMP)对应的URLProtocol结构体 ff_librtmp_protocol
:
1 | url_open() -> rtmp_open() -> RTMP_Init(), RTMP_SetupURL(), RTMP_Connect(), RTMP_ConnectStream() |
UDP协议对应的URLProtocol结构体 ff_udp_protocol
:
1 | url_open() -> udp_open() |
1.5.3 右中区域(AVInputFormat封装格式处理函数)
AVInputFormat包含如下封装格式处理函数指针:
- read_probe():检查格式
- read_header():读取文件头
- read_packet():读取一帧数据
- read_seek():调整进度
- read_close():关闭
【例子】不同的封装格式对应着上述接口有不同的实现函数,举几个例子:
FLV封装格式对应的AVInputFormat结构体 ff_flv_demuxer
:
1 | read_probe() -> flv_probe() –> probe() |
MKV封装格式对应的AVInputFormat结构体 ff_matroska_demuxer
:
1 | read_probe() -> matroska_probe() |
MPEG2TS封装格式对应的AVInputFormat结构体 ff_mpegts_demuxer
:
1 | read_probe() -> mpegts_probe() |
AVI封装格式对应的AVInputFormat结构体 ff_avi_demuxer
:
1 | read_probe() -> avi_probe() |
1.5.4 右下区域(AVCodec编解码函数)
AVCodec包含如下编解码函数指针:
- init():初始化
- decode():解码一帧数据
- close():关闭
【例子】不同的编解码器对应着上述接口有不同的实现函数,举几个例子:
HEVC解码对应的AVCodec结构体 ff_hevc_decoder
:
1 | init() -> hevc_decode_init() |
H.264解码对应的AVCodec结构体 ff_h264_decoder
:
1 | init() -> ff_h264_decode_init() |
VP8解码(libVPX)对应的AVCodec结构体 ff_libvpx_vp8_decoder
:
1 | init() -> vpx_init() -> vpx_codec_dec_init() |
MPEG2解码对应的AVCodec结构体 ff_mpeg2video_decoder
:
1 | init() -> mpeg_decode_init() |
1.6 avformat_open_input() 函数
2. FFmpeg源代码结构图 - 编码
2.1 函数调用关系图
下图表明了FFmpeg在编码一个视频的时候的函数调用流程。为了保证结构清晰,其中仅列出了最关键的函数,剔除了其它不是特别重要的函数。
下面解释一下图中关键标记的含义。
2.2 函数背景色
函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:
- 粉红色背景函数:FFmpeg 的 API 函数。
- 白色背景的函数:FFmpeg 的内部函数。
- 黄色背景的函数:URLProtocol 结构体中的函数,包含了读写各种协议的功能。
- 绿色背景的函数:AVOutputFormat 结构体中的函数,包含了读写各种封装格式的功能。
- 蓝色背景的函数:AVCodec 结构体中的函数,包含了编解码的功能。
2.3 区域
整个关系图可以分为以下几个区域:
- 左边区域——架构函数区域:这些函数并不针对某一特定的视频格式。
- 右上方黄色区域——协议处理函数区域:不同的协议(RTP,RTMP,FILE)会调用不同的协议处理函数。
- 右边中间绿色区域——封装格式处理函数区域:不同的封装格式(MKV,FLV,MPEG2TS,AVI)会调用不同的封装格式处理函数。
- 右边下方蓝色区域——编解码函数区域:不同的编码标准(HEVC,H.264,MPEG2)会调用不同的编解码函数。
2.4 箭头线
为了把调用关系表示的更明显,图中的箭头线也使用了不同的颜色:
红色的箭头线:标志了编码的流程。
其他颜色的箭头线:标志了函数之间的调用关系。其中:
- 调用 URLProtocol 结构体中的函数用黄色箭头线标识;
- 调用 AVOutputFormat 结构体中的函数用绿色箭头线标识;
- 调用 AVCodec 结构体中的函数用蓝色箭头线标识。
2.5 函数所在的文件
每个函数标识了它所在的文件路径。
2.6 函数功能简述
下面简单列出几个区域中函数之间的调用关系(函数之间的调用关系使用缩进的方式表现出来)。详细的函数分析可以参考相关的《FFmpeg源代码分析》系列文章。
2.6.1 左边区域(架构函数)
1. av_register_all()【函数简单分析】
1) avcodec_register_all()
- (a) REGISTER_HWACCEL()
- (b) REGISTER_ENCODER()
- (c) REGISTER_DECODER()
- (d) REGISTER_PARSER()
- (e) REGISTER_BSF()
2) REGISTER_MUXER()
3) REGISTER_DEMUXER()
4) REGISTER_PROTOCOL()
2. avformat_alloc_output_context2()【函数简单分析】
1) avformat_alloc_context()
(a) av_malloc(sizeof(AVFormatContext))
(b) avformat_get_context_defaults()
- a) av_opt_set_defaults()
2) av_guess_format()
- (a) av_oformat_next()
- (b) av_match_name()
- (c) av_match_ext()
3. avio_open2()【函数简单分析】
1) ffurl_open()
- (a) ffurl_alloc()
- a) url_find_protocol()
- b) url_alloc_for_protocol()
- (b) ffurl_connect()
- a) URLProtocol->url_open()
- (a) ffurl_alloc()
2) ffio_fdopen()
- (a) av_malloc(buffer_size)
- (b) avio_alloc_context()
- a) av_mallocz(sizeof(AVIOContext))
- b) ffio_init_context()
4. avformat_new_stream()【函数简单分析】
1) av_mallocz(sizeof(AVStream))
2) avcodec_alloc_context3()
- (a) av_malloc(sizeof(AVCodecContext))
- (b) avcodec_get_context_defaults3()
5. avcodec_find_encoder()【函数简单分析】
- 1) find_encdec()
6. avcodec_open2()【函数简单分析】
- 1) AVCodec->init()
7. avformat_write_header()【函数简单分析】
1) init_muxer()
2) AVOutputFormat->write_header()
3) init_pts()
8. avcodec_encode_video2()【函数简单分析】
- 1) AVCodec->encode2()
9. av_write_frame()【函数简单分析】
1) check_packet()
2) compute_pkt_fields2()
3) write_packet()
- (a) AVOutputFormat->write_packet()
10. av_write_trailer()【函数简单分析】
1) write_packet()
2) AVOutputFormat->write_trailer()
11. avcodec_close()【函数简单分析】
- 1) AVCodec->close()
12. avformat_free_context()【函数简单分析】
- 1) ff_free_stream()
13. avio_close()【函数简单分析】
- 1) avio_flush()
- (a) flush_buffer()
- 2) ffurl_close()
- (a) ffurl_closep()
- a) URLProtocol->url_close()
- (a) ffurl_closep()
2.6.2 右上区域(URLProtocol协议处理函数)
URLProtocol结构体包含如下协议处理函数指针:
- url_open():打开
- url_read():读取
- url_write():写入
- url_seek():调整进度
- url_close():关闭
【例子】不同的协议对应着上述接口有不同的实现函数,举几个例子:
File协议(即文件)对应的URLProtocol结构体 ff_file_protocol
:
1 | url_open() -> file_open() -> open() |
RTMP协议(libRTMP)对应的URLProtocol结构体 ff_librtmp_protocol
:
1 | url_open() -> rtmp_open() -> RTMP_Init(), RTMP_SetupURL(), RTMP_Connect(), RTMP_ConnectStream() |
UDP协议对应的URLProtocol结构体 ff_udp_protocol
:
1 | url_open() -> udp_open() |
2.6.3 右中区域(AVOutputFormat封装格式处理函数)
AVOutputFormat包含如下封装格式处理函数指针:
- write_header():写文件头
- write_packet():写一帧数据
- write_trailer():写文件尾
【例子】不同的封装格式对应着上述接口有不同的实现函数,举几个例子:
FLV封装格式对应的AVOutputFormat结构体 ff_flv_muxer
:
1 | write_header() -> flv_write_header() |
MKV封装格式对应的AVOutputFormat结构体 ff_matroska_muxer
:
1 | write_header() -> mkv_write_header() |
MPEG2TS封装格式对应的AVOutputFormat结构体 ff_mpegts_muxer
:
1 | write_header() -> mpegts_write_header() |
AVI封装格式对应的AVOutputFormat结构体 ff_avi_muxer
:
1 | write_header() -> avi_write_header() |
2.6.4 右下区域(AVCodec编解码函数)
AVCodec包含如下编解码函数指针:
- init():初始化
- encode2():编码一帧数据
- close():关闭
【例子】不同的编解码器对应着上述接口有不同的实现函数,举几个例子:
HEVC编码器对应的AVCodec结构体 ff_libx265_encoder
:
1 | init() -> libx265_encode_init() -> x265_param_alloc(), x265_param_default_preset(), x265_encoder_open() |
H.264编码器对应的AVCodec结构体 ff_libx264_encoder
:
1 | init() -> X264_init() -> x264_param_default(), x264_encoder_open(), x264_encoder_headers() |
VP8编码器(libVPX)对应的AVCodec结构体 ff_libvpx_vp8_encoder
:
1 | init() -> vpx_init() -> vpx_codec_enc_config_default() |
MPEG2编码器对应的AVCodec结构体 ff_mpeg2video_encoder
:
1 | init() -> encode_init() |