技术实践 | 网易云信在融合通信场景下的探索和实践之 RTMPGateway 服务架构
導(dǎo)讀:隨著各個(gè)行業(yè)的互聯(lián)網(wǎng)化進(jìn)程不斷演進(jìn),融合通信在越來(lái)越多的場(chǎng)景中得到應(yīng)用,例如金融場(chǎng)景的視頻面簽、醫(yī)療場(chǎng)景的遠(yuǎn)程會(huì)診、企業(yè)協(xié)作場(chǎng)景的多人視頻會(huì)議等。
文|本森 網(wǎng)易云信資深音視頻服務(wù)端開(kāi)發(fā)工程師
在這些場(chǎng)景中,通過(guò)微信小程序?qū)崿F(xiàn)音視頻互通,可以降低用戶溝通成本,提升業(yè)務(wù)運(yùn)營(yíng)效率,同時(shí)幫助企業(yè)或組織進(jìn)一步打通微信社群中的溝通障礙,以一種輕量化的敏捷運(yùn)營(yíng)模式輕松實(shí)現(xiàn)微信生態(tài)中的客戶轉(zhuǎn)化閉環(huán)。因此,小程序音視頻必將受到越來(lái)越多企業(yè)和組織的青睞。
那么,一個(gè)能夠支持微信小程序之間實(shí)現(xiàn)音視頻通話,同時(shí)能夠支持微信小程序跨平臺(tái)互聯(lián)互通的媒體服務(wù)器需要完成哪些事情?
?
RTMPGateway
微信在6.5.21版本通過(guò)小程序開(kāi)放了實(shí)時(shí)音視頻能力,開(kāi)發(fā)者們可以使用組件 <live-pusher> 實(shí)現(xiàn)基于 RTMP 的直播推流(錄制),在新版本中加入了 RTC 模式,用于實(shí)時(shí)音視頻通話上行,使用組件 <live-player> 實(shí)現(xiàn)基于 RTMP 的直播拉流(播放),RTC 模式則用于實(shí)時(shí)音視頻通話下行。
可以看出,微信小程序的音視頻是基于 RTMP 協(xié)議的,同時(shí),微信小程序的音視頻只是提供了終端上的能力,并沒(méi)有實(shí)現(xiàn)媒體服務(wù)器,那么如何實(shí)現(xiàn)微信小程序之間的音視頻通話,同時(shí)實(shí)現(xiàn)和多平臺(tái)之間互聯(lián)互通?答案是肯定的,我們需要自己開(kāi)發(fā)一個(gè)媒體服務(wù)器。
在網(wǎng)易云信融合通信場(chǎng)景下,
-
微信小程序端使用 RTMP 協(xié)議,接入邊緣媒體網(wǎng)關(guān),即 RTMPGateway;
-
RTMPGateway 支持 RTMP 協(xié)議,完成微信小程序間的媒體轉(zhuǎn)發(fā);
-
同時(shí),RTMPGateway 將 RTMP 協(xié)議轉(zhuǎn)換成 RTP 協(xié)議,轉(zhuǎn)發(fā)給云信邊緣媒體服務(wù)器,完成與云信 SDK、標(biāo)準(zhǔn) WebRTC 終端的互聯(lián)互通。
?
關(guān)于 RTMP 連接
上文提到 RTMP,這里我們?cè)俸?jiǎn)單介紹一下,RTMP(Real Time Messaging Protocol,即實(shí)時(shí)消息傳送協(xié)議),是 Adobe 公司開(kāi)發(fā)的一個(gè)基于 TCP 的應(yīng)用層協(xié)議,被廣泛應(yīng)用于直播領(lǐng)域。
RTMP 協(xié)議是基于 TCP 的,而在 TCP 連接建立完成后,無(wú)論是發(fā)布還是播放一個(gè) RTMP 協(xié)議的流媒體,都還需要經(jīng)過(guò)以下幾個(gè)步驟:
?RTMP 握手(Handshake)
-
握手開(kāi)始于客戶端發(fā)送 C0、C1 塊;服務(wù)器收到 C0 或 C1 后發(fā)送 S0 和 S1。
-
客戶端收齊 S0 和 S1 后,開(kāi)始發(fā)送 C2;服務(wù)器收齊 C0 和 C1 后,開(kāi)始發(fā)送 S2。
-
客戶端和服務(wù)器分別收到 S2 和 C2 后,握手完成。
這就是一個(gè)完整的握手過(guò)程。
在實(shí)際工程應(yīng)用中,一般是客戶端先將 C0、C1 塊同時(shí)發(fā)出,服務(wù)器在收到 C1 之后同時(shí)將 S0、 S1、 S2 發(fā)給客戶端。之后客戶端向服務(wù)器端發(fā)送 C2 塊,簡(jiǎn)單握手完成。
Flash 播放器連接服務(wù)器時(shí),若服務(wù)器只支持簡(jiǎn)單握手,則無(wú)法播放 H.264 和 AAC 的流,可能是 Adobe 的限制,Adobe 將簡(jiǎn)單握手改為了有一系列加密算法的復(fù)雜握手(complex handshake)。
簡(jiǎn)單握手的包是隨機(jī)的1536字節(jié)(S1/S2/C1/C2),復(fù)雜握手則是需要進(jìn)行摘要和加密,此處不再贅述。
?建立網(wǎng)絡(luò)連接(NetConnection)?
-
客戶端發(fā)送命令消息(connect)到服務(wù)器,請(qǐng)求與一個(gè)服務(wù)器建立連接。
-
服務(wù)器接收消息后,發(fā)送確認(rèn)窗口大小協(xié)議,設(shè)置帶寬協(xié)議,設(shè)置塊大小協(xié)議消息到客戶端。
-
客戶端處理設(shè)置帶寬協(xié)議消息后,發(fā)送確認(rèn)窗口大小協(xié)議消息到服務(wù)器端。
-
服務(wù)端向客戶端發(fā)送“流開(kāi)始”(Stream Begin)。
-
服務(wù)器發(fā)送(_result),通知客戶端連接的狀態(tài)。
?建立網(wǎng)絡(luò)流(Create Stream)?
-
客戶端發(fā)送命令消息(CreateStream)命令到服務(wù)器端。
-
服務(wù)器端接收消息后,發(fā)送(_result),通知客戶端流的狀態(tài)。
可以看出,建立一個(gè) RTMP 連接,需要完成復(fù)雜的協(xié)議交互,并且這些協(xié)議交互都是同步的。那么,如何實(shí)現(xiàn)一個(gè)異步的 RTMP 協(xié)議棧?
對(duì)于一個(gè)需要處理大量連接的 Server,無(wú)非兩種策略:多線程或使用異步 Socket。
例如對(duì)于 RTMPDump 提供的基于同步 Socket 的 librtmp,只能使用多線程處理多個(gè)連接,但是多線程的并發(fā)數(shù)上限明顯,同時(shí)需要處理復(fù)雜的鎖和同步等。Winlin 則是在 SRS 中使用 st 協(xié)程在單線程中實(shí)現(xiàn)了異步的 RTMP 協(xié)議棧。
而在云信的 RTMPGatway 中,我們使用狀態(tài)機(jī)的方式,同樣在單線程實(shí)現(xiàn)了異步的 RTMP 協(xié)議棧。針對(duì)每一條 RTMP 連接,RTMPGatway 均會(huì)記錄連接目前所處狀態(tài),以跟蹤整個(gè)連接的建立。
?
關(guān)于媒體協(xié)議封裝
為了實(shí)現(xiàn)微信小程序與云信 SDK、標(biāo)準(zhǔn) WebRTC 終端的互聯(lián)互通,RTMPGatway 需要實(shí)現(xiàn) RTMP 協(xié)議和 RTP 協(xié)議的轉(zhuǎn)換。
?RTMP 封裝 AAC?
微信小程序推拉流使用 RTMP 協(xié)議,音頻使用 AAC 編碼。
使用 RTMP 推送 AAC 直播流,首先需要發(fā)送“AAC sequence header”,其中包含重要的編碼信息,沒(méi)有它解碼器將無(wú)法解碼。AAC sequence header 存放的是 AudioSpecificConfig 結(jié)構(gòu),其結(jié)構(gòu)描述非常復(fù)雜(詳見(jiàn)“ISO-14496-3 Audio”),可以簡(jiǎn)化為下表:
在發(fā)送這個(gè) header 需要在前面分別加上1個(gè)字節(jié)(8bits)的 AudioTags 數(shù)據(jù),其中每 bit 表示的意義如下圖:
其中 SoundData 的組成如下:
當(dāng)數(shù)據(jù)的第一個(gè)字節(jié)為0時(shí),后面跟 AAC sequence header;當(dāng)數(shù)據(jù)的第一個(gè)字節(jié)為1時(shí),后面跟AAC 數(shù)據(jù)。
?RTMP 封裝 H.264?
微信小程序使用 RTMP 協(xié)議進(jìn)行推拉流,使用 H.264 進(jìn)行視頻編碼。
使用 RTMP 推送 H.264 直播流,首先需要發(fā)送"AVC sequence header",同樣包含重要的編碼信息,沒(méi)有它解碼器將無(wú)法解碼。
AVC sequence header 就是 AVCDecoderConfigurationRecord 結(jié)構(gòu)(詳見(jiàn)“ISO-14496-15 AVC file format”),簡(jiǎn)化為下表:
在發(fā)送這個(gè) Header 需要在前面分別加上1個(gè)字節(jié)(8bits)的 VideoTags 數(shù)據(jù),其中每 bit 表示的意義如下圖:
發(fā)送的為 avc 數(shù)據(jù),所以 CodecID(后 4bit)的值為 7;videodata 的數(shù)據(jù)打包方式為 AVCVIDEOPACKET,具體的信息見(jiàn)下圖:
?RTP 協(xié)議?
RTP( Real-time Transport Protocol,即實(shí)時(shí)傳輸協(xié)議),為語(yǔ)音、圖像、傳真等多種需要實(shí)時(shí)傳輸?shù)亩嗝襟w數(shù)據(jù)提供端到端的實(shí)時(shí)傳輸服務(wù),服務(wù)質(zhì)量則由 RTCP(Real-time Transport Control Protocol,即實(shí)時(shí)傳輸控制協(xié)議)來(lái)提供。
RTP 協(xié)議頭信息包括 RTP 固定頭以及 RTP 擴(kuò)展頭,如下圖:
RTP 固定頭
RTP 擴(kuò)展頭
若 RTP 固定頭中的擴(kuò)展比特位置 1,則一個(gè)長(zhǎng)度可變的頭擴(kuò)展部分被加到 RTP 固定頭之后,如果有 CSRC 列表,則在 CSRC 列表之后。頭擴(kuò)展包含 16 比特的長(zhǎng)度域,指示擴(kuò)展項(xiàng)中 32 比特字的個(gè)數(shù),不包括 4 個(gè)字節(jié)擴(kuò)展頭(因此零是有效值)。
RTP 固定頭之后只允許有一個(gè)頭擴(kuò)展。為允許多個(gè)互操作實(shí)現(xiàn)獨(dú)立生成不同的頭擴(kuò)展,或某種特定實(shí)現(xiàn)有多種不同的頭擴(kuò)展,擴(kuò)展項(xiàng)的前 16 比特用以識(shí)別標(biāo)識(shí)符或參數(shù)。這 16 比特的格式由具體實(shí)現(xiàn)的上層協(xié)議定義。基本的 RTP 說(shuō)明并不定義任何頭擴(kuò)展本身。
IETF 針對(duì) RFC3550 在檔次方面定義了一系列擴(kuò)展協(xié)議,一些總結(jié)如下:
-
RFC3550 - RTP: A Transport Protocol for Real-Time Applications (RTP):定義最基本的RTP/RTCP報(bào)文格式和收發(fā)規(guī)則;
-
RFC3551 - RTP Profile for Audio and Video Conferences with Minimal Control :定義音視頻會(huì)議最基本的音視頻數(shù)據(jù)負(fù)載格式、編碼和傳輸,是其它檔次的基礎(chǔ);
-
RFC3711 - The Secure Real-time Transport Protocol (SRTP):定義了RTP在安全方面的增強(qiáng),如加密、認(rèn)證和重放保護(hù);
-
RFC4585 - Extended RTP Profile for Real-time Transport Control Protocol (RTCP)-Based Feedback (RTP/AVPF) :定義了RTP基于RTCP在及時(shí)反饋方面的增強(qiáng),如定義NACK,PLI,SLI等RTCP報(bào)文;
-
RFC5124 - Extended Secure RTP Profile for Real-time Transport Control Protocol (RTCP)-Based Feedback (RTP/SAVPF):綜合RTP/SAVP和RTP/AVPF的安全性和及時(shí)反饋性的最全面的檔次。
RFC 3550 定義五種 RTCP 報(bào)文,類型在報(bào)文頭部的PT域定義。
SR 報(bào)文用于發(fā)送端報(bào)告本端的數(shù)據(jù)發(fā)送統(tǒng)計(jì)信息和數(shù)據(jù)接收統(tǒng)計(jì)信息,RR 報(bào)文用于報(bào)告本端的數(shù)據(jù)接收統(tǒng)計(jì)信息,SDES 報(bào)文用于報(bào)告本端的描述性信息,BYE 在本端離開(kāi)會(huì)話時(shí)發(fā)送,而 APP 則是特定于應(yīng)用的數(shù)據(jù)。IETF 根據(jù)實(shí)際需求對(duì) RTCP 的報(bào)文類型進(jìn)行擴(kuò)展,定義了一系列協(xié)議,此處不再贅述。
?RTP 封裝 H.264?
針對(duì) H.264 的封裝,WebRTC 選擇了使用 RFC3984 的 Non-Interleaved 封裝方案。
Single NAL Unit Packet
Single NAL Unit Packet 是 RTP 最基本的打包方式,其中,
-
forbidden_bit:禁止位,初始為0,當(dāng)網(wǎng)絡(luò)發(fā)現(xiàn) NAL 單元有比特錯(cuò)誤時(shí)可設(shè)置該比特為 1,以便接收方糾錯(cuò)或丟掉該單元。
-
nal_reference_bit:nal 重要性指示,標(biāo)志該 NAL 單元的重要性,值越大,越重要,解碼器在解碼處理不過(guò)來(lái)的時(shí)候,可以丟掉重要性為 0 的 NALU。
-
Type:NAL 單元中的 RBSP 數(shù)據(jù)結(jié)構(gòu)的類型,其中 0 未指,1-19 在 H.264 協(xié)議中有定義,20-23 為 264 協(xié)議指定的保留位,24-29 在 RFC3984 中進(jìn)行了指定。
Type 后面的數(shù)據(jù)為 RBSP 的數(shù)據(jù),需要注意的是:編碼器的每個(gè) slice 或者每幀頭一般會(huì)有由0x000001 或者 0x00000001 作為起始頭,在 RTP 封裝中需要去掉。此外在 H.264 裸碼流數(shù)據(jù)后面可能還會(huì)帶有 padding 的數(shù)據(jù)由 RTP 頭的 padding 位決定。
STAP-A
STAP-A 的作用是可以把多個(gè) nal 單元封裝在一個(gè) RTP 包里面進(jìn)行傳輸,需要注意:-A 的格式都是不允許跨幀的,也就是 nal 單元的時(shí)間戳必須是相同的。常見(jiàn)的場(chǎng)景是 sps 和 pps 兩個(gè)小包被合并封裝。
RTP 頭后面僅跟著 STAP-A 的頭,由 F、NRI 和 Type 組合而成,占一個(gè)字節(jié),這里的 Type 為 24。后面兩個(gè)字節(jié)為第一個(gè) nalu 單元的長(zhǎng)度,后面跟第一個(gè) nalu 數(shù)據(jù)同 Single NAL Unit 的封裝一致,第一個(gè)數(shù)據(jù)結(jié)束后,跟著第二個(gè) nalu 的長(zhǎng)度,占 2 個(gè)字節(jié),依次類推。
FU-A
FU-A 的作用是把一個(gè)原始大的 nalu 切成多個(gè)數(shù)據(jù)包進(jìn)行傳輸,主要使用場(chǎng)景在 slice 比較大的情況下。FU-A 比較特殊,有 FU-A 起始包、FU-A 包(如果只切兩個(gè)包可能沒(méi)有)和 FU-A 結(jié)束包組成。
-
FU indicator 占一個(gè)字節(jié),由 F、NRI 和 Type 組合而成,這里的 Type 為28。
-
FU header 占一個(gè)字節(jié):
-
-
S:占1位如果是1表示當(dāng)前這個(gè)包是 FU-A 的起始包
-
E:占1位如果是1表示當(dāng)前這個(gè)包是 FU-A 的結(jié)束包
-
R:占1位,保留位,為0
-
Type:實(shí)際包含 nalu 的類型
-
?
關(guān)于協(xié)議轉(zhuǎn)換
?幀完整性判斷?
為了實(shí)現(xiàn)微信小程序與云信 SDK、標(biāo)準(zhǔn) ?WebRTC終端的互聯(lián)互通,在協(xié)議轉(zhuǎn)換時(shí),無(wú)論是 RTMP 到 RTP 還是 RTP 到 RTMP,對(duì)于視頻都需要首先拿到 H.264 數(shù)據(jù),最重要?jiǎng)t是對(duì)幀完整性的判斷。
在 RTMPGateway 收到 RTP 視頻包后,則需要先組成完整的幀才能再轉(zhuǎn)封裝成 RTMP 協(xié)議,失敗則進(jìn)行I幀請(qǐng)求。而僅根據(jù) RTP 頭部中的M比特判斷完整性是不準(zhǔn)確的,在 RTMPGateway 中,幀的完整性判斷還需要滿足 :
-
本幀包個(gè)數(shù) = 本幀末包序 - 本幀首包序 + 1
-
本幀首包序 = 上一幀末包序 + 1
下面以例子說(shuō)明:
幀1幀2正常進(jìn)入轉(zhuǎn)封裝。
幀3超時(shí),進(jìn)行I幀請(qǐng)求,上一幀末包序置19。
幀4判斷完整,非I幀,上一幀末包序置22。
幀5判斷完整,且為I幀,進(jìn)入轉(zhuǎn)封裝。
幀6超時(shí),且丟失M包,上一幀末包序置27。
幀7一定超時(shí),上一幀末包序置31。
幀8判斷完整,且為I幀,進(jìn)入轉(zhuǎn)封裝。
?音頻轉(zhuǎn)碼?
對(duì)于音頻,因?yàn)槲⑿判〕绦蚴褂?AAC 編碼,云信邊緣媒體服務(wù)器支持 Opus 編碼,因此在完成協(xié)議解封裝后,還需要完成音頻的轉(zhuǎn)碼。在云信 RTMPGateway 中,我們采用了獨(dú)立的音頻轉(zhuǎn)碼線程組,減輕邏輯處理線程的壓力的目的。每個(gè)轉(zhuǎn)碼任務(wù)將被分配到固定的音頻轉(zhuǎn)碼線程,線程根據(jù)任務(wù)數(shù)量進(jìn)行負(fù)載均衡。
?
關(guān)于小程序用戶和云信云端用戶管理
RTMPGateway 與小程序端間的信令采用 WebSocket 傳輸協(xié)議,此時(shí) RTMPGateway 是 WebSocket 的服務(wù)端,接收來(lái)自小程序端的連接請(qǐng)求;RTMPGateway 與媒體服務(wù)器之間的信令同樣采用 WebSocket 傳輸協(xié)議,此時(shí) RTMPGateway 作為 WebSocket 的客戶端,向媒體服務(wù)器發(fā)起連接請(qǐng)求。
-
微信小程序端登錄后除了在 RTMPGateway 創(chuàng)建對(duì)應(yīng)的 mini-user 對(duì)象,還需要模擬成一個(gè)對(duì)應(yīng)的 mini-fakeClient 向媒體服務(wù)器登錄加入會(huì)議;
-
同時(shí),RTMPGateway 在創(chuàng)建房間時(shí),會(huì)針對(duì)這個(gè)房間模擬出一個(gè)特殊的用戶 room-fakeClient 加入房間;
-
在廣播房間及流信息時(shí),如新的 NRTC 用戶加入,媒體服務(wù)器會(huì)向所有的用戶(包括mini-fakeClient、room-fakeClient)。對(duì)于 RTMPGateway,則只需要處理 room-fakeClient 收到的媒體服務(wù)器主動(dòng)下發(fā)的信令,達(dá)到信令去重目的;
-
當(dāng)新的 NRTC 用戶加入房間,room-fakeClient 將收到廣播信令,RTMPGateway 同步管理該 NRTC 用戶(nrtc-user)狀態(tài)。例如當(dāng)收到該用戶的發(fā)布流的信令后,將由 room-fakeClient 向媒體服務(wù)器發(fā)起訂閱音視頻流的請(qǐng)求。
?
寫(xiě)在最后的話
一個(gè)完整的微信小程序網(wǎng)關(guān)服務(wù)架構(gòu),除了完成上述的 RTMP 協(xié)議棧、媒體協(xié)議轉(zhuǎn)封裝、房間業(yè)務(wù)管理等基礎(chǔ)模塊,我們還需要考慮:
-
RTMP 協(xié)議是基于 TCP 協(xié)議的,而 TCP 協(xié)議自身的一些擁塞算法,在弱網(wǎng)環(huán)境下對(duì)網(wǎng)絡(luò)的退避策略過(guò)于激進(jìn),因此需要我們?nèi)で蠼鉀Q方案。例如在 RTMPGateway 中,對(duì)于 RTMP 下行在服務(wù)器引入 BBR 算法,通過(guò)直接修改內(nèi)核 TCP 協(xié)議機(jī)制代碼,達(dá)到更高的帶寬利用率,降低卡頓。
-
關(guān)于 RTP 解封裝后的音視頻時(shí)間戳計(jì)算,可以有多種方案。例如在收到 SR 之前使用系統(tǒng)時(shí)間代替 NTP 時(shí)間,收到 SR 之后進(jìn)行系統(tǒng)時(shí)間和 NTP 時(shí)間的誤差修正;也可以在收到 SR 之前,將收到的第一個(gè) RTP 包時(shí)間設(shè)置為起始時(shí)間,并使用固定采樣頻率根據(jù)公式計(jì)算時(shí)間戳,收到 SR 之后則先計(jì)算真實(shí)采樣頻率,再根據(jù)公式計(jì)算時(shí)間戳。為了進(jìn)一步解決音畫(huà)同步問(wèn)題,在 RTMPGateway 中,對(duì)于 RTP 解封裝后的音視頻數(shù)據(jù),將分別進(jìn)入對(duì)應(yīng)的 buffer,通過(guò)控制兩個(gè)buffer的讀取速度,以達(dá)到音畫(huà)同步以及控制時(shí)延的目的。
由于篇幅關(guān)系,更多細(xì)節(jié)不再展開(kāi),以上便是本次分享的全部?jī)?nèi)容。隨著應(yīng)用場(chǎng)景越來(lái)越多元化,比如企業(yè)內(nèi)部 APP 移動(dòng)工作臺(tái),系統(tǒng)集成電話呼叫功能,智能硬件,諸如智能門(mén)禁,智能機(jī)器人等將會(huì)對(duì)全終端的互通能力提出更高的要求,網(wǎng)易云信在這條賽道的探索會(huì)將持續(xù)進(jìn)行,盡力滿足用戶不同場(chǎng)景的需求,真正做到助力用戶內(nèi)生長(zhǎng)。
歡迎持續(xù)關(guān)注本公眾號(hào),了解更多網(wǎng)易云信關(guān)于微信小程序網(wǎng)關(guān)服務(wù)的技術(shù)探索和實(shí)踐。
?作者介紹?
本森,網(wǎng)易云信資深音視頻服務(wù)端開(kāi)發(fā)工程師,負(fù)責(zé)云信流媒體服務(wù)器、小程序網(wǎng)關(guān)服務(wù)器、 WebRTC 網(wǎng)關(guān)服務(wù)器及直播源站的開(kāi)發(fā)工作,是帥的。
?延伸閱讀?
-
技術(shù)干貨 | iOS 高階容器詳解
-
技術(shù)實(shí)踐 | 網(wǎng)易云信 QUIC 加速服務(wù)架構(gòu)與實(shí)踐
-
技術(shù)實(shí)踐 | 聊聊網(wǎng)易云信的信令網(wǎng)絡(luò)庫(kù)實(shí)踐
?
?
?
?
與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的技术实践 | 网易云信在融合通信场景下的探索和实践之 RTMPGateway 服务架构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 云信小课堂|5分钟快速实现iOS端PK连
- 下一篇: 嘉宾PPT分享|泛娱乐领域音视频技术探索