WebRTC音視頻同步詳解

1 WebRTC版本

m74算法

2 時間戳

音視頻採樣後會給每一個音頻採樣、視頻幀打一個時間戳,打包成RTP後放在RTP頭中,稱爲RTP時間戳,RTP時間戳的單位依賴於音視頻流各自的採樣率。緩存

RTP Header格式以下:
在這裏插入圖片描述
網絡

2.1 視頻時間戳

視頻時間戳的單位爲1/90000秒,可是90000並非視頻的採樣率,而只是一個單位,幀率纔是視頻的採樣率。ide

不一樣打包方式下的時間戳:函數

  • Single Nalu:若是一個視頻幀包含1個NALU,能夠單獨打包成一個RTP包,那麼RTP時間戳就對應這個幀的採集時間;
  • FU-A:若是一個視頻幀的NALU過大(超過MTU)須要拆分紅多個包,可使用FU-A方式來拆分並打到不一樣的RTP包裏,那麼這幾個包的RTP時間戳是同樣的;
  • STAP-A:若是某幀較大不能單獨打包,可是該幀內部單獨的NALU比較小,可使用STAP-A方式合併多個NALU打包發送,可是這些NALU的時間戳必須一致,打包後的RTP時間戳也必須一致。

2.2 音頻時間戳

在這裏插入圖片描述
音頻時間戳的單位就是採樣率的倒數,例如採樣率48000,那麼1秒就有48000個採樣,每一個採樣1/48ms,每一個採樣對應一個時間戳。RTP音頻包通常打包20ms的數據,對應的採樣數爲 48000 * 20 / 1000 = 960,也就是說每一個音頻包裏攜帶960個音頻採樣,由於1個採樣對應1個時間戳,那麼相鄰兩個音頻RTP包的時間戳之差就是960。
spa

2.3 NTP時間戳

RTP的標準並無規定音頻、視頻流的第一個包必須同時採集、發送,也就是說開始的一小段時間內可能只有音頻或者視頻,再加上可能的網絡丟包,音頻或者視頻流的開始若干包可能丟失,那麼不能簡單認爲接收端收到的第一個音頻包和視頻包是對齊的,須要一個共同的時間基準來作時間對齊,這就是NTP時間戳的做用。.net

NTP時間戳是從1900年1月1日00:00:00以來通過的秒數,發送端以必定的頻率發送SR(Sender Report)這個RTCP包,分爲視頻SR和音頻SR,SR包內包含一個RTP時間戳和對應的NTP時間戳,接收端收到後就能夠肯定某個流的RTP時間戳和NTP時間戳的對應關係,這樣音頻、視頻的時間戳就能夠統一到同一個時間基準下。
在這裏插入圖片描述
如上圖,發送端的音視頻流並無對齊,可是週期地發送SR包,接收端獲得音視頻SR包的RTP時間戳、NTP時間戳後經過線性迴歸獲得NTP時間戳Tntp和RTP時間戳Trtp時間戳的對應關係:

3d

  • Tntp_audio = f(Trtp_audio)
  • Tntp_video = f(Trtp_video)

其中Tntp = f(Trtp) = kTrtp + b 爲線性函數,這樣接收端每收到一個RTP包,均可以將RTP時間戳換算成NTP時間戳,從而在同一時間基準下進行音視頻同步。code

2 延遲

視頻延遲的單位爲ms,對音頻來講,因爲採樣跟時間戳一一對應,全部時間延遲都會被換算成了緩存大小(音頻包的個數),其值爲:視頻

音頻延遲 = 時間延遲 << 8 / 20

也就是說,對48000的採樣率,960個採樣對應一個20ms包,時間延遲 / 20ms等於延遲了幾個包,左移8(乘以256)也就是所謂的Q8,是爲了用定點數表示必定精度的浮點數。

3 同步

3.1 一張圖看懂音視頻同步

在這裏插入圖片描述
首先接收端須要按照音、視頻各自的幀率來解碼、渲染,保證流暢地播放,在這個基礎上,須要計算音視頻兩個流目前的相對延遲,分別給音、視頻兩個流施加必定的延遲,保證音視頻的同步。

延遲播放,也就意味着在緩存中暫時存放數據,延遲換流暢。

對音頻來講,施加的延遲直接影響到音頻緩存的大小,音頻緩存的大小就體現了音頻的播放延遲。

對視頻來講,施加的延遲影響到視頻幀的渲染時間,經過比較渲染時間和當前時間來決定解碼後的視頻幀須要等待仍是須要馬上渲染。

正確設置好音視頻各自的播放延遲後,音視頻達到同步的效果。

能夠看到,音視頻同步中主要須要作到兩點:

  • 正確計算音視頻相對延遲;
  • 正確設置音視頻各自的播放延遲。

3.2 音視頻相對延遲

在這裏插入圖片描述
如上圖:

最近一對音視頻包的相對延遲 = (Tvideo_recv - Taudio_recv) - (Tvideo_send - Taudio_send)

