访问量: 10 次浏览
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 对象中。RTCPeerConnection.iceConnectionState (只读)
− 返回一个描述连接状态的 RTCIceConnectionState 枚举值。当此值发生变化时,会触发一个 iceconnectionstatechange 事件。可能的值有:
new
− ICE代理正在等待远程候选者或收集地址
RTCPeerConnection.iceGatheringState(只读)
− 返回描述连接的ICE收集状态的 RTCIceGatheringState 枚举 −
新建
− 对象刚刚创建。
RTCSessionDescription。如果尚未设置,则可能为null。RTCIdentityAssertion。它由idp(域名)和表示远程对等身份的名称组成。RTCSessionDescription。如果尚未设置,则可能为null.返回描述本地连接的 RTCSignalingState 枚举,描述了信令状态。此状态描述SDP提供。当此值更改时,将触发 signalingstatechange 事件。可能的值为-
stable
− 初始状态。没有SDP offer/answer交换正在进行中。
下面是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(点对点)连接已建立。在接下来的教程中,我们将添加视频和音频流,以及文本聊天支持。