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的接入是另外一个话题,涉及很多协议的互转。

一朵喇叭花压海棠
关注

6


11

18


专栏目录
JanusAndroid:在 Android 上运行 Janus 服务器的 Android 应用程序-源码
07-03
杰纳斯安卓 Janus Android 是一个 Android 应用程序,用于在 Android 上运行 Janus 服务器。 此应用程序使用原始 Janus 项目 ( ) 的 Android 移植,位于 https://github.com/tpiotrow/janusproject-Android 杰纳斯计划 Janus 是一个完全用 Java 1.7 实现的开源多代理平台。 Janus 使开发人员能够快速创建基于 Web、企业和桌面多代理的应用程序。 它提供了一套全面的功能来开发、运行、显示和监控基于多代理的应用程序。 基于 Janus 的应用程序可以分布在网络上。 Janus 可以用作面向代理的平台、组织平台和/或完整平台。 它还本地管理递归代理和完全子的概念。 Janus 由 Laboratoire Systèmes et Transports 和 Grupo de Inv
springboot基于webrtc和janus的点对点视频通话流程图
gfzdgd的博客
 541
什么时候创建offer都可以,反正在调用call的时候传offer就可以
评论11
 


云帆00
2021.12.10

咨询下,上面说的抽帧发给客户端。如果是抽帧的话,假设第二个p帧需要依赖第一个p帧,那么是不是这样有可能就不是非常流畅?

一朵喇叭花压海棠作者
回复
云帆00
2021.12.10

是的。正常情况下参考帧是上一帧,这样抽帧肯定不行,长参考帧有可能可以解决这个问题。

grp0916
2021.01.18

楼主能分享一下从基础-精通janus的学习路线图嘛

qq_36679549
2020.12.18

楼主 现在有开源的janus c++ 客户端吗

rou1983
回复
qq_36679549
2022.02.08

janus没有c++客户端,也不需要c++客户端。janus有自己的一套信令系统,可以参考官方的 js demo。传输层就如楼主所说使用libwebsocket传输信令就可以了。

qq_36679549
回复
qq_36679549
2020.12.18

现在主流都是浏览器上使用

————————————————
版权声明:本文为CSDN博主「一朵喇叭花压海棠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sonysuqin/article/details/82050970

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值