WebRTC - RTCPeerConnection API



RTCPeerConnection API 是浏览器之间点对点连接的核心。要创建 RTCPeerConnection 对象,只需编写以下代码:

var pc = RTCPeerConnection(config);

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

根据您是呼叫方还是被呼叫方,RTCPeerConnection 对象在连接的每一侧的使用方式略有不同。

以下是用戶流程示例:

  • 注册 onicecandidate 处理程序。它在收到任何 ICE 候选者时将其发送到另一个对等方。

  • 注册 onaddstream 处理程序。它在从远程对等方收到视频流后处理视频流的显示。

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

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

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

RTCPeerConnection API

属性

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

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

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

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

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

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

    • disconnected - 至少一个组件不再处于活动状态。

    • closed - ICE 代理已关闭。

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

    • new - 对象刚刚创建。

    • gathering - ICE 代理正在收集候选者

    • complete ICE 代理已完成收集。

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

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

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

  • RTCPeerConnection.signalingState (只读) - 返回一个 RTCSignalingState 枚举,描述本地连接的信令状态。此状态描述 SDP offer。当此值更改时,会触发 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 - 连接已关闭。

事件处理程序

序号 事件处理程序 & 描述
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(身份提供者)在验证身份断言时发现错误时,会发送此事件。

8

RTCPeerConnection.onnegotiationneeded

当触发 negotiationneeded 事件时,将调用此处理程序。浏览器发送此事件以通知将来某个时间点需要协商。

9

RTCPeerConnection.onpeeridentity

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

10

RTCPeerConnection.onremovestream

当触发 signalingstatechange 事件时,将调用此处理程序。当 signalingState 的值更改时,会发送此事件。

11

RTCPeerConnection.onsignalingstatechange

当触发 removestream 事件时,将调用此处理程序。当 MediaStream 从此连接中移除时,会发送此事件。

方法

序号 方法 & 描述
1

RTCPeerConnection()

返回一个新的 RTCPeerConnection 对象。

2

RTCPeerConnection.createOffer()

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

3

RTCPeerConnection.createAnswer()

在 offer/answer 协商过程中创建对远程对等方收到的 offer 的答复。此方法的前两个参数是成功和错误回调函数。可选的第三个参数是创建答复的选项。

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://127.0.0.1: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发送到另一个对等方。现在打开页面并尝试登录。您应该会看到以下控制台输出 -

Establishing a Connection

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

Console Output

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

广告