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的握手時間。
  • 服務端I幀緩存
          除了信令、通道建立時間需要優化以外,傳統直播中秒開需要考慮的I幀緩存在這裏也需要考慮,應該確保第一個發給客戶端的數據就是可直接解碼的I幀,否則客戶端即使收到數據也是黑屏。
          Janus要緩存最近一個GOP的數據,在建立好通道後,立刻發送最近的一個I幀,而GOP內的非關鍵幀可以用抽幀的方式發給客戶端,不用發送完整GOP,這樣發送的數據減少,且讓客戶端以加速播放的方式追上當前的播放時間。
  • CDN的調度
          要提供全網的優質實時直播服務,Janus必須部署到CDN中,Janus與CDN的接入是另外一個話題,涉及很多協議的互轉。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章