FFmpeg源代码简单分析-架构图-编码

参考链接

  • FFmpeg源代码结构图 - 编码_雷霄骅的博客-CSDN博客_ffmpeg 源码

FFmpeg源代码简单分析-架构图-编码_第1张图片

函数背景色

  • 函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:
    • 粉红色背景函数:FFmpeg的API函数。
    • 白色背景的函数:FFmpeg的内部函数。
    • 黄色背景的函数:URLProtocol结构体中的函数,包含了读写各种协议的功能。
    • 绿色背景的函数:AVOutputFormat结构体中的函数,包含了读写各种封装格式的功能。
    • 蓝色背景的函数:AVCodec结构体中的函数,包含了编解码的功能。

区域

  • 整个关系图可以分为以下几个区域:
    • 左边区域——架构函数区域:这些函数并不针对某一特定的视频格式。
    • 右上方黄色区域——协议处理函数区域:不同的协议(RTP,RTMP,FILE)会调用不同的协议处理函数。
    • 右边中间绿色区域——封装格式处理函数区域:不同的封装格式(MKV,FLV,MPEG2TS,AVI)会调用不同的封装格式处理函数。
    • 右边下方蓝色区域——编解码函数区域:不同的编码标准(HEVC,H.264,MPEG2)会调用不同的编解码函数。

箭头线

  • 为了把调用关系表示的更明显,图中的箭头线也使用了不同的颜色:
    • 红色的箭头线:标志了编码的流程。
    • 其他颜色的箭头线:标志了函数之间的调用关系。其中:
      • 调用URLProtocol结构体中的函数用黄色箭头线标识;
      • 调用AVOutputFormat结构体中的函数用绿色箭头线标识;
      • 调用AVCodec结构体中的函数用蓝色箭头线标识。

函数所在的文件

  • 每个函数标识了它所在的文件路径。

模块介绍

  • 和解码重合的内容,请参考下面的链接
  • ​​​​​​FFmpeg源代码简单分析-架构图-解码_MY CUP OF TEA的博客-CSDN博客

右中区域(AVOutputFormat封装格式处理函数)

  • AVOutputFormat包含如下封装格式处理函数指针:
    • write_header():写文件头
    • write_packet():写一帧数据
    • write_trailer():写文件尾
  • 参考链接:FFmpeg: AVOutputFormat Struct Reference
