WebRTC 入门(三) —— 基础概念之RTCPeerConnection

相关概念

Posted by JianGuo on March 16, 2017

WebRTC 入门(三) —— 基础概念之RTCPeerConnection

RTCPeerConnection

RTCPeerConnection 是WebRTC的一个组件,用来在浏览器之间高效的处理固定流数据的通道。这个流数据通道是点对点的,无需服务器的中转,但为了建立起这个数据通道,我们仍然需要借助于服务器实现信令的传输。关于如何实现信令交互,请阅读关于信令的内容。

下面是一个 WebRTC的架构图,显示出RTCPeerConnection的角色。如你注意到的一样,绿色部分是相当复杂的!

WebRTC architecture (from webrtc.org)

从JavaScript的角度来看,从这个图中理解的主要事情是,RTCPeerConnection屏蔽了web开发人员隐藏在之下无数复杂的操作。WebRTC使用的编解码器和协议做了大量的工作,使实时通信成为可能,甚至在不可靠的网络上:

  • 丢包隐藏(packet loss concealment)
  • 回声抵消(echo cancellation)
  • 带宽适应(bandwidth adaptivity)
  • 动态抖动缓冲(dynamic jitter buffering)
  • 自动增益控制(automatic gain control)
  • 降噪和抑制(noise reduction and suppression)
  • 图像“清洁”( image ‘cleaning’)

上文中的W3C代码展示了一种简单的使用信令透视的WebRTC的例子。下面是两个工作WebRTC应用程序的演练:第一个是一个简单的例子来演示RTCPeerConnection; 第二个是完全可操作的视频聊天客户端。

实现没有服务器的本地RTCPeerConnection

下面的代码取自“单页”WebRTC演示:https://webrtc.github.io/samples/src/content/peerconnection/pc1, 在这个例子中,在同一个页面上分别有一个本地和“远程”的RTCPeerConnection对象和一个本地(数据来自硬件设备)和远程(数据来自于本地RTCPeerConnection)的图像。这种呼叫者和被呼叫者图像在同一页面的实现虽然是没有意义的,但它确实使得我们对RTCPeerConnection API的处理流程更加清晰,因为页面上的RTCPeerConnection对象可以直接进行数据和消息交换,而避免了复杂的信令机制。

有一点需要注意:RTCPeerConnection()第二个可选的参数’constraints’不同于getUserMedia()方法中使用到的constraints类型,详见 w3.org/TR/webrtc/#constraints

在此示例中,pc1表示本地peer(呼叫者),pc2表示远程peer(被叫者)。

呼叫者 Caller

  1. 创建一个新的RTCPeerConnection对象,从getUserMedia()获取流,并添加流:
    // servers is an optional config file (see TURN and STUN discussion below)
    pc1 = new webkitRTCPeerConnection(servers);
    // ...
    pc1.addStream(localStream);
    
  2. 创建一个offer,并且将它作为本地描述设置给pc1,作为pc2的远程描述设置为pc2。 这里可以直接在代码中使用而不使用信令,是因为呼叫者和被叫者在同一页上:
    pc1.createOffer(gotDescription1);
    //...
    function gotDescription1(desc){
      pc1.setLocalDescription(desc);
      trace("Offer from pc1 \n" + desc.sdp);
      pc2.setRemoteDescription(desc);
      pc2.createAnswer(gotDescription2);
    }
    

被叫者 Callee

  1. 创建pc2,在流从pc1添加时,显示到video元素中:
    pc2 = new webkitRTCPeerConnection(servers);
    pc2.onaddstream = gotRemoteStream;
    //...
    function gotRemoteStream(e){
      vid2.src = URL.createObjectURL(e.stream);
    }
    

RTCPeerConnection + servers 实现数据通道

在真实的应用场景中,WebRTC往往需要服务器的配合才能完成之间数据交互。幸好这些过程并不复杂,以下是在交互中可能发生的情况:

  • 用户通过信令服务器在互联网上发现彼此并且交换真实世界中的一些数据,例如姓名等。
  • WebRTC客户端应用(peers)交换网络信息。
  • Peers交换视频格式,分辨率等媒体信息。
  • WebRTC客户端应用穿透NAT网络和防火墙,实现彼此连接。

换句话说, WebRTC需要四种类型的服务器端功能:

  1. 用户发现以及通信
  2. 信令传输
  3. NAT/防火墙穿透
  4. 如果点对点通信建立失败,可以作为中转服务器

NAT穿透 指的是在处于使用了NAT设备的私有TCP/IP网络中的主机之间需要建立连接时需要使用NAT穿越技术。以往在VoIP领域经常会遇到这个问题。目前已经有很多NAT穿越技术,但没有一项是完美的,因为NAT的行为是非标准化的。这些技术中大多使用了一个公共服务器,这个服务使用了一个从全球任何地方都能访问得到的IP地址。 peer-to-peer网络, 以及创建一个用于用户发现和发送信令的服务器所需要满足的需求,超出了本文的范围。 这里只需要说明, 在RTCPeeConnection中,STUN协议及其扩展TURN 用于 ICE框架,使得RTCPeerConnection能够处理NAT穿透和其他网络变化。

ICE全名叫交互式连接建立(Interactive Connectivity Establishment),是一种比较成熟的NAT穿透技术,常用于需要进行P2P连接的框架,例如两个视频聊天客户端。ICE整合了诸如STUN、TRUN(Traversal Using Relay NAT 中继NAT实现的穿透)等服务。在实现上,ICE首先尝试通过UDP直接连接到两个客户端(这里描述为客户端应该不是很准确,就是连个对应的peer端,这里暂且叫它客户端吧),以尽可能降低延迟。在此过程中,STUN服务器具有单个任务:使NAT后面的客户端能够找到其公共地址和端口。(Google有几个STUN服务器,如:stun:stun.l.google.com:19302。其中一个用于apprtc.appspot.com示例。)

Finding connection candidates

如果UDP连接失败,ICE服务器尝试使用TCP: 首先是HTTP, 然后尝试使用HTTPS。如果直接连接失败,特别是因为企业NAT穿越和防火墙的影响,ICE则切换使用中间(relay)TURN服务器。 换句话说,ICE将首先使用STUN与UDP直接连接两个客户端,如果失败,将回退到TURN中继服务器。 “finding candidates”是指找到网络接口和端口的过程。

WebRTC data pathways

WebRTC工程师Justin Uberti在2013年的Google I/O WebRTC演示中提供了有关ICE,STUN和TURN的更多信息。(演示幻灯片给出了TURN和STUN服务器实现的示例。)