網易雲信在融合通訊場景下的探索和實踐之 RTMPGateway 服務架構

0 導讀

隨着各個行業的互聯網化進程不斷演進,融合通訊在愈來愈多的場景中獲得應用,例如金融場景的視頻面籤、醫療場景的遠程會診、企業協做場景的多人視頻會議等。在這些場景中,經過微信小程序實現音視頻互通,能夠下降用戶溝通成本,提高業務運營效率,同時幫助企業或組織進一步打通微信社羣中的溝通障礙,以一種輕量化的敏捷運營模式輕鬆實現微信生態中的客戶轉化閉環。所以,小程序音視頻必將受到愈來愈多企業和組織的青睞。算法

那麼,一個可以支持微信小程序之間實現音視頻通話,同時可以支持微信小程序跨平臺互聯互通的媒體服務器須要完成哪些事情?小程序

1 RTMPGateway

微信在6.5.21版本經過小程序開放了實時音視頻能力,開發者們可使用組件 < live-pusher > 實現基於 RTMP 的直播推流(錄製),在新版本中加入了 RTC 模式,用於實時音視頻通話上行,使用組件 < live-player > 實現基於 RTMP 的直播拉流(播放),RTC 模式則用於實時音視頻通話下行。能夠看出,微信小程序的音視頻是基於 RTMP 協議的,同時,微信小程序的音視頻只是提供了終端上的能力,並無實現媒體服務器,那麼如何實現微信小程序之間的音視頻通話,同時實現和多平臺之間互聯互通?答案是確定的,咱們須要本身開發一個媒體服務器。微信小程序

在網易雲信融合通訊場景下,安全

  • 微信小程序端使用 RTMP 協議,接入邊緣媒體網關,即 RTMPGateway;
  • RTMPGateway 支持 RTMP 協議,完成微信小程序間的媒體轉發;
  • 同時,RTMPGateway 將 RTMP 協議轉換成 RTP 協議,轉發給雲信邊緣媒體服務器,完成與雲信 SDK、標準 WebRTC 終端的互聯互通。

融合通訊場景

2 關於 RTMP 鏈接

上文提到 RTMP,這裏咱們再簡單介紹一下,RTMP(Real Time Messaging Protocol,即實時消息傳送協議),是 Adobe 公司開發的一個基於 TCP 的應用層協議,被普遍應用於直播領域。服務器

RTMP 協議是基於 TCP 的,而在 TCP 鏈接創建完成後,不管是發佈仍是播放一個 RTMP 協議的流媒體,都還須要通過如下幾個步驟:微信

RTMP 鏈接步驟

RTMP 握手(Handshake)

握手

  • 握手開始於客戶端發送 C0、C1 塊;服務器收到 C0 或 C1 後發送 S0 和 S1。
  • 客戶端收齊 S0 和 S1 後,開始發送 C2;服務器收齊 C0 和 C1 後,開始發送 S2。
  • 客戶端和服務器分別收到 S2 和 C2 後,握手完成。

這就是一個完整的握手過程。網絡

在實際工程應用中,通常是客戶端先將 C0、C1 塊同時發出,服務器在收到 C1 以後同時將 S0、 S一、 S2 發給客戶端。以後客戶端向服務器端發送 C2 塊,簡單握手完成。數據結構

Flash 播放器鏈接服務器時,若服務器只支持簡單握手,則沒法播放 H.264 和 AAC 的流,多是 Adobe 的限制,Adobe 將簡單握手改成了有一系列加密算法的複雜握手(complex handshake)。多線程

簡單握手的包是隨機的1536字節(S1/S2/C1/C2),複雜握手則是須要進行摘要和加密,此處再也不贅述。架構

創建網絡鏈接(NetConnection)

創建網絡鏈接

  • 客戶端發送命令消息(connect)到服務器,請求與一個服務器創建鏈接。
  • 服務器接收消息後,發送確認窗口大小協議,設置帶寬協議,設置塊大小協議消息到客戶端。
  • 客戶端處理設置帶寬協議消息後,發送確認窗口大小協議消息到服務器端。
  • 服務端向客戶端發送「流開始」(Stream Begin)。
  • 服務器發送(_result),通知客戶端鏈接的狀態。

創建網絡流(Create Stream)

創建網絡流

  • 客戶端發送命令消息(CreateStream)命令到服務器端。
  • 服務器端接收消息後,發送(_result),通知客戶端流的狀態。

能夠看出,創建一個 RTMP 鏈接,須要完成複雜的協議交互,而且這些協議交互都是同步的。那麼,如何實現一個異步的 RTMP 協議棧?

