WebRTC 音視頻同步方法

016-11-25 doraWebRTC編風網WebRTC編風網

來源:編風網

作者:weizhenwei,編風網專欄作家

   音視頻同步事關多媒體產品的最直觀用戶體驗,是音視頻媒體數據傳輸和渲染播放的最基本質量保證。音視頻如果不同步,有可能造成延遲、卡頓等非常影響用戶體驗的現象。因此,它非常重要。一般說來,音視頻同步維護媒體數據的時間線順序,即發送端在某一時刻採集的音視頻數據,接收端在另一時刻同時播放和渲染。


本文在深入研究WebRTC源代碼的基礎上,分析其音視頻同步的實現細節,包括RTP時間戳的產生,RTCP SR報文的構造、發送和接收,音視頻同步的初始化和同步過程。RTP時間戳是RTP數據包的基石,而RTCP SR報文是時間戳和NTP時間之間進行轉換的基準。下面詳細描述之。

一、RTP時間戳的產生

個人認爲,RTP時間戳和序列號是RTP協議的精華所在:前者定義媒體負載數據的採樣時刻,描述負載數據的幀間順序;後者定義RTP數據包的先後順序,描述媒體數據的幀內順序。關於RTP時間戳:


“The timestamp reflects the sampling instant of the first octet in the RTP data packet. The sampling instant must be derived from a clock that increments monotonically and linearly in time to allow synchronization and jitter calculations. The resolution of the clock must be sufficient for the desired synchronization accuracy and for measuring packet arrival jitter (one tick per video frame is typically not sufficient). ”


由以上定義可知,RTP時間戳反映RTP負載數據的採樣時刻,從單調線性遞增的時鐘中獲取。時鐘的精度由RTP負載數據的採樣頻率決定,比如視頻的採樣頻率一般是90khz,那麼時間戳增加1,則實際時間增加1/90000秒。


下面回到WebRTC源代碼內部,以視頻採集爲例分析RTP時間戳的產生過程,如圖1所示。



圖1 RTP時間戳構造過程


視頻採集線程以幀爲基本單位採集視頻數據,視頻幀從系統API被採集出來,經過初步加工之後到達VideoCaptureImpl::IncomingFrame()函數,設置render_time_ms_爲當前時間(其實就是採樣時刻)。


執行流程到達VideoCaptureInput::IncomingCapturedFrame()函數後,在該函數設置視頻幀的timestamp,ntp_time_ms和render_time_ms。其中render_time_ms爲當前時間,以毫秒爲單位;ntp_time_ms爲採樣時刻的絕對時間表示,以毫秒爲單位;timestamp則是採樣時間的時間戳表示,是ntp_time_ms和採樣頻率frequency的乘積,以1/frequency秒爲單位。由此可知,timestamp和ntp_time_ms是同一採樣時刻的不同表示。


接下來視頻幀經過編碼器編碼之後,發送到RTP模塊進行RTP打包和發送。構造RTP數據包頭部時調用RtpSender::BuildRTPheader()函數,確定時間戳的最終值爲rtphdr->timestamp = start_timestamp + timestamp,其中start_timestamp是RtpSender在初始化時設置的初始時間戳。RTP報文構造完畢之後,經由網絡發送到對端。

二、SR報文構造和收發

由上一節論述可知,NTP時間和RTP時間戳是同一時刻的不同表示,區別在於精度不同。NTP時間是絕對時間,以毫秒爲精度,而RTP時間戳則和媒體的採樣頻率有關。因此,我們需要維護一個NTP時間和RTP時間戳的對應關係,該用以對兩種時間的進行轉換。RTCP協議定義的SR報文維護了這種對應關係,下面詳細描述。

>>>>

2.1 時間戳初始化

在初始化階段,ModuleRtpRtcpImpl::SetSendingStatus()函數會獲取當前NTP時間的時間戳表示(ntp_time * frequency),作爲時間戳初始值分別設置RTPSender和RTCPSender的start_timestamp參數(即上節在確定RTP數據包頭不時間戳時的初始值)。

視頻數據在編碼完之後發往RTP模塊構造RTP報文時,視頻幀的時間戳timestamp和本地時間capture_time_ms通過RTCPSender::SetLastRtpTime()函數記錄到RTCPSender對象的last_rtp_timestamp和last_frame_capture_time_ms參數中,以將來將來構造RTCP SR報文使用。

>>>>

2.2 SR報文構造及發送

WebRTC內部通過ModuleProcessThread線程週期性發送RTCP報文,其中SR報文通過RTCPSender::BuildSR(ctx)構造。其中ctx中包含當前時刻的NTP時間,作爲SR報文[1]中的NTP時間。接下來需要計算出此刻對應的RTP時間戳,即假設此刻有一幀數據剛好被採樣,則其時間戳爲:


rtp_timestamp = start_timestamp_ + last_rtp_timestamp_ +
(clock_->TimeInMilliseconds() - last_frame_capture_time_ms_) *
(ctx.feedback_state_.frequency_hz / 1000);


