課程地址:零聲學(xué)院 WebRTC入門與提高 https://ke.qq.com/course/435382?tuin=137bb271
技術(shù)支持QQ群:782508536
本文主要介紹WebRTC中的RTP/RTCP協(xié)議,作者:weizhenwei 试和,文章最早發(fā)表在編風(fēng)網(wǎng)阱飘,微信ID:befoio
支持原創(chuàng)窘游,轉(zhuǎn)載必須注明出處语婴,歡迎關(guān)注我的微信公眾號(hào)blacker(微信ID:blackerteam 或 webrtcorgcn)榜田。
一 前言
RTP/RTCP協(xié)議是流媒體通信的基石填大。RTP協(xié)議定義流媒體數(shù)據(jù)在互聯(lián)網(wǎng)上傳輸?shù)臄?shù)據(jù)包格式,而RTCP協(xié)議則負(fù)責(zé)可靠傳輸燕垃、流量控制和擁塞控制等服務(wù)質(zhì)量保證枢劝。在WebRTC項(xiàng)目中,RTP/RTCP模塊作為傳輸模塊的一部分卜壕,負(fù)責(zé)對(duì)發(fā)送端采集到的媒體數(shù)據(jù)進(jìn)行進(jìn)行封包您旁,然后交給上層網(wǎng)絡(luò)模塊發(fā)送;在接收端RTP/RTCP模塊收到上層模塊的數(shù)據(jù)包后轴捎,進(jìn)行解包操作鹤盒,最后把負(fù)載發(fā)送到解碼模塊蚕脏。因此,RTP/RTCP 模塊在WebRTC通信中發(fā)揮非常重要的作用侦锯。
本文在深入研究WebRTC源代碼的基礎(chǔ)上驼鞭,以Video數(shù)據(jù)的發(fā)送和接收為例,力求用簡潔語言描述RTP/RTCP模塊的實(shí)現(xiàn)細(xì)節(jié)尺碰,為進(jìn)一步深入掌握WebRTC打下良好基礎(chǔ)挣棕。
二 RTP/RTCP協(xié)議概述
RTP協(xié)議是Internet上針對(duì)流媒體傳輸?shù)幕A(chǔ)協(xié)議,該協(xié)議詳細(xì)說明在互聯(lián)網(wǎng)上傳輸音視頻的標(biāo)準(zhǔn)數(shù)據(jù)包格式亲桥。RTP協(xié)議本身只保證實(shí)時(shí)數(shù)據(jù)的傳輸洛心,RTCP協(xié)議則負(fù)責(zé)流媒體的傳輸質(zhì)量保證,提供流量控制和擁塞控制等服務(wù)题篷。在RTP會(huì)話期間词身,各參與者周期性彼此發(fā)送RTCP報(bào)文。報(bào)文中包含各參與者數(shù)據(jù)發(fā)送和接收等統(tǒng)計(jì)信息番枚,參與者可以據(jù)此動(dòng)態(tài)控制流媒體傳輸質(zhì)量法严。
RFC3550 [1]定義RTP/RTCP協(xié)議的基本內(nèi)容,包括報(bào)文格式户辫、傳輸規(guī)則等渐夸。除此之外嗤锉,IETF還定義一系列擴(kuò)展協(xié)議渔欢,包括RTP協(xié)議基于檔次的擴(kuò)展,和RTCP協(xié)議基于報(bào)文類型的擴(kuò)展瘟忱,等等奥额。詳細(xì)內(nèi)容可參考文獻(xiàn)[2]。
三 WebRTC線程關(guān)系和數(shù)據(jù)流
WebRTC對(duì)外提供兩個(gè)線程:Signal和Worker访诱,前者負(fù)責(zé)信令數(shù)據(jù)的處理和傳輸垫挨,后者負(fù)責(zé)媒體數(shù)據(jù)的處理和傳輸。在WebRTC內(nèi)部触菜,有一系列線程各司其職九榔,相互協(xié)作完成數(shù)據(jù)流管線。下面以Video數(shù)據(jù)的處理流程為例涡相,說明WebRTC內(nèi)部的線程合作關(guān)系哲泊。
如圖1所示,Capture線程從攝像頭采集原始數(shù)據(jù)催蝗,得到VideoFrame切威;Capture線程是系統(tǒng)相關(guān)的,在Linux系統(tǒng)上可能是調(diào)用V4L2接口的線程丙号,而在Mac系統(tǒng)上可能是調(diào)用AVFoundation框架的接口先朦。接下來原始數(shù)據(jù)VideoFrame從Capture線程到達(dá)Worker線程缰冤,Worker線程起搬運(yùn)工的作用,沒有對(duì)數(shù)據(jù)做特別處理喳魏,而是轉(zhuǎn)發(fā)到Encoder線程棉浸。Encoder線程調(diào)用具體的編碼器(如VP8, H264)對(duì)原始數(shù)據(jù)VideoFrame進(jìn)行編碼,編碼后的輸出進(jìn)一步進(jìn)行RTP封包形成RTP數(shù)據(jù)包刺彩。然后RTP數(shù)據(jù)包發(fā)送到Pacer線程進(jìn)行平滑發(fā)送涮拗,Pacer線程會(huì)把RTP數(shù)據(jù)包推送到Network線程。最終Network線程調(diào)用傳輸層系統(tǒng)函數(shù)把數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)迂苛。
在接收端三热,Network線程從網(wǎng)絡(luò)接收字節(jié)流,接著Worker線程反序列化為RTP數(shù)據(jù)包三幻,并在VCM模塊進(jìn)行組幀操作就漾。Decoder線程對(duì)組幀完成的數(shù)據(jù)幀進(jìn)行解碼操作,解碼后的原始數(shù)據(jù)VideoFrame會(huì)推送到IncomingVideoStream線程念搬,該線程把VideoStream投放到render進(jìn)行渲染顯示抑堡。至此,一幀視頻數(shù)據(jù)完成從采集到顯示的完整過程朗徊。
在上述過程中首妖,RTP數(shù)據(jù)包產(chǎn)生在發(fā)送端編碼完成后,其編碼輸出被封裝為RTP報(bào)文爷恳,然后經(jīng)序列化發(fā)送到網(wǎng)絡(luò)有缆。在接收端由網(wǎng)絡(luò)線程收到網(wǎng)絡(luò)數(shù)據(jù)包后,經(jīng)過反序列化還原成RTP報(bào)文温亲,然后經(jīng)過解包得到媒體數(shù)據(jù)負(fù)載棚壁,供解碼器進(jìn)行解碼。RTP報(bào)文在發(fā)送和接收過程中栈虚,會(huì)執(zhí)行一系列統(tǒng)計(jì)操作袖外,統(tǒng)計(jì)結(jié)果作為數(shù)據(jù)源供構(gòu)造RTCP報(bào)文之用。RTP報(bào)文構(gòu)造魂务、發(fā)送/接收統(tǒng)計(jì)和RTCP報(bào)文構(gòu)造曼验、解析反饋,是接下來分析的重點(diǎn)粘姜。
四 RTP報(bào)文發(fā)送和接收
RTP報(bào)文的構(gòu)造和發(fā)送發(fā)生在編碼器編碼之后鬓照、網(wǎng)絡(luò)層發(fā)送數(shù)據(jù)包之前,而接收和解包發(fā)生在網(wǎng)絡(luò)層接收數(shù)據(jù)之后相艇、解碼器編碼之前颖杏。本節(jié)詳細(xì)分析這兩部分的內(nèi)容。
4.1 RTP報(bào)文構(gòu)造和發(fā)送
圖2描述發(fā)送端編碼之后RTP報(bào)文的構(gòu)造和發(fā)送過程坛芽,涉及三個(gè)線程:Encoder留储、Pacer和Network翼抠,分別負(fù)責(zé)編碼和構(gòu)造RTP報(bào)文,平滑發(fā)送和傳輸層發(fā)送获讳。下面詳細(xì)描述這三個(gè)線程的協(xié)同工作過程阴颖。
圖2 RTP報(bào)文構(gòu)造和發(fā)送
Encode線程調(diào)用編碼器(比如VP8)對(duì)采集到的Raw VideoFrame進(jìn)行編碼,編碼完成以后丐膝,其輸出EncodedImage通過回調(diào)到達(dá)VideoSendStream::Encoded()函數(shù)量愧,進(jìn)而通過PayloadRouter路由到ModuleRtpRtcpImpl::SendOutgoingData()。接下來帅矗,該函數(shù)向下調(diào)用RtpSender::SendOutgoingData()偎肃,進(jìn)而調(diào)用RtpSenderVideo::SendVideo()。該函數(shù)對(duì)EncodedImage進(jìn)行打包浑此,然后填充RTP頭部構(gòu)造RTP報(bào)文累颂;如果配置了FEC,則進(jìn)一步封裝為FEC報(bào)文凛俱。最后返回RtpSender::SendToNetwork()進(jìn)行下一步發(fā)送紊馏。
RtpSender::SendToNetwork()函數(shù)把報(bào)文存儲(chǔ)到RTPPacketHistory結(jié)構(gòu)中進(jìn)行緩存。接下來如果開啟PacedSending蒲犬,則構(gòu)造Packe發(fā)送到PacedSender進(jìn)行排隊(duì)朱监,否則直接發(fā)送到網(wǎng)絡(luò)層。
Pacer線程周期性從隊(duì)列中獲取Packet原叮,然后調(diào)用PacedSender::SendPacket()進(jìn)行發(fā)送赫编,接下來經(jīng)過ModuleRtpRtcpImpl到達(dá)RtpSender::TimeToSendPacket()。該函數(shù)首先從RtpPacketHistory緩存中拿到Packet的負(fù)載篇裁,然后調(diào)用PrepareAndSendPacket()函數(shù):更新RtpHeader的相關(guān)域沛慢,統(tǒng)計(jì)延遲和數(shù)據(jù)包赡若,調(diào)用SendPacketToNetwork()把報(bào)文發(fā)送到傳輸模塊达布。
Network線程則調(diào)用傳輸層套接字執(zhí)行數(shù)據(jù)發(fā)送操作。至此逾冬,發(fā)送端的RTP構(gòu)造和發(fā)送流程完成黍聂。需要注意的是,在RtpSender中進(jìn)行Rtp發(fā)送后身腻,會(huì)統(tǒng)計(jì)RTP報(bào)文相關(guān)信息产还。這些信息作為RTCP構(gòu)造SR/RR報(bào)文的數(shù)據(jù)來源,因此非常重要嘀趟。
4.2 RTP報(bào)文接收和解析
在接收端脐区,RTP報(bào)文的接收和解包操作主要在Worker線程中執(zhí)行,RTP報(bào)文從Network線程拿到后她按,進(jìn)入Worker線程牛隅,經(jīng)過解包操作炕柔,進(jìn)入VCM模塊,由Decode線程進(jìn)行解碼媒佣,最終由Render線程進(jìn)行渲染匕累。下圖3描述RTP報(bào)文在Worker線程中的處理流程。
圖3 RTP報(bào)文接收和解析
RTP數(shù)據(jù)包經(jīng)網(wǎng)絡(luò)層到達(dá)Call對(duì)象默伍,根據(jù)其SSRC找到對(duì)應(yīng)的VideoReceiveStream欢嘿,通過調(diào)用其DeliverRtp()函數(shù)到RtpStreamReceiver::DeliverRtp()。該函數(shù)首先解析數(shù)據(jù)包得到RTP頭部信息也糊,接下來執(zhí)行三個(gè)操作:1.碼率估計(jì)炼蹦;2.繼續(xù)發(fā)送數(shù)據(jù)包;3.接收統(tǒng)計(jì)狸剃。碼率估計(jì)模塊使用GCC算法估計(jì)碼率框弛,構(gòu)造REMB報(bào)文,交給RtpRtcp模塊發(fā)送回發(fā)送端捕捂。而接收統(tǒng)計(jì)則統(tǒng)計(jì)RTP接收信息瑟枫,這些信息作為RTCP RR報(bào)文的數(shù)據(jù)來源。下面重點(diǎn)分析接下來的數(shù)據(jù)包發(fā)送流程指攒。
RtpStreamReceiver::ReceivePacket()首先判斷數(shù)據(jù)包是否是FEC報(bào)文慷妙,如果是則調(diào)用FecReceiver進(jìn)行解包,否則直接調(diào)用RtpReceiver::IncomingRtpPacket()允悦。該函數(shù)分析RTP報(bào)文得到通用的RTP頭部描述結(jié)構(gòu)膝擂,然后調(diào)用RtpReceiverVideo::ParseRtpPacket()進(jìn)一步得到Video相關(guān)信息和負(fù)載,接著經(jīng)過回調(diào)返回RtpStreamReceiver對(duì)象隙弛。該對(duì)象把Rtp描述信息和負(fù)載發(fā)送到VCM模塊架馋,繼續(xù)接下來的JitterBuffer緩存和解碼渲染操作。
RTP報(bào)文解包過程是封包的逆過程全闷,重要的輸出信息是RTP頭部描述和媒體負(fù)載叉寂,這些信息是下一步JitterBuffer緩存和解碼的基礎(chǔ)。另外對(duì)RTP報(bào)文進(jìn)行統(tǒng)計(jì)得到的信息則是RTCP RR報(bào)文的數(shù)據(jù)來源总珠。
五 RTCP報(bào)文發(fā)送和接收
RTCP協(xié)議是RTP協(xié)議的控制下可以屏鳍,負(fù)責(zé)流媒體的服務(wù)質(zhì)量保證。比較常用的RTCP報(bào)文由發(fā)送端報(bào)告SR和接收端報(bào)告RR局服,分別包含數(shù)據(jù)發(fā)送統(tǒng)計(jì)信息和數(shù)據(jù)接收信息钓瞭。這些信息對(duì)于流媒體質(zhì)量保證非常重要,比如碼率控制淫奔、負(fù)載反饋山涡,等等。其他RTCP報(bào)文還有諸如SDES、BYE鸭丛、SDES等霍殴,RFC3550對(duì)此有詳細(xì)定義。
本節(jié)重點(diǎn)分析WebRTC內(nèi)部RTCP報(bào)文的構(gòu)造系吩、發(fā)送来庭、接收、解析穿挨、反饋等流程月弛。需要再次強(qiáng)調(diào)的是,RTCP報(bào)文的數(shù)據(jù)源來自RTP報(bào)文發(fā)送和接收時(shí)的統(tǒng)計(jì)信息科盛。在WebRTC內(nèi)部帽衙,RTCP報(bào)文的發(fā)送采取周期性發(fā)送和及時(shí)發(fā)送相結(jié)合的策略:ModuleProcess線程周期性發(fā)送RTCP報(bào)文;而RtpSender則在每次發(fā)送RTP報(bào)文之前都判斷是否需要發(fā)送RTCP報(bào)文贞绵;另外在接收端碼率估計(jì)模塊構(gòu)造出REMB報(bào)文后厉萝,通過設(shè)置超時(shí)讓ModuleProcess模塊立即發(fā)送RTCP報(bào)文。
5.1 RTCP報(bào)文構(gòu)造和發(fā)送
在發(fā)送端榨崩,RTCP以周期性發(fā)送為基準(zhǔn)谴垫,輔以RTP報(bào)文發(fā)送時(shí)的及時(shí)發(fā)送和REMB報(bào)文的立即發(fā)送。發(fā)送過程主要包括Feedback信息獲取母蛛、RTCP報(bào)文構(gòu)造翩剪、序列化和發(fā)送。圖4描述了RTCP報(bào)文的構(gòu)造和發(fā)送過程彩郊。
圖4 RTCP報(bào)文構(gòu)造和發(fā)送
ModuleProcess線程周期性調(diào)用ModuleRtpRtcpImpl::Process()函數(shù)前弯,該函數(shù)通過RTCPSender::TimeToSendRtcpReport()函數(shù)確定當(dāng)前是否需要立即發(fā)送RTCP報(bào)文。若是秫逝,則首先從RTPSender::GetDataCounters()獲取RTP發(fā)送統(tǒng)計(jì)信息恕出,然后調(diào)用RTCPSender::SendRTCP(),接著是SendCompoundRTCP()發(fā)送RTCP組合報(bào)文违帆。關(guān)于RTCP組合報(bào)文的定義浙巫,請(qǐng)參考文獻(xiàn)[1]。
在SendCompoundRTCP()函數(shù)中前方,首先通過PrepareReport()確定將要發(fā)送何種類型的RTCP報(bào)文狈醉。然后針對(duì)每一種報(bào)文,調(diào)用其構(gòu)造函數(shù)(如構(gòu)造SR報(bào)文為BuildSR()函數(shù))惠险,構(gòu)造好的報(bào)文存儲(chǔ)在PacketContainer容器中。最后調(diào)用SendPackets()進(jìn)行發(fā)送抒线。
接下來每種RTCP報(bào)文都會(huì)調(diào)用各自的序列化函數(shù)班巩,把報(bào)文序列化為網(wǎng)絡(luò)字節(jié)流。最后通過回調(diào)到達(dá)PacketContainer::OnPacketReady(),最終把字節(jié)流發(fā)送到傳輸層模塊:即通過TransportAdapter到達(dá)BaseChannel抱慌,Network線程調(diào)用傳輸層套接字API發(fā)送數(shù)據(jù)到網(wǎng)絡(luò)逊桦。
RTCP報(bào)文的構(gòu)造和發(fā)送過程總體不是很復(fù)雜,最核心的操作就是獲取數(shù)據(jù)源抑进、構(gòu)造報(bào)文强经、序列化和發(fā)送。相對(duì)來說構(gòu)造報(bào)文和序列化比較繁瑣寺渗,基于RFC定義的細(xì)節(jié)進(jìn)行匿情。
5.2 RTCP報(bào)文接收和解析
接收端的RTCP報(bào)文接收和解析過程如圖5所示。
圖5 RTCP報(bào)文接收和解析
在接收端信殊,RTCP報(bào)文的接收流程和RTP一樣炬称,經(jīng)過網(wǎng)絡(luò)接收之后到達(dá)Call對(duì)象,進(jìn)而通過SSRC找到VideoReceiveStream涡拘,繼而到達(dá)RtpStreamReceiver玲躯。接下來RTCP報(bào)文的解析和反饋操作都在ModuleRtpRtcpImpl::IncomingRtcpPacket()函數(shù)中完成。該函數(shù)首先調(diào)用RTCPReceiver::IncomingRtcpPacket()解析RTCP報(bào)文鳄乏,得到RTCPPacketInformation對(duì)象跷车,然后調(diào)用 TriggerCallbacksFromRTCPPacket(),觸發(fā)注冊(cè)在此處的各路觀察者執(zhí)行回調(diào)操作橱野。
RTCPReceiver::IncomingRtcpPacket()使用RTCPParser解析組合報(bào)文姓赤,針對(duì)每一種報(bào)文類型,調(diào)用對(duì)應(yīng)的處理函數(shù)(如處理SDES的HandleSDES函數(shù))仲吏,反序列化后拿到報(bào)文的描述結(jié)構(gòu)不铆。最后所有報(bào)文綜合在一起形成RTCPPacketInformation對(duì)象。該對(duì)象接下來作為參數(shù)調(diào)用TriggerCallbacksFromRTCPPacket()函數(shù)觸發(fā)回調(diào)操作裹唆,如處理NACK的回調(diào)誓斥,處理SLI的回調(diào),處理REMB的回調(diào)许帐,等等劳坑。這些回調(diào)在各自模塊控制流媒體數(shù)據(jù)的編碼、發(fā)送成畦、碼率等服務(wù)質(zhì)量保證距芬,這也是RTCP報(bào)文最終起作用的地方。
至此循帐,我們分析了RTCP報(bào)文發(fā)送和接收的整個(gè)流程框仔。
六 總結(jié)
本文在深入分析WebRTC源代碼的基礎(chǔ)上,結(jié)合流程圖描述出RTP/RTCP模塊的實(shí)現(xiàn)流程拄养,在關(guān)鍵問題上(如RTCP報(bào)文的數(shù)據(jù)來源)進(jìn)行深入細(xì)致的研究离斩。為進(jìn)一步深入掌握WebRTC的實(shí)現(xiàn)原理和細(xì)節(jié)打下良好基礎(chǔ)。
參考文獻(xiàn)
[1] RFC3550 - RTP: A Transport Protocol for Real-Time Applications
https://www.ietf.org/rfc/rfc3550.txt
[2] 超越RFC3550 - RTP/RTCP協(xié)議族分析 : http://www.reibang.com/p/e5e21aeb219f