WebRTC音視頻同步機制實現分析(轉)

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

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

1 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報文構造完畢之后,經由網絡發送到對端。

2 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()函數獲取。

3 音視頻同步

前面兩節做必要的鋪墊后,本節詳細分析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線程中會周期性執行。

4 總結

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內容