至此,NTP時間和RTP時間戳全部齊活兒,就可以構造SR報文進行發送了。

>>>>2.3 SR接收

接收端在收到SR報文後,把其中包含的NTP時間和RTP時間戳記錄在RTCPSenderInfo對象中,供其他模塊獲取使用。比如通過RTCPReceiver::NTP()或者SenderInfoReceived()函數獲取。

三、音視頻同步

前面兩節做必要的鋪墊後,本節詳細分析WebRTC內部的音視頻同步過程。

>>>>

3.1 初始化配置

音視頻同步的核心就是根據媒體負載所攜帶RTP時間戳進行同步。在WebRTC內部,同步的基本對象是AudioReceiveStream/VideoReceiveStream,根據sync_group進行相互配對。同步的初始化設置過程如圖2所示。



圖2 音視頻同步初始化配置


Call對象在創建Audio/VideoReceiveStream時,調用ConfigureSync()進行音視頻同步的配置。配置參數爲sync_group,該參數在PeerConnectionFactory在創建MediaStream時指定。在ConfigureSync()函數內部,通過sync_group查找得到AudioReceiveStream,然後再在video_receive_streams中查找得到VideoReceiveStream。得到兩個媒體流,調用VideoReceiveStream::SetSyncChannel同步,在ViESyncModule::ConfigureSync()函數中把音視頻參數進行保存,包括音頻的voe_channel_id、voe_sync_interface, 和視頻的video_rtp_receiver、video_rtp_rtcp。

>>>>3.2 同步過程

音視頻的同步過程在ModuleProcessThread線程中執行。ViESyncModule作爲一個模塊註冊到ModuleProcessThread線程中,其Process()函數被該線程週期性調用,實現音視頻同步操作。


音視頻同步的核心思想就是以RTCP SR報文中攜帶的NTP時間和RTP時間戳作爲時間基準,以AudioReceiveStream和VideoReceiveStream各自收到最新RTP時間戳timestamp和對應的本地時間receive_time_ms作爲參數,計算音視頻流的相對延遲,然後結合音視頻的當前延遲計算最終的目標延遲,最後把目標延遲發送到音視頻模塊進行設置。目標延遲作爲音視頻渲染時的延遲下限值。整個過程如圖3所示。



圖3 音視頻同步過程


首先,從VideoReceiver獲得當前視頻延遲current_video_delay,即video_jitter_delay,decode_delay和render_delay的總和。然後從VoEVideoSyncImpl獲得當前音頻延遲current_audio_delay,即audio_jitter_delay和playout_delay的總和。


然後,音視頻分別以各自的rtp_rtcp和rtp_receiver更新各自的measure。其基本操作包括:從rtp_receiver獲取最新接收到的RTP報文的RTP時間戳latest_timestamp和對應的本地接收時刻latest_receive_time_ms,從rtp_rtcp獲取最新接收的RTCP SR報文中的NTP時間和RTP時間戳。然後把這些數據都存儲到measure中。注意measure中保存最新兩對RTCP SR報文中的NTP時間和RTP時間戳,用來在下一步計算媒體流的採樣頻率。


接下來,計算最新收到的音視頻數據的相對延遲。其基本流程如下:首先得到最新收到RTP時間戳latest_timestamp對應的NTP時間latest_capture_time。這裏用到measure中存儲的latest_timestamp和RTCP SR的NTP時間和RTP時間戳timestamp,利用兩對數值計算得到採樣頻率frequency,然後有latest_capture_time = latest_timestamp / frequency,得到單位爲毫秒的採樣時間。最後得到音視頻的相對延遲:


relative_delay = video_measure.latest_receive_time_ms -
audio_measure.latest_receive_time_ms -
(video_last_capture_time - audio_last_capture_time);


至此,我們得到三個重要參數:視頻當前延遲current_video_delay, 音頻當前延遲current_audio_delay和相對延遲relative_delay。接下來用這三個參數計算音視頻的目標延遲:首先計算總相對延遲current_diff = current_video_delay – current_audio_delay + relative_delay,根據歷史值對其求加權平均值。如果current_diff > 0,表明當前視頻延遲比音頻延遲長,需要減小視頻延遲或者增大音頻延遲;反之如果current < 0,則需要增大視頻延遲或者減小視頻延遲。經過此番調整之後,我們得到音視頻的目標延遲audio_target_delay和video_target_delay。


最後,我們把得到的目標延遲audio_target_delay和video_target_delay分別設置到音視頻模塊中,作爲將來渲染延遲的下限值。到此爲止,一次音視頻同步操作完成。該操作在ModuleProcessThread線程中會週期性執行。

四、總結

本文詳細分析了WebRTC內部音視頻同步的實現細節,包括RTP時間戳的產生,RTCP SR報文的構造、發送和接收,音視頻同步的初始化和同步過程。通過本文,對RTP協議、流媒體通信和音視頻同步有更深入的認識。

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