FFmpeg 源码之结构体 AVPacket、AVPacketSideData、AVBufferRef 与 AVBuffer

901 阅读11分钟

AVPacket 存储的是编码后的帧数据,它通常由 demuxer 输出,然后作为输入传递给 decoder,或者从 encoder 接收作为输出,然后传递给 muxer。

diagram.png

对于视频,它通常应该包含一个压缩帧。对于音频,它可能包含几个压缩帧。encoder 允许输出空数据包、不包含压缩数据、只包含 side data(例如,在编码结束时更新一些流参数)。

数据所有权的语义取决于 buf 字段。如果设置了该值,则动态分配 Packet 数据并无限期有效,直到对 av_packet_unref() 的调用将引用计数减少到 0。

如果没有设置 buf 字段,av_packet_ref() 将复制一个副本,而不是增加引用计数。

side data 总是由 av_malloc() 分配,由 av_packet_ref() 复制,由 av_packet_unref() 释放。

sizeof(AVPacket) 作为 public ABI 的一部分已被弃用。一旦 av_init_packet() 函数被移除,新的 Packet 将只能由 av_packet_alloc() 分配,新的字段可能会被添加到结构的末尾。

下面先学习 AVPacket,接着从 AVPacket 结构体引出 AVBufferRef 和 AVPacketSideData,最后又由 AVBufferRef 引出 AVBuffer,AVPacketSideData 引出 AVPacketSideDataType 枚举。

一、AVPacket

libavcodec/packet.h

typedef struct AVPacket {
    /**
     * A reference to the reference-counted buffer where the packet data is
     * stored.
     * May be NULL, then the packet data is not reference-counted.
     */
    AVBufferRef *buf;
    /**
     * Presentation timestamp in AVStream->time_base units; the time at which
     * the decompressed packet will be presented to the user.
     * Can be AV_NOPTS_VALUE if it is not stored in the file.
     * pts MUST be larger or equal to dts as presentation cannot happen before
     * decompression, unless one wants to view hex dumps. Some formats misuse
     * the terms dts and pts/cts to mean something different. Such timestamps
     * must be converted to true pts/dts before they are stored in AVPacket.
     */
    int64_t pts;
    /**
     * Decompression timestamp in AVStream->time_base units; the time at which
     * the packet is decompressed.
     * Can be AV_NOPTS_VALUE if it is not stored in the file.
     */
    int64_t dts;
    uint8_t *data;
    int   size;
    int   stream_index;
    /**
     * A combination of AV_PKT_FLAG values
     */
    int   flags;
    /**
     * Additional packet data that can be provided by the container.
     * Packet can contain several types of side information.
     */
    AVPacketSideData *side_data;
    int side_data_elems;

    /**
     * Duration of this packet in AVStream->time_base units, 0 if unknown.
     * Equals next_pts - this_pts in presentation order.
     */
    int64_t duration;

    int64_t pos;                            ///< byte position in stream, -1 if unknown

#if FF_API_CONVERGENCE_DURATION
    /**
     * @deprecated Same as the duration field, but as int64_t. This was required
     * for Matroska subtitles, whose duration values could overflow when the
     * duration field was still an int.
     */
    attribute_deprecated
    int64_t convergence_duration;
#endif
} AVPacket;

下面是每个字段的含义。

字段含义
AVBufferRef * buf对存储 packet 数据的引用计数缓冲区的引用。
int64_t pts使用 AVStream->time_base 时间基显示的时间戳,将解包后的 packet 呈现给用户的时间。
int64_t dts使用 AVStream->time_base 时间基解包的时间戳,packet 被解包的时间。
uint8_t * datapacket 的实际数据 buffer。
int sizepacket 实际数据的大小。
int stream_index流的 index。
int flagsAV_PKT_FLAG 值的组合。
AVPacketSideData * side_data容器可以提供的额外数据。
int side_data_elemsside_data 元数个数。
int64_t duration这个 packet 的持续时长以 AVStream->time_base 为单位,如果未知则为 0。
int64_t pos流中的字节位置,如果未知则为 -1。

以下是 AV_PKT_FLAG 可使用的组合值。

libavcodec/packet.h

#define AV_PKT_FLAG_KEY 0x0001 //关键帧
#define AV_PKT_FLAG_CORRUPT 0x0002 //损坏的数据
#define AV_PKT_FLAG_DISCARD 0x0004 //用于丢弃需要保持有效解码器状态但不需要输出的 packet,并且在解码后应该丢弃。
#define AV_PKT_FLAG_TRUSTED   0x0008 //packet 来自一个可信的来源。
#define AV_PKT_FLAG_DISPOSABLE 0x0010 //用来指示包含可以被解码器丢弃帧的 packet,即非引用帧。

