音视频直播技术FFmpeg程序员

通过WebRTC进行实时通信-通过RTCPeerConnecti

2018-10-16  本文已影响18人  音视频直播技术专家

目录

我们将要学习的知识点

在这一步,我们将知道下面的事情怎么做:

本步完整的版本在 step-02目录下。

什么是RTCPeerConnection

RTCPeerConnection 是调用WebRTC传输音视频和交换数据的API。这个例子是在同一个页面中两个RTCPeerConnection对象之间建立连接。没有什么实际价值,但却能很好的证明RTCPeerConnection是如何工作的。

添加视频元素和控制按钮

在index.html里将一个video元素替换为两个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>

一个视频元素用于显示从getUserMedia()上获取的视频流,另一个通过RTCPeerConnection显示同样的视频流。在真实的应用中,一个视频元素显示本地流,另一个显示远端流。

添加 adapter.js 片段

在main.js链接之上,添加一个到当前 adapter.js版本的连接。

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

adapter.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 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

在 codelab里对大段代码做剪切复制不是很好的做法。但是为了得到 RTCPeerConnection 并使它运行,没有别的办法,只能全力以赴。

很快你就会学会如何进行编码工作。

呼叫

打开 index.html, 点击Start button 从webcam 获取视频, 点击 Call 建军一个对等连接 。 你将看到在两个video元素上显示同样的来自于webcam的视频。看浏览器的console ,可以看到 WebRTC的日志。

它是如何工作的?

这部分有很多内容...

如果你想跳过下面的说明也没问题。

你仍然可以继续 codelab!
WebRTC使用 RTCPeerConnection API在 WebRTC客户端之间建立连接传输视频,称之为 peers。

在这个例子中,两个 RTCPeerConnection 对象是在同一页上,pc1 和pc2。没什么实际价值,但很好的证明了 API 的工作。

在 WebRTC peer之间建立一个呼叫,包括三个任务:

想像一下Alice和 Bob想使用 RTCPeerConnection建立视频聊天。

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

  1. Alice 创建带有icecandidate handler(addEventListener('icecandidate'))的 RTCPeerConnection 对象。 这对应下面 main.js中的代码:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);

在这个例子中,RTCPeerConnection 的 servers参数没什么用处。

这里可以指定 STUN 和 TURN 服务的地址。

WebRTC旨在实现点对点工作,因此用户可以通过最直接的路由进行连接。但是,WebRTC旨在应对现实世界的网络: 客户端应用程序需要遍历NAT网关和防火墙,并且在直接连接失败的情况下,对等网络需要回退。

作为这个过程的一部分, 在点对点连接失败的情况下,WebRTC APIs 使用 STUN 服务得到你的计算机的IP 地址,并且使用 TURN 服务作为 relay 服务。 WebRTC in the real world 阐明了更多细节。

  1. Alice 调用 getUserMedia() 并且添加传递给它的流:
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.');
  1. 当网络候选者变为有效时,在第一步中的 onicecandidate handler将被调用。
  2. Alice 将序列化后的候选者数据发给 Bob,在真实的应用中,这个过程(称为信令)通过消息服务发生- 在后面的步骤中,你将学到如何处理它。当然,在本步骤中,在同一页中的两个RTCPeerConnection对象直接通信不需要额外的消息。
  3. 当Bob从Alice得到候选者消息后,他调用 addIceCandidate()添加候选者到远端描述:
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端点之间还需要找出并交换本地和远程音频和视频媒体信息,例如分辨率和编解码器能力。 通过使用称为SDP的会话描述协议格式交换元数据blob(称为 offer 和 answer)来进行交换媒体配置信息的信令:

  1. Alice 运行 RTCPeerConnection 的 createOffer() 方法. 返回值提供了一个RTCSessionDescription,也就是 Alice的本地会话描述:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. 如果成功,Alice使用setLocalDescription()设置本地描述,然后通过其信令通道将此会话描述发送给Bob。
  2. Bob 使用setRemoteDescription设置 Alice 发送给它的描述作为远端描述。
  3. Bob 运行RTCPeerConnection的 createAnswer () 方法,将他从Alice哪儿得到的远端描术传递给它,这样就可以生成一个与她兼容的本地会话。必须传递给 createAnswer() 一个 RTCSessionDescription,并发送给 Alice。
  4. 当 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);
}

  1. Ping!

点滴

  1. 看一下chrome://webrtc-internals,这个提供了WebRTC的状态和调试数据。(Chrom URLs的完整列表是在 chrome://about
  2. 这页的CSS风格:
  1. 从 Chrome Dev Tool 控制台,看localSteam, localPeerConnection 以及 remotePeerConnection。
  2. 从控制台看localPeerConnectionpc1.localDescription SDP格式看起来是什么样儿?

我们学到了什么

在这一步你学会了如何去:

本步骤完整的版本在 step-2目录中。

提示

在这一步学习了很多内容,另一个详细的解释了RTCPeerConnection的资源是webrtc.org/start.这页包括了JavaScript架构的建议 - 如果你喜欢 WebRTC,而且不想因API扯皮。

最佳实践

为了使您的代码能够面向未来,请使用新的基于Promise的API,并通过使用 adapter.js实现与不支持它们的浏览器的兼容性。

接下来

此步骤显示如何使用WebRTC在端点之间传输视频 - 但此codelab与数据无关!

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

上一篇下一篇

猜你喜欢

热点阅读