[译]WebRTC基础实践 - 5.通过RTCPeerConnection传输流媒体视频

栏目: 后端 · 前端 · 发布时间: 6年前

内容简介:在本节课程中, 我们将学习以下内容:本节的完整版代码位于在WebRTC规范中,

本节内容

在本节课程中, 我们将学习以下内容:

  • 使用WebRTC兼容库: adapter.js , 来抹平各浏览器间的差异。
  • 通过 RTCPeerConnection API 传输流媒体视频。
  • 控制 media 的捕捉和传输。

本节的完整版代码位于 step-02 文件夹中。

RTCPeerConnection 简介

在WebRTC规范中, RTCPeerConnection 用于视频流/音频流、以及数据的传输。

下面的示例程序, 将会在一个页面上, 通过两个 RTCPeerConnection 对象建立一个连接通道。

这个demo本身没什么实用价值, 目的只是为了理解 RTCPeerConnection 的原理。

添加 video 元素及控制按钮

index.html 文件中, 配置两个 video 元素, 以及三个按钮:

<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>

<div>
  <button id="startButton">Start</button>
  <button id="callButton">Call</button>
  <button id="hangupButton">Hang Up</button>
</div>

第一个 video 元素( id="localVideo" )用于展示通过 getUserMedia() 获取到的本地视频流, 第二个 video 元素( id="remoteVideo" )则通过RTCPeerconnection, 接收并显示同样的视频。在实际应用中, 页面中一般都有两个 video 元素: 一个用来展示本地视频, 另一个用来播放远程传输过来的视频( 可以参考微信视频聊天界面, 其中有一大一小,两个视频展示窗口 )。

添加 adapter.js 兼容库

main.js 引用的前面, 引入 adapter.js 的最新版本:

<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

adapter.js是一个适配程序, 隔离了应用程序和规范之间的变更、前缀等差异.(当然, WebRTC实现所使用的标准和协议都已经是稳定版了, 有前缀的API也没几个。)

在本节课程中, 我们引入了 adapter.js 的最新版本。这个库对于实验和教程来说足够用了, 但如果想用于生产环境, 可能还需要进一步完善。 adapter.js 的地址为: https://github.com/webrtc/adapter , Github提供的服务让我们可以使用到最新的版本。

WebRTC 详细的交互日志, 请参考: https://webrtc.org/web-apis/interop/

现在, Index.html 的内容如下:

<!DOCTYPE html>
<html>

<head>
  <title>Realtime communication with WebRTC</title>
  <link rel="stylesheet" href="css/main.css" />
</head>

<body>
  <h1>Realtime communication with WebRTC</h1>

  <video id="localVideo" autoplay playsinline></video>
  <video id="remoteVideo" autoplay playsinline></video>

  <div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

调用 RTCPeerConnection

使用 step-02/main.js 文件替换 work/main.js 文件。

按道理来说,在demo教程中,是不应该存在这种大量的复制粘贴行为的, 但没办法, RTCPeerConnection 相关的代码是一个整体, 要跑起来就必须全部配置好才行。

下面我们对代码进行详细的讲解。

拨打视频

浏览器中通过http协议打开 index.html 页面, 单击 Start 按钮获取摄像头的视频, 之后点击 Call 按钮来建立对等连接(peer connection)。 如果连接成功, 那么就可以在两个 video 中中看到同样的画面. 请打开浏览器的控制台, 查看 WebRTC 相关的日志信息。

原理简介

这一步做了很多的操作…

具体的内容比较复杂, 如果不关心实现过程, 可直接跳到下一节。

跳过下面的步骤, 依然可以进行该教程的学习!

通过 RTCPeerConnection, 可以在 WebRTC 客户端之间创建连接, 来传输流媒体视频, 每个客户端就是一个端点( peer )。

本节的示例中, 两个 RTCPeerConnection 对象在同一个页面中: 即 pc1pc2 。 所以并没有什么实际价值, 只是用来演示api的使用。

在 WebRTC 客户端之间创建视频通话, 需要执行三个步骤:

  • 为每个客户端创建一个 RTCPeerConnection 实例, 并且通过 getUserMedia() 获取本地媒体流。
  • 获取网络信息并发送给对方: 有可能成功的连接点(endpoint), 被称为 ICE 候选。
  • 获取本地和远程描述信息并分享: SDP 格式的本地 media 元数据。

假设 Alice 和 Bob 想通过 RTCPeerConnection 进行视频聊天。

