目錄
🌈前言🌈 ? ?
📁 整體架構 +?詳細流程
📁 數據流向?
📁 隊列設計?編輯
📁 線程設計
?📁 音視頻同步
📁 音頻輸出設計
📁 視頻輸出設計
📁 總結
🌈前言🌈 ? ?
? ? ? ? 這篇文章是我在學習FFmpeg時,看到一位UP主的開源項目。我認為還是比較好認,通過和這個項目可以快速入門FFmpeg 7。
? ? ? ? 因為種種原因,目前網上關于FFmpeg 7.x?版本相關介紹太少,并且相較于之前版本,接口有很大變化,學習途中可能有很大困惱。因此,我希望通過這個項目和我的理解,帶大家快速入門FFmpeg以及7版本以上的接口使用流程。
? ? ? ? 這篇文章的圖片和源碼均來自B站UP主:程序員老廖音視頻入門必備項目-最新FFmpeg7.1播放器開發_嗶哩嗶哩_bilibili。
????????我也將該項目做了一遍,并且將源碼上傳至Gitee,大家可以直接進行下載。
AVPlayer: 本地音樂播放器(FFmpeg + SDL)
📁 整體架構 +?詳細流程
1. 初始化:創建并初始化必要的隊列、線程和組件。
2. 媒體處理:
? ? ? ? i. 解復用線程從文件讀取數據包。
? ? ? ?ii. 解碼線程將數據包解碼為幀。
? ? ? iii. 音視頻輸出模塊將幀渲染輸出。
3. 用戶交互: 處理用戶事件,如暫停、退出等。
4.? 資源釋放: 程序結束時按照正確的順序釋放資源,避免內存泄露。
📁 數據流向
1. 解復用階段:
????????DemuxThread讀取媒體文件
????????分離音視頻數據包
????????將音視頻包放入對應的AVPacketQueue
2. 解碼階段:
????????DecodeThread從AVPacketQueue獲取數據包
????????使用FFmpeg解碼器解碼數據包
????????生成音視頻幀并放入AVFrameQueue
3. 渲染階段:
????????AudioOutput/VideoOutput從AVFrameQueue獲取幀
????????處理幀數據(重采樣、格式轉換等)
????????通過SDL渲染到輸出設備
📁 隊列設計
1. 模板設計:使用C++模板實現通用隊列結構,提高代碼復用率
2. 線程安全:使用互斥鎖和條件變量保證多線程環境下的數據一致性
3. 特化實現:為AVPacket和AVFrame提供特化隊列,處理FFmpeg資源的引用計數
4. 終止機制:通過abort標志控制隊列終止,實現優雅退出
5. 資源管理:
????????AVPacketQueue負責管理AVPacket資源,使用av_packet_free釋放
????????AVFrameQueue負責管理AVFrame資源,使用av_frame_free釋放
? ? ? ? 在Queue中加鎖解鎖的操作會用到兩個管理類 (當然可以都使用第二個):
std::lock_guard (簡單鎖):?
?? ??? ?1. 輕量級, 性能更高, 無額外開銷
?? ??? ?2. 嚴格作用域鎖: 不能手動控制
?? ??? ?3. 不可轉移所有權
?? ??? ?4. 不支持條件變量
std::unique_lock (靈活鎖):
?? ??? ?1. 功能更強大, 有額外的狀態存儲
?? ??? ?2. 支持手動的加鎖解鎖
?? ??? ?3. 支持所有權轉移
?? ??? ?4. 支持條件變量
📁 線程設計
1. 基類封裝:Thread基類封裝線程創建,啟動和停止的通用邏輯
2. 虛函數機制:通過純虛函數Run要求派生類實現具體的業務邏輯
3. 狀態控制:使用abort控制線程循環狀態,實現退出
4. 資源管理:
????????DemuxThread管理文件讀取和格式解析資源(AVFormatContext)
????????DecodeThread管理解碼器資源(AVCodecContext)
5. 線程協作
? ? ? ? 通過隊列實現線程間數據傳遞,解耦生產者和消費者。
?📁 音視頻同步
1. 主時鐘選擇:
? ? ? ? i. 使用音頻PTS作為主時鐘基準
? ? ? ?ii. 音頻在回調函數中更新時鐘值
2. 視頻同步策略:
? ? ? ? i. 計算視頻幀PTS與當前音頻時鐘的差值
? ? ? ?ii. 差值為正(視頻超前):延遲顯示
? ? ? iii. 差值為負(視頻滯后):立即顯示
? ? ?iiii. 差值過大:考慮跳幀或重復幀
3. 時鐘管理:
? ? ? ? i. AVSync類提供時鐘讀寫接口
? ? ? ?ii. 音頻線程設置時鐘
? ? ? iii. 視頻線程讀取時鐘
? ? ? ? AVSync中記錄一個動態變差值,可以簡單理解為記錄音頻的pts。
為什么不能直接保存音頻的pts呢?
??
pts
只在音頻回調時更新??,而視頻可能在任意時刻查詢?GetClock()
。
如果音頻回調間隔是 10ms,而視頻在兩次回調之間查詢?
GetClock()
,它拿到的?pts
是 ??過時的??(沒有考慮這期間的時間流逝)。??結果??:視頻計算的時間偏差不準確,導致音畫不同步。
??無法處理音頻播放速度變化??(如加速、卡頓)。
如果音頻因緩沖不足而卡頓,
pts
更新變慢,但系統時間仍在流逝。直接返回?
_current_audio_pts
無法反映這種延遲。
📁 音頻輸出設計
? ? ? ? 聲音輸出模塊負責從幀隊列取出音頻幀,進行必要的重采樣,并通過SDL輸出音頻。
1. 初始化流程:
? ? ? ? i. 初始化SDL音頻播放子系統
? ? ? ?ii. 設置音頻參數
? ? ? iii. 設置音頻回調函數
? ? ?iiii. 創建重采樣上下文(如果需要)
2. 回調機制:
? ? ? ? i. SDL音頻系統在需要數據時調用設置的回調函數
? ? ? ?ii. 回調函數從幀隊列獲取音頻幀
? ? ? iii. 根據需要進行重采樣 (使用SwrContext)
? ? ?iiii. 將處理后的音頻數據填充到SDL提供的緩沖區
3. 音頻時鐘:
? ? ? ? i. 以音頻PTS為主時鐘
? ? ? ?ii. 在每次回調中更新音頻時鐘
? ? ? iii. 作為視頻同步的基準
4. 資源管理:
? ? ? ? i. 管理重采樣上下文(SwrContext)
? ? ? ?ii. 管理音頻緩沖區
? ? ? iii. 在Delnit和析構函數中釋放資源
????????AVRational 是 FFmpeg 中用于表示 ??分數(有理數)?? 的結構體,主要用于時間基(time base)、幀率(frame rate)、采樣率(sample rate)等場景
📁 視頻輸出設計
????????畫面輸出模塊負責從幀隊列獲取視頻幀,與音頻同步,并通過SDL渲染到屏幕
1. 初始化流程:
????????初始化SDL視頻子系統
????????創建窗口和渲染器
????????創建紋理用于視頻渲染
2. 主循環機制:
????????處理SDL事件(如退出、按鍵等)
????????刷新視頻幀
????????控制幀率以實現音視頻同步
3. 同步策略:
????????比較視頻幀PTS與音頻時鐘
????????如果視頻超前,等待適當時間再顯示
????????如果視頻滯后,立即顯示并可能丟幀
4. 渲染過程:
????????將YUV數據更新到SDL紋理
????????將紋理渲染到窗口
????????釋放已顯示的幀
5. 資源管理:
????????管理SDL資源(窗口、渲染器、紋理)
????????在DeInit和析構函數中釋放資源
📁 總結
? ? ? ? 以上就是該項目的整體流程,相對來說還是比較簡單的。我認為將這個項目跑一邊,對于重點代碼寫一遍,那么對FFmpeg 7版本的接口就會有比較深刻的印象了,例如解封裝,解碼,轉碼等內容。