其中Tvideo_recv、Taudio_recv分別是接收端收到視頻包、音頻包記錄的本地時間,能夠直接獲取,而Tvideo_send,Taudio_send做爲視頻包、音頻包的發送時間沒法直接獲取,由於接收到的RTP包只有RTP時間戳,沒法直接做爲本地時間來與Tvideo_recv、Taudio_recv進行運算,這時候就須要SR包中攜帶的NTP時間戳和RTP的對應關係來進行換算。

經過SR包中的NTP時間戳和RTP時間戳作線性迴歸(經過採樣概括映射關係)獲得二者的線性關係:
Tntp = f(Trtp) = kTrtp + b

這樣RTP時間戳就能夠直接轉化爲NTP時間戳,也就是發送端本地時間。從最近一對音視頻包相對延遲的計算公式能夠看出,分別對發送端和接收端的時間作運算,二者都在同一時間基準,能夠排除NTP時間同步問題的影響。

stream_synchronization.cc:34
StreamSynchronization::ComputeRelativeDelay

3.3 指望目標延遲

指望目標延遲就是保證音頻流、視頻流各自流暢播放的指望延遲

從3.1的圖能夠看出,對視頻來講,指望目標延遲 = 網絡延遲 + 解碼延遲 + 渲染延遲,對音頻來講,指望目標延遲 = 先後兩個音頻包之間的到達間隔的指望值。在接收時間的基礎上,加上各自的指望目標延遲進行播放,能夠保證音頻、視頻流能夠按照各自的步調進行流暢無卡頓的播放。

既要流暢播放又要進行同步,這就是爲何在計算音視頻流相對延遲的時候要同時考慮最近一對音視頻包的相對延遲又要考慮音視頻目標延遲差的緣由。

stream_synchronization.cc:34
StreamSynchronization::ComputeRelativeDelay

在這裏插入圖片描述

當前音視頻流相對延遲 = 最近一對音視頻包的相對延遲 + 音視頻目標延遲之差

3.3.1 指望視頻目標延遲

在這裏插入圖片描述

指望視頻目標延遲 = 網絡延遲 + 解碼延遲 + 渲染延遲

網絡延遲其實就是視頻JittterBuffer輸出的延遲googJitterBufferMs,能夠參考個人文章《WebRTC視頻JitterBuffer詳解》7.1節[抖動計算],簡單說就是經過卡爾曼濾波器計算視頻幀的到達延遲差(抖動),做爲網絡的延遲。

解碼時間的統計方法:統計最近最多10000次解碼的時間消耗,計算其95百分位數Tdecode,也就是說最近95%的幀的解碼時間都小於Tdecode,以之做爲解碼時間。

視頻渲染延遲默認是一個定值:10ms。

timing.cc:210
VCMTiming::TargetVideoDelay

3.3.2 指望音頻目標延遲

在這裏插入圖片描述
指望音頻目標延遲的算法和視頻解碼時間的算法相似,可是用直方圖來存放最近的65個音頻包的到達間隔,取95百分位數Taudio_target_delay,也就是說最近一段時間內,有95%的音頻包的到達間隔都小於Taudio_target_delay。同時考慮到網絡突發的可能,增長了峯值檢測,去掉異常的時間間隔。

取這個值做爲指望目標延遲來影響音頻的播放,能夠保證絕大多數狀況下音頻流的流暢。

neteq_impl.cc:311
NetEqImpl::FilteredCurrentDelayMs

3.4 音視頻同步

在這裏插入圖片描述
同步器的外部輸入有:

  • 指望音頻目標延遲,以該延遲播放,音頻是流暢的;
  • 指望視頻目標延遲,以該延遲播放,視頻是流暢的;
  • 最近一對音視頻包的相對延遲。

最近一對音視頻包的相對延遲與音視頻的目標延遲差之和,獲得當前時刻的音視頻相對延遲,也就是音、視頻流目前的時間誤差。

  • 當相對延遲 > 0,說明視頻比較慢,視頻延遲與基準(base_target_delay_ms_,默認0)比較:
    • extra_video_delay_ms > base_target_delay_ms_,減少視頻流延遲,設置音頻延遲爲基準;
    • extra_video_delay_ms <= base_target_delay_ms_,增大音頻流延遲,設置視頻延遲爲基準;
  • 當相對延遲 < 0,說明音頻比較慢,音頻延遲與基準(base_target_delay_ms_,默認0)比較:
    • extra_audio_delay_ms > base_target_delay_ms_,減少音頻流延遲,設置視頻延遲爲基準;
    • extra_audio_delay_ms <= base_target_delay_ms_,增大視頻流延遲,設置音頻延遲爲基準。

使用這個算法,能夠保證音、頻流的延遲都趨向於逼近基準,不會出現無限增長、減少的狀況。同時,一次延遲增大、減少的延遲diff_ms被設置爲相對延遲的一半,並限制在80ms範圍以內,也就是說WebRTC對一次同步的追趕時間作了限制,一次延遲增大、減少最大隻能是80ms,所以若是某個時刻某個流發生了較大抖動,須要一段時間另一個流才能同步。

通過了以上校準以後,輸出了同步後音頻、視頻流各自的最小播放延遲。

