参考链接
![](https://i-blog.csdnimg.cn/blog_migrate/2aab79e0b33f46846f16ff1b6c3105f3.png)
函数背景色
- 函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:
- 粉红色背景函数:FFmpeg的API函数。
- 白色背景的函数:FFmpeg的内部函数。
- 黄色背景的函数:URLProtocol结构体中的函数,包含处理协议(Protocol)的功能。
- 绿色背景的函数:AVInputFormat结构体中的函数,包含处理封装格式(Format)的功能。
- 蓝色背景的函数:AVCodec结构体中的函数,包含了编解码器(Codec)的功能。
- PS:URLProtocol,AVInputFormat,AVCodec在FFmpeg开始运行并且注册完组件之后,都会分别被连接成一个个的链表。因此实际上是有很多的URLProtocol,AVInputFormat,AVCodec的。
- 图中画出了解码一个输入协议是“文件”(其实就是打开一个文件。“文件”也被当做是一种广义的协议),封装格式为FLV,视频编码格式是H.264的数据的函数调用关系。
区域
- 整个架构图可以分为以下几个区域:
- 左边区域——架构函数区域:这些函数并不针对某一特定的视频格式,通用架构,通用流程
- 右上方黄色区域——协议处理函数区域:不同的协议(RTP,RTMP,FILE)会调用不同的协议处理函数。
- 右边中间绿色区域——封装格式处理函数区域:不同的封装格式(MKV,FLV,MPEGTS,AVI)会调用不同的封装格式处理函数。
- 右边下方蓝色区域——编解码函数区域:不同的编码标准(HEVC,H.264,MPEG2)会调用不同的编解码函数。
箭头线
- 为了把调用关系表示的更明显,图中的箭头线也使用了不同的颜色:
- 黑色箭头线:标志了函数之间的调用关系。
- 红色的箭头线:标志了解码的流程。
- 其他颜色的箭头线:标志了函数之间的调用关系。其中:
- 调用URLProtocol结构体中的函数用黄色箭头线标识;
- 调用AVInputFormat结构体中的函数用绿色箭头线标识;
- 调用AVCodec结构体中的函数用蓝色箭头线标识。
函数所在的文件
- 每个函数旁边标识了它所在的文件的路径。
- 此外,还有一点需要注意的是,一些API函数内部也调用了另一些API函数。也就是说,API函数并不一定全部都调用FFmpeg的内部函数,他也有可能调用其他的API函数。
- 例如从图中可以看出来,avformat_close_input()调用了avformat_free_context()和avio_close()。这些在内部代码中被调用的API函数也标记为粉红色。
现在的执行流程
![](https://i-blog.csdnimg.cn/blog_migrate/467cf870e705cf309f7be536a9e76d88.png)
模块介绍
右上区域(URLProtocol协议处理函数)
- URLProtocol结构体包含如下协议处理函数指针:
- url_open():打开
- url_read():读取
- url_write():写入
- url_seek():调整进度
- url_close():关闭
- 结构体代码
typedef struct URLProtocol {
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
/**
* This callback is to be used by protocols which open further nested
* protocols. options are then to be passed to ffurl_open_whitelist()
* or ffurl_connect() for those nested protocols.
*/
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
int (*url_accept)(URLContext *s, URLContext **c);
int (*url_handshake)(URLContext *c);
/**
* Read data from the protocol.
* If data is immediately available (even less than size), EOF is
* reached or an error occurs (including EINTR), return immediately.
* Otherwise:
* In non-blocking mode, return AVERROR(EAGAIN) immediately.
* In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
* and return AVERROR(EAGAIN) on timeout.
* Checking interrupt_callback, looping on EINTR and EAGAIN and until
* enough data has been read is left to the calling function; see
* retry_transfer_wrapper in avio.c.
*/
int (*url_read)( URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
int (*url_read_pause)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, int stream_index,
int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
int (*url_get_multi_file_handle)(URLContext *h, int **handles,
int *numhandles);
int (*url_get_short_seek)(URLContext *h);
int (*url_shutdown)(URLContext *h, int flags);
const AVClass *priv_data_class;
int priv_data_size;
int flags;
int (*url_check)(URLContext *h, int mask);
int (*url_open_dir)(URLContext *h);
int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
int (*url_close_dir)(URLContext *h);
int (*url_delete)(URLContext *h);
int (*url_move)(URLContext *h_src, URLContext *h_dst);
const char *default_whitelist;
} URLProtocol;
注意事项:
- 【例子】不同的协议对应着上述接口有不同的实现函数,举几个例子:
- File协议(即文件)对应的URLProtocol结构体ff_file_protocol:
- ff_file_protocol结构体的文件在<libavformat/file.c>
- url_open() -> file_open() -> open()
- url_read() -> file_read() -> read()
- url_write() -> file_write() -> write()
- url_seek() -> file_seek() -> lseek()
- url_close() -> file_close() -> close()
const URLProtocol ff_file_protocol = {
.name = "file",
.url_open = file_open,
.url_read = file_read,
.url_write = file_write,
.url_seek = file_seek,
.url_close = file_close,
.url_get_file_handle = file_get_handle,
.url_check = file_check,
.url_delete = file_delete,
.url_move = file_move,
.priv_data_size = sizeof(FileContext),
.priv_data_class = &file_class,
.url_open_dir = file_open_dir,
.url_read_dir = file_read_dir,
.url_close_dir = file_close_dir,
.default_whitelist = "file,crypto,data"
};
- RTMP协议(libRTMP)对应的URLProtocol结构体ff_librtmp_protocol:
- ff_librtmp_protocol结构体的文件在<libavformat/protocol>
- url_open() -> rtmp_open() -> RTMP_Init(), RTMP_SetupURL(), RTMP_Connect(), RTMP_ConnectStream()
- url_read() -> rtmp_read() -> RTMP_Read()
- url_write() -> rtmp_write() -> RTMP_Write()
- url_seek() -> rtmp_read_seek() -> RTMP_SendSeek()
- url_close() -> rtmp_close() -> RTMP_Close()
RTMP_CLASS(rtmp)
const URLProtocol ff_librtmp_protocol = {
.name = "rtmp",
.url_open = rtmp_open,
.url_read = rtmp_read,
.url_write = rtmp_write,
.url_close = rtmp_close,
.url_read_pause = rtmp_read_pause,
.url_read_seek = rtmp_read_seek,
.url_get_file_handle = rtmp_get_file_handle,
.priv_data_size = sizeof(LibRTMPContext),
.priv_data_class = &librtmp_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
};
- UDP协议对应的URLProtocol结构体ff_udp_protocol:
- ff_udp_protocol结构体位于<libavformat/udp.c>
- url_open() -> udp_open()
- url_read() -> udp_read()
- url_write() -> udp_write()
- url_seek() -> udp_close()
- url_close() -> udp_close()
const URLProtocol ff_udp_protocol = {
.name = "udp",
.url_open = udp_open,
.url_read = udp_read,
.url_write = udp_write,
.url_close = udp_close,
.url_get_file_handle = udp_get_file_handle,
.priv_data_size = sizeof(UDPContext),
.priv_data_class = &udp_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
};
右中区域(AVInputFormat封装格式处理函数)
- AVInputFormat包含如下封装格式处理函数指针:
- read_probe():检查格式
- read_header():读取文件头
- read_packet():读取一帧数据
- read_seek():调整进度
- read_close():关闭
注意事项:
- 【例子】不同的封装格式对应着上述接口有不同的实现函数,举几个例子:
- FLV封装格式对应的AVInputFormat结构体ff_flv_demuxer:
- read_probe() -> flv_probe() –> probe()
- read_header() -> flv_read_header() -> create_stream() -> avformat_new_stream()
- read_packet() -> flv_read_packet()
- read_seek() -> flv_read_seek()
- read_close() -> flv_read_close()
- FFmpeg: libavformat/flvdec.c File Reference
= {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.priv_data_size = sizeof(FLVContext),
.read_probe = flv_probe,
.read_header = flv_read_header,
.read_packet = flv_read_packet,
.read_seek = flv_read_seek,
.read_close = flv_read_close,
.extensions = "flv",
.priv_class = &flv_class,
}
- MKV封装格式对应的AVInputFormat结构体ff_matroska_demuxer:
- read_probe() -> matroska_probe()
- read_header() -> matroska_read_header()
- read_packet() -> matroska_read_packet()
- read_seek() -> matroska_read_seek()
- read_close() -> matroska_read_close()
- 参考链接:FFmpeg: libavformat/matroskadec.c File Reference
= {
.name = "matroska,webm",
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
.extensions = "mkv,mk3d,mka,mks",
.priv_data_size = sizeof(MatroskaDemuxContext),
.read_probe = matroska_probe,
.read_header = matroska_read_header,
.read_packet = matroska_read_packet,
.read_close = matroska_read_close,
.read_seek = matroska_read_seek,
.mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska"
}
- MPEG2TS封装格式对应的AVInputFormat结构体ff_mpegts_demuxer:
- read_probe() -> mpegts_probe()
- read_header() -> mpegts_read_header()
- read_packet() -> mpegts_read_packet()
- read_close() -> mpegts_read_close()
- 参考链接:FFmpeg: libavformat/mpegts.c File Reference
= {
.name = "mpegts",
.long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
.priv_data_size = sizeof(MpegTSContext),
.read_probe = mpegts_probe,
.read_header = mpegts_read_header,
.read_packet = mpegts_read_packet,
.read_close = mpegts_read_close,
.read_timestamp = mpegts_get_dts,
.flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,
.priv_class = &mpegts_class,
}
- AVI封装格式对应的AVInputFormat结构体ff_avi_demuxer:
- read_probe() -> avi_probe()
- read_header() -> avi_read_header()
- read_packet() -> avi_read_packet()
- read_seek() -> avi_read_seek()
- read_close() -> avi_read_close()
- 参考链接:FFmpeg: libavformat/avidec.c File Reference
= {
.name = "avi",
.long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),
.priv_data_size = sizeof(AVIContext),
.extensions = "avi",
.read_probe = avi_probe,
.read_header = avi_read_header,
.read_packet = avi_read_packet,
.read_close = avi_read_close,
.read_seek = avi_read_seek,
.priv_class = &demuxer_class,
}
右下区域(AVCodec编解码函数)
typedef struct AVCodec {
/**
* Name of the codec implementation.
* The name is globally unique among encoders and among decoders (but an
* encoder and a decoder can share the same name).
* This is the primary way to find a codec from the user perspective.
*/
const char *name;
/**
* Descriptive name for the codec, meant to be more human readable than name.
* You should use the NULL_IF_CONFIG_SMALL() macro to define it.
*/
const char *long_name;
enum AVMediaType type;
enum AVCodecID id;
/**
* Codec capabilities.
* see AV_CODEC_CAP_*
*/
int capabilities;
uint8_t max_lowres; ///< maximum value for lowres supported by the decoder
const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
#if FF_API_OLD_CHANNEL_LAYOUT
/**
* @deprecated use ch_layouts instead
*/
attribute_deprecated
const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0
#endif
const AVClass *priv_class; ///< AVClass for the private context
const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
/**
* Group name of the codec implementation.
* This is a short symbolic name of the wrapper backing this codec. A
* wrapper uses some kind of external implementation for the codec, such
* as an external library, or a codec implementation provided by the OS or
* the hardware.
* If this field is NULL, this is a builtin, libavcodec native codec.
* If non-NULL, this will be the suffix in AVCodec.name in most cases
* (usually AVCodec.name will be of the form "<codec_name>_<wrapper_name>").
*/
const char *wrapper_name;
/**
* Array of supported channel layouts, terminated with a zeroed layout.
*/
const AVChannelLayout *ch_layouts;
} AVCodec;
注意事项:
- 【例子】不同的编解码器对应着上述接口有不同的实现函数,举几个例子:
- HEVC解码对应的AVCodec结构体ff_hevc_decoder:
- init() -> hevc_decode_init()
- decode() -> hevc_decode_frame() -> decode_nal_units()
- close() -> hevc_decode_free()
- 参考链接:FFmpeg: libavcodec/hevc.c File Reference
= {
.name = "hevc",
.long_name = NULL_IF_CONFIG_SMALL("HEVC (High Efficiency Video Coding)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_HEVC,
.priv_data_size = sizeof(HEVCContext),
.priv_class = &hevc_decoder_class,
.init = hevc_decode_init,
.close = hevc_decode_free,
.decode = ,
.flush = hevc_decode_flush,
.update_thread_context = hevc_update_thread_context,
.init_thread_copy = hevc_init_thread_copy,
.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS,
.profiles = NULL_IF_CONFIG_SMALL(profiles),
}
= {
.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 = ff_h264_decode_init,
.close = h264_decode_end,
.decode = h264_decode_frame,
.capabilities = AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_FRAME_THREADS,
.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(profiles),
.priv_class = &h264_class,
}
- VP8解码(libVPX)对应的AVCodec结构体ff_libvpx_vp8_decoder:
- init() -> vpx_init() -> vpx_codec_dec_init()
- decode() -> vp8_decode() -> vpx_codec_decode(), vpx_codec_get_frame()
- close() -> vp8_free() -> vpx_codec_destroy()
- 版本差异
- 参考链接:FFmpeg: libavcodec/libvpxdec.c Source File
AVCodec ff_libvpx_vp8_decoder = {
.name = "libvpx",
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_VP8,
.priv_data_size = sizeof(VP8Context),
.init = vp8_init,
.close = vp8_free,
.decode = vp8_decode,
.capabilities = CODEC_CAP_AUTO_THREADS | CODEC_CAP_DR1,
.long_name = NULL_IF_CONFIG_SMALL("libvpx VP8"),
};
= {
.name = "mpeg2video",
.long_name = NULL_IF_CONFIG_SMALL("MPEG-2 video"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_MPEG2VIDEO,
.priv_data_size = sizeof(Mpeg1Context),
.init = mpeg_decode_init,
.close = mpeg_decode_end,
.decode = mpeg_decode_frame,
.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_TRUNCATED | AV_CODEC_CAP_DELAY |
AV_CODEC_CAP_SLICE_THREADS,
.flush = flush,
.max_lowres = 3,
.profiles = NULL_IF_CONFIG_SMALL(mpeg2_video_profiles),
}
注意事项