WebRTC RTCPeerConnection APIs
RTCPeerConnection API是浏览器之间点对点连接的核心。要创建RTCPeerConnection对象,只需编写以下代码:
var pc = RTCPeerConnection(config);
其中config参数至少包含一个key:iceServers。它是一个URL对象的数组,包含有关STUN和TURN服务器的信息,在发现ICE候选者时使用。您可以在以下网址找到可用的公共STUN服务器列表:code.google.com
根据您是呼叫方还是被呼叫方,RTCPeerConnection对象在连接的每一侧使用方式略有不同。
这里是用户流程的示例:
- 注册onicecandidate处理程序。它会在收到ICE候选者时将其发送到另一方。
-
注册onaddstream处理程序。它会在从远程对等方接收到视频流后处理视频流的显示。
-
注册message处理程序。您的信令服务器还应该有一个用于处理从另一方接收到的消息的处理程序。如果消息包含RTCSessionDescription对象,应使用setRemoteDescription()方法将其添加到RTCPeerConnection对象中。如果消息包含RTCIceCandidate对象,应使用addIceCandidate()方法将其添加到RTCPeerConnection对象中。
-
使用getUserMedia()设置本地媒体流,并使用addStream()方法将其添加到RTCPeerConnection对象中。
-
开始提供/应答协商过程。呼叫方的流程与被呼叫方的流程唯一不同的步骤是:呼叫方使用createOffer()方法开始协商,并注册一个回调函数来接收RTCSessionDescription对象。然后,此回调函数应将此RTCSessionDescription对象添加到您的RTCPeerConnection对象中,使用setLocalDescription()方法。最后,呼叫方应使用信令服务器将此RTCSessionDescription发送给远程对等方。被呼叫方则使用createAnswer()方法注册相同的回调函数。请注意,只有在接收到呼叫方的提供后,被呼叫方的流程才会启动。
RTCPeerConnection API
属性
-
RTCPeerConnection.iceConnectionState (只读) − 返回一个描述连接状态的RTCIceConnectionState枚举值。当此值发生变化时,会触发一个iceconnectionstatechange事件。可能的值有:
- new − ICE代理正在等待远程候选者或收集地址
-
checking − ICE代理具有远程候选者,但尚未找到连接
-
connected − ICE代理已找到可用的连接,但仍在检查更多远程候选者以获得更好的连接
-
completed − ICE代理已找到可用的连接并停止测试远程候选者
-
失败 − ICE代理已检查所有远程候选者,但至少有一个组件找不到匹配项。
-
断开连接 − 至少有一个组件不再存活。
-
已关闭 − ICE代理已关闭。
-
RTCPeerConnection.iceGatheringState(只读) − 返回描述连接的ICE收集状态的RTCIceGatheringState枚举 −
- 新建 − 对象刚刚创建。
-
收集中 − ICE代理正在收集候选者。
-
完成 − ICE代理已完成收集。
-
RTCPeerConnection.localDescription (只读) - 返回一个描述本地会话的RTCSessionDescription。如果尚未设置,则可能为null。
-
RTCPeerConnection.peerIdentity (只读) - 返回一个RTCIdentityAssertion。它由idp(域名)和表示远程对等身份的名称组成。
-
RTCPeerConnection.remoteDescription (只读) - 返回描述远程会话的RTCSessionDescription。如果尚未设置,则可能为null。
-
RTCPeerConnection.signalingState (只读) - 返回描述本地连接的RTCSignalingState枚举,描述了信令状态。此状态描述SDP提供。当此值更改时,将触发signalingstatechange事件。可能的值为-
- stable − 初始状态。没有SDP offer/answer交换正在进行中。
-
have-local-offer − 连接的本地一侧已经应用了SDP offer。
-
have-remote-offer − 连接的远程一侧已经应用了SDP offer。
-
have-local-pranswer − 已经应用了远程SDP offer,并且本地已经应用了SDP pranswer。
-
have-remote-pranswer − 已经应用了本地SDP,并且远程已经应用了SDP pranswer。
-
closed − 连接已关闭。
事件处理程序
下面是RTCPeerConnection常用的事件处理程序。
序号 | 事件处理程序和描述 |
---|---|
1 | RTCPeerConnection.onaddstream 当addstream事件被触发时,调用此处理程序。当远端对等点将MediaStream添加到此连接时,会发送此事件。 |
2 | RTCPeerConnection.ondatachannel 当datachannel事件被触发时,调用此处理程序。当RTCDataChannel被添加到此连接时,会发送此事件。 |
3 | RTCPeerConnection.onicecandidate 当icecandidate事件被触发时,调用此处理程序。当RTCIceCandidate对象被添加到脚本中时,会发送此事件。 |
4 | RTCPeerConnection.oniceconnectionstatechange 当iceconnectionstatechange事件被触发时调用此处理程序。当iceConnectionState的值发生变化时,会发送此事件。 |
5 | RTCPeerConnection.onidentityresult 当identityresult事件被触发时调用此处理程序。在创建offer或者answer时通过getIdentityAssertion()生成身份断言时,会发送此事件。 |
6 | RTCPeerConnection.onidpassertionerror 当idpassertionerror事件被触发时调用此处理程序。当身份提供者(IdP)在生成身份断言时发现错误时,会发送此事件。 |
7 | RTCPeerConnection.onidpvalidation 当idpvalidationerror事件触发时调用此处理程序。当IdP(Identitry Provider)在验证身份断言时发现错误时,会发送此事件。 |
8 | RTCPeerConnection.onnegotiationneeded 当negotiationneeded事件触发时调用此处理程序。浏览器通过发送此事件通知将来某个时刻需要进行协商。 |
9 | RTCPeerConnection.onpeeridentity 当peeridentity事件触发时调用此处理程序。当对等体身份已经在此连接上设置和验证时,会发送此事件。 |
10 | RTCPeerConnection.onremovestream 当触发 signalingstatechange 事件时调用该处理程序。当 signalingState 的值发生变化时,将发送该事件。 |
11 | RTCPeerConnection.onsignalingstatechange 当触发 removestream 事件时调用该处理程序。当一个 MediaStream 从该连接中移除时,将发送该事件。 |
方法
下面列出了常用的RTCPeerConnection方法。
序号 | 方法与描述 |
---|---|
1 | RTCPeerConnection() 返回一个新的RTCPeerConnection对象。 |
2 | RTCPeerConnection.createOffer() 创建一个请求以找到远程对等点。此方法的前两个参数是成功和错误回调函数。可选的第三个参数是选项,如启用音频或视频流。 |
3 | RTCPeerConnection.createAnswer() 创建应答以回应在Offer/Answer协商过程中接收到的远程对等点的请求。此方法的前两个参数是成功和错误回调函数。可选的第三个参数是用于创建应答的选项。 |
4 | RTCPeerConnection.setLocalDescription() 更改本地连接描述。描述定义了连接的属性。连接必须能够支持旧和新的描述。该方法需要三个参数,RTCSessionDescription对象,如果描述更改成功的回调,如果描述更改失败的回调。 |
5 | RTCPeerConnection.setRemoteDescription() 更改远程连接描述。描述定义了连接的属性。连接必须能够支持旧和新的描述。该方法需要三个参数,RTCSessionDescription对象,如果描述更改成功的回调,如果描述更改失败的回调。 |
6 | RTCPeerConnection.updateIce() 更新 ICE 代理进程,ping 远程候选者并收集本地候选者。 |
7 | RTCPeerConnection.addIceCandidate() 向 ICE 代理提供远程候选者。 |
8 | RTCPeerConnection.getConfiguration() 返回一个 RTCConfiguration 对象,表示 RTCPeerConnection 对象的配置。 |
9 | RTCPeerConnection.getLocalStreams() 返回一个本地 MediaStream 连接的数组。 |
10 | RTCPeerConnection.getRemoteStreams() 返回一个远程 MediaStream 连接的数组。 |
11 | RTCPeerConnection.getStreamById() 返回根据给定ID的本地或远程MediaStream。 |
12 | RTCPeerConnection.addStream() 将MediaStream添加为本地的视频或音频源。 |
13 | RTCPeerConnection.removeStream() 将MediaStream从本地的视频或音频源中移除。 |
14 | RTCPeerConnection.close() 关闭连接。 |
15 | RTCPeerConnection.createDataChannel() 创建新的RTCDataChannel。 |
16 | RTCPeerConnection.createDTMFSender() 创建一个新的RTCDTMFSender,关联到特定的MediaStreamTrack。允许通过连接发送DTMF(双音多频)电话信令。 |
17 | RTCPeerConnection.getStats() 创建一个包含有关连接的统计信息的新RTCStatsReport。 |
18 | RTCPeerConnection.setIdentityProvider() 设置IdP。接受三个参数-名称、用于通信的协议和可选的用户名。 |
19 | RTCPeerConnection.getIdentityAssertion() 收集身份断言。不希望在应用程序中处理此方法,所以只有在需要提前操作时才能显式调用它。 |
建立连接
现在让我们创建一个示例应用程序。首先,通过运行“node server”命令来运行我们在“信令服务器”教程中创建的信令服务器。
页面上会有两个文本输入框,一个用于登录,一个用于连接到的用户名。创建一个 index.html 文件并添加以下代码 –
<html lang = "en">
<head>
<meta charset = "utf-8" />
</head>
<body>
<div>
<input type = "text" id = "loginInput" />
<button id = "loginBtn">Login</button>
</div>
<div>
<input type = "text" id = "otherUsernameInput" />
<button id = "connectToOtherUsernameBtn">Establish connection</button>
</div>
<script src = "client2.js"></script>
</body>
</html>
你可以看到,我们添加了用于登录的文本输入框,登录按钮,用于其他对等端用户名的文本输入框和连接至他的按钮。现在创建一个client.js文件,并添加以下代码 –
var connection = new WebSocket('ws://localhost:9090');
var name = "";
var loginInput = document.querySelector('#loginInput');
var loginBtn = document.querySelector('#loginBtn');
var otherUsernameInput = document.querySelector('#otherUsernameInput');
var connectToOtherUsernameBtn = document.querySelector('#connectToOtherUsernameBtn');
var connectedUser, myConnection;
//when a user clicks the login button
loginBtn.addEventListener("click", function(event){
name = loginInput.value;
if(name.length > 0){
send({
type: "login",
name: name
});
}
});
//handle messages from the server
connection.onmessage = function (message) {
console.log("Got message", message.data);
var data = JSON.parse(message.data);
switch(data.type) {
case "login":
onLogin(data.success);
break;
case "offer":
onOffer(data.offer, data.name);
break;
case "answer":
onAnswer(data.answer);
break;
case "candidate":
onCandidate(data.candidate);
break;
default:
break;
}
};
//when a user logs in
function onLogin(success) {
if (success === false) {
alert("oops...try a different username");
} else {
//creating our RTCPeerConnection object
var configuration = {
"iceServers": [{ "url": "stun:stun.1.google.com:19302" }]
};
myConnection = new webkitRTCPeerConnection(configuration);
console.log("RTCPeerConnection object was created");
console.log(myConnection);
//setup ice handling
//when the browser finds an ice candidate we send it to another peer
myConnection.onicecandidate = function (event) {
if (event.candidate) {
send({
type: "candidate",
candidate: event.candidate
});
}
};
}
};
connection.onopen = function () {
console.log("Connected");
};
connection.onerror = function (err) {
console.log("Got error", err);
};
// Alias for sending messages in JSON format
function send(message) {
if (connectedUser) {
message.name = connectedUser;
}
connection.send(JSON.stringify(message));
};
你可以看到我们建立了一个与信令服务器的套接字连接。当用户单击登录按钮时,应用程序将他的用户名发送到服务器。如果登录成功,应用程序将创建RTCPeerConnection对象,并设置onicecandidate处理程序,该处理程序将所有找到的ice候选项发送给其他对等体。现在打开页面并尝试登录。你应该能看到以下控制台输出 –
下一步是向对方提供一个请求。将以下代码添加到您的client.js文件中−
//setup a peer connection with another user
connectToOtherUsernameBtn.addEventListener("click", function () {
var otherUsername = otherUsernameInput.value;
connectedUser = otherUsername;
if (otherUsername.length > 0) {
//make an offer
myConnection.createOffer(function (offer) {
console.log();
send({
type: "offer",
offer: offer
});
myConnection.setLocalDescription(offer);
}, function (error) {
alert("An error has occurred.");
});
}
});
//when somebody wants to call us
function onOffer(offer, name) {
connectedUser = name;
myConnection.setRemoteDescription(new RTCSessionDescription(offer));
myConnection.createAnswer(function (answer) {
myConnection.setLocalDescription(answer);
send({
type: "answer",
answer: answer
});
}, function (error) {
alert("oops...error");
});
}
//when another user answers to our offer
function onAnswer(answer) {
myConnection.setRemoteDescription(new RTCSessionDescription(answer));
}
//when we got ice candidate from another user
function onCandidate(candidate) {
myConnection.addIceCandidate(new RTCIceCandidate(candidate));
}
您可以看到,当用户点击“建立连接”按钮时,应用程序会向另一个对等点发出SDP提议。我们还设置了 onAnswer 和 onCandidate 处理程序。重新加载页面,在两个标签页中打开它,使用两个用户登录,并尝试在它们之间建立连接。您应该会看到以下控制台输出 –
现在P2P(点对点)连接已建立。在接下来的教程中,我们将添加视频和音频流,以及文本聊天支持。