對於一個須要處理大量鏈接的 Server,無非兩種策略:多線程或使用異步 Socket。

例如對於 RTMPDump 提供的基於同步 Socket 的 librtmp,只能使用多線程處理多個鏈接,可是多線程的併發數上限明顯,同時須要處理複雜的鎖和同步等。Winlin 則是在 SRS 中使用 st 協程在單線程中實現了異步的 RTMP 協議棧。

而在雲信的 RTMPGatway 中,咱們使用狀態機的方式,一樣在單線程實現了異步的 RTMP 協議棧。針對每一條 RTMP 鏈接,RTMPGatway 均會記錄鏈接目前所處狀態,以跟蹤整個鏈接的創建。

RTMP 鏈接

3 關於媒體協議封裝

爲了實現微信小程序與雲信 SDK、標準 WebRTC 終端的互聯互通,RTMPGatway 須要實現 RTMP 協議和 RTP 協議的轉換。

RTMP 封裝 AAC

微信小程序推拉流使用 RTMP 協議,音頻使用 AAC 編碼。

使用 RTMP 推送 AAC 直播流,首先須要發送「AAC sequence header」,其中包含重要的編碼信息,沒有它解碼器將沒法解碼。AAC sequence header 存放的是 AudioSpecificConfig 結構,其結構描述很是複雜(詳見「ISO-14496-3 Audio」),能夠簡化爲下表:

AAC 編碼

在發送這個 header 須要在前面分別加上1個字節(8bits)的 AudioTags 數據,其中每 bit 表示的意義以下圖:

每 bit 表示的意義 每 bit 表示的意義-2

其中 SoundData 的組成以下:

SoundData 的組成

當數據的第一個字節爲0時,後面跟 AAC sequence header;當數據的第一個字節爲1時,後面跟AAC 數據。

RTMP 封裝 H.264

微信小程序使用 RTMP 協議進行推拉流,使用 H.264 進行視頻編碼。

使用 RTMP 推送 H.264 直播流,首先須要發送"AVC sequence header",一樣包含重要的編碼信息,沒有它解碼器將沒法解碼。AVC sequence header 就是 AVCDecoderConfigurationRecord 結構(詳見「ISO-14496-15 AVC file format」),簡化爲下表:

RTMP 封裝 H.264

在發送這個 Header 須要在前面分別加上1個字節(8bits)的 VideoTags 數據,其中每 bit 表示的意義以下圖:

每 bit 表示的意義

發送的爲 avc 數據,因此 CodecID(後 4bit)的值爲 7;videodata 的數據打包方式爲 AVCVIDEOPACKET,具體的信息見下圖:

videodata 的數據

RTP 協議

RTP( Real-time Transport Protocol,即實時傳輸協議),爲語音、圖像、傳真等多種須要實時傳輸的多媒體數據提供端到端的實時傳輸服務,服務質量則由 RTCP(Real-time Transport Control Protocol,即實時傳輸控制協議)來提供。

RTP 協議頭信息包括 RTP 固定頭以及 RTP 擴展頭,以下圖:

RTP 固定頭

RTP 固定頭

RTP 擴展頭

RTP 擴展頭

若 RTP 固定頭中的擴展比特位置 1,則一個長度可變的頭擴展部分被加到 RTP 固定頭以後,若是有 CSRC 列表,則在 CSRC 列表以後。頭擴展包含 16 比特的長度域,指示擴展項中 32 比特字的個數,不包括 4 個字節擴展頭(所以零是有效值)。

RTP 固定頭以後只容許有一個頭擴展。爲容許多個互操做實現獨立生成不一樣的頭擴展,或某種特定實現有多種不一樣的頭擴展,擴展項的前 16 比特用以識別標識符或參數。這 16 比特的格式由具體實現的上層協議定義。基本的 RTP 說明並不定義任何頭擴展自己。

RTP

IETF 針對 RFC3550 在檔次方面定義了一系列擴展協議,一些總結以下:

  • RFC3550 - RTP: A Transport Protocol for Real-Time Applications (RTP):定義最基本的RTP/RTCP報文格式和收發規則;
  • RFC3551 - RTP Profile for Audio and Video Conferences with Minimal Control :定義音視頻會議最基本的音視頻數據負載格式、編碼和傳輸,是其它檔次的基礎;
  • RFC3711 - The Secure Real-time Transport Protocol (SRTP):定義了RTP在安全方面的加強,如加密、認證和重放保護;
  • RFC4585 - Extended RTP Profile for Real-time Transport Control Protocol (RTCP)-Based Feedback (RTP/AVPF) :定義了RTP基於RTCP在及時反饋方面的加強,如定義NACK,PLI,SLI等RTCP報文;
  • RFC5124 - Extended Secure RTP Profile for Real-time Transport Control Protocol (RTCP)-Based Feedback (RTP/SAVPF):綜合RTP/SAVP和RTP/AVPF的安全性和及時反饋性的最全面的檔次。

