当前位置:   article > 正文

FFmpeg源代码简单分析-架构图-解码_雷霄骅 ffmpeg源码分析

雷霄骅 ffmpeg源码分析

参考链接

函数背景色

  • 函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:
    • 粉红色背景函数: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函数也标记为粉红色。

现在的执行流程

模块介绍 

右上区域(URLProtocol协议处理函数)

  • URLProtocol结构体包含如下协议处理函数指针:
    • url_open():打开
    • url_read():读取
    • url_write():写入
    • url_seek():调整进度
    • url_close():关闭
  • 结构体代码
  1. typedef struct URLProtocol {
  2. const char *name;
  3. int (*url_open)( URLContext *h, const char *url, int flags);
  4. /**
  5. * This callback is to be used by protocols which open further nested
  6. * protocols. options are then to be passed to ffurl_open_whitelist()
  7. * or ffurl_connect() for those nested protocols.
  8. */
  9. int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
  10. int (*url_accept)(URLContext *s, URLContext **c);
  11. int (*url_handshake)(URLContext *c);
  12. /**
  13. * Read data from the protocol.
  14. * If data is immediately available (even less than size), EOF is
  15. * reached or an error occurs (including EINTR), return immediately.
  16. * Otherwise:
  17. * In non-blocking mode, return AVERROR(EAGAIN) immediately.
  18. * In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
  19. * and return AVERROR(EAGAIN) on timeout.
  20. * Checking interrupt_callback, looping on EINTR and EAGAIN and until
  21. * enough data has been read is left to the calling function; see
  22. * retry_transfer_wrapper in avio.c.
  23. */
  24. int (*url_read)( URLContext *h, unsigned char *buf, int size);
  25. int (*url_write)(URLContext *h, const unsigned char *buf, int size);
  26. int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
  27. int (*url_close)(URLContext *h);
  28. int (*url_read_pause)(URLContext *h, int pause);
  29. int64_t (*url_read_seek)(URLContext *h, int stream_index,
  30. int64_t timestamp, int flags);
  31. int (*url_get_file_handle)(URLContext *h);
  32. int (*url_get_multi_file_handle)(URLContext *h, int **handles,
  33. int *numhandles);
  34. int (*url_get_short_seek)(URLContext *h);
  35. int (*url_shutdown)(URLContext *h, int flags);
  36. const AVClass *priv_data_class;
  37. int priv_data_size;
  38. int flags;
  39. int (*url_check)(URLContext *h, int mask);
  40. int (*url_open_dir)(URLContext *h);
  41. int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
  42. int (*url_close_dir)(URLContext *h);
  43. int (*url_delete)(URLContext *h);
  44. int (*url_move)(URLContext *h_src, URLContext *h_dst);
  45. const char *default_whitelist;
  46. } 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()
  1. const URLProtocol ff_file_protocol = {
  2. .name = "file",
  3. .url_open = file_open,
  4. .url_read = file_read,
  5. .url_write = file_write,
  6. .url_seek = file_seek,
  7. .url_close = file_close,
  8. .url_get_file_handle = file_get_handle,
  9. .url_check = file_check,
  10. .url_delete = file_delete,
  11. .url_move = file_move,
  12. .priv_data_size = sizeof(FileContext),
  13. .priv_data_class = &file_class,
  14. .url_open_dir = file_open_dir,
  15. .url_read_dir = file_read_dir,
  16. .url_close_dir = file_close_dir,
  17. .default_whitelist = "file,crypto,data"
  18. };
  • 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()
  1. RTMP_CLASS(rtmp)
  2. const URLProtocol ff_librtmp_protocol = {
  3. .name = "rtmp",
  4. .url_open = rtmp_open,
  5. .url_read = rtmp_read,
  6. .url_write = rtmp_write,
  7. .url_close = rtmp_close,
  8. .url_read_pause = rtmp_read_pause,
  9. .url_read_seek = rtmp_read_seek,
  10. .url_get_file_handle = rtmp_get_file_handle,
  11. .priv_data_size = sizeof(LibRTMPContext),
  12. .priv_data_class = &librtmp_class,
  13. .flags = URL_PROTOCOL_FLAG_NETWORK,
  14. };
  • 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()
  1. const URLProtocol ff_udp_protocol = {
  2. .name = "udp",
  3. .url_open = udp_open,
  4. .url_read = udp_read,
  5. .url_write = udp_write,
  6. .url_close = udp_close,
  7. .url_get_file_handle = udp_get_file_handle,
  8. .priv_data_size = sizeof(UDPContext),
  9. .priv_data_class = &udp_class,
  10. .flags = URL_PROTOCOL_FLAG_NETWORK,
  11. };

