WebP2P 让你的直播免流

你在下载的时候,有没有体验过 P2P 下载,能够让你的网速从 10KB 直接提升到 10MB? 你在企业内传输文件的时候,有没有体验过文件秒传? 你在看直播的时候,想不想用别人的流量看直播呢? ...

能做到上面这些场景的技术,叫做 P2P。P2P 技术中,最出名的叫做 WebRTC。WebRTC 是一个含金量非常高的技术。做好的话你可以养活一家公司,做不好,那就只能是一个 demo。

WebRTC 虽然能做很多事,但是并不是所有场景都适合。最大的使用场景是 两个终端在同一个 NAT 内,简单来说,都在一个 wifi 内。这个场景中,最显著的效果就是带宽无限并且高速,你走的就是内部的线路,根本不消耗运营商的流量。

P2P 技术在基于 WebRTC 标准下,可以做很多事情:

  • 录屏应用
  • APP Drop
  • 视频直播
  • ...

作为 Web 开发,WebRTC 又能够给前端赋能些什么呢?

在了解这些基本内容后,接下来,我们会从底层一步一步介绍一下 P2P 在 Web 直播的应用。

P2P 穿透

P2P 穿透也可以叫做 NAT 穿透,这是 P2P 最大的一个难点。为了解决 ipv4 不够用,推出了 NAT 技术。NAT (Network Address Translation)是用来将内网私有 ip,转化为公有 ip。简单点,就是让很多台电脑公用同一个 IP。但是 NAT 有个非常重要的点:

NAT 不允许外网主机主动访问内网主机。

这个不允许访问的机制也有很多种,根据这些特性,我们可以将 NAT 分为多种:

  • 完整锥型NAT(Full Cone NAT)
  • 受限锥型NAT(Restricted Cone NAT):
  • 端口受限型NAT(Port Restricted Cone NAT)
  • 对称型NAT(Symmetric NAT)

一般情况下,前面三种 NAT 是可以穿透的,但是,对称型NAT 无法穿透。具体内容,大家网上搜一搜 NAT 穿透,资源应该很多。在穿透时,我们不仅需要考虑 NAT 还需要考虑到集群机器的防火墙设定,如果防火墙限制了 UDP 打洞,那么我们还需要切换为 TCP 打洞(TCP 打洞一般会慢一点)。

总的来说,我们穿透时需要考虑的问题就有:

  • NAT 类型
  • 两端处在 NAT 的位置:都在一层 NAT 后还是多层 NAT 后...
  • 防火墙连接协议的设定

这些问题一旦组合起来,这个复杂度就是 N*N 的关系了。如果搭建 p2p 每次都需要从头解决这个内容,P2P 也不会像现在发展的这么好了。WebRTC 就是用来解决这一问题的标准模板,通过 STUN/TURN Server 来实现打洞穿透。

WebRTC 打洞流程

这里,我们按照一个比较常见的情况作为模板讲解一下。两端都位于 NAT 层背后,并且,NAT 是可以穿透的 Full Cone NAT 类型。具体穿透流程如下:

  • A 和 B 需要和 STUN 服务器建立连接,获得 A/B 的公网 ip:port 和私网 ip:port。
  • B 往 A 发送一个打洞包,此时,已经在 B 的 NAT 上留下到 A 的 打洞 session。但是,由于该包没有 A NAT 的 session 记录,会被 A 拒绝掉。
  • A 往 B 发送一个打洞包,该包会在 A 的 NAT 上增加 B 的 session。此时,由于 B 的 NAT 上存在 A 的 session,该包是可以直接被 B 的 NAT 通过的。
  • 打洞完成

在 WebRTC 完成这里流程的 API 是: RTCPeerConnection。通过自建的一个中间 Server,来交换指定的 SDP 和 candidate。

SDP 是当前 Point 的一些基本描述信息,当前 WebRTC 版本 ICE 的描述信息,以及,对已经连接的 ICE 内容的描述,比如 video/audio 信息。SDP 这一环节,其实就是告诉了哪两个 Point 会进行连接。本身和打洞并没有太大的关联。具体内容可以参考:SDP antonomy

candidate 则是打洞的关键信息,里面会包含当前 Point 的内外网 ip:port,以及防火墙设定规则 tcp/upd。这里有一点需要注意的是,一个 point 为了能够提高 NAT 打洞的成功率,会产生多个 candidate。这里主要取决于几个 candidate 里面几个基本参数:

  • sdpMid 用来指定该次 candidate 的传输的 mediaStream 内容。例如, videoaudio
  • protocol:指定连接的协议, tcpudp
  • type: 表示能够穿透 NAT 的类型
    • host: 能够直连,或者在同一个 NAT 内
    • srflx/prflx: 外网直连通道,STUN 已经帮忙打了一个洞。如果两端不在同一个 NAT 里面,会用到该内容.
    • relay:是 TURN 服务器的中转通道。针对的是,对称型的 NAT,数据交换只能走中间的 server。

