作者: 葉餘 來源: https://www.cnblogs.com/leisure_chn/p/10410320.html
1. 資料結構定義
struct AVPacket定義于<libavcodec/avcodec.h>
struct AVPacket packet;
AVPacket中存儲的是經過編碼的壓縮資料。在解碼中,AVPacket由解複用器輸出到解碼器;在編碼中,AVPacket由編碼器輸出到複用器。下圖中,解複用器(demuxer)的輸出和複用器(muxer)的輸入“encoded data packets”的資料類型就是AVPacket:
_______ ______________
| | | |
| input | demuxer | encoded data | decoder
| file | ---------> | packets | -----+
|_______| |______________| |
v
_________
| |
| decoded |
| frames |
|_________|
________ ______________ |
| | | | |
| output | <-------- | encoded data | <----+
| file | muxer | packets | encoder
|________| |______________|
對于視訊而言,一個AVPacket通常隻包含一個壓縮視訊幀。而對于音頻而言,一個AVPacket可能包含多個完整的音頻壓縮幀。AVPacket也可以不包含壓縮編碼資料,而隻包含side data,這種包可以稱為空packet。例如,編碼結束後隻需要更新一些參數時就可以發空packet。
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;
音視訊資料緩沖區
-
:uint8_t *data
-
資料緩沖區位址與大小。音視訊編碼壓縮資料存儲于此片記憶體區域。此記憶體區域由int size
管理。AVBufferRef *buf
-
資料緩沖區引用,也可叫引用計數緩沖區。對上一字段AVBufferRef *buf
uint8_t *data
指向的記憶體區域提供引用計數等管理機制。
AVBufferRef對資料緩沖區提供了管理機制,使用者不應直接通路資料緩沖區。參考“
FFmpeg資料結構AVBuffer”
如果
值為NULL,則buf
指向的資料緩沖區不使用引用計數機制。data
将執行資料緩沖區的拷貝,而非僅僅增加緩沖區引用計數。av_packet_ref(dst, src)
值非NULL,則buf
指向的資料緩沖區使用引用計數機制。data
将不拷貝緩沖區,而僅增加緩沖區引用計數。av_packet_ref(dst, src)
av_packet_unref()
将資料緩沖區引用計數減1,當緩沖區引用計數為0時,緩沖區記憶體被FFmpeg回收。
對于
對象,如果struct AVPacket pkt
值非NULL,則有pkt.data == pkt.buf->data == pkt.buf->buffer.datapkt.buf
額外類型資料
-
AVPacketSideData *side_data
-
由容器(container)提供的額外包資料。TODO: 待研究int side_data_elems
packet屬性
-
顯示時間戳。機關time_base,幀率的倒數。int64_t pts
-
解碼時間戳。機關time_base,幀率的倒數。int64_t dts
-
目前包(packet)所有流(stream)的索引(index)。int stream_index
-
packet标志位。比如是否關鍵幀等。int flags
-
目前包解碼後的幀播放持續的時長。機關timebase。值等于下一幀pts減目前幀pts。int64_t duration
-
目前包在流中的位置,機關位元組。int64_t pos
2. 關鍵函數實作
這裡列出的幾個關鍵函數,主要是為了幫助了解
struct AVPacket
資料結構
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()
分析參考“
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()
3. 參考資料
[1]
FFmpeg資料結構:AVPacket解析,
https://www.cnblogs.com/wangguchangqing/p/5790705.html4. 修改記錄
2018-12-14 V1.0 初稿
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
