5.使用RTCPeerConnection流式传输视频

你会学到什么

在此步骤中,您将了解如何:

  • 使用WebRTC shim,adapter.js抽取浏览器的差异。

  • 使用RTCPeerConnection API流式传输视频。

  • 控制媒体捕获和流媒体。

此步骤的完整版本在步骤2文件夹中。

什么是RTCPeerConnection?

RTCPeerConnection是用于使WebRTC调用流视频和音频以及交换数据的API。

此示例在同一页上的两个RTCPeerConnection对象(称为对等体)之间建立连接。

没有太多的实际用途,但是对于了解RTCPeerConnection如何工作有好处。

添加视频元素和控制按钮

在index.html中,使用两个视频元素和三个按钮替换单个视频元素:

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

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

一个视频元素将显示来自getUserMedia()的流,另一个将通过RTCPeerconnection显示相同的视频流。 (在现实世界的应用程序中,一个视频元素将显示本地流,另一个视频元素将显示远程流。)

添加adapter.js垫片

在指向main.js的链接之上添加一个指向adapter.js的链接:

<script src="js/lib/adapter.js"></script>

dapter.js是将应用程序与规范更改和前缀差异隔离的垫片。

事实上,用于WebRTC实现的标准和协议是非常稳定的,只有几个前缀名。

在此步骤中,我们链接到最近版本的adapter.js的本地副本 - 对于codelab来说是好的,但对于生产应用程序来说不是很好的做法。 adapter.js GitHub repo解释了确保您的应用程序始终访问最新版本的技术。

有关完整的WebRTC互操作信息,请参阅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></video>
  <video id="remoteVideo" autoplay></video>
  <div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
  </div>
  <script src="js/lib/adapter.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

安装RTCPeerConnection的代码

将main.js替换为步骤02文件夹中的版本。

在codelab中使用大块代码进行剪切和粘贴并不理想,但为了让RTCPeerConnection运行起来,除了这样别无选择。

我们将在稍后解释代码的工作原理。

进行调用

打开index.html,单击开始按钮从网络摄像头获取视频,然后单击通话以使对等连接。您应该在两个视频元素中看到相同的视频(来自您的网络摄像头)。查看浏览器控制台以查看WebRTC日志记录。

运行原理

这一步做了很多...

如果你想跳过下面的说明,那没关系。 你还可以继续使用codelab!

WebRTC使用RTCPeerConnection API在WebRTC客户端(称为对等体)之间建立流式传输视频的连接。

在此示例中,两个RTCPeerConnection对象位于同一页面:pc1pc2。没有太多的实际用途,但有益于展示API的工作原理。

在WebRTC对等体之间建立呼叫涉及三个任务:

  • 为呼叫的每一端创建RTCPeerConnection,并在每一端添加来自getUserMedia()的本地流。

  • 获取和共享网络信息:潜在连接端点称为ICE候选。

  • 获取和分享本地和远程描述:有关SDP格式的本地媒体的元数据。

想象一下,爱丽丝和鲍勃想要使用RTCPeerConnection建立一个视频聊天。

首先,Alice和Bob交换网络信息。 “发现候选人”一词是指使用ICE框架查找网络接口和端口的过程。

  1. Alice创建一个带有onicecandidate处理程序的RTCPeerConnection对象。这对应于main.js中的以下代码:

    pc1 = new RTCPeerConnection(servers);
    trace('Created local peer connection object pc1');
    pc1.onicecandidate = function(e) {
      onIceCandidate(pc1, e);
    };

此示例中不使用RTCPeerConnection的服务器参数。

这是您可以指定STUN和TURN服务器的地方。

WebRTC设计用于对等工作,因此用户可以通过最直接的路由连接。然而,WebRTC是为了应对现实世界的网络:客户端应用程序需要通过NAT网关和防火墙,并且对等网络需要回退,以防直接连接失败。 作为此过程的一部分,WebRTC API使用STUN服务器获取计算机的IP地址,TURN服务器用作中继服务器,以防对等通信失败。 WebRTC在现实世界中有更详细的解释。

  1. Alice调用getUserMedia()并添加传递给它的流:

    pc1.addStream(localStream);
  2. 当网络候选人变得可用时,将调用第1步中的onicecandidate处理程序。

  3. Alice向Bob发送序列化的候选数据。在实际应用中,这个过程(称为信令)通过消息传递服务进行 - 我们将在后面的步骤中显示如何进行。当然,在这一步中,两个RTCPeerConnection对象位于同一页面上,可直接通信,无需外部消息传递。

  4. 当Bob从Alice获取候选消息时,他调用addIceCandidate(),将候选者添加到远程对等体描述中:

    function onIceCandidate(pc, event) {
      if (event.candidate) {
        getOtherPc(pc).addIceCandidate(
          new RTCIceCandidate(event.candidate)
        ).then(
          function() {
            onAddIceCandidateSuccess(pc);
          },
          function(err) {
            onAddIceCandidateError(pc, err);
          }
        );
        trace(getName(pc) + ' ICE candidate: \n' + event.candidate.candidate);
      }
    }

