【FFmpeg】FFmpeg 內存結構 ⑥ ( 搭建開發環境 | AVPacket 創建與釋放代碼分析 | AVPacket 內存使用注意事項 )

文章目錄

  • 一、搭建開發環境
    • 1、開發環境搭建參考
    • 2、項目搭建
  • 二、AVPacket 創建與釋放代碼分析
    • 1、AVPacket 創建與釋放代碼
    • 2、Qt 單步調試方法
    • 3、單步調試 - 分析 AVPacket 創建與銷毀代碼
  • 三、AVPacket 內存使用注意事項
    • 1、謹慎使用 av_init_packet 函數
    • 2、av_init_packet 函數棄用
    • 3、av_init_packet 函數導致內存泄漏的反面示例
    • 4、av_packet_move_ref 函數 后可以使用 av_init_packet 函數
    • 5、av_packet_clone 函數 后不可以使用 av_init_packet 函數
    • 6、av_packet_ref 函數 與 av_packet_unref 函數 成對使用


FFmpeg 4.0 版本源碼地址 :

  • GitHub : https://github.com/FFmpeg/FFmpeg/tree/release/4.0
  • GitCode : https://gitcode.com/gh_mirrors/ff/FFmpeg/tree/release/4.0
  • FFmpeg/libavcodec/avpacket.c 源碼 : https://gitcode.com/gh_mirrors/ff/FFmpeg/blob/release/4.0/libavcodec/avpacket.c




一、搭建開發環境



開發前 , 先回顧下 開發環境 和 項目 搭建流程 ;


1、開發環境搭建參考


開發環境搭建參考如下博客 :

  • 【FFmpeg】Windows 10 平臺 FFmpeg 開發環境搭建 ① ( 安裝 Visual Studio 2015 | JavaScript_ProjectSystem 安裝包丟失或損壞 )
  • 【FFmpeg】Windows 10 平臺 FFmpeg 開發環境搭建 ② ( Qt 配置 MSVC2015 編譯器 | 安裝 VS2015 并配置 Qt 環境的 C/C++ 編譯器 )
  • 【FFmpeg】Windows 10 平臺 FFmpeg 開發環境搭建 ③ ( CDB 調試器下載安裝 | Qt 中配置 CDB 調試器 | Qt 中配置 32 位 / 64 位的構建套件 )
  • 【FFmpeg】Windows 10 平臺 FFmpeg 開發環境搭建 ④ ( FFmpeg 開發庫 | 創建項目導入并配置 FFmpeg 開發庫 | 拷貝 DLL 動態庫到 SysWOW64 目錄)

2、項目搭建