typedef struct AVOutputFormat {
    const char *name;
    /**
     * Descriptive name for the format, meant to be more human-readable
     * than name. You should use the NULL_IF_CONFIG_SMALL() macro
     * to define it.
     */
    const char *long_name;
    const char *mime_type;
    const char *extensions; /**< comma-separated filename extensions */
    /* output support */
    enum AVCodecID audio_codec;    /**< default audio codec */
    enum AVCodecID video_codec;    /**< default video codec */
    enum AVCodecID subtitle_codec; /**< default subtitle codec */
    /**
     * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER,
     * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS,
     * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH,
     * AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE
     */
    int flags;

    /**
     * List of supported codec_id-codec_tag pairs, ordered by "better
     * choice first". The arrays are all terminated by AV_CODEC_ID_NONE.
     */
    const struct AVCodecTag * const *codec_tag;


    const AVClass *priv_class; ///< AVClass for the private context

    /*****************************************************************
     * No fields below this line are part of the public API. They
     * may not be used outside of libavformat and can be changed and
     * removed at will.
     * New public fields should be added right above.
     *****************************************************************
     */
    /**
     * size of private data so that it can be allocated in the wrapper
     */
    int priv_data_size;

    /**
     * Internal flags. See FF_FMT_FLAG_* in internal.h.
     */
    int flags_internal;

    int (*write_header)(struct AVFormatContext *);
    /**
     * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags,
     * pkt can be NULL in order to flush data buffered in the muxer.
     * When flushing, return 0 if there still is more data to flush,
     * or 1 if everything was flushed and there is no more buffered
     * data.
     */
    int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
    int (*write_trailer)(struct AVFormatContext *);
    /**
     * A format-specific function for interleavement.
     * If unset, packets will be interleaved by dts.
     *
     * @param s           An AVFormatContext for output. pkt will be added to
     *                    resp. taken from its packet buffer.
     * @param[in,out] pkt A packet to be interleaved if has_packet is set;
     *                    also used to return packets. If no packet is returned
     *                    (e.g. on error), pkt is blank on return.
     * @param flush       1 if no further packets are available as input and
     *                    all remaining packets should be output.
     * @param has_packet  If set, pkt contains a packet to be interleaved
     *                    on input; otherwise pkt is blank on input.
     * @return 1 if a packet was output, 0 if no packet could be output,
     *         < 0 if an error occurred
     */
    int (*interleave_packet)(struct AVFormatContext *s, AVPacket *pkt,
                             int flush, int has_packet);
    /**
     * Test if the given codec can be stored in this container.
     *
     * @return 1 if the codec is supported, 0 if it is not.
     *         A negative number if unknown.
     *         MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC
     */
    int (*query_codec)(enum AVCodecID id, int std_compliance);

    void (*get_output_timestamp)(struct AVFormatContext *s, int stream,
                                 int64_t *dts, int64_t *wall);
    /**
     * Allows sending messages from application to device.
     */
    int (*control_message)(struct AVFormatContext *s, int type,
                           void *data, size_t data_size);

    /**
     * Write an uncoded AVFrame.
     *
     * See av_write_uncoded_frame() for details.
     *
     * The library will free *frame afterwards, but the muxer can prevent it
     * by setting the pointer to NULL.
     */
    int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index,
                               AVFrame **frame, unsigned flags);
    /**
     * Returns device list with it properties.
     * @see avdevice_list_devices() for more details.
     */
    int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
    enum AVCodecID data_codec; /**< default data codec */
    /**
     * Initialize format. May allocate data here, and set any AVFormatContext or
     * AVStream parameters that need to be set before packets are sent.
     * This method must not write output.
     *
     * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure
     *
     * Any allocations made here must be freed in deinit().
     */
    int (*init)(struct AVFormatContext *);
    /**
     * Deinitialize format. If present, this is called whenever the muxer is being
     * destroyed, regardless of whether or not the header has been written.
     *
     * If a trailer is being written, this is called after write_trailer().
     *
     * This is called if init() fails as well.
     */
    void (*deinit)(struct AVFormatContext *);
    /**
     * Set up any necessary bitstream filtering and extract any extra data needed
     * for the global header.
     *
     * @note pkt might have been directly forwarded by a meta-muxer; therefore
     *       pkt->stream_index as well as the pkt's timebase might be invalid.
     * Return 0 if more packets from this stream must be checked; 1 if not.
     */
    int (*check_bitstream)(struct AVFormatContext *s, struct AVStream *st,
                           const AVPacket *pkt);
} AVOutputFormat;

注意事项 

  • 【例子】不同的封装格式对应着上述接口有不同的实现函数,举几个例子:
  • FLV封装格式对应的AVOutputFormat结构体ff_flv_muxer:
    • write_header() -> flv_write_header()
    • write_packet() –> flv_write_packet()
    • write_trailer() -> flv_write_trailer()
  • 参考链接:FFmpeg: libavformat/flvenc.c File Reference
