但個別格式的音訊流和影片流是分開儲存的,前面一大段放了所有的音訊幀,後面一大段放了所有的影片幀,並非音訊幀與影片幀交錯儲存的模式。對於這種格式,playsync.c播放時先放完所有的聲音,這期間畫面是空白的;再快速放完所有的影片畫面,這期間沒有聲音,顯然播放過程是有問題的。
若想糾正playsync.c的播放問題,就得重新設計音影片的同步播放機制,不能採取一邊遍歷一邊播放的方式,而要先把音訊幀和影片幀都讀到快取佇列中,再依次檢查音訊與影片的時間戳,從而決定在哪個時刻才播放對應時間戳的音影片。具體到程式碼實現上,需要補充下列幾點改造。
1、除了已有的影片處理執行緒和影片包佇列之外,還要增加宣告音訊處理執行緒和音訊包佇列,當然音訊包佇列配套的佇列鎖也要補充宣告。增補後的宣告程式碼如下所示:
2、在程式初始化的時候,不但要建立影片處理執行緒和影片佇列的互斥鎖,還要建立音訊處理執行緒和音訊佇列的互斥鎖。修改後的初始化程式碼如下所示:
3、對音影片檔案遍歷資料包時,不能立即渲染音訊,而要把音訊包加入音訊佇列,把影片包加入影片佇列,由兩個處理執行緒根據時間戳來排程具體的播放進度。另外,在所有資料包都遍歷完之後,影片包佇列可能還有剩餘的資料,所以程式末尾得輪詢影片包佇列,直至所有影片幀都渲染結束才算完成播放。據此修改音影片檔案的遍歷與輪詢程式碼如下所示:
除了上述的三大塊改造,尚有下面四個函式要補充修改:
thread_work_audio函式:這是音訊處理執行緒新增的工作函式,主要從音訊包佇列取資料,然後解碼為音訊幀再重取樣,並將重取樣的結果資料送給揚聲器。
thread_work_video函式:這是影片處理執行緒原有的工作函式,除了給影片包佇列及其對應的互斥鎖改名之外,其他程式碼照搬即可。
play_video_frame函式:這是播放影片畫面的新增函式,就是把原來SDL渲染畫面的程式碼塊重新包裝成獨立的函式,方便多次呼叫罷了。
release函式:這是釋放音影片資源的函式,與之前的釋放程式碼相比,主要增加了音訊處理執行緒的等待操作,以及音訊佇列鎖的銷燬操作。
上述修改後的程式碼已經附在了《FFmpeg開發實戰:從零基礎到短影片上線》一書第10章的原始碼chapter10/playsync2.c,這個c程式碼是playsync.c的改進版,能夠正常播放音訊流和影片流分開儲存的影片檔案。
接著執行下面的編譯命令。
編譯完成後執行以下命令啟動測試程式,期望播放影片檔案fuzhou.mp4。
程式執行完畢,發現控制檯輸出以下的日誌資訊。
同時彈出SDL視窗播放影片畫面,並且揚聲器傳來了陣陣歌聲,表示上述程式碼正確實現了同步播放音影片的功能。