播放器技術分享(3):音畫同步
搞音視訊開發好些年,分享過許多部落格文章,比如:前幾年釋出的《FFmpeg Tips》系列,《Android 音訊開發》系列,《直播疑難雜症排查》系列等等。最近想把多年來開發和優化播放器的經驗也分享出來,希望能幫助到音視訊領域的初學者。第一期文章要推出的內容主要涉及到播放器比較核心的幾個技術點,大概的目錄如下:
3. 播放器技術分享(3):音畫同步
4. 播放器技術分享(4):首開時間
5. 播放器技術分享(5):延時優化
本篇是系列文章的第三篇,主要聊一聊播放器的音畫同步。
1 概述
首先,我們給出音畫同步的定義:
音畫同步是指播放器正在渲染的每一幀畫面和正在播放的每一段聲音都是嚴格對應起來的,不存在人耳和肉眼可以分辨出來的偏差。
2 時間戳
機器沒有眼睛也沒有耳朵,那它是如何去感知視訊檔案中的某一幀視訊資料跟某一幀音訊資料是屬於 同一時間 的呢 ?
現實生活中,判斷兩件事情是否同一時刻發生的方法就是記錄並且比較事件發生的日期時間,同樣,我們可以在音訊和視訊資料“生產”出來的時候,為資料標記上一個“時間戳”,代表著它的出生時間。由此,播放器就知道如何判斷了。
如圖所示,音視訊在採集(生產)的時候,就給每一幀都打上 “時間戳”(注意,音視訊的時間戳需要使用一個共同的時間戳基準),假設以音訊開始採集時刻的系統絕對時間點作為時間戳 0 點,假設音訊 20ms 取樣一幀,視訊幀率 30fps -> 即 33ms 產生一幀,那麼,視訊幀和音訊幀的時間戳大致的遞增規律為:
視訊幀:10,43,76,109,142,……
音訊幀:0,20,40,60,80,120,……
注:實際上資料採集不會這麼 “均勻”,會有略微的浮動。
如此一來,播放器就可以通過附加在音訊和視訊幀上的時間戳資訊,感知到音訊幀和視訊幀的 “相對關係”,實現音畫同步就輕而易舉了,判斷方法如下;
音畫同步的方法:判斷視訊與音訊幀的時間戳差值,是不是在一定的 “閾值” 範圍內,如果是,則可以渲染/播放,否則就 “等” 到合適的時間。
3 音畫同步的 “閾值”
由於音視訊是分開採集的,所以它們的時間戳不可能完全對應得上,因此,只要在一個合理的 “閾值” 範圍內即可認為是同步的。那麼,這個 “閾值” 是怎麼定義的呢 ?
如圖所示,這個 “閾值” 有一個國際標準定義了它,叫做:RFC-1359,它是這麼定義的:
1. 無法察覺:音訊和視訊的時間戳差值在:-100ms ~ +25ms 之間
2. 能夠察覺:音訊滯後了 100ms 以上,或者超前了 25ms 以上
3. 無法接受:音訊滯後了 185ms 以上,或者超前了 90ms 以上
有了這個國際標準和經驗值,我們做音畫同步取的閾值就不用瞎猜了~~
4 音畫同步的實現
音畫同步的實現策略一般有 3 種:
1. 視訊同步到音訊
2. 音訊同步到視訊
3. 音視訊同步到外部時鐘
比較常用的策略是:視訊同步到音訊。
因為音訊是流式的,按照規律的勻速的速率去播放,才能顯得更加 “平滑”,而視訊的播放其實是一張一張圖片進行重新整理顯示,它的重新整理時間的調整相對而言更容易一些,使用者肉眼的敏感度也更弱一些。
下面就講講使用 “視訊同步到音訊” 的方式,如何實現播放器的音畫同步:
如圖所示,A 代表音訊幀,P 代表視訊幀,音訊幀持續送入揚聲器以既定的速率播放,每播放一幀音訊資料,則把該音訊幀的時間戳更新到 Master Clock,作為主時鐘,視訊幀則參考 Master Clock 來決定自己是否渲染、何時渲染或者丟棄,演算法如下:
1. 假設 min 為音畫同步閾值(如:25ms),則當前視訊時間戳如果與 master clock 的絕對差值在 25ms 閾值範圍內則都可以送入渲染
2. 如果 diff > 0,即 pts > m-clock,則代表視訊幀提前準備好了。這種情況下,是可以通過 sleep 來等待主時鐘的,但是為了防止異常時間戳導致 sleep 時間過長,可以設定一個 max 異常閾值(如:1000ms),如果 diff 超過這個 max 可以認為是異常幀,丟棄掉
3. 如果 diff < 0,即 pts < m-clock,則代表視訊幀滯後了。這種情況下,如果滯後超出約定閾值(如:25ms)的視訊幀就應該被丟棄
音畫同步最核心的思想就在這裡了,看起來好像也並不是有多複雜,對吧~
6 總結
播放器的音畫同步,就分享到這裡了,如有疑問的小夥伴歡迎來信 [email protected] 交流。另外,也歡迎大家關注我的新浪微博 @盧_俊 或者 微信公眾號 @Jhuster 獲取最新的文章和資訊。