Bootstrap

webrtc支持H265(一)webrtc datachannel的特性

总体方案webrtc datachannel + wasm H265 + webgl渲染,这将是一系列文章介绍方案的实现及细节。

背景

问题及需求

最近有个项目,服务器采用的ZLMediaKit,通过它拉摄像头的流,然后通过web去播放。
摄像头支持h264和h265,web端通过http-flv在ZLMediaKit上拉摄像头的流,基本架构如下图所示:

image.png

但是有这样一个问题:web 播放器通过http-flv拉h264或h265的流进行播放,有明显的延迟,即使在内网延迟也有2s,通过调试发现,主要是web播放器的缓存的机制导致。

但是这种缓存机制又无法优化,http-flv基于tcp协议,播放器的实现必然需要引入缓存来兼容tcp 协议流的特性,因为应用层无法预测什么时候有数据,数据有多少,所以必须有个缓存处理tcp协议上抛数据的抖动性,来保证播放的流畅。tcp对上层应用是屏蔽了网络状态(丢包率,抖动,延迟等),播放器也无法根据网络状态调整缓存大小来减少延迟。
像这种监控的项目,绝大多少都是部署在内网,在内网中还有这么高的延迟显然体验不太好。

因为ZLMediaKit支持webrtc协议,于是将播放器的实现换成了webrtc,对h264的延迟降低到了500ms以内,优化效果比较明显。但是webrtc不支持h265,显然已无法采用webrtc原生API去实现了,但是又必须兼容h265。

实现思路

在兼容h265的基础上,为了让web前端业务改动最小。决定使用 webrtc datachannel 作为传输协议,通过wasm来实现h265的解码(用C++实现,编译成js库,供前端调用),webgl来渲染的方案。
webrtc 中的datachannel实现是基于udp,在解码和渲染中可以自己来控制缓存,那么udp + 加上必要的缓存 理论上是可以大大的降低延迟。

image.png
那么实现细节实现包括三个方面:

  1. webrtc datachannel 能否支持传送视频数据,效果如何? 是否有可靠性保证?

webrtc datachannel设计的初衷是用于传输除了音视频数据以外的所有数据,比如传文件,传一些业务消息。如果传输像视频这种数据量较大,且频率较高的数据,datachannel通道是否出现丢包,抖动等问题?

  1. 修改 ZLMediaKit,使h265的码流通过datachannel传输

ZLMediaKit支持webrtc接入,但是要转发音视频流前提是协商出了音视频通道。通过datachannel实现传音视频数据,是不会协商出音视频通道的,所以需要修改ZLMediaKit代码将视频转发出来。

  1. 如何实现 h265解码?

这是第一篇,先了解webrtc datachannel的特性。

webrtc datachannel

协议栈

datachannel基于sctp协议,sctp协议是与udp,tcp同级的协议,属于传输层协议。sctp协议栈是在实现os中,与udp/tcp一样由os提供接口。

但是webrtc 中的datachannel 并非在传输层,而是在应用层,基于usrsctp库实现,usrsctp库基于udp实现了sctp协议栈。

RFC 8831 说明webrtc data channel的特性,最主要就是了解webrtc datachannel 协议栈的结构,如下:
image.png

  • SCTP 由usrctp实现的协议栈。
  • 然后经过DTLS加密。
  • 再打包到udp报文中。

更详细的协议栈图:
image.png

webrtc中如此设计sctp 协议,为了让data channel 通道也能穿透NAT。

可靠性

sctp协议标准中将可靠性定为可配置,可以配置为保障可靠到达(像tcp协议),也可以配置为不保障可靠性(像udp协议)。
并且是针对应用消息的配置,也就是在发送应用消息时来设置它的可靠性,比如重要的消息设置为可靠到达对端。
这样相比udp和tcp就灵活很多。

webrtc datachannel提供了三个可靠性配置的参数:

  • ordered 是否有序
  • maxPacketLifTime
  • maxRetrasmits

不设置它们,默认就是可靠的。
可靠性的参数可以参考这个链接

所以就可以确认第一个问题,webrtc datachannel是否可靠?
webrtc datachannel是可靠的,从它暴露的参数配置来看,它有重传机制且保障有序。

PPID

PPID是定义在sctp消息中,用于描述负载类型的字段,作用类似于rtp消息中的payload type字段。

Each SCTP user message contains a Payload Protocol Identifier (PPID) that is passed to SCTP by its upper layer on the sending side and provided to its upper layer on the receiving side. The PPID can be used to multiplex/demultiplex multiple upper layers over a single SCTP association.

In the WebRTC context, the PPID is used to distinguish between UTF-8 encoded user data, binary-encoded user data, and the Data Channel Establishment Protocol (DCEP) defined in [RFC8832]. Please note that the PPID is not accessible via the JavaScript API.

PPID值usrsctp库会抛给应用,应用可以判断是什么类型,需要怎么处理。

在RFC 8831中为webrtc data channel扩展了类型值:

  • WebRTC String PPID 为51
  • WebRTC Binary PPID 为53

在RFC 8832中也为webrtc data channel扩展了类型值:

  • WebRTC DCEP PPID为50

DCEP 为RFC 8832中定义的协议,用于webrtc datachannel建立通道,全称为Webrtc Data Channel Establishment Protocol。

这个协议似乎并没有被支持,我查看了mediasoup代码(ZLMediaKit对webrtc的支持使用的是mediasoup的代码),对PPID 为50的消息是直接丢掉了。

if (ppid == 50)
{
    MS_WARN_TAG(sctp, "ignoring SCTP data with ppid:50 (WebRTC DataChannel Control)");

    return;
}

那么如果需要通过webrtc datachannel来传输视频数据,就需要将PPID设置为53。

资料

RFC 8831 webrtc data channel 技术定义
RFC 8832 WebRTC Data Channel Establishment Protocol

;