ffmpeg+sdl教程----編寫一個簡單的播放器5(同步視頻到音頻)

    個人認爲,這這部分教程的新增代碼量雖然不是最多的,難度卻是最大的,重複看了多次才明白,因爲有兩個問題的困擾,搞得還不清楚:

1.音頻和視頻既然都有各自的時間戳,各自按各自的時間戳來播放不就行了,爲什麼還需要同步呢?

2.如果要把視頻同步到音頻,怎麼同步?或者說以什麼標準來同步?

    第一個問題的答案可能是,一是音頻和視頻的開始播放的時間是不一樣,二是播放每幀音頻或視頻時可能必須把解碼數據,視頻格式轉換消耗的時間考慮進來,解碼數據,視頻格式轉換等步驟又和不同的機器配置相關,設想一下這種極端的情況,有一幀圖片應該在此時刻播放了(根據時間戳),而解碼器還沒來及解碼完(程序代碼或者機器配置太爛),解碼完後,可能需要丟棄該幀,不然視頻趕不上音頻。

    第二個問題的答案也不是很清楚,但根據這個教程的代碼來分析,特別是video_refresh_timer函數中關於時間戳部分的代碼告訴了這個問題答案,首先大結構體VideoState保存了兩個用於同步的兩個重要變量audio_clock和video_clock,分別保存了音頻和視頻的播放了多長時間,也就是教程中說的音頻和視頻的內部時鐘。把視頻同步到音頻的方法就是比較video_clock(當前視頻播放到的時刻)和audio_clock(當前音頻播放到的時刻)差的絕對值如果大於同步閾值sync_threshold,就重新計算延遲delay,這時視頻如果播放得太快就直接讓延遲delay = 2*delay,反之則立即播放。而video_clock和audio_clock之差在可接受範圍內,delay的值直接取上一幀和當前幀的延遲,delay將被用於下一幀的播放延遲。

    時鐘的同步,教程中分爲三個部分。

    第一部分講如何保存視頻幀的時間戳和video_clock,代碼主要在video_thread函數中,該函數中的這行代碼len1 = avcodec_decode_video(is->video_st->codec, pFrame, &frameFinished,packet->data, packet->size);解碼packet中的數據到pFrame時,會調用我們自己些的幀內存分配函數our_get_buffer,把第一個包的時間戳保存到幀pFrame中(一個幀可能由多個數據包組成),幀的時間戳優先取最後一個包的dts,沒有才取第一個包的pts,都沒有就取video_clock。

   video_thread函數在解碼完一幀後,立馬調用synchronize_video函數,synchronize_video函數的作用就是維護video_clock的值,讓video_clock變量始終保存視頻播放了多長時間的信息,其中還考慮到了幀重複的問題。得到當前視頻幀的時間戳相對音頻來說簡單些。

    第二部分講,假設我們能得到當前音頻的時間戳,如何讓視頻同步到音頻,也就是上面第二個問題的答案。

    第三部分講如何獲得當前音頻幀的時間戳,代碼主要在get_audio_clock函數中,這個並不是簡單地返回is->audio_clock了事,如果那樣做就忽略了把數據包轉移到輸出緩衝的時間花費,我們聲音時鐘中記錄的時間比實際的要早太多。所以我們必須要檢查一下我們還有多少沒有寫入。audio_clock的維護工作交給了audio_decode_frame函數來處理,首先在函數末尾初始化is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;然後解碼完一幀音頻後又有更新。

    《代碼5》

轉自鏈接點擊打開鏈接

發佈了53 篇原創文章 · 獲贊 44 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章