1. 程式人生 > >WebRTC+libwebsockets+Janus的秒開實踐

WebRTC+libwebsockets+Janus的秒開實踐

背景

客戶端SDK集成了WebRTC和libwebsockets,服務端使用了Janus,需要支援拉流秒開。

關於WebSocket

      Janus作為SFU,使用WebSocket協議與客戶端通訊。客戶端在挑選開源庫時其實沒有太多選擇,C層主要是libwebsockets庫,這個也是Janus使用的庫,還有Boost的Beast庫,不過比較新,不敢踩坑,IOS上有RocketSocket,但不是跨平臺,因此最後採用了libwebsockets庫。
      libwebsockets庫主要的問題是IO介面不太友好,需要自己啟動一個執行緒輪詢獲取IO事件,在其回撥中處理所有事件。

秒開要考慮的問題

  • libwebsockets IO的優化
          主要是寫資料的處理。
          libwebsockets需要呼叫者自己維護髮送佇列,呼叫者呼叫lws_callback_on_writable來告知libwebsockets有資料要寫,然後資料放入傳送佇列,libwebsockets會通過LWS_CALLBACK_CLIENT_WRITEABLE事件通知可寫,這個時候呼叫者才可以從傳送佇列取出資料傳送,這個是一個非同步的過程。
          在libwebsockets的IO事件迴圈中,lws_service用於阻塞等待IO事件,但是lws_callback_on_writable並不會讓lws_service退出阻塞狀態,lws_service有一個最大等待時間,如果等待lws_service超時才處理待發送的資料無疑會增加整體接續時間。這裡可以通過在呼叫執行緒中呼叫lws_cancel_service方法強制lws_service退出阻塞來立刻處理髮送佇列中的資料。這裡需要用互斥鎖對傳送佇列做一個同步。
  • Janus信令流程的簡化
    Janus拉流的流程跟其外掛式結構有關,主要流程:
    1.客戶端建立Janus session;
    2.客戶端建立Janus外掛handle;
    3.客戶端發Join請求給Janus,拉某個目標流;
    4.Janus通過事件通知該流的sdp;
    5.客戶端發Start訊息給Janus,告知客戶端的sdp,這樣完成了sdp的互動和協商;
    6.同時客戶端傳送Candidate給Janus,用於進行ICE通訊、DTLS握手;
    7.Janus傳送事件給客戶端,告知流的啟動結果。
    這些互動步驟中有些是必須序列的,每次互動都會消耗一個RTT,勢必會增加接續時間,精簡後的流程如下:
    1.客戶端把建立Session、外掛Handle、Join請求合併成一個命令發給Janus;
    2.Janus回覆目標流的sdp;
    3.客戶端發Start訊息給Janus,告知客戶端的sdp,這樣完成了sdp的互動和協商;
    4.同時客戶端傳送Candidate給Janus,用於進行ICE通訊、DTLS握手;
    5.Janus傳送事件給客戶端,告知流的啟動結果。
  • 客戶端的處理流程優化
    音視訊裝置的初始化不管是在哪個平臺是都一個耗時的過程,可以放在初始化的時候執行,而不是每次接續的時候都執行。
  • DTLS握手的優化
          WebRTC和Janus會在ICE打洞成功之後進行DTLS握手,這裡DTLS主要是用來交換SRTP的加密金鑰。目前版本的Janus有一個問題,在ICE打洞成功之後並不能很實時的告知DTLS的握手模組,以至於總會丟失第一個客戶端的握手包,這樣必須等待客戶端重發握手包。
          目前版本的WebRTC在使用BoringSSL的時候其重發超時依賴於RTT估算,最低是50ms,但是如果使用OpenSSL1.0.2則需要等待1S,因為OpenSSL1.0.2還沒有設定DTLS握手超時的介面,需要自己適配。最好的辦法是修改Janus,讓Janus儘快響應DTLS握手包,否則這裡將無差別的增加繼續時間。
          另外,Janus還有一個問題,必須等待第一個客戶端的Candidate信令到達之後才會啟用DTLS握手過程,實際上,Janus作為SFU服務端可以開啟ICE-Lite,其ICE過程應該是被動的,不需要主動向客戶端的地址打洞。這樣等待第一個客戶端的Candidate信令到達之後才會啟用DTLS握手過程這個特性實際上沒有太多理由,會增加半個RTT的時間,因為客戶端傳送的第一個Candidate是本地的地址,並不需要跟Stun通訊,跟Stun互動不影響DTSL的握手時間。
  • 服務端GOP快取
          除了信令、通道建立時間需要優化以外,傳統直播中秒開需要考慮的GOP快取在這裡也需要考慮,應該確保第一個發給客戶端的資料就是可直接解碼的I幀,否則客戶端即使收到資料也會因為無法解碼導致黑屏。
          Janus要快取最近一個GOP的資料,在建立好通道後,立刻傳送最近的一個I幀,而GOP內的非關鍵幀可以用抽幀的方式發給客戶端,不用傳送完整GOP,這樣傳送的資料減少,且讓客戶端以加速播放的方式追上當前的播放時間。
  • CDN的排程
          要提供全網的優質實時直播服務,Janus必須部署到CDN中,Janus與CDN的接入是另外一個話題,涉及很多協議的互轉。