WebRTC - RTCPeerConnection API


RTCPeerConnection API 是每个浏览器之间点对点连接的核心。要创建 RTCPeerConnection 对象,只需编写

var pc = RTCPeerConnection(config);

其中配置参数至少包含一个键,iceServers。它是一个 URL 对象数组,包含有关 STUN 和 TURN 服务器的信息,在查找 ICE 候选者期间使用。您可以在code.google.com上找到可用公共 STUN 服务器的列表

根据您是调用者还是被调用者,RTCPeerConnection 对象在连接两端的使用方式略有不同。

这是用户流程的示例 -

  • 注册onicecandidate处理程序。它会在收到任何 ICE 候选者后将其发送给其他对等方。

  • 注册onaddstream处理程序。一旦从远程对等点接收到视频流,它就会处理视频流的显示。

  • 注册消息处理程序。您的信令服务器还应该有一个处理程序,用于处理从其他对等方收到的消息。如果消息包含RTCSessionDescription对象,则应使用setRemoteDescription()方法将其添加到RTCPeerConnection对象。如果消息包含RTCIceCandidate对象,则应使用addIceCandidate()方法将其添加到RTCPeerConnection对象。

  • 利用getUserMedia()设置本地媒体流并使用addStream()方法将其添加到RTCPeerConnection对象。

  • 开始报价/答复谈判流程。这是调用者流程与被调用者流程不同的唯一步骤。调用者使用createOffer()方法开始协商,并注册接收RTCSessionDescription对象的回调。然后,此回调应使用setLocalDescription()将此RTCSessionDescription对象添加到您的RTCPeerConnection对象。最后,调用者应使用信令服务器将此RTCSessionDescription发送到远程对等点。另一方面,被调用者注册相同的回调,但在createAnswer()方法中。请注意,只有在收到呼叫者的报价后,才会启动被呼叫者流程。

RTCPeerConnection API

特性

  • RTCPeerConnection.iceConnectionState(只读) - 返回描述连接状态的 RTCIceConnectionState 枚举。当该值更改时,将触发iceconnectionstatechange 事件。可能的值 -

    • - ICE 代理正在等待远程候选人或收集地址

    • 检查- ICE 代理有远程候选者,但尚未找到连接

    • 已连接- ICE 代理已找到可用的连接,但仍在检查更远程的候选连接以获得更好的连接。

    • 已完成- ICE 代理已找到可用的连接并停止测试远程候选者。

    • 失败- ICE 代理已检查所有远程候选者,但未找到至少一个组件的匹配项。

    • 断开连接- 至少一个组件不再存在。

    • 关闭- ICE 代理关闭。

  • RTCPeerConnection.iceGatheringState(只读) - 返回一个 RTCIceGatheringState 枚举,描述连接的 ICE 收集状态 -

    • new - 对象刚刚创建。

    • 收集- ICE 代理人正在收集候选人

    • 完成ICE 代理已完成收集。

  • RTCPeerConnection.localDescription(只读) - 返回描述本地会话的 RTCSessionDescription。如果尚未设置,则可以为 null。

  • RTCPeerConnection.peerIdentity(只读) - 返回 RTCIdentityAssertion。它由 idp(域名)和代表远程对等点身份的名称组成。

  • RTCPeerConnection.remoteDescription(只读) - 返回描述远程会话的 RTCSessionDescription。如果尚未设置,则可以为 null。

  • RTCPeerConnection.signalingState(只读) - 返回描述本地连接的信令状态的 RTCSignalingState 枚举。该状态描述了 SDP 提议。当该值更改时,将触发signalingstatechange 事件。可能的值 -

    • 稳定- 初始状态。没有正在进行的 SDP 提议/应答交换。

    • have-local-offer - 连接的本地端已在本地应用 SDP Offer。

    • have-remote-offer - 连接的远程端已在本地应用 SDP Offer。

    • have-local-pranswer - 已应用远程 SDP Offer,并在本地应用 SDP pranswer。

    • have-remote-pranswer - 已应用本地 SDP,并远程应用 SDP pranswer。

    • 已关闭- 连接已关闭。

