背景
客户端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的接入是另外一个话题,涉及很多协议的互转。
————————————————
版权声明:本文为CSDN博主「一朵喇叭花压海棠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sonysuqin/java/article/details/82050970