WebRTC - RTCPeerConnection API

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

var pc = RTCPeerConnection(config);

其中 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 事件。可能的值 −

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

    • 正在检查 − ICE 代理有远程候选,但尚未找到连接

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

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

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

    • 已断开连接 − 至少一个组件不再处于活动状态。

    • 已关闭 − ICE 代理已关闭。

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

    • new − 对象刚刚创建。

    • gathering − ICE 代理正在收集候选对象

    • complete ICE 代理已完成收集。

  • RTCPeerConnection.localDescription(只读) − 返回一个描述本地会话的 RTCSessionDescription。如果尚未设置,则可能为空。

  • RTCPeerConnection.peerIdentity(只读) −返回一个 RTCIdentityAssertion。它由一个 idp(域名)和一个表示远程对等方身份的名称组成。

  • RTCPeerConnection.remoteDescription(只读) − 返回一个描述远程会话的 RTCSessionDescription。如果尚未设置,则可能为空。

  • RTCPeerConnection.signalingState(只读) − 返回一个描述本地连接信令状态的 RTCSignalingState 枚举。此状态描述 SDP 提议。当此值发生变化时,将触发 signalingstatechange 事件。可能的值 −

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

    • have-local-offer −连接的本地端已在本地应用了 SDP 提议。

    • have-remote-offer − 连接的远程端已在本地应用了 SDP 提议。

    • have-local-pranswer − 已应用远程 SDP 提议,并在本地应用了 SDP pranswer。

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

    • closed −连接已关闭。

事件处理程序

S.No. 事件处理程序和说明
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 时,将发送此事件。

方法

S.No. 方法和说明
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()

创建一个新的 RTCDTMFSender,与特定的 MediaStreamTrack 关联。允许通过连接发送 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;
  
//当用户点击登录按钮时
loginBtn.addEventListener("click", function(event){ 
   name = loginInput.value; 
	
   if(name.length > 0){ 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
  
//处理来自服务器的消息 
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; 
   } 
};
  
//当用户登录时
function onLogin(success) { 

   if (success === false) { 
      alert("oops...try a different username"); 
   } else { 
      //创建我们的 RTCPeerConnection 对象
		
      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); 
};
  
// 以 JSON 格式发送消息的别名
function send(message) { 

   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   connection.send(JSON.stringify(message)); 
};

您可以看到我们建立了与信令服务器的套接字连接。当用户单击登录按钮时,应用程序会将其用户名发送到服务器。如果登录成功,应用程序将创建 RTCPeerConnection 对象并设置 onicecandidate 处理程序,该处理程序将所有找到的 icecandidate 发送给另一个对等方。现在打开页面并尝试登录。您应该看到以下控制台输出 −

建立连接

下一步是向另一个对等方创建要约。将以下代码添加到您的 client.js 文件 −

//与另一个用户建立对等连接
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."); 
      }); 
   } 
}); 
 
//当有人想打电话给我们时
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"); 
   }); 
}
  
//当另一个用户回答我们的提议时
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 处理程序。重新加载页面,在两个选项卡中打开它,使用两个用户登录并尝试在它们之间建立连接。您应该看到以下控制台输出 −

控制台输出

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