= {
    .name           = "flv",
    .long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
    .mime_type      = "video/x-flv",
    .extensions     = "flv",
    .priv_data_size = sizeof(FLVContext),
    .audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,
    .video_codec    = AV_CODEC_ID_FLV1,
    .write_header   = flv_write_header,
    .write_packet   = flv_write_packet,
    .write_trailer  = flv_write_trailer,
    .codec_tag      = (const * const []) {
                          flv_video_codec_ids, flv_audio_codec_ids, 0
                      },
    .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
                      AVFMT_TS_NONSTRICT,
}
  • MKV封装格式对应的AVOutputFormat结构体ff_matroska_muxer:
    • write_header() -> mkv_write_header()
    • write_packet() –> mkv_write_flush_packet()
    • write_trailer() -> mkv_write_trailer()
  • 结构体ff_matroska_muxer 已经不存在
  • 参考链接:FFmpeg: libavformat/matroskaenc.c Source File
 const AVOutputFormat ff_matroska_muxer = {
     .name              = "matroska",
     .long_name         = NULL_IF_CONFIG_SMALL("Matroska"),
     .mime_type         = "video/x-matroska",
     .extensions        = "mkv",
     .priv_data_size    = sizeof(MatroskaMuxContext),
     .audio_codec       = CONFIG_LIBVORBIS_ENCODER ?
                          AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3,
     .video_codec       = CONFIG_LIBX264_ENCODER ?
                          AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
     .init              = mkv_init,
     .deinit            = mkv_deinit,
     .write_header      = mkv_write_header,
     .write_packet      = mkv_write_flush_packet,
     .write_trailer     = mkv_write_trailer,
     .flags             = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
                          AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH,
     .codec_tag         = (const AVCodecTag* const []){
          ff_codec_bmp_tags, ff_codec_wav_tags,
          additional_audio_tags, additional_video_tags, additional_subtitle_tags, 0
     },
     .subtitle_codec    = AV_CODEC_ID_ASS,
     .query_codec       = mkv_query_codec,
     .check_bitstream   = mkv_check_bitstream,
     .priv_class        = &matroska_webm_class,
 };
 #endif
  • MPEG2TS封装格式对应的AVOutputFormat结构体ff_mpegts_muxer:
    • write_header() -> mpegts_write_header()
    • write_packet() –> mpegts_write_packet()
    • write_trailer() -> mpegts_write_end()
  • 参考链接:FFmpeg: libavformat/mpegtsenc.c File Reference
= {
    .name           = "mpegts",
    .long_name      = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
    .mime_type      = "video/MP2T",
    .extensions     = "ts,m2t,m2ts,mts",
    .priv_data_size = sizeof(MpegTSWrite),
    .audio_codec    = AV_CODEC_ID_MP2,
    .video_codec    = AV_CODEC_ID_MPEG2VIDEO,
    .write_header   = mpegts_write_header,
    .write_packet   = mpegts_write_packet,
    .write_trailer  = mpegts_write_end,
    .flags          = AVFMT_ALLOW_FLUSH | AVFMT_VARIABLE_FPS,
    .priv_class     = &mpegts_muxer_class,
}
  • AVI封装格式对应的AVOutputFormat结构体ff_avi_muxer:
    • write_header() -> avi_write_header()
    • write_packet() –> avi_write_packet()
    • write_trailer() -> avi_write_trailer()
  • 参考链接:FFmpeg: libavformat/avienc.c File Reference
= {
    .name           = "avi",
    .long_name      = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),
    .mime_type      = "video/x-msvideo",
    .extensions     = "avi",
    .priv_data_size = sizeof(AVIContext),
    .audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_AC3,
    .video_codec    = AV_CODEC_ID_MPEG4,
    .write_header   = avi_write_header,
    .write_packet   = avi_write_packet,
    .write_trailer  = avi_write_trailer,
    .codec_tag      = (const AVCodecTag * const []) {
        ff_codec_bmp_tags, ff_codec_wav_tags, 0
    },
}

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

  • AVCodec包含如下编解码函数指针:
    • init():初始化
    • encode2():编码一帧数据
    • close():关闭
  • 参考链接:FFmpeg: AVCodec Struct Reference
  • 上述参考链接里面包含的结构体包含上述编解码函数指针
  • 但是新版本好像没有了
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 "_").
     */
    const char *wrapper_name;
 
    /**
     * Array of supported channel layouts, terminated with a zeroed layout.
     */
    const AVChannelLayout *ch_layouts;
} AVCodec;

注意事项:

  • 【例子】不同的编解码器对应着上述接口有不同的实现函数,举几个例子:
  • HEVC编码器对应的AVCodec结构体ff_libx265_encoder:
    • init() -> libx265_encode_init() -> x265_param_alloc(), x265_param_default_preset(), x265_encoder_open()
    • encode2() -> libx265_encode_frame() -> x265_encoder_encode()
    • close() -> libx265_encode_close() -> x265_param_free(), x265_encoder_close()
  • 参考链接:FFmpeg: libavcodec/libx265.c File Reference