不过, srflx/prflx 类型的 candidate 只会在你初始化 RTCPeerConnection时,传入 iceServers 的 STUN 服务器 URL 时,才会获得。

config = {
     "iceServers": [{
         "urls": ["stun:stun.l.google.com:19302"]
     }],
     "iceTransportPolicy": "all",
     "iceCandidatePoolSize": "0"
 }

在 webRTC 代码层面,我们并不需要额外针对 candidate 做逻辑处理。我们只需要将 candidate 传给另外一端,通过 addIceCandidate() 注册到 RTC 内部即可。

local.addIceCandidate(remoteCandidate)
.then(e=>{
    console.log("add success");
})

通过云服务器完成 candidate 和 sdp 信息的交换后,我们就已经做完了 RTC 连接的必要准备。剩下的就是在连接建立完成之后做的状态监听和其他扩展事情。

连接状态判断

这里面最大的一个问题在于,我们完成数据添加之后,怎么判断 P2P 是否连接上。WebRTC 提供了我们 7 个基本的事件监听:

attribute EventHandler  onnegotiationneeded;
attribute EventHandler  onicecandidate;
attribute EventHandler  onicecandidateerror;
attribute EventHandler  onsignalingstatechange;
attribute EventHandler  oniceconnectionstatechange;
attribute EventHandler  onicegatheringstatechange;
attribute EventHandler  onconnectionstatechange;

通过 onconnectionstatechange 就可以得到连接状态的变化。直接绑定该方法,即可获得相关内容:

peer.addEventListener('connectionstatechange',event=>{
    // get the state from event
},false);

其能够提供的事件回调信息,可以直接参考:connection states。里面,我们只需要判断状态是否是 connected,来决定该次连接是否成功。

如果不成功,我们可以直接从 onicecandidateerror 里面获得相关的错误信息,具体可以参考:ICE gather error。当然,在连接过程中,也可以直接从 Promise 中,获得连接失败的信息,这部分内容可以直接参考:RTC Error。

上面整个代码流程可以直接参考:webRTC trickle candidate

DataChannel 数据穿洞

打洞过程是 WebRTC 最基础的一步,如果连这一步都没成功,那么后面就需要做一些其它适配的兼容。比如,直接通过 CDN 拉去资源。WebRTC 打洞成功后,我们就可以利用这个打洞包,根据用户的种子资源数、上行带宽、下载进度来判断 P2P 传输的资源。

WebRTC 原生提供了 RTC Media API、RTCDataChannel、DTMF 这三个传输通道。Media 能够直接结合 getUserMedia API 传输当前摄像头和麦克获取的音视频。并且 Chrome 浏览器在底层内嵌了很多功能强大的编码器,这里可以直接参考:webRTC FRQ.

不过,经过测试 WebRTC 回声消除和双端同时对话的效果并不是特别好。这块,大家可以考虑一下,能不能直接在底层替换编码器或者购买其他服务。

Media 和 DTMF 通常都需要建立在 getUserMedia 的前提下,但是,IOS11 并不支持,它只支持 DataChannel 传输数据的 API。所以,这里,我们只会针对 DataChannel 来做一些讲解。如何通过 DataChannel 来传输你的自定义文件内容。

DataChannel 是 PeerConnection 的一个拓展 API,可以直接通过 createDataChannel 来创建一个 SCTP 通道。SCTP 是一种高效的帧传输协议,它和 TCP/UDP 是在同一层的,集中了两者之间的优势。我们只需要在发送端创建 Channel,接收端直接监听 ondatachannel 事件即可。

let senderChannel = localPeer.createDataChannel('dataChannel');
senderChannel.onopen = ()=>{
    if(senderChannel.readyState === 'open'){
        senderChannel.send(someBuffer);
    }

};

senderChannel.birnayType = 'arrayBuffer';

remotePeer.ondatachannel = function(event){
    // receive data from event.channel
    remotePeer = event.channel;
    remotePeer.binaryType = 'arraybuffer';
    remotePeer.onmessage = onReceiveMessageCallback;
}

RTCDataChannel 一共可以发送两种数据:String 和 Binary(也可以叫做 ArrayBuffer,ArrayBufferView or Blob) 具体直接参考:dataView。

Channel 具体的用法其实就和 WebSocket 一样,通过 send 来传递相关的信息,再监听 onmessage 事件获得数据的回调。

上面的流程大致的覆盖了打洞的云端流程,比如穿透 NAT 层,P2P 数据 Channel 建立。不过,中间还有很多细节并没有解决清楚:

  • 怎么找到最优的 Point
  • 需要传输的数据是哪些
  • 传输数据的进度控制

