本文爲做者原創,轉載請註明出處:http://www.javashuo.com/article/p-fxcsqrre-en.htmlhtml
本文基於FFmpeg 4.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
指向的內存區域提供引用計數等管理機制。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
packet屬性編碼
int64_t pts
:int64_t dts
:int stream_index
:int flags
:int64_t duration
:int64_t pos
:這裏列出的幾個關鍵函數,主要是爲了幫助理解struct AVPacket
數據結構code
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」
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」
[1] FFmpeg數據結構:AVPacket解析, http://www.javashuo.com/article/p-bdnshwjk-q.html
2018-12-14 V1.0 初稿