WebRTC对等体还需要找出并交换本地和远程的音频和视频媒体信息,如分辨率和编解码能力。交换媒体配置信息的信号通过交换被称为offer和answer的元数据块来进行,使用会话描述协议格式,称为SDP:

  1. Alice运行RTCPeerConnection createOffer()方法。返回的承诺提供了一个RTCSessionDescription:Alice的本地会话描述:

    pc1.createOffer(
        offerOptions
      ).then(
        onCreateOfferSuccess,
        onCreateSessionDescriptionError
      );
  2. 如果成功,Alice会使用setLocalDescription()设置本地描述,然后通过信令通道向Bob发送此会话描述。

  3. Bob使用setRemoteDescription()设置Alice发送给他的描述作为远程描述。

  4. Bob运行RTCPeerConnection createAnswer()方法,传递从Alice获得的远程描述,因此可以生成与她兼容的本地会话。 createAnswer()承诺传递RTCSessionDescription:Bob将其设置为本地描述并将其发送到Alice。

  5. 当Alice获取Bob的会话描述时,她使用setRemoteDescription()设置其为远程描述。

    function onCreateOfferSuccess(desc) {
      pc1.setLocalDescription(desc).then(
        function() {
          onSetLocalSuccess(pc1);
        },
        onSetSessionDescriptionError
      );
      pc2.setRemoteDescription(desc).then(
        function() {
          onSetRemoteSuccess(pc2);
        },
        onSetSessionDescriptionError
      );
      // Since the 'remote' side has no media stream we need
      // to pass in the right constraints in order for it to
      // accept the incoming offer of audio and video.
      pc2.createAnswer().then(
        onCreateAnswerSuccess,
        onCreateSessionDescriptionError
      );
    }
    
    function onCreateAnswerSuccess(desc) {
      pc2.setLocalDescription(desc).then(
        function() {
          onSetLocalSuccess(pc2);
        },
        onSetSessionDescriptionError
      );
      pc1.setRemoteDescription(desc).then(
        function() {
          onSetRemoteSuccess(pc1);
        },
        onSetSessionDescriptionError
      );
    }
  6. Ping

加分点

  1. 看看chrome://webrtc-internals。这提供了WebRTC统计信息和调试数据。 (Chrome的完整列表是chrome://about。)

  2. 使用CSS调整页面风格:

  3. 把视频并排。

  4. 使按钮的宽度相同,文字更大。

  5. 确保布局在移动设备上工作。

  6. 从Chrome开发工具控制台,查看localStream,pc1和pc2。

  7. 从控制台,查看pc1.localDescription。 SDP格式是什么样的?

你学到了什么

在这一步你学会了如何:

  • 使用WebRTC shim,adapter.js抽取浏览器的差异。

  • 使用RTCPeerConnection API流式传输视频。

  • 控制媒体捕获和流媒体。

  • 在对等体之间共享媒体和网络信息,以启用WebRTC呼叫。

此步骤的完整版本在步骤-2文件夹中。

提示

  • 在这一步中要学习很多东西!要更详细地找到解释RTCPeerConnection的其他资源,请查看webrtc.org/start。此页面包含JavaScript框架的建议 - 如果您想使用WebRTC,但不想争论API。

  • adapter.js GitHub repo中了解更多关于adapter.js垫片的信息。

  • 想看看世界上最好的视频聊天应用程序是什么样的?看看AppRTC,WebRTC项目的WebRTC电话的规范应用程序:appcode。呼叫建立时间小于500 ms。

最佳实践

  • 为了面向未来的代码,使用新的基于Promise的API,并通过使用adapter.js来兼容不支持它们的浏览器。

接下来

这一步显示如何使用WebRTC在同级之间流式传输视频 - 但是这个codelab也是关于数据的!

在下一步中,了解如何使用RTCDataChannel流式传输任意数据。

Last updated