RFC 3550 定義五種 RTCP 報文,類型在報文頭部的PT域定義。

RFC 3550 定義五種 RTCP 報文

SR 報文用於發送端報告本端的數據發送統計信息和數據接收統計信息,RR 報文用於報告本端的數據接收統計信息,SDES 報文用於報告本端的描述性信息,BYE 在本端離開會話時發送,而 APP 則是特定於應用的數據。IETF 根據實際需求對 RTCP 的報文類型進行擴展,定義了一系列協議,此處再也不贅述。

RTP 封裝 H.264

針對 H.264 的封裝,WebRTC 選擇了使用 RFC3984 的 Non-Interleaved 封裝方案。

H.264 的封裝

Single NAL Unit Packet

Single NAL Unit Packet

Single NAL Unit Packet 是 RTP 最基本的打包方式,其中,

  • forbidden_bit:禁止位,初始爲0,當網絡發現 NAL 單元有比特錯誤時可設置該比特爲 1,以便接收方糾錯或丟掉該單元。
  • nal_reference_bit:nal 重要性指示,標誌該 NAL 單元的重要性,值越大,越重要,解碼器在解碼處理不過來的時候,能夠丟掉重要性爲 0 的 NALU。
  • Type:NAL 單元中的 RBSP 數據結構的類型,其中 0 未指,1-19 在 H.264 協議中有定義,20-23 爲 264 協議指定的保留位,24-29 在 RFC3984 中進行了指定。

Type 後面的數據爲 RBSP 的數據,須要注意的是:編碼器的每一個 slice 或者每幀頭通常會有由0x000001 或者 0x00000001 做爲起始頭,在 RTP 封裝中須要去掉。此外在 H.264 裸碼流數據後面可能還會帶有 padding 的數據由 RTP 頭的 padding 位決定。

STAP-A

STAP-A 的做用是能夠把多個 nal 單元封裝在一個 RTP 包裏面進行傳輸,須要注意:-A 的格式都是不容許跨幀的,也就是 nal 單元的時間戳必須是相同的。常見的場景是 sps 和 pps 兩個小包被合併封裝。

STAP-A

RTP 頭後面僅跟着 STAP-A 的頭,由 F、NRI 和 Type 組合而成,佔一個字節,這裏的 Type 爲 24。後面兩個字節爲第一個 nalu 單元的長度,後面跟第一個 nalu 數據同 Single NAL Unit 的封裝一致,第一個數據結束後,跟着第二個 nalu 的長度,佔 2 個字節,依次類推。

FU-A

FU-A 的做用是把一個原始大的 nalu 切成多個數據包進行傳輸,主要使用場景在 slice 比較大的狀況下。FU-A 比較特殊,有 FU-A 起始包、FU-A 包(若是隻切兩個包可能沒有)和 FU-A 結束包組成。

FU-A

  • FU indicator 佔一個字節,由 F、NRI 和 Type 組合而成,這裏的 Type 爲28。
  • FU header 佔一個字節:

FU header

  • S: 佔1位若是是1表示當前這個包是 FU-A 的起始包
  • E: 佔1位若是是1表示當前這個包是 FU-A 的結束包
  • R: 佔1位,保留位,爲0
  • Type: 實際包含 nalu 的類型

4 關於協議轉換

幀完整性判斷

爲了實現微信小程序與雲信 SDK、標準 WebRTC終端的互聯互通,在協議轉換時,不管是 RTMP 到 RTP 仍是 RTP 到 RTMP,對於視頻都須要首先拿到 H.264 數據,最重要則是對幀完整性的判斷。

在 RTMPGateway 收到 RTP 視頻包後,則須要先組成完整的幀才能再轉封裝成 RTMP 協議,失敗則進行I幀請求。而僅根據 RTP 頭部中的M比特判斷完整性是不許確的,在 RTMPGateway 中,幀的完整性判斷還須要知足 :

  • 本幀包個數 = 本幀末包序 - 本幀首包序 + 1
  • 本幀首包序 = 上一幀末包序 + 1

下面以例子說明:

