WebRTC音視頻同步機(jī)制實(shí)現(xiàn)分析

WebRTC音視頻同步機(jī)制實(shí)現(xiàn)分析

2016-11-25 doraWebRTC編風(fēng)網(wǎng)WebRTC編風(fēng)網(wǎng)

來(lái)源:編風(fēng)網(wǎng)

作者:weizhenwei众辨,編風(fēng)網(wǎng)專欄作家

音視頻同步事關(guān)多媒體產(chǎn)品的最直觀用戶體驗(yàn)导梆,是音視頻媒體數(shù)據(jù)傳輸和渲染播放的最基本質(zhì)量保證。音視頻如果不同步,有可能造成延遲、卡頓等非常影響用戶體驗(yàn)的現(xiàn)象。因此婆殿,它非常重要。一般說(shuō)來(lái)待笑,音視頻同步維護(hù)媒體數(shù)據(jù)的時(shí)間線順序鸣皂,即發(fā)送端在某一時(shí)刻采集的音視頻數(shù)據(jù),接收端在另一時(shí)刻同時(shí)播放和渲染暮蹂。

本文在深入研究WebRTC源代碼的基礎(chǔ)上寞缝,分析其音視頻同步的實(shí)現(xiàn)細(xì)節(jié),包括RTP時(shí)間戳的產(chǎn)生仰泻,RTCP SR報(bào)文的構(gòu)造荆陆、發(fā)送和接收,音視頻同步的初始化和同步過(guò)程集侯。RTP時(shí)間戳是RTP數(shù)據(jù)包的基石被啼,而RTCP SR報(bào)文是時(shí)間戳和NTP時(shí)間之間進(jìn)行轉(zhuǎn)換的基準(zhǔn)。下面詳細(xì)描述之棠枉。

一浓体、RTP時(shí)間戳的產(chǎn)生

個(gè)人認(rèn)為,RTP時(shí)間戳和序列號(hào)是RTP協(xié)議的精華所在:前者定義媒體負(fù)載數(shù)據(jù)的采樣時(shí)刻辈讶,描述負(fù)載數(shù)據(jù)的幀間順序杀赢;后者定義RTP數(shù)據(jù)包的先后順序哈雏,描述媒體數(shù)據(jù)的幀內(nèi)順序寡壮。關(guān)于RTP時(shí)間戳:

“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時(shí)間戳反映RTP負(fù)載數(shù)據(jù)的采樣時(shí)刻,從單調(diào)線性遞增的時(shí)鐘中獲取月幌。時(shí)鐘的精度由RTP負(fù)載數(shù)據(jù)的采樣頻率決定碍讯,比如視頻的采樣頻率一般是90khz,那么時(shí)間戳增加1扯躺,則實(shí)際時(shí)間增加1/90000秒捉兴。

下面回到WebRTC源代碼內(nèi)部,以視頻采集為例分析RTP時(shí)間戳的產(chǎn)生過(guò)程录语,如圖1所示轴术。

[圖片上傳失敗...(image-6cee82-1543066451821)]

圖1 RTP時(shí)間戳構(gòu)造過(guò)程

視頻采集線程以幀為基本單位采集視頻數(shù)據(jù),視頻幀從系統(tǒng)API被采集出來(lái)钦无,經(jīng)過(guò)初步加工之后到達(dá)VideoCaptureImpl::IncomingFrame()函數(shù),設(shè)置render_time_ms_為當(dāng)前時(shí)間(其實(shí)就是采樣時(shí)刻)盖袭。

執(zhí)行流程到達(dá)VideoCaptureInput::IncomingCapturedFrame()函數(shù)后失暂,在該函數(shù)設(shè)置視頻幀的timestamp彼宠,ntp_time_ms和render_time_ms。其中render_time_ms為當(dāng)前時(shí)間弟塞,以毫秒為單位凭峡;ntp_time_ms為采樣時(shí)刻的絕對(duì)時(shí)間表示,以毫秒為單位决记;timestamp則是采樣時(shí)間的時(shí)間戳表示摧冀,是ntp_time_ms和采樣頻率frequency的乘積,以1/frequency秒為單位系宫。由此可知索昂,timestamp和ntp_time_ms是同一采樣時(shí)刻的不同表示。

