FFmpeg 與 C++ 構建音視頻處理全鏈路實戰(三)—— FFmpeg 內存模型

經過前面文章的 FFmpeg 編程實踐,相信你已經對AVPacketAVFrame這兩個核心結構體不再陌生。當我們編寫代碼時,頻繁調用unref系列 API 釋放內存的操作,或許讓你心生疑惑:這些函數究竟是如何實現內存釋放的?又該在何時準確調用,才能避免埋下內存泄漏的隱患?

在 FFmpeg 編程的浩瀚征途中,內存泄漏就像隱匿于迷霧中的 “幽靈”,悄無聲息卻極具破壞力。哪怕只是一個細微的疏忽,都可能讓程序的內存占用如失控的氣球般不斷膨脹,最終導致性能嚴重下降,甚至引發程序崩潰。想要徹底征服這個難纏的 “幽靈”,深入理解 FFmpeg 的內存模型是必經之路,而其中的關鍵,就藏在AVPacketAVFrame這兩個核心結構體的內存管理機制中。它們如同精密鐘表里不可或缺的齒輪,而內部的buf引用計數系統,則恰似讓齒輪順滑運轉的 “潤滑油”,每一次精準咬合,都關乎著整個程序的穩定與高效。接下來,讓我們層層拆解,揭開 FFmpeg 內存管理的神秘面紗,探尋其中的運行奧秘。

一、FFmpeg 內存管理的重要性與挑戰

在多媒體處理領域,音視頻數據量龐大且處理流程復雜。每一幀視頻、每一段音頻數據都需要占用大量的內存空間。如果內存管理不當,不僅會造成資源浪費,還可能導致程序出現各種難以調試的問題。想象一下,一個擁擠的車站,每時每刻都有大量乘客下車,如果到站的乘客不離開站臺就會越來越多人擁擠在空間有限的站臺,最終場面會變得混亂不堪。FFmpeg 程序中的內存管理也是如此,合理的內存分配、使用和及時釋放,是保證程序穩定高效運行的關鍵。

然而,FFmpeg 的內存管理面臨諸多挑戰。一方面,它需要處理多種不同類型的媒體數據,每種數據的存儲和處理方式都有所不同;另一方面,FFmpeg 的 API 設計注重靈活性和高效性,這就要求開發者對其內存模型有深入的理解,才能正確地使用相關接口,避免內存泄漏等問題。

二、AVPacket/AVFrame:音視頻數據的 “包裹” 與引用計數

avpacket和avframe再引用計數方面設計相同,下面以avpacket為例。

(一)AVPacket 的基本結構與作用

AVPacket是 FFmpeg 中用于存儲壓縮音視頻數據的結構體,它就像是一個專門用來運輸音視頻數據的 “包裹”。這個 “包裹” 里不僅裝著實際的音視頻數據(存儲在data指針指向的內存區域),還包含了許多重要的元信息,如數據的大小(size)、時間戳(ptsdts)、所屬的流索引(stream_index)等。這些元信息對于音視頻的解碼、播放和處理至關重要,就像包裹上的收件人信息、快遞單號等,指引著數據在 FFmpeg 的各個模塊中準確傳遞。

(二)AVPacket 的 buf 引用計數系統

AVPacket內部,AVBufferRef類型的buf成員是實現內存管理和引用計數的核心。引用計數機制通過記錄AVPacket內存被引用的次數,來決定何時釋放內存,它就像一個精準的 “使用計數器”,精確控制著內存的生命周期。

當使用av_packet_alloc創建一個AVPacket時,若不進行額外操作,其buf指針初始化為NULL,此時不存在引用計數的概念,因為尚未關聯實際的內存緩沖區。只有當通過av_new_packet等函數為AVPacket分配數據內存時,才會創建AVBuffer并初始化引用計數為 1。

當使用av_packet_refAVPacket進行引用操作時,實際上是增加了目標AVPacketbuf引用計數。例如:

AVPacket* pkt1 = av_packet_alloc();
// 此時pkt->buf為NULL,無引用計數
av_new_packet(pkt1, 1024); 
// 執行后,pkt->buf指向新分配的內存,引用計數為1
AVPacket* pkt2 = av_packet_alloc();
av_packet_ref(pkt2, pkt1);
// 此時pkt1->buf和pkt2->buf指向同一塊內存,引用計數均為2

這種共享內存的方式極大提升了數據傳遞效率,多個AVPacket可共用同一塊內存區域,減少不必要的內存拷貝。

(三)AVPacket 在接收與拷貝操作中的引用計數變化

1.接收操作(如av_read_frame
在解封裝過程中,av_read_frame從解封裝器獲取AVPacket時,解封裝器會將內部的AVPacket傳遞出來,并將該AVPacketbuf引用計數增加 1 。此時調用者獲得的AVPacket已持有有效的引用,若后續不再使用,直接調用av_packet_unref即可安全釋放。例如:

AVPacket* pkt = av_packet_alloc();
int ret = av_read_frame(ifmt_ctx, pkt);
if (ret >= 0) {// 此時pkt->buf引用計數至少為1(解碼器內部已引用)// 處理pkt數據...av_packet_unref(pkt); // 引用計數減1,若變為0則釋放內存
}

若希望在多個地方使用該AVPacket,可通過av_packet_ref創建新的引用:

AVPacket* pkt2 = av_packet_alloc();
av_packet_ref(pkt2, pkt1);
// 此時pkt1->buf和pkt2->buf引用計數均為2
av_packet_unref(pkt1); 
// 引用計數減為1,內存不釋放
av_packet_unref(pkt2); 
// 引用計數減為0,釋放關聯內存

?2.拷貝操作(如av_packet_copy
av_packet_copy函數會復制AVPacket的元數據(如sizepts等),并且會自動處理buf引用計數。(等同于av_packet_alloc()+av_packet_unref()):

AVPacket* src_pkt = av_packet_alloc();
av_new_packet(src_pkt, 1024); 
AVPacket* dst_pkt = av_packet_alloc();
dst_pkt = av_packet_copy( src_pkt);

?若想完全獨立復制數據,可使用av_packet_move_ref,它會將源AVPacketbuf所有權轉移給目標AVPacket,同時重置源AVPacketbuf

AVPacket* src_pkt = av_packet_alloc();
av_new_packet(src_pkt, 1024); 
AVPacket* dst_pkt = av_packet_alloc();
av_packet_move_ref(dst_pkt, src_pkt);
// 此時dst_pkt擁有buf所有權,引用計數為1
// src_pkt的buf變為NULL,不再引用內存

三、AVPacket/AVFrame常用API

AVPacket常用API?

函數原型說明
AVPacket *av_packet _alloc (void);
分配 AVPacket,此時buffer為空
void av_packet _free (AVPacket **pkt);
釋放 AVPacket對象,包含buf的釋放
void av_init_packet(AVPacket *pkt);
初始化 AVPacket,經初始換avpacket字段(4.0后已棄用,功能被包含在_alloc內)
int av_new_packet(AVPacket *pkt, int size);
AVPacket buf 分配內存,引用計數置1
int av_packet_ref(AVPacket *dst, const AVPacket *src);
增加引用計數
void av_packet_ unref (AVPacket *pkt);
減少引用計數
void av_packet_move_ref (AVPacket *dst, AVPacket *src);
轉移引用計數(轉移所屬權)
AVPacket *av_packet_clone(const AVPacket *src);
等于
av_packet_alloc()+av_packet_ref()

AVFrame常用API??

函數原型說明
AVFrame *av_frame _alloc (void);
分配AVFrame
void av_frame _free (AVFrame **frame);
釋放AVFrame
int av_frame_ ref (AVFrame *dst, const AVFrame *src);
增加引用計數
void av_frame_ unref (AVFrame *frame);
減少引用計數
void av_frame_ move_ref (AVFrame *dst, AVFrame *src);
轉移引用計數(轉移所屬權)
int av_frame_get_buffer (AVFrame *frame, int align);
根據AVFrame分配內存
AVFrame *av_frame_clone(const AVFrame *src);
等于
av_frame_alloc()+av_frame_ref()

四、總結

FFmpeg 的內存模型,尤其是AVPacketAVFrame的 buf 引用計數系統,是保證音視頻處理程序穩定運行的關鍵。理解它們的工作原理、在不同操作中的引用計數變化,以及正確的內存分配、使用和釋放方法,是每一個 FFmpeg 開發者的必修課。

在實際編程過程中,我們要時刻牢記引用計數這個 “賬本”,小心處理每一次的引用、拷貝和釋放操作,避免因內存管理不當而引入的各種問題。只有這樣,我們才能真正駕馭 FFmpeg 這個強大的多媒體處理工具,開發出高效、穩定的音視頻應用程序。希望通過本文的詳細講解,能幫助你在 FFmpeg 編程的道路上走得更加順暢,不再被內存泄漏這個 “幽靈” 所困擾。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80879.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80879.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80879.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c 中的哈希表

哈希是一種可以接受各種類型、大小的輸入,輸出一個固定長度整數的過程。你可以將哈希理解成一種特殊的映射,哈希映射,將一個理論無限的集合A映射到有限整數集合B上。 哈希函數:哈希函數是哈希過程的核心,它決定了哈希映…

【一次成功!】Ubuntu22.04安裝cartographer

之前在ubuntu20.04上成功安裝cartographer,但是翻遍全網都沒找到官方的22.04安裝教程,然后找到小魚的,試了一下,一次成功,連接如下: gd2l-ros2/docs/humble/chapt10/get_started/2.Carto介紹及安裝.md at …

【WPF】Opacity 屬性的使用

在WPF(Windows Presentation Foundation)中,Opacity 屬性是定義一個元素透明度的屬性,其值范圍是從 0.0(完全透明)到 1.0(完全不透明)。由于 Opacity 是在 UIElement 類中定義的&…

8天Python從入門到精通【itheima】-6~10

目錄 7節-開發出第一個Python程序: 1.在cmd窗口寫下第一個最簡單的程序:Hello World!!! 9節: 1.如何卸載python: 2.報錯:不是可運行的程序 ?編輯 3.報錯:無法初始化設備PRN: 4.報錯:語法錯誤——非法的字符 10節-python解釋器: 1.python解釋器的原理: 2.解…

Mac 3大好用的復制粘貼管理工具對比

剪貼板管理器是查看復制粘貼歷史記錄的工具,幾乎是每個蘋果電腦用戶必備工具。市面上的工具很多,我結合了功能豐富、設計簡潔、交互便利整理了目前3款頭部剪貼板應用 Paste、PasteNow、PasteMe。 Paste 優勢:老牌剪切板應用,功能…

2025年全國青少年信息素養大賽初賽模擬測試網站崩了的原因及應對比賽流程

2025年全國青少年信息素養大賽初賽模擬測試昨天開始,由于同一時間涌入太多的人,導致網站的服務器奔了,出現了各種狀況,導致很多人沒有模擬上,大家今天可以刷新或者提前打開網頁。 有的是一直“轉圈圈”,有的…

02 | 大模型微調 | 從0學習到實戰微調 | 從數學概率到千億參數大模型

一、導讀 作為非AI專業技術開發者(我是小小爬蟲開發工程師😋) 本系列文章將圍繞《大模型微調》進行學習(也是我個人學習的筆記,所以會持續更新),最后以上手實操模型微調的目的。 (…

十四、繼承與組合(Inheritance Composition)

十四、繼承與組合(Inheritance & Composition) 引言 C最引人注目的特性之一是代碼復用。組合:在新類中創建已有類的對象。繼承:將新類作為已有類的一個類型來創建。 14.1 組合的語法 Useful.h //C14:Useful.h #ifndef US…

2025年5月-信息系統項目管理師高級-軟考高項一般計算題

決策樹和期望貨幣值 加權算法 自制和外購分析 溝通渠道 三點估算PERT 當其他條件一樣時,npv越大越好

OpenJDK 17 中線程啟動的完整流程用C++ 源碼詳解

1. 線程創建入口(JNI 層) 當 Java 層調用 Thread.start() 時,JVM 通過 JNI 進入 JVM_StartThread 函數: JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))// 1. 檢查線程狀態,防止重復啟動if (java_…

Spring MVC參數傳遞

本內容采用最新SpringBoot3框架版本,視頻觀看地址:B站視頻播放 1. Postman基礎 Postman是一個接口測試工具,Postman相當于一個客戶端,可以模擬用戶發起的各類HTTP請求,將請求數據發送至服務端,獲取對應的響應結果。 2. Spring MVC相關注解 3. Spring MVC參數傳遞 Spri…

Python面向對象編程(OOP)深度解析:從封裝到繼承的多維度實踐

引言 面向對象編程(Object-Oriented Programming, OOP)是Python開發中的核心范式,其三大特性——??封裝、繼承、多態??——為構建模塊化、可維護的代碼提供了堅實基礎。本文將通過代碼實例與理論結合的方式,系統解析Python OOP的實現機制與高級特性…

0.66kV0.69kV接地電阻柜常規配置單

0.66kV/0.69kV接地電阻柜是變壓器中性點接地電阻柜中的特殊存在,主要應用于低壓柴油發電機組220V、火力發電廠380V、煤炭企業660V/690V等電力系統或電力用戶1000V的低壓系統中。 我們來看看0.66kV0.69kV接地電阻柜配置單: 配置特點如下: 1…

矩陣短劇系統:如何用1個后臺管理100+小程序?深度解析多端綁定技術

短劇行業效率革命!一套系統實現多平臺內容分發、數據統管與流量聚合 在短劇行業爆發式增長的今天,內容方和運營者面臨兩大核心痛點:多平臺運營成本高與流量分散難聚合。傳統模式下,每個小程序需獨立開發后臺,導致人力…

CSS可以繼承的樣式匯總

CSS可以繼承的樣式匯總 在CSS中,以下是一些常見的可繼承樣式屬性: 字體屬性:包括 font-family (字體系列)、 font-size (字體大小)、 font-weight (字體粗細)、 font-sty…

BFS算法篇——打開智慧之門,BFS算法在拓撲排序中的詩意探索(上)

文章目錄 引言一、拓撲排序的背景二、BFS算法解決拓撲排序三、應用場景四、代碼實現五、代碼解釋六、總結 引言 在這浩瀚如海的算法世界中,有一扇門,開啟后通向了有序的領域。它便是拓撲排序,這個問題的解決方法猶如一場深刻的哲學思考&#…

【Qt開發】信號與槽

目錄 1,信號與槽的介紹 2,信號與槽的運用 3,自定義信號 1,信號與槽的介紹 在Qt框架中,信號與槽機制是一種用于對象間通信的強大工具。它是在Qt中實現事件處理和回調函數的主要方法。 信號:窗口中&#x…

數據庫基礎:概念、原理與實戰示例

在當今信息時代,數據已經成為企業和個人的核心資產。無論是社交媒體、電子商務、金融交易,還是物聯網設備,幾乎所有的現代應用都依賴于高效的數據存儲和管理。數據庫(Database)作為數據管理的核心技術,幫助…

前端-HTML基本概念

目錄 什么是HTML 常用的瀏覽器引擎是什么? 常見的HTML實體字符 HTML注釋 HTML語義化是什么?為什么要語義化?一定要語義化嗎? 連續空格如何渲染? 聲明文檔類型 哪些字符集編碼支持簡體中文? 如何解…

Linux進程信號處理(26)

文章目錄 前言一、信號的處理時機處理情況“合適”的時機 二、用戶態與內核態概念重談進程地址空間信號的處理過程 三、信號的捕捉內核如何實現信號的捕捉?sigaction 四、信號部分小結五、可重入函數六、volatile七、SIGCHLD 信號總結 前言 這篇就是我們關于信號的最…