事件处理程序

编号 事件处理程序和描述
1

RTCPeerConnection.onaddstream

当 addstream 事件被触发时,这个处理程序被调用。当远程对等方将 MediaStream 添加到此连接时,将发送此事件。

2

RTCPeerConnection.ondatachannel

当 datachannel 事件被触发时,这个处理程序被调用。当 RTCDataChannel 添加到此连接时发送此事件。

3

RTCPeerConnection.onicecandidate

当icecandidate事件被触发时,这个处理程序被调用。当 RTCIceCandidate 对象添加到脚本时发送此事件。

4

RTCPeerConnection.oniceconnectionstatechange

当iceconnectionstatechange事件被触发时,这个处理程序被调用。当iceConnectionState的值改变时发送该事件。

5

RTCPeerConnection.onidentityresult

当 IdentityResult 事件被触发时,将调用此处理程序。在通过 getIdentityAssertion() 创建报价或应答期间生成身份断言时,会发送此事件。

6

RTCPeerConnection.onidpassertionerror

当 idpassertionerror 事件被触发时,将调用此处理程序。当 IdP(身份提供商)在生成身份断言时发现错误时发送此事件。

7

RTCPeerConnection.onidpvalidation

当 idpvalidationerror 事件被触发时,将调用此处理程序。当 IdP(身份提供商)在验证身份断言时发现错误时发送此事件。

8

RTCPeerConnection.onnegotiationneeded

当 Negotiationneeded 事件被触发时,将调用此处理程序。该事件由浏览器发送,通知将来某个时刻需要进行协商。

9

RTCPeerConnection.onpeeridentity

当peeridentity事件被触发时,这个处理程序被调用。当在此连接上设置并验证对等身份时发送此事件。

10

RTCPeerConnection.onremovestream

当signalingstatechange事件被触发时,这个处理程序被调用。当signalingState的值改变时发送该事件。

11

RTCPeerConnection.onsignalingstatechange

当removestream 事件被触发时,这个处理程序被调用。当 MediaStream 从此连接中删除时发送此事件。

方法

编号 方法与说明
1

RTCPeerConnection()

返回一个新的 RTCPeerConnection 对象。

2

RTCPeerConnection.createOffer()

创建一个要约(请求)以查找远程对等点。该方法的前两个参数是成功回调和错误回调。可选的第三个参数是选项,例如启用音频或视频流。

3

RTCPeerConnection.createAnswer()

在要约/应答协商过程中创建对远程对等方收到的要约的应答。该方法的前两个参数是成功回调和错误回调。可选的第三个参数是要创建的答案的选项。

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()

创建一个与特定 MediaStreamTrack 关联的新 RTCDTMFSender。允许通过连接发送 DTMF(双音多频)电话信令。

17 号

RTCPeerConnection.getStats()

创建一个新的 RTCStatsReport,其中包含有关连接的统计信息。

18

RTCPeerConnection.setIdentityProvider()

设置 IdP。采用三个参数 - 名称、用于通信的协议和可选的用户名。

19

RTCPeerConnection.getIdentityAssertion()

收集身份断言。预计应用程序中不会处理此方法。因此,您可以明确地调用它,只是为了预测需要。

建立连接

现在让我们创建一个示例应用程序。首先,通过“节点服务器”运行我们在“信令服务器”教程中创建的信令服务器。

页面上将有两个文本输入,一个用于登录,一个用于我们要连接的用户名。创建一个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 处理程序,该处理程序将所有找到的icecandidate 发送到其他对等方。现在打开页面并尝试登录。您应该看到以下控制台输出 -

建立连接

下一步是向其他同行创建报价。将以下代码添加到您的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 提议。我们还设置了onAnsweronCandidate处理程序。重新加载您的页面,在两个选项卡中打开它,使用两个用户登录并尝试在他们之间建立连接。您应该看到以下控制台输出 -

控制台输出

现在点对点连接已建立。在接下来的教程中,我们将添加视频和音频流以及文本聊天支持。