接下來(lái)視頻幀經(jīng)過(guò)編碼器編碼之后扩借,發(fā)送到RTP模塊進(jìn)行RTP打包和發(fā)送椒惨。構(gòu)造RTP數(shù)據(jù)包頭部時(shí)調(diào)用RtpSender::BuildRTPheader()函數(shù),確定時(shí)間戳的最終值為rtphdr->timestamp = start_timestamp + timestamp潮罪,其中start_timestamp是RtpSender在初始化時(shí)設(shè)置的初始時(shí)間戳康谆。RTP報(bào)文構(gòu)造完畢之后,經(jīng)由網(wǎng)絡(luò)發(fā)送到對(duì)端嫉到。

二沃暗、SR報(bào)文構(gòu)造和收發(fā)

由上一節(jié)論述可知,NTP時(shí)間和RTP時(shí)間戳是同一時(shí)刻的不同表示何恶,區(qū)別在于精度不同孽锥。NTP時(shí)間是絕對(duì)時(shí)間,以毫秒為精度导而,而RTP時(shí)間戳則和媒體的采樣頻率有關(guān)忱叭。因此,我們需要維護(hù)一個(gè)NTP時(shí)間和RTP時(shí)間戳的對(duì)應(yīng)關(guān)系今艺,該用以對(duì)兩種時(shí)間的進(jìn)行轉(zhuǎn)換韵丑。RTCP協(xié)議定義的SR報(bào)文維護(hù)了這種對(duì)應(yīng)關(guān)系,下面詳細(xì)描述虚缎。

2.1 時(shí)間戳初始化

在初始化階段撵彻,ModuleRtpRtcpImpl::SetSendingStatus()函數(shù)會(huì)獲取當(dāng)前NTP時(shí)間的時(shí)間戳表示(ntp_time * frequency),作為時(shí)間戳初始值分別設(shè)置RTPSender和RTCPSender的start_timestamp參數(shù)(即上節(jié)在確定RTP數(shù)據(jù)包頭不時(shí)間戳?xí)r的初始值)实牡。

視頻數(shù)據(jù)在編碼完之后發(fā)往RTP模塊構(gòu)造RTP報(bào)文時(shí)陌僵,視頻幀的時(shí)間戳timestamp和本地時(shí)間capture_time_ms通過(guò)RTCPSender::SetLastRtpTime()函數(shù)記錄到RTCPSender對(duì)象的last_rtp_timestamp和last_frame_capture_time_ms參數(shù)中,以將來(lái)將來(lái)構(gòu)造RTCP SR報(bào)文使用创坞。

2.2 SR報(bào)文構(gòu)造及發(fā)送

WebRTC內(nèi)部通過(guò)ModuleProcessThread線程周期性發(fā)送RTCP報(bào)文碗短,其中SR報(bào)文通過(guò)RTCPSender::BuildSR(ctx)構(gòu)造。其中ctx中包含當(dāng)前時(shí)刻的NTP時(shí)間题涨,作為SR報(bào)文[1]中的NTP時(shí)間偎谁。接下來(lái)需要計(jì)算出此刻對(duì)應(yīng)的RTP時(shí)間戳总滩,即假設(shè)此刻有一幀數(shù)據(jù)剛好被采樣,則其時(shí)間戳為:

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

至此巡雨,NTP時(shí)間和RTP時(shí)間戳全部齊活兒闰渔,就可以構(gòu)造SR報(bào)文進(jìn)行發(fā)送了。

2.3 SR接收

接收端在收到SR報(bào)文后铐望,把其中包含的NTP時(shí)間和RTP時(shí)間戳記錄在RTCPSenderInfo對(duì)象中冈涧,供其他模塊獲取使用。比如通過(guò)RTCPReceiver::NTP()或者SenderInfoReceived()函數(shù)獲取正蛙。

三督弓、音視頻同步