首先, Alice 和 Bob 需要交换双方的网络信息。 “寻找候选” 指的是通过 ICE 框架来查找可用网络和端口信息的过程。 可以分为以下步骤:

  1. Alice 创建一个 RTCPeerConnection 对象, 设置好 onicecandidate 回调 [即 addEventListener('icecandidate', XXX) ] 。 在 main.js 中对应的代码为:

    let localPeerConnection;

    以及,

    localPeerConnection = new RTCPeerConnection(servers);
    localPeerConnection.addEventListener('icecandidate', handleConnection);
    localPeerConnection.addEventListener(
        'iceconnectionstatechange', handleConnectionChange);

在本例中, RTCPeerConnection 构造函数的 servers 参数为 null。

servers 参数中, 可以指定 STUN 和 TURN 服务器相关的信息。

WebRTC 是为 peer-to-peer 网络设计的, 所以用户可以在大部分可以直连的网络中使用. 但现实情况非常复杂, WebRTC面临的真实环境是: 客户端程序需要穿透 NAT网关 ,以及各类防火墙。 所以在直连失败的情况下, peer-to-peer 网络需要一种回退措施。

为了解决 peer-to-peer 直连通信失败的问题, WebRTC 通过 STUN 服务来获取客户端的公网IP, 并使用 TURN 作为中继服务器。 详细信息请参考: WebRTC in the real world

  1. Alice 调用 getUserMedia() , 将获取到的本地 stream 传给 localVideo:

    navigator.mediaDevices.getUserMedia(mediaStreamConstraints).
      then(gotLocalMediaStream).
      catch(handleLocalMediaStreamError);
    function gotLocalMediaStream(mediaStream) {
      localVideo.srcObject = mediaStream;
      localStream = mediaStream;
      trace('Received local stream.');
      callButton.disabled = false;  // Enable call button.
    }
    localPeerConnection.addStream(localStream);
    trace('Added local stream to localPeerConnection.');
  2. 在网络候选者变为可用时, 步骤1中引入的 onicecandidate 回调函数, 会被执行。

  3. Alice 将序列化之后的候选者信息发送给 Bob。这个过程被称为 signaling (信令), 实际应用中, 会通过消息服务来传递。 在后面的教程中会看到. 当然,在本节中, 因为两个 RTCPeerConnection 实例处于同一个页面, 所以可以直接通信, 不再需要外部消息服务。

  4. Bob从Alice处获得候选者信息后, 调用 addIceCandidate() 方法, 将候选信息传给 remote peer description:

function handleConnection(event) {
  const peerConnection = event.target;
  const iceCandidate = event.candidate;

  if (iceCandidate) {
    const newIceCandidate = new RTCIceCandidate(iceCandidate);
    const otherPeer = getOtherPeer(peerConnection);

    otherPeer.addIceCandidate(newIceCandidate)
      .then(() => {
        handleConnectionSuccess(peerConnection);
      }).catch((error) => {
        handleConnectionFailure(peerConnection, error);
      });

    trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
          `${event.candidate.candidate}.`);
  }
}

WebRTC客户端还需要获取本地和远程的音频/视频媒体信息, 比如分辨率、编码/解码器的能力等等. 交换媒体配置信息的信令过程, 是通过交换元数据的blob数据进行的, 即一次 offer 与一次 answer , 使用会话描述协议(Session Description Protocol), 简称 SDP :

  1. Alice 执行 RTCPeerConnection 的 createOffer() 方法。返回的 promise 中提供了一个 RTCSessionDescription 对象: 其中包含 Alice 本地的会话描述信息:

    trace('localPeerConnection createOffer start.');
    localPeerConnection.createOffer(offerOptions)
      .then(createdOffer).catch(setSessionDescriptionError);
  2. 如果执行成功, Alice 通过 setLocalDescription() 方法将本地会话信息保存, 接着通过信令通道, 将这些信息发送给Bob。

  3. Bob使用RTCPeerConnection的 setRemoteDescription() 方法, 将Alice传过来的远端会话信息填进去。

  4. Bob执行RTCPeerConnection的 createAnswer() 方法, 传入获取到的远端会话信息, 然后就会生成一个和Alice适配的本地会话。 createAnswer() 方法返回的 promise 会传入一个 RTCSessionDescription 对象: Bob将它设置为本地描述, 当然也需要发送给Alice。

  5. 当Alice获取到Bob的会话描述信息之后, 使用 setRemoteDescription() 方法将远端会话信息设置进去。

    // Logs offer creation and sets peer connection session descriptions.
    function createdOffer(description) {
      trace(`Offer from localPeerConnection:\n${description.sdp}`);
    
      trace('localPeerConnection setLocalDescription start.');
      localPeerConnection.setLocalDescription(description)
        .then(() => {
          setLocalDescriptionSuccess(localPeerConnection);
        }).catch(setSessionDescriptionError);
    
      trace('remotePeerConnection setRemoteDescription start.');
      remotePeerConnection.setRemoteDescription(description)
        .then(() => {
          setRemoteDescriptionSuccess(remotePeerConnection);
        }).catch(setSessionDescriptionError);
    
      trace('remotePeerConnection createAnswer start.');
      remotePeerConnection.createAnswer()
        .then(createdAnswer)
        .catch(setSessionDescriptionError);
    }
    
    // Logs answer to offer creation and sets peer connection session descriptions.
    function createdAnswer(description) {
      trace(`Answer from remotePeerConnection:\n${description.sdp}.`);
    
      trace('remotePeerConnection setLocalDescription start.');
      remotePeerConnection.setLocalDescription(description)
        .then(() => {
          setLocalDescriptionSuccess(remotePeerConnection);
        }).catch(setSessionDescriptionError);
    
      trace('localPeerConnection setRemoteDescription start.');
      localPeerConnection.setRemoteDescription(description)
        .then(() => {
          setRemoteDescriptionSuccess(localPeerConnection);
        }).catch(setSessionDescriptionError);
    }
  6. 然后, 视频会话就接通了!