而这些问题就需要落地到具体的业务当中了。因为现在直播行业非常的火热,本人也在该行业里面摸爬滚打,了解到该行业一些基本的痛点。最大的就是非常吃带宽,为了解决这一问题,我们完全可以利用 P2P 来做直播带宽的节省。

但是,说起来很容易,怎么做这才是关键?

不过,对于 Web 开发来说,这里只介绍一下 的基本思路。在 Web 直播中比较流行的是通过 http-chunked 模式来实现直播。而 http-chunked 协议很难得到具体播放的进度,那我们就需要一个能够很容易获得播放进度的协议--切片协议。

对于切片协议而言,最为突出的就是 HLS,但是它的延时性过高,没办法满足直播低延时的问题。针对这个点,MPEG 提出了一个 DASH 协议,来作为直播内容协议的补充。直播 DASH 是基于 HTTP-URL 的动态直播协议,通过 URL 上面的时间戳就很容易做到对流的时间记录,整个端的 P2P 流程图为:

云端这一步,就主要在 SegmentProtocol 做的事情,右边的则是本地播放的 IS/MS 处理。而如果你能够在云上做好这一整套流程,比如:

  • 确定最佳上行 Peer
  • 维护一整套种子和资源的云端管理
  • Peer 纠错机制
  • WebRTC 和 DASH 的最佳切换

那么,你的 P2P 功能和编码能力应该比一般程序员高太多了。

入门 Tip

事情需要一点一点做,饭需要一口一口的吃,DASH 由于协议比较复杂,推荐大家先从 HTTP-Chunked 协议入手,自己先搭建一个直播的 DEMO 试试水,这里推荐一下 HTTPLIVE 库。

另外,如果读者对前端音视频很感兴趣,不甘心只做一个纯纯的 UI 工程师的话,可以直接关注我的公众号前端小吉米,输入 MSE 加入 前端音视频的交流小组

原文发布于微信公众号 - 前端小吉米(villainThr)

原文发表时间:2018-02-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏崔庆才的专栏

爬虫代理哪家强?十大付费代理详细对比评测出炉!

前言 随着大数据时代的到来,爬虫已经成了获取数据的必不可少的方式,做过爬虫的想必都深有体会,爬取的时候莫名其妙 IP 就被网站封掉了,毕竟各大网站也不想自己的...

48012
来自专栏腾讯移动品质中心TMQ的专栏

糖大夫--测量流程性能监控自动化方案设计

糖大夫(简称)是一款血糖仪(想了解更多的同学请看这里http://tdf.qq.com/),但不止血糖仪。血糖仪终端具备触屏、联网、高准度血糖检测单元。除了终端...

1936
来自专栏杨建荣的学习笔记

Linux高级流量控制tc使用

在做MHA测试的时候,有一个重要的环节就是测试MHA Manager节点和Master节点的网络情况,如果产生了抖动,那么MHA本身提供了一个参数seconda...

2745
来自专栏北京马哥教育

Python黑科技 | 一步一步教你如何搭建自己的视频聚合站

? 前言 作为一个炉石传说玩家,经常有事没事开着直播网站看看大神们的精彩表演。不过因为各个平台互相挖人的关系,导致关注的一些主播分散到了各个直播平台,来回切...

3587
来自专栏闵开慧

hadoop负载均衡与垃圾回收

负载均衡 负载的均衡,是分布式系统中一个永恒的话题,要让大家各尽其力齐心干活,发挥各自独特的优势,不能忙得忙死闲得闲死,影响战斗力。而且,负载均衡也是一个...

3408
来自专栏java达人

武林外传—一灯大师与众弟子漫谈Api网关选型

南帝段王爷隐居桃源后,潜心研究,构筑了一套武林秘籍访问系统,系统是微服务部署的,拆分为多个模块,每个模块只做一件事情。系统刚上线的时候非常顺利,但随着访问量的增...

835
来自专栏A周立SpringCloud

实用技巧:Hystrix传播ThreadLocal对象(两种方案)

目前,Spring Cloud已在南京公司推广开来,不仅如此,深圳那边近期也要基于Spring Cloud新开微服务了。 于是,领导要求我出一套基于Spring...

5375
来自专栏何俊林

Android音视频开发之-WebRTC技术实践

WebRTC是Google于2011年6月3日开源的即时通讯项目,旨在使其成为客户端视频通话的标准。其实在Google将WebRTC开源之前,微软和苹果各自的通...

1212
来自专栏媒矿工厂

定义和测量延迟

想要优化延迟,可Latency到底是多少?延迟始终是媒体内容传输的一个重要关注点,人们也在不断尝试用新的方法来优化延迟,本文参考AWS的一些新技术,介绍了延迟的...

843
来自专栏点滴积累

网络爬虫之投票

一、前言        在你心中什么是网络爬虫?在网线里钻来钻去的小虫?先看一下百度百科的解释: 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,...

3196

扫码关注云+社区