前面兩節(jié)做必要的鋪墊后,本節(jié)詳細(xì)分析WebRTC內(nèi)部的音視頻同步過(guò)程跟畅。

3.1 初始化配置

音視頻同步的核心就是根據(jù)媒體負(fù)載所攜帶RTP時(shí)間戳進(jìn)行同步咽筋。在WebRTC內(nèi)部,同步的基本對(duì)象是AudioReceiveStream/VideoReceiveStream徊件,根據(jù)sync_group進(jìn)行相互配對(duì)奸攻。同步的初始化設(shè)置過(guò)程如圖2所示。

[圖片上傳失敗...(image-62912e-1543066451820)]

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

Call對(duì)象在創(chuàng)建Audio/VideoReceiveStream時(shí)虱痕,調(diào)用ConfigureSync()進(jìn)行音視頻同步的配置睹耐。配置參數(shù)為sync_group,該參數(shù)在PeerConnectionFactory在創(chuàng)建MediaStream時(shí)指定部翘。在ConfigureSync()函數(shù)內(nèi)部硝训,通過(guò)sync_group查找得到AudioReceiveStream,然后再在video_receive_streams中查找得到VideoReceiveStream新思。得到兩個(gè)媒體流窖梁,調(diào)用VideoReceiveStream::SetSyncChannel同步,在ViESyncModule::ConfigureSync()函數(shù)中把音視頻參數(shù)進(jìn)行保存夹囚,包括音頻的voe_channel_id纵刘、voe_sync_interface, 和視頻的video_rtp_receiver、video_rtp_rtcp荸哟。

3.2 同步過(guò)程

音視頻的同步過(guò)程在ModuleProcessThread線程中執(zhí)行假哎。ViESyncModule作為一個(gè)模塊注冊(cè)到ModuleProcessThread線程中,其Process()函數(shù)被該線程周期性調(diào)用鞍历,實(shí)現(xiàn)音視頻同步操作舵抹。

音視頻同步的核心思想就是以RTCP SR報(bào)文中攜帶的NTP時(shí)間和RTP時(shí)間戳作為時(shí)間基準(zhǔn),以AudioReceiveStream和VideoReceiveStream各自收到最新RTP時(shí)間戳timestamp和對(duì)應(yīng)的本地時(shí)間receive_time_ms作為參數(shù)劣砍,計(jì)算音視頻流的相對(duì)延遲惧蛹,然后結(jié)合音視頻的當(dāng)前延遲計(jì)算最終的目標(biāo)延遲,最后把目標(biāo)延遲發(fā)送到音視頻模塊進(jìn)行設(shè)置。目標(biāo)延遲作為音視頻渲染時(shí)的延遲下限值赊淑。整個(gè)過(guò)程如圖3所示爵政。

[圖片上傳失敗...(image-2400d0-1543066451820)]

圖3 音視頻同步過(guò)程

首先,從VideoReceiver獲得當(dāng)前視頻延遲current_video_delay陶缺,即video_jitter_delay,decode_delay和render_delay的總和洁灵。然后從VoEVideoSyncImpl獲得當(dāng)前音頻延遲current_audio_delay饱岸,即audio_jitter_delay和playout_delay的總和。

然后徽千,音視頻分別以各自的rtp_rtcp和rtp_receiver更新各自的measure苫费。其基本操作包括:從rtp_receiver獲取最新接收到的RTP報(bào)文的RTP時(shí)間戳latest_timestamp和對(duì)應(yīng)的本地接收時(shí)刻latest_receive_time_ms,從rtp_rtcp獲取最新接收的RTCP SR報(bào)文中的NTP時(shí)間和RTP時(shí)間戳双抽。然后把這些數(shù)據(jù)都存儲(chǔ)到measure中百框。注意measure中保存最新兩對(duì)RTCP SR報(bào)文中的NTP時(shí)間和RTP時(shí)間戳,用來(lái)在下一步計(jì)算媒體流的采樣頻率牍汹。