參考 【FFmpeg】Windows 10 平臺 FFmpeg 開發環境搭建 ④ ( FFmpeg 開發庫 | 創建項目導入并配置 FFmpeg 開發庫 | 拷貝 DLL 動態庫到 SysWOW64 目錄) 博客后半部分內容 :

  • 創建 Qt 項目 : 選擇 " Non-Qt Project " 下的 " Plain C Application " 類型的項目 , 構建系統使用默認的 qmake , 構建套件選擇 MSVC2015 套件 ;

  • 拷貝頭文件和庫函數 : 將 ffmpeg-4.2.1-win32-dev 開發庫目錄 , 拷貝到 Qt 工程目錄下 , 其中是 FFmpeg 的頭文件和庫函數 ;
    在這里插入圖片描述
    在這里插入圖片描述

  • 配置 頭文件和庫函數 : 在 .pro 配置文件 , 配置 上面拷貝的 頭文件 和 函數庫 , 完整配置內容如下 :

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qtSOURCES += main.cwin32 {
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 庫 , 并調用 av_version_info 函數 , 獲取 FFmpeg 版本號 ;
#include <stdio.h>                  // 引入標準輸入輸出頭文件
#include "libavutil/avutil.h"        // 引入FFmpeg庫中的avutil頭文件,avutil包含一些工具函數int main()
{// 輸出 "Hello World" 到控制臺printf("Hello World\n");// 輸出 FFmpeg 的版本信息,av_version_info() 函數返回當前FFmpeg庫的版本信息printf("FFmpeg version is %s\n", av_version_info());// 返回0表示程序正常結束return 0;
}
  • 代碼運行庫配置 : 將下面的文件拷貝到 C:\Windows\SysWOW64 目錄 或者 項目的構建目錄根目錄 中 ;

在這里插入圖片描述





二、AVPacket 創建與釋放代碼分析




1、AVPacket 創建與釋放代碼


下面的代碼是 AVPacket 從聲明 , 分配結構體內存 , 分配緩沖區數據內存 , 解除緩沖區數據引用 , 釋放緩沖區內存 的完整過程 , 之后會單步調試 , 查看具體的數據信息 ;

void av_packet_1()
{AVPacket *pkt = NULL;    // 定義一個指向 AVPacket 結構體的指針,用于存儲數據包int ret = 0;             // 定義一個整型變量 ret,用于存儲函數的返回值// 分配一個新的 AVPacket,返回一個指向該數據包的指針pkt = av_packet_alloc();// 為數據包分配內存,并初始化引用計數為1ret = av_new_packet(pkt, MEM_ITEM_SIZE);// 使用 memccpy 函數將 av_packet_1 的數據拷貝到 pkt->data 中// 從 pkt->data 地址開始,復制 MEM_ITEM_SIZE 字節的數據// 復制的數據來源是當前函數 av_packet_1 的地址memccpy(pkt->data, (void *)&av_packet_1, 1, MEM_ITEM_SIZE);// 解除數據包的引用,減少引用計數av_packet_unref(pkt);// 釋放數據包所占的內存,并將 pkt 指針設置為 NULLav_packet_free(&pkt);
}

2、Qt 單步調試方法


單步調試上述代碼 :

  • 在函數第一行代碼位置打上斷點 :
    在這里插入圖片描述

  • 點擊左下角的 " Start debugging of startup project " 按鈕 ,

在這里插入圖片描述

  • 程序開始運行后 , 停留在 斷點 位置 ;
    在這里插入圖片描述

  • 切換到指令集操作模式 : 之前在代碼模式中 , 無法進行 單步調試 , 參考 【錯誤記錄】Qt 單步調試 F10 快捷鍵失靈 ( Start and Break on Main 和 單步調試 默認都是 F10 快捷鍵 | 單步調試技巧 - 轉換匯編指令模式 ) 博客 ;
    在這里插入圖片描述

  • 在 " 指令集操作模式 " 中 , 按 F10 單步運行 , 然后再切換回代碼模式 , 即可進行單步調試 ;
    在這里插入圖片描述


3、單步調試 - 分析 AVPacket 創建與銷毀代碼


再次回到代碼模式 , 即可進行單步調試 , 目前代碼停留在函數的第三行代碼處 ;

此時可以看到 , 聲明的 AVPacket 結構體指針值為 0 , 暫時沒有賦值 ;
在這里插入圖片描述
繼續下一步單步調試 , 執行 av_packet_alloc 函數 , 結果如下 , 該函數為 AVPacket 指針分配了內存 , AVPacket 結構體的成員都設置了默認值 , 其中 buf 緩沖區 被設置為了 0 , 也就是 NULL , 暫時沒有進行初始化 ;

av_packet_alloc 函數 的 作用 是 : 在 堆內存中 , 為其分配內存空間 , 并為其成員設置默認值 ;

在這里插入圖片描述

繼續執行下一步 , 調用 av_new_packet 函數時 , 開始為 AVPacket 的數據分配內存 , 并將引用計數設置為 1 ;

此時 AVBuffer 結構體的 AVBufferRef *buf 成員 有了指向 , 不再是 NULL ;

真實的數據是 AVBufferRef 結構體的 AVBuffer *buffer 成員 中 , 其真實數據 和 引用計數 都在該結構體中 , 目前可以看到該結構體在堆內存中的地址是 0xec5240 ;

在這里插入圖片描述

執行 memccpy 函數 , 設置數據緩沖區中的數據 , 執行后 , 發現數據發生了改變 ;

在這里插入圖片描述

繼續向后執行 , 執行 av_packet_unref 函數 , 解除 AVPacket 數據包引用 , 減少引用計數 ;

參考 【FFmpeg】FFmpeg 內存結構 ④ ( AVPacket 函數簡介 | av_packet_unref 函數 | av_packet_move_ref 函數 ) 博客 中的 av_packet_unref 函數 分析章節 ;

在這里插入圖片描述

最后 執行 av_packet_free 函數 , 釋放 AVPacket 結構體 及其 內部的成員 所占用的內存空間 ;

執行完畢后 , 發現 AVPacket 結構體指針的值被置空 ;

在這里插入圖片描述





三、AVPacket 內存使用注意事項




1、謹慎使用 av_init_packet 函數


av_init_packet 函數 內容如下 , 分析下面的代碼 , 可知 該函數會將所有的字段都設置為 默認值 , 尤其是 pkt->buf 字段 , 這是數據緩沖區的引用 , 使用該函數不當 , 可以會導致數據緩沖區內存泄漏 ;

// 初始化 AVPacket 結構體
void av_init_packet(AVPacket *pkt)
{// 設置顯示時間戳 (PTS) 為未定義值pkt->pts                  = AV_NOPTS_VALUE;// 設置解碼時間戳 (DTS) 為未定義值pkt->dts                  = AV_NOPTS_VALUE;// 設置文件中的位置為 -1 (表示未知)pkt->pos                  = -1;// 設置幀的時長為 0pkt->duration             = 0;#if FF_API_CONVERGENCE_DURATION// 以下代碼處理棄用字段 convergence_duration 的初始化FF_DISABLE_DEPRECATION_WARNINGSpkt->convergence_duration = 0; // 設置幀的收斂時間為 0 (已廢棄字段)FF_ENABLE_DEPRECATION_WARNINGS
#endif// 設置標志位為 0 (表示無標志)pkt->flags                = 0;// 設置流索引為 0 (初始化為默認值)pkt->stream_index         = 0;// 將引用的緩沖區指針設為 NULLpkt->buf                  = NULL;// 將側數據的指針設為 NULLpkt->side_data            = NULL;// 設置側數據元素個數為 0pkt->side_data_elems      = 0;
}

2、av_init_packet 函數棄用


av_init_packet 函數 已經棄用 , FFmpeg 官方不再建議調用該函數 , 可參考 【FFmpeg】FFmpeg 內存結構 ② ( AVPacket 函數簡介 | av_packet_alloc 函數 | av_packet_free 函數 | av_new_packet 函數 ) 博客 ,

av_packet_alloc 函數 是 FFmpeg 官方推薦使用的 初始化 AVPacket 結構體的函數 , 并且建議與 av_packet_free 函數 配對使用 ;

av_packet_alloc 函數 中 調用了 av_init_packet 函數 , 已經包含了 av_init_packet 函數的功能 ;


3、av_init_packet 函數導致內存泄漏的反面示例


在下面的代碼中 , 在數據釋放之前的某個時間點 調用了 av_init_packet 函數 , 導致 AVPacket 的 buf 緩沖區提前置空 , 但是 buf 指向的數據緩沖區還沒有釋放 , 這段內存就出現了泄漏 , 再也無法進行釋放 ;

void av_packet_1()
{AVPacket *pkt = NULL;    // 定義一個指向 AVPacket 結構體的指針,用于存儲數據包int ret = 0;             // 定義一個整型變量 ret,用于存儲函數的返回值// 分配一個新的 AVPacket,返回一個指向該數據包的指針pkt = av_packet_alloc();// 為數據包分配內存,并初始化引用計數為1ret = av_new_packet(pkt, MEM_ITEM_SIZE);// 使用 memccpy 函數將 av_packet_1 的數據拷貝到 pkt->data 中// 從 pkt->data 地址開始,復制 MEM_ITEM_SIZE 字節的數據// 復制的數據來源是當前函數 av_packet_1 的地址memccpy(pkt->data, (void *)&av_packet_1, 1, MEM_ITEM_SIZE);// 如果在數據釋放之前的某個時間點 調用了 av_init_packet 函數// 就會導致 AVPacket 的 buf 緩沖區置空// 但是 buf 指向的數據緩沖區還沒有釋放 這段內存就出現了泄漏 再也無法進行釋放// 此處調用 av_init_packet 函數 就會導致內存泄漏 ☆☆☆av_init_packet(pkt);// 解除數據包的引用,減少引用計數av_packet_unref(pkt);// 釋放數據包所占的內存,并將 pkt 指針設置為 NULLav_packet_free(&pkt);
}

4、av_packet_move_ref 函數 后可以使用 av_init_packet 函數


av_packet_move_ref 函數 的 源碼如下 :

void av_packet_move_ref(AVPacket *dst, AVPacket *src)
{*dst = *src;av_init_packet(src);src->data = NULL;src->size = 0;
}

參考 https://raw.gitcode.com/gh_mirrors/ff/FFmpeg/raw/release%2F4.0/libavcodec/avpacket.c 源碼 ;

在 av_packet_move_ref 函數中 , 調用了 av_init_packet 函數 , 如果在之后繼續調用一次 av_init_packet 函數 , 是不會對內存結構產生影響的 , 相當于調用了兩次 av_init_packet 函數 ;


5、av_packet_clone 函數 后不可以使用 av_init_packet 函數


av_packet_clone 函數 源碼如下 :

// 克隆一個 AVPacket
AVPacket *av_packet_clone(const AVPacket *src)
{// 分配一個新的 AVPacket 對象AVPacket *ret = av_packet_alloc();// 如果分配失敗,直接返回 NULLif (!ret)return ret;// 復制源數據包 (src) 的內容到新分配的數據包 (ret)// 如果復制失敗,釋放新分配的數據包內存if (av_packet_ref(ret, src))av_packet_free(&ret);// 返回克隆的數據包指針 (如果成功,則指向新數據包;失敗則返回 NULL)return ret;
}

av_packet_clone 函數 相當于 av_packet_alloc 函數 + av_packet_ref 函數 的 組合函數 ;

調用完 av_packet_clone 函數 后 , 引用計數會增加 1 , 如果原來引用計數是 1 , 則新的引用計數是 2 ;

如果在 av_packet_clone 函數 后 調用 av_init_packet 函數 , 會導致新的 AVPacket 的 buf 成員被置空 , 之后 調用 av_packet_free 函數 無法訪問到 AVPacket 的 buf 成員 , 自然無法將引用計數自減 1 , 這樣就導致了 內存泄漏 ;


6、av_packet_ref 函數 與 av_packet_unref 函數 成對使用


av_packet_ref 函數 和 av_packet_unref 函數 需要成對使用 , 它們的主要目的是對 AVPacket 對象的引用計數進行管理 , 從而避免內存泄漏或重復釋放 ;

在使用 av_packet_ref 引用數據包時 , 引用計數會增加 ;

如果在某些地方不再需要這個引用 , 必須使用 av_packet_unref 釋放它 ;

否則 , 會導致內存泄漏 ;

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

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

相關文章

D94【python 接口自動化學習】- pytest進階之fixture用法

day94 pytest的fixture詳解 學習日期&#xff1a;20241210 學習目標&#xff1a;pytest基礎用法 -- pytest的fixture詳解 學習筆記&#xff1a; fixture的介紹 fixture是 pytest 用于將測試前后進行預備、清理工作的代碼處理機制。 fixture相對于setup和teardown來說有以…

2024首屆世界酒中國菜國際地理標志產品美食文化節成功舉辦篇章

2024首屆世界酒中國菜國際地理標志產品美食文化節成功舉辦&#xff0c;開啟美食文化交流新篇章 近日&#xff0c;首屆世界酒中國菜國際地理標志產品美食文化節在中國國際地理標志大廈成功舉辦&#xff0c;這場為期三天的美食文化盛會吸引了來自世界各地的美食愛好者、行業專家…

AI發展與LabVIEW程序員就業

人工智能&#xff08;AI&#xff09;技術的快速發展確實對許多行業帶來了變革&#xff0c;包括自動化、數據分析、軟件開發等領域。對于LabVIEW程序員來說&#xff0c;AI的崛起確實引發了一個值得關注的問題&#xff1a;AI會不會取代他們的工作&#xff0c;導致大量失業&#x…

展柜設計公司平面布置小程序的分析與設計springboot+論文源碼調試講解

3系統的需求分析 需求分析的任務是通過詳細調查展柜設計公司平面布置小程序軟件所需的對象&#xff0c;充分了解系統的工作概況&#xff0c;明確功能實現的各種需求&#xff0c;然后在此基礎上確定系統的功能。系統必須充分考慮今后可能的擴充和改變。 3.1可行性分析 通過對…

家校通小程序實戰教程10部門管理前后端連接

目錄 1 加載后端的數據2 為什么不直接給變量賦值3 保存部門信息4 最終的效果5 總結 現在部門管理已經完成了后端功能和前端開發&#xff0c;就需要在前端調用后端的數據完成界面的展示&#xff0c;而且在錄入部門信息后需要提交到數據庫里&#xff0c;本篇我們介紹一下前后端如…

spark-sql 備忘錄

wordcount sc.textFile("../data/data.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(__).collect 讀取json 文件 并通過sql 執行 join 查詢 public static void main(String[] args) {SparkSession session SparkSession.builder().master(&qu…

Java并發編程學習(二)

線程的狀態 有說5種的&#xff0c;有說6種的 5種的&#xff0c;從操作系統層面來講 初始狀態&#xff1a;也就是語言層面創建了線程對象&#xff0c;還未與操作系統線程關聯。Java中也就是new了一個線程&#xff0c;還未調用。可運行狀態&#xff1a;&#xff08;就緒狀態&a…

Docker方式安裝人人影視離線完整安裝包

本文軟件由網友 ルリデ 推薦&#xff1b; 上周&#xff0c;人人影視創始人宣布將人人影視二十年字幕數據開源分享 目前提供了兩種使用方式&#xff1a; “在線應用” &#xff1a;意味著需要有互聯網才可以使用。官方提供了網站&#xff1a;https://yyets.click “離線使用” …

Leetcode 3389. Minimum Operations to Make Character Frequencies Equal

Leetcode 3389. Minimum Operations to Make Character Frequencies Equal 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3389. Minimum Operations to Make Character Frequencies Equal 1. 解題思路 這一題從答題從test的結果來說來說做出的人很少&#xff0c;主要確實有些…

大文件處理的終極武器:Yield詳解

【大文件處理的終極武器&#xff1a;Yield詳解】&#x1f680; 一、大文件處理的痛點 內存限制數據量巨大傳統方法效率低 二、Yield解決方案 def read_large_file(file_path):with open(file_path, r) as file:# 每次只讀取一行&#xff0c;而不是全文for line in file:yie…

SpringBoot 學習

SpringBoot 學習 什么是 Springboot Spring Boot 是 Spring 提供的一個子項目&#xff0c;用于快速構建 Spring 應用程序 傳統的問題&#xff1a; 導入依賴繁瑣項目配置繁瑣 SpringBoot 的特性 起步依賴&#xff1a;整合所有 web 的依賴配置好了自動配置&#xff1a;bean…

到達率的變化動態調整服務器的服務率,實現負載均衡,提高資源利用效率

中心可以根據任務到達率的變化動態調整服務器的服務率,實現負載均衡,提高資源利用效率 服務率和到達率 中心可以根據任務到達率的變化動態調整服務器的服務率,實現負載均衡,提高資源利用效率服務率(Service Rate)到達率(Arrival Rate)控制參數實現負載均衡的方法在云計…

最新全開源IM即時通訊系統源碼(PC+WEB+IOS+Android)部署指南

全開源IM&#xff08;即時通訊&#xff09;系統源碼部署是一個復雜但系統的過程&#xff0c;涉及多個組件和步驟。以下是一個詳細的部署指南&#xff0c;旨在幫助開發者或系統管理員成功部署一個全開源的IM系統&#xff0c;如OpenIM。      IM即時通訊系統源碼準備工作   …

CAD c# 生成略縮圖預覽

代碼如下&#xff1a; using (Transaction tr currentdb.TransactionManager.StartTransaction()){//當前數據庫開啟事務using (Database tempdb new Database(false, true)) //創建臨時數據庫(兩個參數&#xff1a;是否創建符號表&#xff0c;不與當前文檔關聯){try{Bitmap …

CloudberryDB(二) 演化路線圖

CloudberryDB 制定了演化路線圖&#xff08;https://github.com/orgs/cloudberrydb/discussions/369&#xff09;并在逐步改進&#xff0c;這是 Cloudberry Database 發揮獨特價值之處。 計劃、正在進行或已完成的一些工作。 支持輕松升級 PostgreSQL 內核版本。 原有 Greenp…

單片機:實現呼吸燈(附帶源碼)

單片機實現呼吸燈詳細解讀 呼吸燈是一種常見的燈光效果&#xff0c;廣泛應用于電子產品、汽車、家居照明等領域。其基本特性是通過逐漸增亮和減弱的方式&#xff0c;使得燈光呈現出“呼吸”的效果&#xff0c;給人一種平緩、舒適的視覺感受。在嵌入式系統中&#xff0c;呼吸燈…

[面試題]--索引用了什么數據結構?有什么特點?

答&#xff1a;使用了B樹&#xff1a; 時間復雜度&#xff1a;O(logN),可以有效控制樹高 B樹特點&#xff1a; 1.葉子節點之間有相互鏈接的作用&#xff0c;會指向下一個相近的兄弟節點。 MySQL在組織葉子節點使用的是雙向鏈表 2.非葉子節點的值都保存在葉子節點當中 MySQL非葉…

ansible自動化運維(五)roles角色管理

Roles角色管理 角色&#xff08;roles&#xff09;是ansible自1.2版本開始引入的新特性&#xff0c;用于層次性&#xff0c;結構化地組織playbook。 roles能夠根據層次型結構自動裝載變量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。簡單的…

操作系統:文件系統

目錄 1、文件 概念&#xff1a; UNIX文件分類&#xff1a; 2、文件系統 3、文件的訪問方式 順序訪問 隨機訪問 4、文件的組織 邏輯組織 物理組織 5、倒排結構&#xff08;了解&#xff09; 5、文件目錄 文件控制塊&#xff08;FCB&#xff09; ?編輯 目錄項 單…

單元測試-FATAL ERROR in native method: processing of -javaagent failed

文章目錄 前言單元測試-FATAL ERROR in native method: processing of -javaagent failed1. 報錯信息2. 解決方案 前言 如果您覺得有用的話&#xff0c;記得給博主點個贊&#xff0c;評論&#xff0c;收藏一鍵三連啊&#xff0c;寫作不易啊^ _ ^。 ??而且聽說點贊的人每天的運…