二、 AVBufferRef

对数据缓冲区的引用。这个结构体的大小不是公共 ABI 的一部分,也不打算直接分配它。

libavutil/buffer.h

typedef struct AVBufferRef {
     AVBuffer *buffer;
 
     /**
      * The data buffer. It is considered writable if and only if
      * this is the only reference to the buffer, in which case
      * av_buffer_is_writable() returns 1.
      */
     uint8_t *data;
     /**
      * Size of data in bytes.
      */
 #if FF_API_BUFFER_SIZE_T
     int      size;
 #else
     size_t   size;
 #endif
} AVBufferRef;
字段含义
AVBuffer *buffer一种引用计数缓冲区类型。它是不透明的,意味着要通过引用(AVBufferRef)使用。
uint8_t *data数据缓冲区。当且仅当这是对缓冲区的唯一引用时,才认为它是可写的,在这种情况下,av_buffer_is_writable() 返回 1。
size_t / int size以字节为单位的 data 大小。

三、AVBuffer

一种引用计数缓冲区类型。定义在 libavutil/buffer_internal.h 中。它是不透明的,意味着要通过引用(AVBufferRef)使用。

libavutil/buffer_internal.h

struct AVBuffer {
    uint8_t *data; /**< data described by this buffer */
    buffer_size_t size; /**< size of data in bytes */

    /**
     *  number of existing AVBufferRef instances referring to this buffer
     */
    atomic_uint refcount;

    /**
     * a callback for freeing the data
     */
    void (*free)(void *opaque, uint8_t *data);

    /**
     * an opaque pointer, to be used by the freeing callback
     */
    void *opaque;

    /**
     * A combination of AV_BUFFER_FLAG_*
     */
    int flags;

    /**
     * A combination of BUFFER_FLAG_*
     */
    int flags_internal;
};
字段含义
uint8_t *data由该缓冲区描述的数据。
buffer_size_t size以字节为单位的数据大小。
atomic_uint refcount引用此缓冲区的现有 AVBufferRef 实例的数目。
void (*free)(void *opaque, uint8_t *data)用于释放数据的回调。
void *opaque一个不透明的指针,由释放回调函数使用。
int flagsAV_BUFFER_FLAG_* 的组合。
int flags_internalBUFFER_FLAG_* 的组合。

AVBuffer 是一个用于引用计数数据缓冲区的 API。

在这个 API 中有两个核心对象 AVBuffer 和 AVBufferRef。AVBuffer 表示数据缓冲区本身;它是不透明的,不能被调用者直接访问,只能通过 AVBufferRef 访问。然而,调用者可能会比较两个 AVBuffer 指针来检查两个不同的引用是否描述了同一个数据缓冲区。AVBufferRef 表示对 AVBuffer 的单个引用,它是可以被调用者直接操作的对象。

有两个函数可以用一个引用来创建新的 AVBuffer —— av_buffer_alloc() 用于分配一个新的缓冲区,av_buffer_create() 用于在 AVBuffer 中包装一个现有数组。从现有引用中,可以使用 av_buffer_ref() 创建其他引用。使用 av_buffer_unref() 释放一个引用(一旦释放了所有的引用,就会自动释放数据)。

这个 API 和 FFmpeg 的其余部分的约定是,如果只存在一个对缓冲区的引用(而且它没有被标记为只读),则认为缓冲区是可写的。提供了 av_buffer_is_writable() 函数来检查这是否为真,并且 av_buffer_make_writable() 将在必要时自动创建一个新的可写缓冲区。

当然,没有什么可以阻止调用代码违反这个约定,但是,只有当所有现有的引用都在它的控制之下时,这才是安全的。

引用和取消引用缓冲区是线程安全的,因此可以由多个线程同时使用,而不需要任何额外的锁。

对同一个缓冲区的两个不同的引用可以指向缓冲区的不同部分(例如,它们的 AVBufferRef.data 数据将不相等)。

四、AVPacketSideData

容器可以提供的额外 Packet 数据。Packet 可以包含几种类型的 side 信息。

libavcodec/packet.h

typedef struct AVPacketSideData {
    uint8_t *data;
#if FF_API_BUFFER_SIZE_T
    int      size;
#else
    size_t   size;
#endif
    enum AVPacketSideDataType type;
} AVPacketSideData;

字段含义
uint8_t *data数据缓存区。
int / size_t size以字节为单位的数据缓存区大小。
enum AVPacketSideDataType typePacket side data 类型。

