FFmpeg數據結構AVPacket

本文爲做者原創,轉載請註明出處:http://www.javashuo.com/article/p-fxcsqrre-en.htmlhtml

本文基於FFmpeg 4.1版本。數據結構

1. 數據結構定義

struct AVPacket定義於<libavcodec/avcodec.h>app

struct AVPacket packet;

AVPacket中存儲的是通過編碼的壓縮數據。在解碼中,AVPacket由解複用器輸出到解碼器;在編碼中,AVPacket由編碼器輸出到複用器。下圖中,解複用器(demuxer)的輸出和複用器(muxer)的輸入「encoded data packets」的數據類型就是AVPacket:less

_______              ______________
|       |            |              |
| input |  demuxer   | encoded data |   decoder
| file  | ---------> | packets      | -----+
|_______|            |______________|      |
                                           v
                                       _________
                                      |         |
                                      | decoded |
                                      | frames  |
                                      |_________|
 ________             ______________       |
|        |           |              |      |
| output | <-------- | encoded data | <----+
| file   |   muxer   | packets      |   encoder
|________|           |______________|

對於視頻而言,一個AVPacket一般只包含一個壓縮視頻幀。而對於音頻而言,一個AVPacket可能包含多個完整的音頻壓縮幀。AVPacket也能夠不包含壓縮編碼數據,而只包含side data,這種包能夠稱爲空packet。例如,編碼結束後只須要更新一些參數時就能夠發空packet。ide

AVPacket對象能夠在棧上分配,注意此處指的是AVPacket對象自己。而AVPacket中包含的數據緩衝區是經過av_malloc()在堆上分配的。
TODO: AVPacket對象在棧上分配,原理不清楚,待研究函數

/**
 * This structure stores compressed data. It is typically exported by demuxers
 * and then passed as input to decoders, or received as output from encoders and
 * then passed to muxers.
 *
 * For video, it should typically contain one compressed frame. For audio it may
 * contain several compressed frames. Encoders are allowed to output empty
 * packets, with no compressed data, containing only side data
 * (e.g. to update some stream parameters at the end of encoding).
 *
 * AVPacket is one of the few structs in FFmpeg, whose size is a part of public
 * ABI. Thus it may be allocated on stack and no new fields can be added to it
 * without libavcodec and libavformat major bump.
 *
 * The semantics of data ownership depends on the buf field.
 * If it is set, the packet data is dynamically allocated and is
 * valid indefinitely until a call to av_packet_unref() reduces the
 * reference count to 0.
 *
 * If the buf field is not set av_packet_ref() would make a copy instead
 * of increasing the reference count.
 *
 * The side data is always allocated with av_malloc(), copied by
 * av_packet_ref() and freed by av_packet_unref().
 *
 * @see av_packet_ref
 * @see av_packet_unref
 */
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;

音視頻數據緩衝區ui

  • uint8_t *data:
  • int size:
    數據緩衝區地址與大小。音視頻編碼壓縮數據存儲於此片內存區域。此內存區域由AVBufferRef *buf管理。
  • AVBufferRef *buf:
    數據緩衝區引用,也可叫引用計數緩衝區。對上一字段uint8_t *data指向的內存區域提供引用計數等管理機制。
    AVBufferRef對數據緩衝區提供了管理機制,用戶不該直接訪問數據緩衝區。參考「FFmpeg數據結構AVBuffer
    若是buf值爲NULL,則data指向的數據緩衝區不使用引用計數機制。av_packet_ref(dst, src)將執行數據緩衝區的拷貝,而非僅僅增長緩衝區引用計數。
    若是buf值非NULL,則data指向的數據緩衝區使用引用計數機制。av_packet_ref(dst, src)將不拷貝緩衝區,而僅增長緩衝區引用計數。av_packet_unref()將數據緩衝區引用計數減1,當緩衝區引用計數爲0時,緩衝區內存被FFmpeg回收。
    對於struct AVPacket pkt對象,若是pkt.buf值非NULL,則有pkt.data == pkt.buf->data == pkt.buf->buffer.data

額外類型數據this

  • AVPacketSideData *side_data
  • int side_data_elems
    由容器(container)提供的額外包數據。TODO: 待研究

packet屬性編碼

  • int64_t pts:
    顯示時間戳。單位time_base,幀率的倒數。
  • int64_t dts:
    解碼時間戳。單位time_base,幀率的倒數。
  • int stream_index:
    當前包(packet)全部流(stream)的索引(index)。
  • int flags:
    packet標誌位。好比是否關鍵幀等。
  • int64_t duration:
    當前包解碼後的幀播放持續的時長。單位timebase。值等於下一幀pts減當前幀pts。
  • int64_t pos:
    當前包在流中的位置,單位字節。

2. 關鍵函數實現

這裏列出的幾個關鍵函數,主要是爲了幫助理解struct AVPacket數據結構code

2.1 av_packet_ref()

int av_packet_ref(AVPacket *dst, const AVPacket *src)
{
    int ret;

    ret = av_packet_copy_props(dst, src);
    if (ret < 0)
        return ret;

    if (!src->buf) {
        ret = packet_alloc(&dst->buf, src->size);
        if (ret < 0)
            goto fail;
        if (src->size)
            memcpy(dst->buf->data, src->data, src->size);

        dst->data = dst->buf->data;
    } else {
        dst->buf = av_buffer_ref(src->buf);
        if (!dst->buf) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        dst->data = src->data;
    }

    dst->size = src->size;

    return 0;
fail:
    av_packet_free_side_data(dst);
    return ret;
}

av_packet_ref()做了處理以下:

a) 若是src->buf爲NULL,則將src緩衝區拷貝到新建立的dst緩衝區,注意src緩衝區不支持引用計數,但新建的dst緩衝區是支持引用計數的,由於dst->buf不爲NULL。

b) 若是src->buf不爲空,則dst與src共用緩衝區,調用av_buffer_ref()增長緩衝區引用計數便可。av_buffer_ref()分析參考「FFmpeg數據結構AVBuffer

2.2 av_packet_unref()

void av_packet_unref(AVPacket *pkt)
{
    av_packet_free_side_data(pkt);
    av_buffer_unref(&pkt->buf);
    av_init_packet(pkt);
    pkt->data = NULL;
    pkt->size = 0;
}

av_packet_unref()註銷AVPacket *pkt對象,並調用av_buffer_unref(&pkt->buf);將緩衝區引用計數減1。
av_buffer_unref()中將緩衝區引用計數減1後,若緩衝區引用計數變成0,則回收緩衝區內存。av_buffer_unref()分析參考「FFmpeg數據結構AVBuffer

3. 參考資料

[1] FFmpeg數據結構:AVPacket解析, http://www.javashuo.com/article/p-bdnshwjk-q.html

4. 修改記錄

2018-12-14 V1.0 初稿

相關文章
相關標籤/搜索