接下來(lái)铐维,計(jì)算最新收到的音視頻數(shù)據(jù)的相對(duì)延遲。其基本流程如下:首先得到最新收到RTP時(shí)間戳latest_timestamp對(duì)應(yīng)的NTP時(shí)間latest_capture_time慎菲。這里用到measure中存儲(chǔ)的latest_timestamp和RTCP SR的NTP時(shí)間和RTP時(shí)間戳timestamp嫁蛇,利用兩對(duì)數(shù)值計(jì)算得到采樣頻率frequency,然后有l(wèi)atest_capture_time = latest_timestamp / frequency露该,得到單位為毫秒的采樣時(shí)間睬棚。最后得到音視頻的相對(duì)延遲:

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

至此,我們得到三個(gè)重要參數(shù):視頻當(dāng)前延遲current_video_delay, 音頻當(dāng)前延遲current_audio_delay和相對(duì)延遲relative_delay解幼。接下來(lái)用這三個(gè)參數(shù)計(jì)算音視頻的目標(biāo)延遲:首先計(jì)算總相對(duì)延遲current_diff = current_video_delay – current_audio_delay + relative_delay抑党,根據(jù)歷史值對(duì)其求加權(quán)平均值。如果current_diff > 0撵摆,表明當(dāng)前視頻延遲比音頻延遲長(zhǎng)底靠,需要減小視頻延遲或者增大音頻延遲;反之如果current < 0台汇,則需要增大視頻延遲或者減小視頻延遲苛骨。經(jīng)過(guò)此番調(diào)整之后,我們得到音視頻的目標(biāo)延遲audio_target_delay和video_target_delay苟呐。

最后痒芝,我們把得到的目標(biāo)延遲audio_target_delay和video_target_delay分別設(shè)置到音視頻模塊中,作為將來(lái)渲染延遲的下限值牵素。到此為止严衬,一次音視頻同步操作完成。該操作在ModuleProcessThread線程中會(huì)周期性執(zhí)行笆呆。

四请琳、總結(jié)

本文詳細(xì)分析了WebRTC內(nèi)部音視頻同步的實(shí)現(xiàn)細(xì)節(jié)粱挡,包括RTP時(shí)間戳的產(chǎn)生,RTCP SR報(bào)文的構(gòu)造俄精、發(fā)送和接收询筏,音視頻同步的初始化和同步過(guò)程。通過(guò)本文竖慧,對(duì)RTP協(xié)議嫌套、流媒體通信和音視頻同步有更深入的認(rèn)識(shí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末圾旨,一起剝皮案震驚了整個(gè)濱河市踱讨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌砍的,老刑警劉巖痹筛,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異廓鞠,居然都是意外死亡帚稠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)诫惭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)翁锡,“玉大人,你說(shuō)我怎么就攤上這事夕土」菹危” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵怨绣,是天一觀的道長(zhǎng)角溃。 經(jīng)常有香客問(wèn)我,道長(zhǎng)篮撑,這世上最難降的妖魔是什么减细? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮赢笨,結(jié)果婚禮上未蝌,老公的妹妹穿的比我還像新娘。我一直安慰自己茧妒,他們只是感情好萧吠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著桐筏,像睡著了一般纸型。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天狰腌,我揣著相機(jī)與錄音除破,去河邊找鬼。 笑死琼腔,一個(gè)胖子當(dāng)著我的面吹牛瑰枫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丹莲,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼躁垛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了圾笨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逊谋,失蹤者是張志新(化名)和其女友劉穎擂达,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胶滋,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡板鬓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了究恤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俭令。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖部宿,靈堂內(nèi)的尸體忽然破棺而出抄腔,到底是詐尸還是另有隱情,我是刑警寧澤理张,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布赫蛇,位于F島的核電站,受9級(jí)特大地震影響雾叭,放射性物質(zhì)發(fā)生泄漏悟耘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一织狐、第九天 我趴在偏房一處隱蔽的房頂上張望暂幼。 院中可真熱鬧,春花似錦移迫、人聲如沸旺嬉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鹰服。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悲酷,已是汗流浹背套菜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留设易,地道東北人逗柴。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像顿肺,于是被迫代替她去往敵國(guó)和親戏溺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容