AVPacket 是什么羞反?
AVPacket是存儲壓縮編碼數(shù)據(jù)相關(guān)信息的結(jié)構(gòu)體
/**
* 該結(jié)構(gòu)用來存儲壓縮數(shù)據(jù). 這是典型的解復(fù)合器的輸出然后塞入解碼器科吭,或者是接收編碼器的輸出然后塞入復(fù)合器
*
* 解復(fù)合器 --> AVPacket --> 解碼器 --> YUV/RGB --> 編碼器 --> AVPacket -->復(fù)合器
*
* 對視頻而言, 它通常包含一個壓縮幀. 對音頻而言,則它可能包含多個壓縮幀.
* 編碼器允許輸出一個空包, 沒有壓縮數(shù)據(jù), 僅包含側(cè)數(shù)據(jù)浑此。
* (例如在編碼結(jié)束時更新一些流參數(shù))
*
* AVPacket是FFmpeg少數(shù)幾個結(jié)構(gòu)之一, 其大小是公共ABI的一部分.
*
* 數(shù)據(jù)所有權(quán)的語義取決于buf域.
* 如果被設(shè)置, 分組數(shù)據(jù)動態(tài)分配累颂,且永遠(yuǎn)有效,直到一個叫av_packet_unref()減少引用計數(shù)為0時才被釋放
*
* 如果buf域沒有被設(shè)置凛俱,那么av_packet_ref()將做一個復(fù)制而不會增加引用計數(shù)
*
* 附加數(shù)據(jù)始終由av_malloc()分配內(nèi)存, 由av_packet_ref()進(jìn)行拷貝紊馏,由av_packet_unref()執(zhí)行釋放.
*
* @瀏覽 av_packet_ref()
* @瀏覽 av_packet_unref()
*/
typedef struct AVPacket
{
// 用來管理data指針引用的數(shù)據(jù)緩存的
// 為NULL時,那么數(shù)據(jù)包是不計數(shù)的
AVBufferRef *buf;
// 顯示時間戳, 對應(yīng)時間戳AVStream->time_base單元; 這個時間點(diǎn), 解壓縮的數(shù)據(jù)包將被提交給用戶
// 如果時間不被存儲在文件里, 則可以寫成AV_NOPTS_VALUE
// pts必須大于或等于dts, 因?yàn)轱@示不能在解壓縮之前被發(fā)生, 除非有人想查看十六進(jìn)制存儲最冰。
// 某些格式誤用了這個名詞dts或者是pts/cts那是意味著別的意思, 所以時間戳必須在被存儲到AVPacket之前轉(zhuǎn)換成真正的PTS/DTS瘦棋。
int64_t pts;
// 解碼時間戳, 對應(yīng)時間戳AVStream->time_base單元; 這個時間點(diǎn), 數(shù)據(jù)包被解碼
// 如果時間不被存儲在文件里, 則可以寫成AV_NOPTS_VALUE
int64_t dts;
// 存儲的數(shù)據(jù),指向一個緩存,這是AVPacket實(shí)際的數(shù)據(jù)
uint8_t *data;
// 數(shù)據(jù)的大小
int size;
// 標(biāo)識該AVPacket所屬的音頻/視頻流的索引
int stream_index;
// 一個AV_PKT_FLAG標(biāo)識值, 最低為置1表示關(guān)鍵幀
int flags;
// 容器可以提供的附加數(shù)據(jù)
// 包可以包含幾種AVPacketSideDataType類型的側(cè)信息
AVPacketSideData *side_data;
// 附加信息元素
int side_data_elems;
// 數(shù)據(jù)的時長,以所屬媒體流的時間基準(zhǔn)為單位
int64_t duration;
// 該數(shù)據(jù)在媒體流中的字節(jié)偏移量
int64_t pos;
// 該字段不再使用
#if FF_API_CONVERGENCE_DURATION
attribute_deprecated
int64_t convergence_duration;
#endif
} AVPacket;
重要的變量
在AVPacket結(jié)構(gòu)體中,重要的變量有以下幾個:
- uint8_t *data:壓縮編碼的數(shù)據(jù)暖哨。
例如對于H.264來說赌朋。1個AVPacket的data通常對應(yīng)一個NAL。
注意:在這里只是對應(yīng)篇裁,而不是一模一樣沛慢。他們之間有微小的差別:使用FFMPEG類庫分離出多媒體文件中的H.264碼流
因此在使用FFMPEG進(jìn)行視音頻處理的時候,常炒锊迹可以將得到的AVPacket的data數(shù)據(jù)直接寫成文件团甲,從而得到視音頻的碼流文件。 - int size:data的大小
- int64_t pts:顯示時間戳
- int64_t dts:解碼時間戳
- int stream_index:標(biāo)識該AVPacket所屬的視頻/音頻流黍聂。
容器
AVPacket實(shí)際上可用看作一個容器躺苦,它本身并不包含壓縮的媒體數(shù)據(jù),而是通過data指針引用數(shù)據(jù)的緩存空間产还。
所以將一個Packet作為參數(shù)傳遞的時候匹厘,就要根據(jù)具體的需要,對data引用的這部分?jǐn)?shù)據(jù)緩存空間進(jìn)行特殊的處理脐区。
當(dāng)從一個Packet去創(chuàng)建另一個Packet的時候愈诚,有兩種情況:
- 兩個Packet的data引用的是同一數(shù)據(jù)緩存空間,這時候要注意數(shù)據(jù)緩存空間的釋放問題牛隅;
- 兩個Packet的data引用不同的數(shù)據(jù)緩存空間炕柔,每個Packet都有數(shù)據(jù)緩存空間的copy;
第二種情況媒佣,數(shù)據(jù)空間的管理比較簡單匕累,但是數(shù)據(jù)實(shí)際上有多個copy造成內(nèi)存空間的浪費(fèi)。
所以要根據(jù)具體的需要默伍,來選擇到底是兩個Packet共享一個數(shù)據(jù)緩存空間欢嘿,還是每個Packet擁有自己獨(dú)自的緩存空間授霸。
引用計數(shù)
對于多個Packet共享同一個緩存空間,F(xiàn)Fmpeg使用的引用計數(shù)的機(jī)制(reference-count):
當(dāng)有新的Packet引用共享的緩存空間時际插,就將引用計數(shù)+1碘耳;
當(dāng)釋放了引用共享空間的Packet,就將引用計數(shù)-1框弛;引用計數(shù)為0時辛辨,就釋放掉引用的緩存空間。
而類AVBufferRef就是用來管理引用機(jī)制的:
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.
*/
int size;
} AVBufferRef;
其中瑟枫,AVPacket會使用兩個函數(shù):
int av_packet_ref(AVPacket *dst, const AVPacket *src):
可以理解為使用引用計數(shù)的淺拷貝斗搞。
該函數(shù)會先拷貝所有非緩存類數(shù)據(jù),
然后創(chuàng)建一個src->data的新的引用計數(shù)慷妙。如果src已經(jīng)設(shè)置了引用計數(shù)發(fā)(src->buffer不為空)僻焚,則直接將其引用計數(shù)+1;
如果src沒有設(shè)置引用計數(shù)(src->buffer為空)膝擂,則為dst創(chuàng)建一個新的引用計數(shù)buf虑啤,并復(fù)制src->data到buf->buffer中。最后架馋,復(fù)制src的其他字段到dst中狞山。所以av_packet_ref()是將2個AVPacket共用一個緩存的。void av_packet_unref(AVPacket *pkt):
可以理解為使用引用計數(shù)的數(shù)據(jù)清理叉寂。
將緩存空間的引用計數(shù)-1萍启,并將Packet中的其他字段設(shè)為初始值。如果引用計數(shù)為0屏鳍,自動的釋放緩存空間勘纯。所以,有兩個Packet共享同一個數(shù)據(jù)緩存空間的時候可用這么做钓瞭。
參考
https://blog.csdn.net/leixiaohua1020/article/details/14215755
https://blog.csdn.net/davidsguo008/article/details/72628675