AVPacketSideDataType 枚举定义了各种 side data 类型。

libavcodec/packet.h

/**
 * @defgroup lavc_packet AVPacket
 *
 * Types and functions for working with AVPacket.
 * @{
 */
enum AVPacketSideDataType {
    /**
     * An AV_PKT_DATA_PALETTE side data packet contains exactly AVPALETTE_SIZE
     * bytes worth of palette. This side data signals that a new palette is
     * present.
     */
    AV_PKT_DATA_PALETTE,

    /**
     * The AV_PKT_DATA_NEW_EXTRADATA is used to notify the codec or the format
     * that the extradata buffer was changed and the receiving side should
     * act upon it appropriately. The new extradata is embedded in the side
     * data buffer and should be immediately used for processing the current
     * frame or packet.
     */
    AV_PKT_DATA_NEW_EXTRADATA,

    /**
     * An AV_PKT_DATA_PARAM_CHANGE side data packet is laid out as follows:
     * @code
     * u32le param_flags
     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT)
     *     s32le channel_count
     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT)
     *     u64le channel_layout
     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE)
     *     s32le sample_rate
     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS)
     *     s32le width
     *     s32le height
     * @endcode
     */
    AV_PKT_DATA_PARAM_CHANGE,

    /**
     * An AV_PKT_DATA_H263_MB_INFO side data packet contains a number of
     * structures with info about macroblocks relevant to splitting the
     * packet into smaller packets on macroblock edges (e.g. as for RFC 2190).
     * That is, it does not necessarily contain info about all macroblocks,
     * as long as the distance between macroblocks in the info is smaller
     * than the target payload size.
     * Each MB info structure is 12 bytes, and is laid out as follows:
     * @code
     * u32le bit offset from the start of the packet
     * u8    current quantizer at the start of the macroblock
     * u8    GOB number
     * u16le macroblock address within the GOB
     * u8    horizontal MV predictor
     * u8    vertical MV predictor
     * u8    horizontal MV predictor for block number 3
     * u8    vertical MV predictor for block number 3
     * @endcode
     */
    AV_PKT_DATA_H263_MB_INFO,

    /**
     * This side data should be associated with an audio stream and contains
     * ReplayGain information in form of the AVReplayGain struct.
     */
    AV_PKT_DATA_REPLAYGAIN,

    /**
     * This side data contains a 3x3 transformation matrix describing an affine
     * transformation that needs to be applied to the decoded video frames for
     * correct presentation.
     *
     * See libavutil/display.h for a detailed description of the data.
     */
    AV_PKT_DATA_DISPLAYMATRIX,

    /**
     * This side data should be associated with a video stream and contains
     * Stereoscopic 3D information in form of the AVStereo3D struct.
     */
    AV_PKT_DATA_STEREO3D,

    /**
     * This side data should be associated with an audio stream and corresponds
     * to enum AVAudioServiceType.
     */
    AV_PKT_DATA_AUDIO_SERVICE_TYPE,

    /**
     * This side data contains quality related information from the encoder.
     * @code
     * u32le quality factor of the compressed frame. Allowed range is between 1 (good) and FF_LAMBDA_MAX (bad).
     * u8    picture type
     * u8    error count
     * u16   reserved
     * u64le[error count] sum of squared differences between encoder in and output
     * @endcode
     */
    AV_PKT_DATA_QUALITY_STATS,

    /**
     * This side data contains an integer value representing the stream index
     * of a "fallback" track.  A fallback track indicates an alternate
     * track to use when the current track can not be decoded for some reason.
     * e.g. no decoder available for codec.
     */
    AV_PKT_DATA_FALLBACK_TRACK,

    /**
     * This side data corresponds to the AVCPBProperties struct.
     */
    AV_PKT_DATA_CPB_PROPERTIES,

    /**
     * Recommmends skipping the specified number of samples
     * @code
     * u32le number of samples to skip from start of this packet
     * u32le number of samples to skip from end of this packet
     * u8    reason for start skip
     * u8    reason for end   skip (0=padding silence, 1=convergence)
     * @endcode
     */
    AV_PKT_DATA_SKIP_SAMPLES,

    /**
     * An AV_PKT_DATA_JP_DUALMONO side data packet indicates that
     * the packet may contain "dual mono" audio specific to Japanese DTV
     * and if it is true, recommends only the selected channel to be used.
     * @code
     * u8    selected channels (0=mail/left, 1=sub/right, 2=both)
     * @endcode
     */
    AV_PKT_DATA_JP_DUALMONO,