练习与实践

  1. 在一个新标签页中打开 chrome://webrtc-internals 。 该页面提供了 WebRTC 相关的统计数据和调试信息。(Chrome 相关的功能url列举在 chrome://about 之中)
  2. 修改页面的CSS样式:
  • 将视频并排在一起。
  • 统一按钮的宽高, 使用更大的字号。
  • 适配移动端。
  1. 在Chrome控制台中(Chrome Dev Tools console), 查看 localStream , localPeerConnectionremotePeerConnection 对象。
  2. 在控制台中, 查看 localPeerConnectionpc1.localDescription 。看看 SDP 格式具体是什么样的?

知识点回顾

在本节课程中, 我们学习了:

  • 使用WebRTC兼容库: adapter.js , 来抹平各浏览器间的差异。
  • 通过 RTCPeerConnection API 传输流媒体视频。
  • 控制 media 的捕捉和传输。
  • 在两个端点(peer)间共享 media 和网络信息, 以接通WebRTC视频通话。

本节的完整版代码位于 step-02 文件夹中。

  • 本节涉及的知识点很多! 关于 RTCPeerConnection 的更多信息, 请参考 webrtc.org/start . 里面有一些对 JavaScript 框架的建议, 如果想使用WebRTC, 也想深入了解API细节的话。
  • 参考 adapter.js GitHub repo 仓库, 获取更多信息。
  • 如果想要体验当下最先进的WebRTC视频聊天应用, 可以看看 AppRTC, 这也是WebRTC项目的标准实现: app访问地址: https://appr.tc/ , 代码地址 https://github.com/webrtc/apprtc 。 创建通话的时间可以控制在 500 ms以内。

最佳实践

  • 想要让代码跟上时代的部分, 请使用基于Promise的API, 并通过 adapter.js 来兼容各种浏览器。

后续内容

本节演示了在两个WebRTC端点之间传输视频流 —— 后续小节将会展示如何传输数据!

接下来, 我们将学习 RTCDataChannel, 并用它来传输任意的数据内容。

原文链接: https://codelabs.developers.google.com/codelabs/webrtc-web/#4

翻译人员: 铁锚 - https://blog.csdn.net/renfufei

翻译日期: 2018年07月12日


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

挑战程序设计竞赛

挑战程序设计竞赛

秋叶拓哉、岩田阳一、北川宜稔 / 巫泽俊、庄俊元、李津羽 / 人民邮电出版社 / 2013-7-1 / CNY 79.00

世界顶级程序设计高手的经验总结 【ACM-ICPC全球总冠军】巫泽俊主译 日本ACM-ICPC参赛者人手一册 本书对程序设计竞赛中的基础算法和经典问题进行了汇总,分为准备篇、初级篇、中级篇与高级篇4章。作者结合自己丰富的参赛经验,对严格筛选的110 多道各类试题进行了由浅入深、由易及难的细致讲解,并介绍了许多实用技巧。每章后附有习题,供读者练习,巩固所学。 本书适合程序设计......一起来看看 《挑战程序设计竞赛》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具