extra_audio_delay_ms -> 音頻最小播放延遲
extra_video_delay_ms -> 視頻最小播放延遲

理論上將這兩個播放延遲分別施加到音、視頻流後,這兩個流就是同步的,再與音、視頻流各自指望目標延遲取最大值,獲得音、視頻流的最優目標延遲(googTargetDelayMs),施加在音、視頻流上,能夠保證作到既同步、又流暢。

stream_synchronization.cc:64
StreamSynchronization::ComputeDelays

3.5 渲染時間

3.5.1 視頻渲染時間

在這裏插入圖片描述
該圖是計算視頻渲染時間的整體描述圖,仍然比較複雜,如下分幾個部分描述。

3.5.1.1 指望接收時間

在這裏插入圖片描述
TimestampExtrapolator類負責指望接收時間的產生,視頻JitterBuffer(的FrameBuffer)每收到一幀,會記錄該幀的RTP時間戳Tframe_rtp和本地接收時間Tframe_rcv,其中第一幀的RTP時間戳爲Tfirst_frame_rtp和本地接收時間Tfirst_frame_rcv
記幀RTP時間戳之差:Tframe_rtp_delta = Tframe_rtp - Tfirst_frame_rtp
幀本地接收時間之差:Tframe_recv_delta = Tframe_recv - Tfirst_frame_rcv
二者爲線性關係,指望RTP時間戳之差Tframe_rtp_delta = _w[0] * Tframe_recv_delta + _w[1]
經過卡爾曼濾波器獲得線性係數_w[0]、_w[1],進而獲得指望接收時間的值:
Tframe_recv = Tfirst_frame_rcv + (Tframe_rtp_delta - _w[1]) / _w[0]





也就是說,卡爾曼濾波器輸入視頻幀的RTP時間戳和本地接收時間觀測值,獲得視頻幀最優的指望接收時間,用於平滑網絡的抖動。

timestamp_extrapolator.cc:137
TimestampExtrapolator::ExtrapolateLocalTime

3.5.1.2 視頻當前延遲 - googCurrentDelayMs

在這裏插入圖片描述
解碼器經過視頻JitterBuffer的NextFrame方法獲取一幀去解碼時會設置該幀的指望渲染時間Texpect_render,以及該幀的實際開始解碼時間Tactual_decode

該幀的指望開始解碼時間爲指望渲染時間減去解碼、渲染的延遲:
Texpect_decode = Texpect_render - Tdecode_delay - Trender_delay

那麼該幀產生的延遲爲實際開始解碼時間減去指望開始解碼時間:
Tframe_delay = Tactual_decode - Texpect_decode

該幀延遲和上一個時刻的視頻當前延遲疊加,若是仍然小於目標延遲,則增加視頻當前延遲。
Tcurrent_delay = max(Tcurrent_delay + Tframe_delay, Ttarget_delay)

也就是視頻當前延遲以目標延遲爲上限逼近目標延遲。

timing.cc:96
VCMTiming::UpdateCurrentDelay

3.5.1.3 計算渲染時間

在這裏插入圖片描述
取同步後的延遲做爲視頻的實際延遲,也就是當前延遲和最小播放延遲的最大者:
Tactual_delay = max(Tcurrent_delay , Tmin_playout_delay)

至此,當前視頻幀的指望接收時間Tframe_recv和視頻實際延遲Tactual_delay都已經獲得,能夠計算最終的視頻幀渲染時間:
Trender_time = Tframe_recv + Tactual_delay

timing.cc:169
VCMTiming::RenderTimeMs

3.5.2 音頻渲染時間

在這裏插入圖片描述
NetEQ中有若干緩存用來暫存數據,主要的是JitterBuffer(PacketBuffer)、SyncBuffer,分別存放解碼前和解碼後的數據,這些緩存的大小就體現了音頻當前的延遲。

NetEQ的BufferLevelFilter類維護音頻的當前延遲,音頻渲染器每取一次音頻數據都根據當前剩餘的緩存大小設置一次音頻的當前延遲並進行平滑,獲得平滑後的當前延遲(googCurrentDelayMs)。

buffer_level_filter.cc:29
BufferLevelFilter::Update

NetEQ的DecisionLogic類比較下一個音頻包的時間戳與SynBuffer中的結尾時間戳,若是不相等,也就是不連續,那麼須要進行丟包隱藏(Expand/PLC)或者融合(Merge);若是相等,也就是連續,則根據當前緩存的大小與目標延遲大小來決定是對音頻數據進行加速、減速,或者正常播放。

decision_logic.cc:100
DecisionLogic::GetDecision
在這裏插入圖片描述

  • 若是音頻當前延遲 < 3 / 4音頻目標延遲,也就是緩存數據較少,須要減速播放等待目標延遲;
  • 若是音頻當前延遲 > 音頻目標延遲,也就是緩存數據過多,須要加速播放追趕目標延遲。

decision_logic.cc:283
DecisionLogic::ExpectedPacketAvailable

音頻就是以緩存長度追趕目標延遲的方式達到延遲必定時間的效果,最終和視頻的目標延遲對齊後,實現了音視頻同步。

相關文章
相關標籤/搜索