右中区域(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
  1. = {
  2. .name = "flv",
  3. .long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
  4. .priv_data_size = sizeof(FLVContext),
  5. .read_probe = flv_probe,
  6. .read_header = flv_read_header,
  7. .read_packet = flv_read_packet,
  8. .read_seek = flv_read_seek,
  9. .read_close = flv_read_close,
  10. .extensions = "flv",
  11. .priv_class = &flv_class,
  12. }
  • 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
  1. = {
  2. .name = "matroska,webm",
  3. .long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
  4. .extensions = "mkv,mk3d,mka,mks",
  5. .priv_data_size = sizeof(MatroskaDemuxContext),
  6. .read_probe = matroska_probe,
  7. .read_header = matroska_read_header,
  8. .read_packet = matroska_read_packet,
  9. .read_close = matroska_read_close,
  10. .read_seek = matroska_read_seek,
  11. .mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska"
  12. }
  • 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
  1. = {
  2. .name = "mpegts",
  3. .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
  4. .priv_data_size = sizeof(MpegTSContext),
  5. .read_probe = mpegts_probe,
  6. .read_header = mpegts_read_header,
  7. .read_packet = mpegts_read_packet,
  8. .read_close = mpegts_read_close,
  9. .read_timestamp = mpegts_get_dts,
  10. .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,
  11. .priv_class = &mpegts_class,
  12. }
  • 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
  1. = {
  2. .name = "avi",
  3. .long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),
  4. .priv_data_size = sizeof(AVIContext),
  5. .extensions = "avi",
  6. .read_probe = avi_probe,
  7. .read_header = avi_read_header,
  8. .read_packet = avi_read_packet,
  9. .read_close = avi_read_close,
  10. .read_seek = avi_read_seek,
  11. .priv_class = &demuxer_class,
  12. }

