作者:付明旺。唐橋科技資深架構(gòu)師涮毫。負(fù)責(zé)實時通信軟件的技術(shù)創(chuàng)新與研發(fā)。曾就職于中興通訊贷屎、諾基亞罢防,在4G和IMS-webRTC等電信通信產(chǎn)品擔(dān)任軟件工程師、系統(tǒng)架構(gòu)師等角色唉侄,具有豐富的無線/互聯(lián)網(wǎng)通信咒吐、實時音視頻通信產(chǎn)品技術(shù)經(jīng)驗。
唐橋科技属划,醫(yī)療云通信專家恬叹,是專業(yè)的智能視頻PaaS及SaaS云服務(wù)提供商。致力于三網(wǎng)融合視頻通訊平臺的研發(fā)和在不同領(lǐng)域的應(yīng)用同眯,已經(jīng)與醫(yī)療行業(yè)應(yīng)用深度結(jié)合绽昼,推出了遠(yuǎn)程醫(yī)療、醫(yī)學(xué)視頻云會議须蜗、互動醫(yī)教硅确、醫(yī)學(xué)線上峰會平臺等一系列行業(yè)解決方案目溉,為醫(yī)療行業(yè)提供專業(yè)音視頻通訊服務(wù)。在此基礎(chǔ)上菱农,唐橋科技將多年積累的音視頻技術(shù)以SDK/API的形式開放給企業(yè)及開發(fā)者缭付,降低技術(shù)門檻,讓企業(yè)跑得更快循未。
webRTC解決方案實現(xiàn)了P2P的音視頻通信陷猫,其中有關(guān)timing的幾個問題值得歸納總結(jié)。開始本文之前建議先行閱讀https://xie.infoq.cn/article/738b8293dce86f7c8748e2629 了解視頻傳輸?shù)年P(guān)鍵路徑的妖。webRTC是一個異步系統(tǒng)绣檬,通信的雙方無需做時間同步。本文主要探討webRTC是怎樣解決下面兩個跟時間有關(guān)的問題:1. 音視頻同步 2. 基于延時的帶寬評估羔味。
音視頻同步(Lip-sync)
發(fā)送端采集-編碼-發(fā)送河咽,接收端解碼-渲染,音頻流和視頻流的處理和網(wǎng)絡(luò)傳輸是互相獨(dú)立的赋元,而且各自的采樣/播放頻率也是不同的忘蟹。如何在接收端還原采集端的真實場景,從來都不是一件容易的事情搁凸。好在人的聽覺/視覺系統(tǒng)本來就有一定容忍能力媚值,ITU(國際電信聯(lián)盟)給了一個建議:音頻之于視頻在這個范圍內(nèi)[-125ms,45ms],也就是落后125ms或早于45ms护糖,人類感覺上是可以接受的褥芒,我們認(rèn)為這是音視頻處于同步狀態(tài)。
webRTC解決這個問題的原理也比較簡單嫡良,發(fā)送端給音頻流和視頻流的數(shù)據(jù)包都打上時間戳锰扶,這些時間戳都可以跟同一個時間基準(zhǔn)對齊,接收端利用時間戳和緩存就可以調(diào)整每個流上音頻/視頻幀渲染時間寝受,最終達(dá)到同步的效果坷牛。我們可以進(jìn)一步了解一下實現(xiàn)的細(xì)節(jié),以視頻流為例很澄。下圖為視頻處理的流水線京闰,每個矩形框是一個線程實例。
實現(xiàn)上有三種時間信息:
1.本地系統(tǒng)時間:從操作系統(tǒng)啟動計時至當(dāng)前的時間差值
2.NTP(Network Time Protocol)時間:全局時間信息甩苛,從1/1/1900-00:00h計時到當(dāng)前的時間差值
3.RTP時間:幀時間戳蹂楣,以視頻采樣90k頻率為例,rtp_timestamp=ntp_timestamp*90
這三種時間坐標(biāo)都是對時間的度量讯蒲,只是描述時間的方式不同痊土。比如當(dāng)前絕對時間2020-08-05T06:08:52+00:00, 它們是這樣表達(dá)的。
本地時間1919620051:表示開機(jī)計數(shù)起墨林,過去1919620051ms了施戴,大概22.2days反浓。
NTP時間3805596543795:表示距離1/1/1900-00:00h,過去3805596543795ms了赞哗。
RTP時間:RTP時間由NTP時間計算而來雷则,時間單位1/90000s,u32存儲肪笋,計算過程會發(fā)送溢出月劈,((u32)3805596543795)*90=1521922030。
發(fā)送端
一幀視頻畫面在caputer線程就記錄下了藤乙,這一幀對應(yīng)的三個時間信息猜揪,尤其重要的是RTP時間。這個rtp_timestamp在Packet pacer模塊會加一個提前設(shè)定的偏移量坛梁,作為最終的rtp時間發(fā)出去而姐。這個偏移量加在了整個rtp時間坐標(biāo)系內(nèi),所有的對外的RTP時間都加了划咐。
視頻流按照自己的RTP時間對每一個包做了標(biāo)記拴念,音頻流也類似的根據(jù)自己的RTP時間對每一個音頻包做了標(biāo)記,但這兩條流里的時間都是按照自己的步調(diào)在走褐缠,是獨(dú)立的政鼠。如果要求接收端使這兩條流同步渲染,就要想辦法讓這些時間統(tǒng)一跟同一個時間基準(zhǔn)對齊队魏。如下圖示公般,邏輯上舉例描述了兩條流如何同步,其中的時間數(shù)字只做參考胡桨,非真實數(shù)據(jù)官帘。
RTCP SR(sender report)的作用之一就是做時間對齊的,將該流中的RTP時間于NTP時間對齊昧谊。所有的流都對齊發(fā)送端的NTP時間刽虹,這樣接收端就有了統(tǒng)一時間基準(zhǔn)。
RTCP SR format 如下:
? ? ? ? 0? ? ? ? ? ? ? ? ? 1? ? ? ? ? ? ? ? ? 2? ? ? ? ? ? ? ? ? 3
? ? ? ? 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P|? ? RC? |? PT=SR=200? |? ? ? ? ? ? length? ? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? ? ? ? ? ? ? SSRC of sender? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
sender |? ? ? ? ? ? ? NTP timestamp, most significant word? ? ? ? ? ? |
info? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? NTP timestamp, least significant word? ? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? ? ? ? ? ? ? RTP timestamp? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? ? ? ? ? sender's packet count? ? ? ? ? ? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? ? ? ? ? ? sender's octet count? ? ? ? ? ? ? ? ? ? |
? ? ? +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |? ? ? ? ? ? ? ? SSRC_1 (SSRC of first source)? ? ? ? ? ? ? ? |
block? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? 1? ? | fraction lost |? ? ? cumulative number of packets lost? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? extended highest sequence number received? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? ? ? ? ? ? interarrival jitter? ? ? ? ? ? ? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? ? ? ? ? ? ? last SR (LSR)? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? ? |? ? ? ? ? ? ? ? ? delay since last SR (DLSR)? ? ? ? ? ? ? ? ? |
? ? ? +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |? ? ? ? ? ? ? ? SSRC_2 (SSRC of second source)? ? ? ? ? ? ? ? |
block? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? 2? ? :? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ...? ? ? ? ? ? ? ? ? ? ? ? ? ? :
? ? ? +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
? ? ? |? ? ? ? ? ? ? ? ? profile-specific extensions? ? ? ? ? ? ? ? ? |
? ? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? The sender report packet consists of three sections, possibly
? followed by a fourth profile-specific extension section if defined.
? The first section, the header, is 8 octets long.? The fields have the
? following meaning:
? version (V): 2 bits
? ? ? Identifies the version of RTP, which is the same in RTCP packets
? ? ? as in RTP data packets.? The version defined by this specification
? ? ? is two (2).
接收端
上圖中可以看到經(jīng)過網(wǎng)絡(luò)傳輸后揽浙,到達(dá)接收端的幀數(shù)據(jù)可能經(jīng)過了jitter(抖動),亂序意敛,比如stream1 的幀2/3/4馅巷。接收端通過RTCP SR和buffer的設(shè)計,采用pull的模式草姻,以渲染作為終點(diǎn)倒推從frame queue中取幀的延遲钓猬。從單條流處理過程中可以看到該延遲包含渲染+解碼+抖動延遲,而多流之間的同步還需要考慮流之間的相對傳輸延遲(參考RtpStreamsSynchronizer)撩独,最終得到每條流的取幀延遲敞曹。
接收端多流同步其實是包含兩部分的含義:單流內(nèi)的流暢播放和多流間的時間同步播放账月。音頻和視頻延遲處理原理類似,只是時延的計算方式有所不同澳迫,接下來以視頻流的延遲處理來說明這個過程局齿。
視頻流流暢播放
視頻解碼線程執(zhí)行l(wèi)oop不斷從frame_queue中取下一幀解碼渲染,通過嚴(yán)格控制每一幀畫面的執(zhí)行解碼-渲染的時間起點(diǎn)來達(dá)到幀與幀之間最終渲染播放的時間間隔是大致相等的橄登,視覺感受是流暢的抓歼。這個開始時間我們用waitTime來表達(dá),即thread等待多久去取幀處理拢锹。
一些重要時間量的說明
waitTime = render_systime - current_systime - render_cost - decoder_cost;
render_systime =local_systime + max(min_playout_delay,target_delay); //計算渲染時間
local_systime = rtpToLocaltime(rtp_time)//把幀rtp時間轉(zhuǎn)換為本地系統(tǒng)時間
target_delay = jitter_delay+render_cost+decoder_cost; //計算預(yù)估目標(biāo)延遲
min_playout_delay //流間同步用的時延調(diào)節(jié)參數(shù)
jitter_delay //抖動延遲
render_cost //渲染延遲谣妻,固定10ms
decoder_cost //解碼延遲
要得到待解碼幀的waitTime首先要計算該幀的渲染時間(render_systime),先把幀附帶的rtp時間轉(zhuǎn)成本地系統(tǒng)時間(local_systime卒稳,轉(zhuǎn)換方法應(yīng)該容易理解蹋半,不展開),然后疊加一個時間延遲計算方法為max(min_playout_delay,target_delay)充坑,min_playout_delay為流間同步用的調(diào)節(jié)參數(shù)减江,這里講流內(nèi)的延遲處理,可以暫時略過匪傍。target_delay為系統(tǒng)評估的三個延遲(抖動延遲+渲染延遲+解碼延遲)之和您市,注意這里計算得到的target_delay延遲是統(tǒng)計累積得來的,實際參與到render_systime的計算時役衡,還有些實現(xiàn)上的處理茵休。這里也做了簡化處理,便于理解原理手蝎。
有了渲染時間(render_systime)后榕莺,waitTime很容易得出了。細(xì)心觀察棵介,每一幀的處理都多等待一個jitter_delay钉鸯,但幀間的解碼間隔還是保持相同的。jitter_delay的存在就是對抗網(wǎng)絡(luò)傳輸?shù)牟淮_定性的邮辽。webRTC動態(tài)計算它的取值唠雕,穩(wěn)定的網(wǎng)絡(luò)下它的值接近0,造成的延遲比較卸质觥岩睁;弱網(wǎng)下延遲不穩(wěn)定,這個值計算出來較大揣云,增加等待時間換取渲染的平穩(wěn)流暢捕儒,很好的做到了延遲與流暢的平衡。
音視頻流同步
單條流內(nèi)做到流暢播放的同時還需要做流之間的時間對齊,以下圖為例刘莹。假設(shè)最近的一對音頻+視頻包在同一時間采樣阎毅,(實際情況可以是不同時間點(diǎn),這里做了簡化)那我們也期望他們同一時間在接收端渲染点弯∩鹊鳎可以看出整個過程主要包含兩個時間信息,傳輸時間延遲(xxx_transfer_delay)和接收方的處理延遲(xxx_current_delay)蒲拉。這兩個時間不同的流各自維護(hù)各自的延遲信息肃拜,如果希望音頻和視頻包經(jīng)過相同的延遲后同時渲染,可以在兩條流上各加一個延遲調(diào)整參數(shù)(xxx_min_playout_delay)雌团,通過增減調(diào)整這個參數(shù)使得兩條流的延遲逼近相等燃领。
webRTC里面是周期性(1s)來計算調(diào)整這些延遲調(diào)整參數(shù)(xxx_min_playout_delay)的。如上圖例子锦援,偽代碼大概如此猛蔽。
//Time diff video vs audio
time_diff = (video_transfer_delay+video_current_delay)-(audio_transfer_delay+audio_current_delay)
if(time_diff>0){ //video is slower
down(video_min_playout_delay);
? ? up(audio_min_playout_delay);
}
else{//video is faster
up(video_min_playout_delay);
? ? down(audio_min_playout_delay);
}
基于延時的帶寬估計
WebRTC的成功之一在于其設(shè)計一套擁塞控制算法,基礎(chǔ)數(shù)據(jù)來自于發(fā)送端的丟包統(tǒng)計和包接收時間的統(tǒng)計灵寺。這里只講一下有關(guān)timing的RTP包接收時間的統(tǒng)計和反饋曼库,不對擁塞算法展開講述。
擁塞控制的邏輯現(xiàn)在默認(rèn)都在發(fā)送端執(zhí)行略板,有關(guān)時間延遲的計算包括發(fā)送時間T和接收時間t毁枯,發(fā)送端自己可以保存每個包T,接收端只需要反饋t即可叮称。算法的邏輯是每個包的接收時間都要反饋种玛,這涉及到交互數(shù)據(jù)和頻次就會比較多,webRTC對此也有精心設(shè)計瓤檐。
webRTC默認(rèn)在發(fā)送端做傳輸帶寬的估計赂韵,媒體流走的RTP/UDP協(xié)議棧,UDP層沒有帶寬估計的功能挠蛉,webRTC通過擴(kuò)展RTP/RTCP的傳輸格式使得可以在發(fā)送端做傳輸層的帶寬估計祭示。
RTP format
? ? 0? ? ? ? ? ? ? ? ? 1? ? ? ? ? ? ? ? ? 2? ? ? ? ? ? ? ? ? 3
? ? 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? |V=2|P|X|? CC? |M|? ? PT? ? ? |? ? ? sequence number? ? ? ? |
? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? |? ? ? ? ? ? ? ? ? ? ? ? ? timestamp? ? ? ? ? ? ? ? ? ? ? ? ? |
? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? |? ? ? ? ? synchronization source (SSRC) identifier? ? ? ? ? ? |
? +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
? |? ? ? ? ? ? contributing source (CSRC) identifiers(if mixed)? |
? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ....? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? |? ? ? ? ? header extension (optional)? ? ? ? ? ? ? ? ? ? ? ? |
? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? |? ? ? ? ? payload header (format depended)? ? ? ? ? ? ? ? ? ? |
? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? |? ? ? ? ? payload data? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
在header extension域組織如下類型的擴(kuò)展內(nèi)容
https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
? ? 0? ? ? ? ? ? ? ? ? 1? ? ? ? ? ? ? ? ? 2? ? ? ? ? ? ? ? ? 3
? ? ? 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ? ? 0xBE? ? |? ? 0xDE? ? ? |? ? ? ? ? length=1? ? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ID? | L=1? |transport-wide sequence number | zero padding? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Tips: 可以注意到RTP包里有兩種sequence number。
equence number:是RTP層的概念谴古,用于RTP stream的重組解復(fù)用质涛。比如多條流復(fù)用的場景,每條流有各自的自增序列掰担。
transport-wide sequence number:是傳輸層概念汇陆,傳輸層包的標(biāo)識,用于傳輸層的碼率統(tǒng)計恩敌。該序列自增不受多流復(fù)用的影響瞬测,因為復(fù)用發(fā)生在RTP層横媚。
發(fā)送端在發(fā)送的時候?qū)γ恳粋€RTP packet都打上transport-wide sequence number的序號(PacketRouter::SendPacket)纠炮,比如發(fā)送seq=53月趟,54,55恢口。
接收端收到該包后孝宗,把該包的到達(dá)時間記錄下來,記錄時間為本地內(nèi)部時間戳(單位ms)耕肩,即開機(jī)多久了因妇。
packet_arrival_times_[53]=1819746010
packet_arrival_times_[54]=1819746020
packet_arrival_times_[55]=1819746026
接收端RemoteEstimatorProxy模塊負(fù)責(zé)傳輸層統(tǒng)計的反饋,周期性的把包接收的時間信息回饋到發(fā)送端猿诸。transport feedback的格式有詳細(xì)的規(guī)則婚被,定義如下 https://tools.ietf.org/id/draft-dt-rmcat-feedback-message-04.html#rfc.section.3.1
這里有一篇寫的不錯的注解可以參考 https://blog.jianchihu.net/webrtc-research-transport-cc-rtp-rtcp.html
? ? 0? ? ? ? ? ? ? ? ? 1? ? ? ? ? ? ? ? ? 2? ? ? ? ? ? ? ? ? 3
? ? 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |V=2|P| FMT=CCFB |? PT = 205? |? ? ? ? ? length? ? ? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ? ? ? ? ? ? ? ? SSRC of packet sender? ? ? ? ? ? ? ? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ? ? ? ? ? ? ? ? SSRC of 1st media source? ? ? ? ? ? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ? ? ? ? begin_seq? ? ? ? ? ? |? ? ? ? ? ? end_seq? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |L|ECN|? Arrival time offset? ? | ...? ? ? ? ? ? ? ? ? ? ? ? ? .
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? .? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .
? ? .? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .
? ? .? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ? ? ? ? ? ? ? ? SSRC of nth media source? ? ? ? ? ? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ? ? ? ? begin_seq? ? ? ? ? ? |? ? ? ? ? ? end_seq? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |L|ECN|? Arrival time offset? ? | ...? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? .? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .
? ? .? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
? ? |? ? ? ? ? ? ? ? Report Timestamp (32bits)? ? ? ? ? ? ? ? ? ? |
? ? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
RTCP transport feedback一般是RTCP通道上最頻繁的傳遞內(nèi)容,webRTC對其傳輸也有特別的設(shè)計梳虽。關(guān)注以下幾個參數(shù)
max_intervel = 250ms //feedback 最大周期
min_intervel =50ms? //feedback 最小周期
rtcp_ratio = 5%? ? ? //feedback占用帶寬比例
Avg_feedback_size = 68bytes? //平均一個feedback包的大小
發(fā)送RTCP transport feedback的時間周期控制在[50ms,250ms]內(nèi)址芯,在這個范圍內(nèi)根據(jù)當(dāng)前帶寬動態(tài)調(diào)整,盡量把RTCP transport feedback的傳輸占用帶寬比例控制在5%窜觉」日ǎ可以計算得到邊界,單單傳輸feedback占用的帶寬范圍[2176bps,10880bps]禀挫,也是一筆不小的開銷了旬陡。
總結(jié)
本文總結(jié)了webRTC中三種timing類型,本地時間语婴、NTP時間描孟、RTP時間,同時分析了音視頻同步和基于延遲帶寬評估兩個專題對時間信息的使用腻格。
附錄
附上webRTC工程上有關(guān)timing的幾個關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
Capturer
class webrtc::VideoFrame{
...
? uint16_t id_;? ? ? ? ? ? //picture id
? uint32_t timestamp_rtp_; //rtp timestamp, (u32)ntp_time_ms_ *90
? int64_t ntp_time_ms_;? ? //ntp timestamp, capture time since 1/1/1900-00:00h
? int64_t timestamp_us_;? //internal timestamp, capture time since system started, round at 49.71days
}
VideoStreamEncoder::OnFrame // caluclate capture timing
VideoStreamEncoder::OnEncodedImage // fill capture timing
RtpVideoSender::OnEncodedImage // timestamp_rtp_+random value
class webrtc::EncodedImage{
...
? //RTP Video Timing extension
? //https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/rtp-hdrext/video-timing
? struct Timing {
? ? uint8_t flags = VideoSendTiming::kInvalid;
? ? int64_t encode_start_ms = 0;? ? ? ? ? ? ? ? //frame encoding start time, base on ntp_time_ms_
? ? int64_t encode_finish_ms = 0;? ? ? ? ? ? ? //frame encoding end time, base on ntp_time_ms_
? ? int64_t packetization_finish_ms = 0;? ? ? ? //encoded frame packetization time, base on ntp_time_ms_
? ? int64_t pacer_exit_ms = 0;? ? ? ? ? ? ? ? ? //packet sent time when leaving pacer, base on ntp_time_ms_
? ? int64_t network_timestamp_ms = 0;? ? ? ? ? //reseved for network node
? ? int64_t network2_timestamp_ms = 0;? ? ? ? ? //reseved for network node
? ? int64_t receive_start_ms = 0;
? ? int64_t receive_finish_ms = 0;
? } timing_;
? uint32_t timestamp_rtp_; //same as caputrer.timestamp_rtp_
? int64_t ntp_time_ms_; ? ? ? //same as caputrer.ntp_time_ms_
? int64_t capture_time_ms_; ? //same as caputrer.capture_time_ms_
}
RTPSenderVideo::SendVideo
class webrtc::RtpPacketToSend{
...
? // RTP Header.
? bool marker_;? ? ? ? ? ? ? ? //frame end marker
? uint16_t sequence_number_;? //RTP sequence number, start at random(1,32767)
? uint32_t timestamp_;? ? ? ? //capturer timestamp_rtp_ + u32.random()
? uint32_t ssrc_;? ? ? ? ? ? ? //Synchronization Source, specify media source
? int64_t capture_time_ms_;? ? //same as capturer.capture_time_ms_
}
===
receiver side
RtpTransport::DemuxPacket
class webrtc::RtpPacketReceived{
...
? NtpTime capture_time_;
? int64_t arrival_time_ms_; //RTP packet arrival time, local internal timestamp
? // RTP Header.
? bool marker_;? ? ? ? ? ? ? ? //frame end marker
? uint16_t sequence_number_;? //RTP sequence number, start at random(1,32767)
? uint32_t timestamp_;? ? ? ? //sender's rtp timestamp maintained by RTPSenderVideo
? uint32_t ssrc_;? ? ? ? ? ? ? //Synchronization Source, specify media source
}
RtpVideoStreamReceiver::ReceivePacket /OnReceivedPayloadData
struct webrtc::RTPHeader{
? ...
? bool markerBit;
? uint16_t sequenceNumber;? ? ? ? ? ? ? ? //RTP sequence, set by sender per RTP packet
? uint32_t timestamp;? ? ? ? ? ? ? ? ? ? //sender's RTP timestamp?
? uint32_t ssrc;
? RTPHeaderExtension extension;? ? ? ? ? //contains PlayoutDelay&VideoSendTiming if has
}
class webrtc::RtpDepacketizer::ParsedPayload{
? ? RTPVideoHeader video;
? ? const uint8_t* payload;
? ? size_t payload_length;
}
class webrtc::RTPVideoHeader{
? ...
? bool is_first_packet_in_frame;
? bool is_last_packet_in_frame;
? PlayoutDelay playout_delay;? ? //playout delay extension
? VideoSendTiming video_timing;? //Video Timing extension, align with sender's webrtc::EncodedImage::timing
}
class webrtc::VCMPacket{
...
? uint32_t timestamp;? ? ? ? ? ? //sender's RTP timestamp
? int64_t ntp_time_ms_;
? uint16_t seqNum;
? RTPVideoHeader video_header;
? RtpPacketInfo packet_info;
}
class webrtc::RtpPacketInfo{
? ...
? uint32_t ssrc_;
? uint32_t rtp_timestamp_;? ? ? ? //sender's rtp timestamp
? //https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/rtp-hdrext/abs-capture-time
? absl::optional<AbsoluteCaptureTime> absolute_capture_time_; //
? int64_t receive_time_ms_;? ? ? //packet receive time, local internal timestamp
}
PacketBuffer::InsertPacket
class webrtc::video_coding::RtpFrameObject: public EncodedImage{
...
? RTPVideoHeader rtp_video_header_;
? uint16_t first_seq_num_;
? uint16_t last_seq_num_;
? int64_t last_packet_received_time_;
? int64_t _renderTimeMs;
? //inherit from webrtc::EncodedImage
? uint32_t timestamp_rtp_;
? int64_t ntp_time_ms_; ? ?
? int64_t capture_time_ms_;
}