首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过WebRTC进行实时通信-通过RTCPeerConnection传输视频

通过WebRTC进行实时通信-通过RTCPeerConnection传输视频

作者头像
音视频_李超
发布2020-04-02 18:30:13
5.1K0
发布2020-04-02 18:30:13
举报
什么是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之间建立一个呼叫,包括三个任务:

  • 为呼叫的每个端创建一个RTCPeerConnection,并且在每端都添加一个从getUserMedia()获取的本地流。
  • 获得并共享的网络信息:潜在的连接端点称为ICE 候选者。
  • 获得并共享本地与远端描述信息:本地多媒体的元数据用SDP格式。

想像一下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风格:
  • 将视频并排放置
  • 将Button设置成相同宽度和文本大小。
  • 确何布局适用于移动端
  1. 从 Chrome Dev Tool 控制台,看localSteam, localPeerConnection 以及 remotePeerConnection。
  2. 从控制台看localPeerConnectionpc1.localDescription SDP格式看起来是什么样儿?

我们学到了什么

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

  • 摘要浏览器与WebRTC的差异,adapter.js
  • 使用RTCPeerConnection API传输视频。
  • 控制媒体的捕获和传输
  • 在端点之间共享媒体和网络信息开启WebRTC呼叫。

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

提示

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

  • 可以从adapter.js GitHub repo找到更多的关于 adapter.js的片段。
  • 想看看世界上最好的视频聊天应用程序是什么样的?看看AppRTC,这是WebRTC项目的WebRTC调用的规范应用程序:app, code。呼叫建立时间小于500毫秒。

最佳实践

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

接下来

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 添加视频元素和控制按钮
  • 添加 adapter.js 片段
  • 安装 RTCPeerConnection 代码
  • 呼叫
  • 它是如何工作的?
  • 点滴
  • 我们学到了什么
  • 提示
  • 最佳实践
  • 接下来
相关产品与服务
NAT 网关
NAT 网关(NAT Gateway)提供 IP 地址转换服务,为腾讯云内资源提供高性能的 Internet 访问服务。通过 NAT 网关,在腾讯云上的资源可以更安全的访问 Internet,保护私有网络信息不直接暴露公网;您也可以通过 NAT 网关实现海量的公网访问,最大支持1000万以上的并发连接数;NAT 网关还支持 IP 级流量管控,可实时查看流量数据,帮助您快速定位异常流量,排查网络故障。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档