1 FFmpeg內(nèi)存管理原理
數(shù)據(jù)包管理過程中當(dāng)數(shù)據(jù)轉(zhuǎn)移到新的數(shù)據(jù)包時存在兩種操作一種是數(shù)據(jù)包之間相互獨立,當(dāng)新創(chuàng)建一份數(shù)據(jù)包時,需要將原來的數(shù)據(jù)重新申請一個數(shù)據(jù)空間并且將數(shù)據(jù)拷貝到新的數(shù)據(jù)包中,具體過程如下圖所示。這種數(shù)據(jù)包的管理優(yōu)勢是在于數(shù)據(jù)之間相互獨立,不會存在數(shù)據(jù)干擾的問題,但是缺點也很明顯就是消耗的內(nèi)存大大增加,同時數(shù)據(jù)之間的拷貝也是耗時的。
另一種內(nèi)存管理的方式是,只新增數(shù)據(jù)包對象,用于管理數(shù)據(jù)對象,對于數(shù)據(jù)本身采用同一個內(nèi)存空間進行管理,當(dāng)所有的內(nèi)存引用為0時釋放這片內(nèi)存空間,具體如下圖所示。這種方式的優(yōu)勢就是數(shù)據(jù)占用內(nèi)存最小化,同時大大減少數(shù)據(jù)拷貝的耗時問題;缺點是增加了內(nèi)存管理的難度。FFmpeg正是采用這種內(nèi)存管理的方式進行數(shù)據(jù)包和數(shù)據(jù)幀的管理。
2 FFmpeg內(nèi)存管理實現(xiàn)
2.1 AVPacket實現(xiàn)
核心API | 功能 |
---|---|
av_packet_alloc | 申請AVPacket |
av_packet_free | 釋放AVPacket |
av_init_packet | 初始化AVPacket |
av_new_packet | 申請AVBufferRef和AVBuffer數(shù)據(jù)空間,引用計數(shù)設(shè)置為1 |
av_buffer_ref | 新申請AVBufferRef,AVBuffer引用計數(shù)加一 |
av_buffer_unref | 釋放AVBufferRef,AVBuffer引用計數(shù)減一 |
AVPacket 內(nèi)存模型如下圖所示,AVBuffer存放具體數(shù)據(jù),AVBufferRef用于管理AVBuffer,AVPacket 是實際對外數(shù)據(jù)包體。
引用計數(shù)加一和引用計數(shù)減一一個逆過程,具體如下圖所示,加一主要是增加AVBufferRef和引用計數(shù);減一主要是釋放AVBufferRef和引用計數(shù)減少。當(dāng)引用計數(shù)減到0時,需要釋放AVBuffer數(shù)據(jù)區(qū)。
源碼走讀如下所示:
//1 申請AVPacket內(nèi)存,并初始化
AVPacket *av_packet_alloc(void)
{
//1 分配AVPacket 內(nèi)存
//2 將AVPacket解引用
av_packet_unref
{
//AVPacket 初始化
av_init_packet
}
}
//釋放AVPacket內(nèi)存,引用減1
void av_packet_free(AVPacket **pkt);
//初始化AVPacket
void av_init_packet(AVPacket *pkt);
//新申請AVPacket的數(shù)據(jù)空間
int av_new_packet(AVPacket *pkt, int size)
{
//分配AVBufferRef內(nèi)存空間
packet_alloc
{
av_buffer_realloc
{
//分配AVBufferRef內(nèi)存
av_buffer_create
{
//先分配AVBuffer,并且注冊釋放回調(diào)函數(shù),refcount引用計數(shù)加一
//再分配AVBufferRef,并將指針與AVBuffer綁定
}
}
}
}
//引用計數(shù)加一
AVBufferRef *av_buffer_ref(AVBufferRef *buf)
{
//新分配一個AVBufferRef;將舊的AVBufferRef復(fù)制給它,同時引用計數(shù)加1
*ret = *buf;//結(jié)構(gòu)體賦值的方式,淺拷貝
}
//引用計數(shù)減一
void av_buffer_unref(AVBufferRef **buf)
{
//AVBufferRef釋放,同時AVBuffer計數(shù)減一,當(dāng)計數(shù)為1時釋放
buffer_replace
{
//利用回調(diào)釋放數(shù)據(jù)
b->free
}
}
2.2 AVFrame實現(xiàn)
核心API | 功能 |
---|---|
av_frame_alloc | 申請AVFrame |
av_frame_free | 釋放AVFrame |
av_frame_get_buffer | 申請AVBufferRef和AVFrame數(shù)據(jù)空間 |
av_frame_ref | 新申請AVBufferRef,AVFrame引用計數(shù)加一 |
av_frame_unref | 釋放AVBufferRef,AVFrame引用計數(shù)減一 |
av_frame_move_ref | AVFrame轉(zhuǎn)移引用計數(shù) |
AVFrame實現(xiàn)原理與AVPacket 一致,都是利用AVBufferRef進行引用計數(shù)的管理,同時數(shù)據(jù)存儲在AVBuffer中,只有保存一份,av_frame_ref負(fù)責(zé)將引用計數(shù)加一,av_frame_unref引用計數(shù)減一,當(dāng)引用計數(shù)減到0后,進行數(shù)據(jù)釋放。
3 FFmpeg內(nèi)存接口實踐
利用qt進行FFmpeg環(huán)境構(gòu)建,首先是下載FFmpeg依賴庫和頭文件,這里使用的是win32的已經(jīng)編譯好的FFmpeg庫,ffmpeg-4.2.1-win32-dev頭文件和庫。
首先構(gòu)建一個空的c項目,同時創(chuàng)建好需要測試的avpacket文件,在依賴項.pro文件需要添加對FFmpeg的依賴,包括兩部分一個是頭文件路徑,第二是庫路徑。具體如下所示。
win32{
INCLUDEPATH += $$PWD/ffmpeg-4.2.1-win32-dev/include
LIBS += $$PWD/ffmpeg-4.2.1-win32-dev/lib/avformat.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avcodec.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avdevice.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avfilter.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/avutil.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/postproc.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/swresample.lib \
$$PWD/ffmpeg-4.2.1-win32-dev/lib/swscale.lib
}
注意ffmpeg-4.2.1-win32-dev文件放到項目的根路徑如下所示。
接下來是測試FFmpeg是否可以正常使用,具體測試程序如下,獲取FFmpeg的版本。
執(zhí)行效果如下所示:
1 實現(xiàn)簡單的內(nèi)存分配和釋放操作
需要注意的點:文章來源:http://www.zghlxwxcb.cn/news/detail-424080.html
- av_packet_alloc內(nèi)部會進行初始化,所以不需要再調(diào)用av_init_packet初始化;
- av_packet_free內(nèi)部會進行解引用,所以不需要再調(diào)用av_packet_unref;
- av_init_packet一定不能在av_new_packet之后使用會造成內(nèi)存泄漏
- 對同一個AVPacket進行多次av_packet_ref而沒有av_packet_unref會造成內(nèi)存泄漏。
av_new_packet用于分配到:AVBufferRef引用管理,同時會分配數(shù)據(jù)部分AVBuffer,同時引用計數(shù)設(shè)置refcount為1.
經(jīng)過av_packet_move_ref之后AVBufferRef整體被轉(zhuǎn)移到pkt2中,同時會調(diào)用av_init_packet初始化pkt;
經(jīng)過av_packet_clone之后會主動分配一個AVPacket因此不需要事先分配alloc一個;clone后pkt和pkt2數(shù)據(jù)區(qū)都是指向同一個AVFrame數(shù)據(jù)區(qū)。
經(jīng)過av_packet_ref后,pkt2首先需要av_packet_alloc一個AVPacket,然后經(jīng)過此函數(shù)后會分配AVBufferRef和將數(shù)據(jù)指向同一個AVFrame數(shù)據(jù)區(qū)。
AVFrame幀的操作與packet分配原理一致,使用方式也類似。主要包括幾個步驟一個是av_frame_alloc分配一個AVFrame幀,然后稍微有點不同的是需要為幀進行初始化,然后來確認(rèn)是視頻幀還是音頻幀。第二步是av_frame_get_buffer獲取幀的數(shù)據(jù)區(qū)也就是AVBufferRef和AVBuffer這里有一個比較特殊的地方是這里預(yù)制了一個長度為8的AVBufferRef指針數(shù)組,主要是用于不同的數(shù)據(jù)存儲格式不一樣需要多個內(nèi)存空間。最后是確保AVFrame是可寫的,在進行數(shù)據(jù)操作。釋放利用av_frame_free。
獲取分配數(shù)據(jù)幀的需要設(shè)置相關(guān)的參數(shù),幀的數(shù)據(jù)格式;如果是視頻需要設(shè)置分辨率;如果是音頻需要設(shè)置步長,通道數(shù)等參數(shù);主要原因是分配數(shù)據(jù)時根據(jù)這些參數(shù)計算分配數(shù)據(jù)空間大小。文章來源地址http://www.zghlxwxcb.cn/news/detail-424080.html
到了這里,關(guān)于FFmpeg源碼走讀之內(nèi)存管理模型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!