幀完整性判斷

  1. 幀1幀2正常進入轉封裝。
  2. 幀3超時,進行I幀請求,上一幀末包序置19。
  3. 幀4判斷完整,非I幀,上一幀末包序置22。
  4. 幀5判斷完整,且爲I幀,進入轉封裝。
  5. 幀6超時,且丟失M包,上一幀末包序置27。
  6. 幀7必定超時,上一幀末包序置31。
  7. 幀8判斷完整,且爲I幀,進入轉封裝。

音頻轉碼

音頻轉碼

對於音頻,由於微信小程序使用 AAC 編碼,雲信邊緣媒體服務器支持 Opus 編碼,所以在完成協議解封裝後,還須要完成音頻的轉碼。在雲信 RTMPGateway 中,咱們採用了獨立的音頻轉碼線程組,減輕邏輯處理線程的壓力的目的。每一個轉碼任務將被分配到固定的音頻轉碼線程,線程根據任務數量進行負載均衡。

關於小程序用戶和雲信雲端用戶管理

關於小程序用戶和雲信雲端用戶管理

RTMPGateway 與小程序端間的信令採用 WebSocket 傳輸協議,此時 RTMPGateway 是 WebSocket 的服務端,接收來自小程序端的鏈接請求;RTMPGateway 與媒體服務器之間的信令一樣採用 WebSocket 傳輸協議,此時 RTMPGateway 做爲 WebSocket 的客戶端,向媒體服務器發起鏈接請求。

  • 微信小程序端登陸後除了在 RTMPGateway 建立對應的 mini-user 對象,還須要模擬成一個對應的 mini-fakeClient 向媒體服務器登陸加入會議;
  • 同時,RTMPGateway 在建立房間時,會針對這個房間模擬出一個特殊的用戶 room-fakeClient 加入房間;
  • 在廣播房間及流信息時,如新的 NRTC 用戶加入,媒體服務器會向全部的用戶(包括mini-fakeClient、room-fakeClient)。對於 RTMPGateway,則只須要處理 room-fakeClient 收到的媒體服務器主動下發的信令,達到信令去重目的;
  • 當新的 NRTC 用戶加入房間,room-fakeClient 將收到廣播信令,RTMPGateway 同步管理該 NRTC 用戶(nrtc-user)狀態。例如當收到該用戶的發佈流的信令後,將由 room-fakeClient 向媒體服務器發起訂閱音視頻流的請求。

6 寫在最後的話

一個完整的微信小程序網關服務架構,除了完成上述的 RTMP 協議棧、媒體協議轉封裝、房間業務管理等基礎模塊,咱們還須要考慮:

  • RTMP 協議是基於 TCP 協議的,而 TCP 協議自身的一些擁塞算法,在弱網環境下對網絡的退避策略過於激進,所以須要咱們去尋求解決方案。例如在 RTMPGateway 中,對於 RTMP 下行在服務器引入 BBR 算法,經過直接修改內核 TCP 協議機制代碼,達到更高的帶寬利用率,下降卡頓。
  • 關於 RTP 解封裝後的音視頻時間戳計算,能夠有多種方案。例如在收到 SR 以前使用系統時間代替 NTP 時間,收到 SR 以後進行系統時間和 NTP 時間的偏差修正;也能夠在收到 SR 以前,將收到的第一個 RTP 包時間設置爲起始時間,並使用固定採樣頻率根據公式計算時間戳,收到 SR 以後則先計算真實採樣頻率,再根據公式計算時間戳。爲了進一步解決音畫同步問題,在 RTMPGateway 中,對於 RTP 解封裝後的音視頻數據,將分別進入對應的 buffer,經過控制兩個buffer的讀取速度,以達到音畫同步以及控制時延的目的。

因爲篇幅關係,更多細節再也不展開,以上即是本次分享的所有內容。隨着應用場景愈來愈多元化,好比企業內部 APP 移動工做臺,系統集成電話呼叫功能,智能硬件,諸如智能門禁,智能機器人等將會對全終端的互通能力提出更高的要求,網易雲信在這條賽道的探索會將持續進行,盡力知足用戶不一樣場景的需求,真正作到助力用戶內生長。

歡迎持續關注咱們,瞭解更多網易雲信關於微信小程序網關服務的技術探索和實踐。

做者介紹

本森,網易雲信資深音視頻服務端開發工程師,負責雲信流媒體服務器、小程序網關服務器、 WebRTC 網關服務器及直播源站的開發工做,是帥的。

相關文章
相關標籤/搜索