右下区域(AVCodec编解码函数)

  • AVCodec包含如下编解码函数指针:
    • init():初始化
    • decode():解码一帧数据
    • close():关闭
  • 参考链接:FFmpeg: AVCodec Struct Reference
  • 上述参考链接里面包含的结构体包含上述编解码函数指针
  • 但是新版本好像没有了
  1. typedef struct AVCodec {
  2. /**
  3. * Name of the codec implementation.
  4. * The name is globally unique among encoders and among decoders (but an
  5. * encoder and a decoder can share the same name).
  6. * This is the primary way to find a codec from the user perspective.
  7. */
  8. const char *name;
  9. /**
  10. * Descriptive name for the codec, meant to be more human readable than name.
  11. * You should use the NULL_IF_CONFIG_SMALL() macro to define it.
  12. */
  13. const char *long_name;
  14. enum AVMediaType type;
  15. enum AVCodecID id;
  16. /**
  17. * Codec capabilities.
  18. * see AV_CODEC_CAP_*
  19. */
  20. int capabilities;
  21. uint8_t max_lowres; ///< maximum value for lowres supported by the decoder
  22. const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
  23. const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
  24. const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
  25. const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
  26. #if FF_API_OLD_CHANNEL_LAYOUT
  27. /**
  28. * @deprecated use ch_layouts instead
  29. */
  30. attribute_deprecated
  31. const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0
  32. #endif
  33. const AVClass *priv_class; ///< AVClass for the private context
  34. const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
  35. /**
  36. * Group name of the codec implementation.
  37. * This is a short symbolic name of the wrapper backing this codec. A
  38. * wrapper uses some kind of external implementation for the codec, such
  39. * as an external library, or a codec implementation provided by the OS or
  40. * the hardware.
  41. * If this field is NULL, this is a builtin, libavcodec native codec.
  42. * If non-NULL, this will be the suffix in AVCodec.name in most cases
  43. * (usually AVCodec.name will be of the form "<codec_name>_<wrapper_name>").
  44. */
  45. const char *wrapper_name;
  46. /**
  47. * Array of supported channel layouts, terminated with a zeroed layout.
  48. */
  49. const AVChannelLayout *ch_layouts;
  50. } 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
  1. = {
  2. .name = "hevc",
  3. .long_name = NULL_IF_CONFIG_SMALL("HEVC (High Efficiency Video Coding)"),
  4. .type = AVMEDIA_TYPE_VIDEO,
  5. .id = AV_CODEC_ID_HEVC,
  6. .priv_data_size = sizeof(HEVCContext),
  7. .priv_class = &hevc_decoder_class,
  8. .init = hevc_decode_init,
  9. .close = hevc_decode_free,
  10. .decode = ,
  11. .flush = hevc_decode_flush,
  12. .update_thread_context = hevc_update_thread_context,
  13. .init_thread_copy = hevc_init_thread_copy,
  14. .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
  15. AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS,
  16. .profiles = NULL_IF_CONFIG_SMALL(profiles),
  17. }
  • H.264解码对应的AVCodec结构体ff_h264_decoder:
    • init() -> ff_h264_decode_init()
    • decode() -> h264_decode_frame() -> decode_nal_units()
    • close() -> h264_decode_end()
  • 参考链接:FFmpeg: libavcodec/h264.c File Reference
  1. = {
  2. .name = "h264",
  3. .long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
  4. .type = AVMEDIA_TYPE_VIDEO,
  5. .id = AV_CODEC_ID_H264,
  6. .priv_data_size = sizeof(H264Context),
  7. .init = ff_h264_decode_init,
  8. .close = h264_decode_end,
  9. .decode = h264_decode_frame,
  10. .capabilities = AV_CODEC_CAP_DR1 |
  11. AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
  12. AV_CODEC_CAP_FRAME_THREADS,
  13. .flush = flush_dpb,
  14. .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),
  15. .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),
  16. .profiles = NULL_IF_CONFIG_SMALL(profiles),
  17. .priv_class = &h264_class,
  18. }
  • 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
  1. AVCodec ff_libvpx_vp8_decoder = {
  2. .name = "libvpx",
  3. .type = AVMEDIA_TYPE_VIDEO,
  4. .id = AV_CODEC_ID_VP8,
  5. .priv_data_size = sizeof(VP8Context),
  6. .init = vp8_init,
  7. .close = vp8_free,
  8. .decode = vp8_decode,
  9. .capabilities = CODEC_CAP_AUTO_THREADS | CODEC_CAP_DR1,
  10. .long_name = NULL_IF_CONFIG_SMALL("libvpx VP8"),
  11. };
  1. = {
  2. .name = "mpeg2video",
  3. .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 video"),
  4. .type = AVMEDIA_TYPE_VIDEO,
  5. .id = AV_CODEC_ID_MPEG2VIDEO,
  6. .priv_data_size = sizeof(Mpeg1Context),
  7. .init = mpeg_decode_init,
  8. .close = mpeg_decode_end,
  9. .decode = mpeg_decode_frame,
  10. .capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 |
  11. AV_CODEC_CAP_TRUNCATED | AV_CODEC_CAP_DELAY |
  12. AV_CODEC_CAP_SLICE_THREADS,
  13. .flush = flush,
  14. .max_lowres = 3,
  15. .profiles = NULL_IF_CONFIG_SMALL(mpeg2_video_profiles),
  16. }

 注意事项

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/901539
推荐阅读
相关标签
  

闽ICP备14008679号