AVPacket
文檔地址:傳送門
在AVPacket結構體的說明部分:有這麼一段描述,足夠說明它的作用和重要性。
該結構存儲壓縮數據。 它通常由解複用器導出,然後作爲輸入傳遞給解碼器,或作爲編碼器的輸出接收,然後傳遞給複用器。
對於視頻而言,它通常應包含一個壓縮幀。 對於音頻,它可能包含幾個壓縮幀。 允許編碼器輸出空包,沒有壓縮數據,只包含不重要的附加信息數據。例如在編碼結束時更新一些流參數。
AVPacket是FFmpeg中爲數不多的結構之一,其大小是公共ABI的一部分。 因此,它可以在棧上分配,並且不會添加新的字段,除非libavcodec和libavformat有大的改動。
雖然前面引用部分已經說了AVPacket
的作用時機,但這裏還是單獨強調和總結一下:
- 解碼時:媒體源數據解封裝(解複用)後,解碼前。
- 編碼時:YUV數據編碼後,封裝前。
AVPacket
主要保存一些媒體流的基本信息,例如PTS、DTS時間。最重要的當然就是媒體數據的buffer地址了。
比較重要的有:
- pts:控制顯示的pts時間
- dts:控制解碼的dts時間
- *data:媒體數據buffer的指針
- duration:AVStream-> time_base單位中此數據包的持續時間,如果未知則爲0。 在演示順序中等於next_pts - this_pts。
初始化與清理
AVPacket* avPacket = av_packet_alloc(); // 初始化
av_packet_unref(avPacket); // 清理
沒什麼稀奇,都是調用特定的函數來處理。這裏需要提一下,雖然在第一小節提到,AVPacket是可以在棧內存上分配,也就是這樣:AVPacket avPacket
,但如非必要,我還是不建議這麼做,畢竟有現成的API,就別給自己挖坑了。
還有一個函數:
int av_packet_ref (AVPacket *dst, const AVPacket *src)
它的作用是:
設置對給定數據包描述的數據的新引用。
如果src的data指針不爲空,則將指針地址作爲dst中data指針的值。 否則在dst中分配一個新緩衝區並將數據從src複製到其中。
所有其他字段都是從src複製的。
這個函數和av_packet_unref函數的作用剛好相反。我們詳細瞭解一下這背後的故事:
我們在一開始有提到AVPacket
保存了數據buffer的地址,所以,它實際上並沒有包含數據內存本身,只是在它的字段中,用了一個uint8_t *data;
來保存數據buffer的地址。
av_packet_ref
函數的作用,就是從已有的AVPacket中複製一份出來。關於data部分,如果src的數據是引用計數的,直接把地址拷貝一份,然後把對應buffer的引用計數+1。如果不是,需要新分配一份保存數據的內存空間,並把src中的數據拷貝過來。
而av_packet_unref
函數,則會看buffer的引用計數器,如果不爲0就-1,爲零的話則會清楚掉buffer,AVPacket的其它數據也會回到初始狀態。
結構定義及成員解讀
typedef struct AVPacket {
AVBufferRef *buf; // data的buffer引用指針計數結構體
int64_t pts; // 控制顯示的pts時間
int64_t dts; // 控制解碼的dts時間
uint8_t *data; // 媒體數據buffer的指針
int size; // 數據大小
int stream_index; // 流index
int flags; // AV_PKT_FLAG值的組合
AVPacketSideData *side_data; // 容器可以提供的附加數據包數據。 數據包可以包含幾種類型的輔助信息。
// AVStream-> time_base單位中此數據包的持續時間,如果未知則爲0。 在演示順序中等於next_pts - this_pts。
int64_t duration;
int64_t pos; // 流中的字節位置,如果未知則爲-1
} AVPacket;
來擴展一下用來計數的結構體:
typedef struct AVBufferRef {
AVBuffer *buffer;
// 當且僅當這是對緩衝區的唯一引用時才被認爲是可寫的,在這種情況下av_buffer_is_writable()返回1。
uint8_t *data;
int size; // 數據大小(以字節爲單位)
} AVBufferRef;
這個結構體,保存了對數據緩衝區的引用。