webrtc python服务器搭建-webrtc 服务器搭建
大家好,我是百度智能云的李永兴。 在百度智能云媒体云团队主要负责RTC产品的研发。
01 开源SFU现状及不足
在开发RTC产品的过程中,我们调研了很多优秀的开源WebRTC服务器,比如Janus、MediaSoup、Licode、SRS4等,这些SFU有着不同的设计理念和特点,我们也从中受益匪浅。 同时我们也发现,如果要基于这些优秀的开源SFU构建高可用、高并发的RTC云服务,就必须对这些SFU进行相应的修改。 本次分享会主要介绍这些“改造零件”。 这些改造其实是有一定通用性的,就是针对开源SFU的通病进行优化和改造,而不局限于某个特定的SFU。
RTC 云服务要求
要构建 RTC 云服务,需要满足以下要求:
目前一些开源的SFU,比如Janus、MediaSoup,服务器端会开放UDP运行范围,即服务器端使用不同的端口来服务不同客户端的媒体连接。 同时,在Janus中,信令和媒体是耦合在一起的; 在MediaSoup中webrtc python服务器搭建,提供了官方的nodejs库,它本身只是一个媒体层库。 但同时官方也提供了demo,媒体层和信令也是耦合在一起的; SRS4其实是一款国产之光,产品推出不久,目前只支持WebRTC流媒体功能。
对于这些开源 SFU,主要的改进是:
当然,对于整个RTC云服务来说,除了SFU的核心功能,RTC云服务还需要支持一些混流、录制、多协议网关支持(比如RTMP接入:方便访问微信小程序和SIP)等等。
02 单口方案
目前无论是Janus还是MediaSoup,服务端都是使用一个单独的UDP端口来服务一个单独的PeerConnection。 SFU启动时,会配置一个可用的UDP端口范围,用于客户端数据传输。 服务器端收到客户端的请求后,会从配置的端口范围中分配一个未使用的端口给客户端,并将服务器端的端口通过SDP传递给客户端。 客户端接收SDP端口并解析,然后可以向服务器发送或接收数据。 这需要服务器同时暴露上千个端口,对网络安全非常不友好,可操作性也很差。 此外,客户端的网络可能会对目标端口施加一些限制。 如果分配的端口在允许的范围之外,客户端将无法连接到服务器,导致整个连接失败。
为了实现云服务的高可用和弹性伸缩,负载均衡设备一般配置为网络接入设备。 在真实的生产环境中,一个IP背后可能有几十台甚至上百台机器。 当一台机器宕机时,不会导致整个服务不可用。 常见的负载均衡设备很少支持UDP PortRange。 即使他们支持,但由于暴露了很多端口,实际上无法完成健康检查。
针对以上问题,我们需要对SFU进行相应的修改,使服务器使用单端口传输流媒体数据。
Janus 使用 Libnice 库作为底层网络传输库。 该库本身是一个多端口实现。 所以在Janus的基础上实现单端口有两种方案:一种是直接替换Libnice库,重建底层,改成单端口。 港口的运输方式。 但是,由于Janus和Libnice库的紧耦合,通过重构底层来实现复杂难度高的实现非常困难; 另一种方式是保留 Libnice 的多端口实现,在 Janus 中增加单端口代理功能。 代理的作用是指客户端的数据通过单个外部端口传输,接收到数据后,将相应的数据同时转发到Libnice内部分配的不同服务器的内部端口。 这样修改起来会更容易。
如果选择使用代理方式,其实现的难点在于不同客户端的数据通过同一个服务器端口传输,以及服务器应该如何确定传输的数据与用户的对应关系。 对此,我们可以通过SDP协商中的ICE-Ufrag字段来解决。 服务器端收到客户端的SDP后,会按照前面的流程创建一个本地服务端口,并将对应的ICE-Ufrag映射到该端口。 起来。 服务器会将外部IP端口写入SDP发送给客户端,然后一直监听外部端口。 当客户端建立连接时,会发送一个Stun包,Stun包中有ICE-Ufrag。 服务器接收并解析ICE-Ufrag,然后根据之前的映射关系从IP-MAPS中找到对应的服务器端口。 同时服务器端也会记录Stun包的源客户端IP和端口,服务器端将用户端的IP和端口映射到服务器端的IP和端口。 每次从客户端收到数据后,可以查看数据源的IP和端口,通过MAP的映射关系找到对应的服务器端口,将数据转发到对应的服务器端口。 同样,服务端发送的数据,也会从映射关系中找到对应客户端的IP和端口,通过单个端口发送。
通过这种单端口方案,我们可以在负载均衡设备后面部署SFU,可以很方便地对线路进行扩容和健康检查,实现高并发和高可用。 另外服务器有公网地址,所以WebRTC的ICE和打洞操作其实是不需要的。 在进行地址映射时,需要使用客户端Stun报文的真实地址。 在测试过程中,我们发现有时真实地址与客户端发送的Candidate中的地址不一样。 如果使用了Candidate中的地址,就会出现连接失败的问题。
MediaSoup虽然也是多端口方案,但是没有使用Libnice库,所以可以直接在底层实现整个单端口方案,不需要Porxy的存在。
这里值得一提的是SRS4。 虽然SRS4目前只支持WebRTC流,但它的实现是基于原生的单端口方案,没有使用Libnice库。 需要 Porxy 的存在。 SRS4在单端口方面相当友好,可以轻松实现集群分布式部署。
03 信令分离
WebRTC标准本身并没有规定信令部分,所以各个开源的SFU基本都是自定义实现的。 Janus 基于 HTTP 或 WebSocket 实现信令。 MediaSoup本身是一个nodejs库,不包含信令部分,但其官方Demo也实现了基于HTTP或WebSocket的信令。 它们的共同点是信令部分和媒体部分的实现是集成的。 信令一般基于TCP协议,媒体一般基于UDP协议。 如果它们的实现是集成的,则需要将客户端的 TCP 信令和 UDP 流数据发送到服务器上的同一台机器。 这主要是因为服务端在收到客户端的信令后,会在本机初始化一些资源。 如果TCP信令和UDP流数据不在同一台机器上,则无法完成。
这样一来,有两种简单的解决方案,一:每台机器都有一个独立的公网IP; 另一种是使用源地址哈希的负载平衡。
如果选择单独的公网IP方案,功能实现没有问题,但无法满足高可用和高并发的要求。 一台机器对应一个IP。 如果本机上的流太多,会加载困难,无法弹性扩展。
我们的主要目的是希望将同一个client的TCP和UDP负载发送到同一个server,但是使用源地址hash的方式,会有两个问题:一个是负载不均衡的问题,如果多个用户分担同一个网络出口会造成负载不均衡; 还有一个问题就是在实际的网络过程中,即使是同一个client,它的TCP egress和UDP egress也可能不一样,这会导致client整个连接失败。
根据以上分析,导致该问题的根本原因是SFU同时提供信令和媒体服务。 我们的解决方案是将信令与 SFU 分开。 信令分离其实有两层含义。 一:信令业务与SFU分离,SFU作为纯流媒体处理器。 二是将信令分为两部分,一部分是与客户端交互的信令,另一部分是信令服务器与SFU或MeidiaServer之间的内部交互信令。
信令服务分离后,可以独立实现信令服务器,为客户端提供基于TCP的信令服务,包括SDP分析和生成服务。 客户端必须首先连接到信令服务器进行媒体协商。 信令服务器会根据一定的策略选择SFU或MeidiaServer节点的IP,通过SDP返回给客户端。 同时,信令服务器也会将接收到的客户端信息广播给对应分配的SFU。 客户端收到SDP后,根据IP连接SFU节点。 SFU节点中的所有机器实际上都有client信息,以便client可以进行正常的推拉流。
因为信令分离,不需要依赖源地址哈希的负载均衡策略。 同一个client的多个PeerConnection可能会命中后端不同的SFU,这样可以实现更好的负载均衡。
信令分离之后,接下来的问题就是信令服务器与SFU或者MeidiaServer之间的内部信令如何交互。 信令服务器需要向SFU或MeidiaServer广播用户信息,SFU需要向信令服务器上报一些媒体状态。 这些内部信令的特点是可以异步处理,不需要等待处理的返回结果。 因此,可以利用消息队列来完成内部信令的交互。 消息队列的引入进一步解耦了信令服务器和SFU的应用。 两者的部署更加灵活。 信令服务器可以与SFU一起部署,也可以单独部署。
信令服务器除了为客户端提供一些信令服务外,还会使用客户端的真实IP,通过http-DNS服务获取最佳的SFU节点地址,返回给客户端。 这将使SFU的调度更加准确,提供更好的服务。
Janus signaling和media的耦合比较紧密,分离起来会稍微复杂一些。 解决方案也有两种:一种是在现有Videoroom插件的基础上修改,另一种是直接自己实现一个SFU插件。 两人的工作量都不算小。 如果自己实现SFU插件,Janus Core中的部分也需要修改。
对于 MediaSoup 本身,它只是一个 nodejs 库,不包含信令部分。 只需要实现一些上层消息队列的发送和接收,以及内部信令的解析功能。 需要单独的信令服务器和客户端来提供信令。 服务。
SRS4 内部有一个非常简单的流拉信令部分。 如果要使用SRS4来实现WebRTC的拉流功能,还需要做信令的分离。
04 级联继电器
对于级联中继部分的改造,RTC多方通话存在跨地域、跨运营商的问题。 为了满足更多用户的高质量体验,用户需要就近接入,即多方分布在不同的SFU上。 这就需要我们的SFU具备级联中继的能力,将相关的媒体流转发给需要媒体流的SFU。 目前开源的Janus和MediaSoup都不具备完整的级联能力,都需要做相应的修改。
级联主要涉及两个问题:一是网络拓扑问题,二是级联协议问题。
级联网络拓扑中最主要的问题是级联路由问题。 传统的 CDN 网络是由中心节点和边缘节点组成的树状结构。 其主要优点是回源结构相对简单,多级放大,并发能力强。 其主要缺点是由于中央源栈的存在,多级回源结构导致延时较大,不适合RTC应用。
由于网络状况每时每刻都在变化,我们无法确定哪种结构最适合RTC的应用。 所以我们在设计的时候,最重要的就是网络的灵活性和适应性。
网络灵活性是指网络的拓扑结构可以通过配置灵活改变,能够适应各种网络环境。 级联可能有非常复杂的应用场景。 比如Relay可能实现在公网,也可能实现在内网,也可能在几个点或内部级联。
自适应是指系统可以根据实时网络情况自动调整路由选择。 为此,我们在级联中引入路由表的设计,路由表包括目的地址和下一跳地址。 中央控制节点将路由表发送给各个节点的SFU。 与CDN回源不同的是,由于RTC中没有中心源站的概念,它采用的是主动转发的方式,而不是像CDN那样将流量拉回源的方式。 中心节点实际上保存了每个流的节点位置信息。 当一个节点需要某个流量时,中心节点将命令转发给最近的有这个流量的节点。 SFU收到转发命令后,将路由信息写入转发包的包头,根据路由表查询下一跳进行转发。 下一跳收到转发报文后,重复相应的过程,直到到达最终目的地。 这种方式的优点是每次转发只需要中心节点下发命令,后续的转发完全由SFU完成。
中央控制节点还具有自动生成路由表的能力。 如果有新节点上线,会自动生成与新节点相关的路由表并进行分发,以保证新节点上线时自动完成流畅的数据流转。 自适应的原理是节点主动检测相邻节点的延迟和丢包,并将这些检测结果上传给中心节点,中心节点根据这些检测结果调整和分发路由表。 仍处于测试阶段。
级联的另一个问题是协议。 级联主要在SFU之间进行,我们采用私有协议进行级联。 WebRTC协议本身是基于P2P的,如果用WebRTC协议来做SFU之间的级联,会显得过于沉重webrtc python服务器搭建,很多内容是没有必要的。 同时,我们会在私有协议中加入一些业务信息,比如房间号、用户号、路由信息等。 当接收端收到数据包时,不需要进行单独的查询操作,也可以自动完成路由数据包的转发。 .
SFU 使用单独的中继端口来监视和转发私有协议和数据。 同时也可以将级联端口开放给客户端,客户端可以通过私有协议访问RTC系统。 由于一些公网中继场景的存在,我们也会在私有协议中加入丢包重传FEC功能,以保证公网下的中继质量。
Janus有一个RTP forword功能,可以将用户的媒体流以RTP方式转发到一个地址。 如果想基于Janus做级联,可以在这个功能的基础上做一些改动,增加级联监听功能,实现整个媒体流的转发。
05 RTC云端架构
上图展示了整体的RTC云端架构。 除了上面提到的流媒体服务器,还包括其他模块,比如业务后台模块的平台,包括Relay,路由表,房间等控件, 平台,信令服务器,流媒体服务器使用MQ进行信令同步和转发。 此外,还有一些流媒体服务将多个视频流混合到一个频道并推送它们以绕过直播或存储。 流媒体服务器的流媒体转发也是通过Relay进行的。 的。 除了以上,还有一些多协议网关,比如支持RTMP(微信小程序)或者SIP(传统视频会议,终端)接入。
【在线分享】移动音视频SDK工程实践
今晚19:30,我们邀请到百度智能云音视频SDK产品技术负责人李明禄,从音视频数据的角度梳理了音视频SDK的发展与技术演进。 分析数据在常见音视频模块中遇到的问题和挑战,并提出相应的解决方案和技术实践。
扫描上方二维码进行预约