= {
    .name             = "libx265",
    .long_name        = NULL_IF_CONFIG_SMALL("libx265 H.265 / HEVC"),
    .type             = AVMEDIA_TYPE_VIDEO,
    .id               = AV_CODEC_ID_HEVC,
    .init             = libx265_encode_init,
    .init_static_data = libx265_encode_init_csp,
    .encode2          = libx265_encode_frame,
    .close            = libx265_encode_close,
    .priv_data_size   = sizeof(libx265Context),
    .priv_class       = &class,
    .defaults         = x265_defaults,
    .capabilities     = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS,
}
  • H.264编码器对应的AVCodec结构体ff_libx264_encoder:
    • init() -> X264_init() -> x264_param_default(), x264_encoder_open(), x264_encoder_headers()
    • encode2() -> X264_frame() -> x264_encoder_encode()
    • close() -> X264_close() -> x264_encoder_close()
  • 参考链接:FFmpeg: libavcodec/libx264.c File Reference
{
    .name           = "libx264",
    .type           = AVMEDIA_TYPE_VIDEO,
    .id             = CODEC_ID_H264,
    .priv_data_size = sizeof(X264Context),
    .init           = X264_init,
    .encode         = X264_frame,
    .close          = X264_close,
    .capabilities   = CODEC_CAP_DELAY,
    .pix_fmts       = (const enum PixelFormat[]) { PIX_FMT_YUV420P, PIX_FMT_YUVJ420P, PIX_FMT_NONE },
    .long_name      = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
    .priv_class     = &class,
}
  • VP8编码器(libVPX)对应的AVCodec结构体ff_libvpx_vp8_encoder:
    • init() -> vpx_init() -> vpx_codec_enc_config_default()
    • encode2() -> vp8_encode() -> vpx_codec_enc_init(), vpx_codec_encode()
    • close() -> vp8_free() -> vpx_codec_destroy()
  • 参考链接:FFmpeg/libvpxenc.c at master · FFmpeg/FFmpeg · GitHub
const FFCodec ff_libvpx_vp8_encoder = {
    .p.name         = "libvpx",
    .p.long_name    = NULL_IF_CONFIG_SMALL("libvpx VP8"),
    .p.type         = AVMEDIA_TYPE_VIDEO,
    .p.id           = AV_CODEC_ID_VP8,
    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
                      AV_CODEC_CAP_OTHER_THREADS,
    .priv_data_size = sizeof(VPxContext),
    .init           = vp8_init,
    FF_CODEC_ENCODE_CB(vpx_encode),
    .close          = vpx_free,
    .caps_internal  = FF_CODEC_CAP_AUTO_THREADS,
    .p.pix_fmts     = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_NONE },
    .p.priv_class   = &class_vp8,
    .defaults       = defaults,
    .p.wrapper_name = "libvpx",
};
  • MPEG2编码器对应的AVCodec结构体ff_mpeg2video_encoder:
    • init() -> encode_init()
    • encode2() -> ff_mpv_encode_picture()
    • close() -> ff_mpv_encode_end()
  • 参考链接:FFmpeg: libavcodec/mpeg12enc.c File Reference
= {
    .name                 = "mpeg2video",
    .long_name            = NULL_IF_CONFIG_SMALL("MPEG-2 video"),
    .type                 = AVMEDIA_TYPE_VIDEO,
    .id                   = AV_CODEC_ID_MPEG2VIDEO,
    .priv_data_size       = sizeof(MpegEncContext),
    .init                 = encode_init,
    .encode2              = ff_mpv_encode_picture,
    .close                = ff_mpv_encode_end,
    .supported_framerates = ff_mpeg2_frame_rate_tab,
    .pix_fmts             = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P,
                                                           AV_PIX_FMT_YUV422P,
                                                           AV_PIX_FMT_NONE },
    .capabilities         = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS,
    .priv_class           = &mpeg2_class,
}

你可能感兴趣的:(音视频,音视频)