    /**
     * A list of zero terminated key/value strings. There is no end marker for
     * the list, so it is required to rely on the side data size to stop.
     */
    AV_PKT_DATA_STRINGS_METADATA,

    /**
     * Subtitle event position
     * @code
     * u32le x1
     * u32le y1
     * u32le x2
     * u32le y2
     * @endcode
     */
    AV_PKT_DATA_SUBTITLE_POSITION,

    /**
     * Data found in BlockAdditional element of matroska container. There is
     * no end marker for the data, so it is required to rely on the side data
     * size to recognize the end. 8 byte id (as found in BlockAddId) followed
     * by data.
     */
    AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,

    /**
     * The optional first identifier line of a WebVTT cue.
     */
    AV_PKT_DATA_WEBVTT_IDENTIFIER,

    /**
     * The optional settings (rendering instructions) that immediately
     * follow the timestamp specifier of a WebVTT cue.
     */
    AV_PKT_DATA_WEBVTT_SETTINGS,

    /**
     * A list of zero terminated key/value strings. There is no end marker for
     * the list, so it is required to rely on the side data size to stop. This
     * side data includes updated metadata which appeared in the stream.
     */
    AV_PKT_DATA_METADATA_UPDATE,

    /**
     * MPEGTS stream ID as uint8_t, this is required to pass the stream ID
     * information from the demuxer to the corresponding muxer.
     */
    AV_PKT_DATA_MPEGTS_STREAM_ID,

    /**
     * Mastering display metadata (based on SMPTE-2086:2014). This metadata
     * should be associated with a video stream and contains data in the form
     * of the AVMasteringDisplayMetadata struct.
     */
    AV_PKT_DATA_MASTERING_DISPLAY_METADATA,

    /**
     * This side data should be associated with a video stream and corresponds
     * to the AVSphericalMapping structure.
     */
    AV_PKT_DATA_SPHERICAL,

    /**
     * Content light level (based on CTA-861.3). This metadata should be
     * associated with a video stream and contains data in the form of the
     * AVContentLightMetadata struct.
     */
    AV_PKT_DATA_CONTENT_LIGHT_LEVEL,

    /**
     * ATSC A53 Part 4 Closed Captions. This metadata should be associated with
     * a video stream. A53 CC bitstream is stored as uint8_t in AVPacketSideData.data.
     * The number of bytes of CC data is AVPacketSideData.size.
     */
    AV_PKT_DATA_A53_CC,

    /**
     * This side data is encryption initialization data.
     * The format is not part of ABI, use av_encryption_init_info_* methods to
     * access.
     */
    AV_PKT_DATA_ENCRYPTION_INIT_INFO,

    /**
     * This side data contains encryption info for how to decrypt the packet.
     * The format is not part of ABI, use av_encryption_info_* methods to access.
     */
    AV_PKT_DATA_ENCRYPTION_INFO,

    /**
     * Active Format Description data consisting of a single byte as specified
     * in ETSI TS 101 154 using AVActiveFormatDescription enum.
     */
    AV_PKT_DATA_AFD,

    /**
     * Producer Reference Time data corresponding to the AVProducerReferenceTime struct,
     * usually exported by some encoders (on demand through the prft flag set in the
     * AVCodecContext export_side_data field).
     */
    AV_PKT_DATA_PRFT,

    /**
     * ICC profile data consisting of an opaque octet buffer following the
     * format described by ISO 15076-1.
     */
    AV_PKT_DATA_ICC_PROFILE,

    /**
     * DOVI configuration
     * ref:
     * dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2.1.2, section 2.2
     * dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2, section 3.3
     * Tags are stored in struct AVDOVIDecoderConfigurationRecord.
     */
    AV_PKT_DATA_DOVI_CONF,

    /**
     * Timecode which conforms to SMPTE ST 12-1:2014. The data is an array of 4 uint32_t
     * where the first uint32_t describes how many (1-3) of the other timecodes are used.
     * The timecode format is described in the documentation of av_timecode_get_smpte_from_framenum()
     * function in libavutil/timecode.h.
     */
    AV_PKT_DATA_S12M_TIMECODE,

