通过WebRTC进行实时通信-通过RTCPeerConnecti
目录
我们将要学习的知识点
在这一步,我们将知道下面的事情怎么做:
- 简单介绍浏览器与WebRTC的不同。
- 使用 RTCPeerConnection API 传输视频。
- 控制媒体的捕获与传输。
本步完整的版本在 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之间建立一个呼叫,包括三个任务:
- 为呼叫的每个端创建一个RTCPeerConnection,并且在每端都添加一个从
getUserMedia()
获取的本地流。 - 获得并共享的网络信息:潜在的连接端点称为ICE 候选者。
- 获得并共享本地与远端描述信息:本地多媒体的元数据用SDP格式。
想像一下Alice和 Bob想使用 RTCPeerConnection建立视频聊天。
首先,Alice和 Bob交换网络信息,“查找候选者”一词是指使用ICE框架查找网络端口的过程。
- 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 阐明了更多细节。
- 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.');
- 当网络候选者变为有效时,在第一步中的
onicecandidate
handler将被调用。 - Alice 将序列化后的候选者数据发给 Bob,在真实的应用中,这个过程(称为信令)通过消息服务发生- 在后面的步骤中,你将学到如何处理它。当然,在本步骤中,在同一页中的两个RTCPeerConnection对象直接通信不需要额外的消息。
- 当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)来进行交换媒体配置信息的信令:
- Alice 运行 RTCPeerConnection 的 createOffer() 方法. 返回值提供了一个RTCSessionDescription,也就是 Alice的本地会话描述:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
.then(createdOffer).catch(setSessionDescriptionError);
- 如果成功,Alice使用setLocalDescription()设置本地描述,然后通过其信令通道将此会话描述发送给Bob。
- Bob 使用
setRemoteDescription
设置 Alice 发送给它的描述作为远端描述。 - Bob 运行RTCPeerConnection的
createAnswer ()
方法,将他从Alice哪儿得到的远端描术传递给它,这样就可以生成一个与她兼容的本地会话。必须传递给 createAnswer() 一个 RTCSessionDescription,并发送给 Alice。 - 当 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);
}
- Ping!
点滴
- 看一下chrome://webrtc-internals,这个提供了WebRTC的状态和调试数据。(Chrom URLs的完整列表是在 chrome://about)
- 这页的CSS风格:
- 将视频并排放置
- 将Button设置成相同宽度和文本大小。
- 确何布局适用于移动端
- 从 Chrome Dev Tool 控制台,看localSteam, localPeerConnection 以及 remotePeerConnection。
- 从控制台看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流式传输任意数据。