    /**
     * The number of side data types.
     * This is not part of the public API/ABI in the sense that it may
     * change when new side data types are added.
     * This must stay the last enum value.
     * If its value becomes huge, some code using it
     * needs to be updated as it assumes it to be smaller than other limits.
     */
    AV_PKT_DATA_NB
};
类型含义
AV_PKT_DATA_PALETTE调色板,数据大小由 AVPALETTE_SIZE 决定。
AV_PKT_DATA_NEW_EXTRADATA用于通知编解码器或格式 extradata 缓冲区已更改,并且接收方应对此采取适当措施。新的 extradata 被嵌入到 side data 缓冲区中,并且应该被立即用于处理当前 frame 或 packet。
AV_PKT_DATA_PARAM_CHANGE参数改变,布局受 AVSideDataParamChangeFlags 类型影响而不同。
AV_PKT_DATA_H263_MB_INFO包含了许多关于宏块信息的结构,这些信息与在宏块边缘将 packet 分割成更小的 packet 有关。
AV_PKT_DATA_REPLAYGAIN与音频流相关联,并以 AVReplayGain 结构体的形式包含重放增益(ReplayGain)信息。
AV_PKT_DATA_DISPLAYMATRIX包含一个 3x3 的变换矩阵,它描述了一个仿射变换,需要应用到解码后的视频帧上才能正确显示。
AV_PKT_DATA_STEREO3D与视频流相关联,并包含以 AVStereo3D 结构体形式呈现的立体 3D 信息。
AV_PKT_DATA_AUDIO_SERVICE_TYPE与一个音频流相关联,并且对应枚举类型 enum AVAudioServiceType。
AV_PKT_DATA_QUALITY_STATS包含来自编码器的质量相关信息。
AV_PKT_DATA_FALLBACK_TRACK包含一个整数值,表示“回退”轨道的流索引。
AV_PKT_DATA_CPB_PROPERTIES对应于 AVCPBProperties 结构体。
AV_PKT_DATA_SKIP_SAMPLES建议跳过指定的样本数量。
AV_PKT_DATA_JP_DUALMONO表明数据包可能包含特定于日本 DTV 的“dual mono”音频,如果它是真的,建议只使用选定的 channel。
AV_PKT_DATA_STRINGS_METADATA字符串键值对列表。
AV_PKT_DATA_SUBTITLE_POSITION副标题事件的位置。
AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL在 matroska 容器的 BlockAdditional 元素中找到的数据。
AV_PKT_DATA_WEBVTT_IDENTIFIERWebVTT cue 的可选第一标识符行。
AV_PKT_DATA_WEBVTT_SETTINGSWebVTT cue 的时间戳说明符之后的可选设置(呈现说明)。
AV_PKT_DATA_METADATA_UPDATE字符串键值对列表。包括出现在流中的更新元数据。
AV_PKT_DATA_MPEGTS_STREAM_IDuint8_t 类型的 MPEGTS 流 ID,这需要从 demuxer 传递流 ID 信息到相应的 muxer。
AV_PKT_DATA_MASTERING_DISPLAY_METADATAMastering 显示元数据(基于SMPTE-2086:2014),该元数据应该与视频流相关联,并以 AVMasteringDisplayMetadata 结构体的形式存放数据。
AV_PKT_DATA_SPHERICAL与视频流相关联,并对应于 AVSphericalMapping 结构体。
AV_PKT_DATA_CONTENT_LIGHT_LEVELContent light 等级(基于CTA-861.3)。该元数据应该与视频流相关联,并以 AVContentLightMetadata 结构体的形式存放数据。
AV_PKT_DATA_A53_CCATSC A53 第 4 部分 Closed Captions(隐藏式字幕?)。
AV_PKT_DATA_ENCRYPTION_INIT_INFO加密初始化数据。
AV_PKT_DATA_ENCRYPTION_INFO包含如何解密 packet 的加密信息。
AV_PKT_DATA_AFDActive Format Description 数据。描述在 ETSI TS 101 154 中使用 AVActiveFormatDescription 枚举指定的由单个字节组成的数据。
AV_PKT_DATA_PRFT生产者引用时间数据对应于 AVProducerReferenceTime 结构体,通常由一些编码器导出(通过在 AVCodecContext export_side_data 字段中设置 prft 标记)。
AV_PKT_DATA_ICC_PROFILE由 ISO 15076-1 描述的格式不透明的八位字节缓冲区组成的 ICC 配置文件数据。
AV_PKT_DATA_DOVI_CONFDOVI 配置。
AV_PKT_DATA_S12M_TIMECODE符合 SMPTE ST 12-1:2014 的 Timecode。
AV_PKT_DATA_NBside data 类型的数量。

参考资料:

  1. ffmpeg.org/doxygen/tru…
  2. ffmpeg.org/doxygen/tru…
  3. ffmpeg.org/doxygen/tru…
  4. ffmpeg.org/doxygen/tru…
  5. ffmpeg.org/doxygen/tru…