WebRTC - RTCPeerConnection API

  • 简述

    RTCPeerConnection API 是每个浏览器之间点对点连接的核心。要创建 RTCPeerConnection 对象,只需编写
    
    var pc = RTCPeerConnection(config);
    
    其中config参数至少包含一个键,iceServers。它是一组 URL 对象,其中包含有关 STUN 和 TURN 服务器的信息,在寻找 ICE 候选者期间使用。您可以在code.google.com找到可用的公共 STUN 服务器列表
    根据您是调用者还是被调用者,RTCPeerConnection 对象在连接的每一端的使用方式略有不同。
    这是用户流程的示例 -
    • 注册onececandidate处理程序。它将任何 ICE 候选者发送给其他对等点,因为它们被接收到。
    • 注册onaddstream处理程序。一旦从远程对等方接收到视频流,它就会处理视频流的显示。
    • 注册消息处理程序。您的信令服务器还应该有一个处理程序来处理从其他对等方接收到的消息。如果消息包含RTCSessionDescription对象,则应使用setRemoteDescription()方法将其添加到RTCPeerConnection对象。如果消息包含RTCIceCandidate对象,则应使用addIceCandidate()方法将其添加到RTCPeerConnection对象。
    • 利用getUserMedia()设置本地媒体流,并使用addStream()方法将其添加到RTCPeerConnection对象。
    • 开始报价/答复协商过程。这是调用者的流程与被调用者的流程不同的唯一步骤。调用者使用createOffer()方法开始协商并注册一个接收RTCSessionDescription对象的回调。然后此回调应使用setLocalDescription()将此RTCSessionDescription对象添加到您的RTCPeerConnection对象。最后,调用者应该使用信令服务器将此RTCSessionDescription发送到远程对等点。另一方面,被调用者注册了相同的回调,但在createAnswer()方法。请注意,仅在收到来自调用者的报价后才会启动被调用者流程。
  • RTCPeerConnection API

    特性

    • RTCPeerConnection.iceConnectionState (read only) - 返回描述连接状态的 RTCIceConnectionState 枚举。当这个值改变时会触发一个 iceconnectionstatechange 事件。可能的值 -
      • new- ICE 代理正在等待远程候选人或收集地址
      • checking- ICE 代理有远程候选者,但尚未找到连接
      • connected- ICE 代理已找到可用连接,但仍在检查更多远程候选连接以获得更好的连接。
      • completed- ICE 代理已找到可用连接并停止测试远程候选者。
      • failed - ICE 代理检查了所有远程候选,但没有找到至少一个组件的匹配项。
      • disconnected - 至少一个组件不再存在。
      • closed- ICE 代理已关闭。
    • RTCPeerConnection.iceGatheringState(只读) - 返回描述连接的 ICE 收集状态的 RTCIceGatheringState 枚举 -
      • new - 刚刚创建的对象。
      • gathering- ICE 代理人正在收集候选人
      • completeICE 代理已完成收集。
    • RTCPeerConnection.localDescription (read only) - 返回描述本地会话的 RTCSessionDescription。如果尚未设置,则可以为 null。
    • RTCPeerConnection.peerIdentity (read only) - 返回一个 RTCIdentityAssertion。它由一个 idp(域名)和一个代表远程对等方身份的名称组成。
    • RTCPeerConnection.remoteDescription (read only) - 返回描述远程会话的 RTCSessionDescription。如果尚未设置,则可以为 null。
    • RTCPeerConnection.signalingState (read only) - 返回描述本地连接信号状态的 RTCSignalingState 枚举。此状态描述 SDP 报价。当这个值改变时会触发一个信号状态改变事件。可能的值 -
      • stable - 初始状态。没有正在进行的 SDP 提议/答案交换。
      • have-local-offer - 连接的本地端已在本地应用 SDP 报价。
      • have-remote-offer - 连接的远程端已在本地应用 SDP 报价。
      • have-local-pranswer - 已应用远程 SDP 报价,并在本地应用 SDP pranswer。
      • have-remote-pranswer - 已应用本地 SDP,并且已远程应用 SDP pranswer。
      • closed- 连接关闭。

    事件处理程序

    序号 事件处理程序和描述
    1
    RTCPeerConnection.onaddstream
    在触发 addstream 事件时调用此处理程序。当远程对等点将 MediaStream 添加到此连接时,将发送此事件。
    2
    RTCPeerConnection.ondatachannel
    当触发数据通道事件时调用此处理程序。将 RTCDataChannel 添加到此连接时发送此事件。
    3
    RTCPeerConnection.oniceccandidate
    当 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 事件被触发时,这个处理程序被调用。当signingingState 的值发生变化时发送此事件。
    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); 
    };
      
    // Mooas for sending messages in JSON format 
    function send(message) { 
       if (connectedUser) { 
          message.name = connectedUser; 
       } 
         
       connection.send(JSON.stringify(message)); 
    };
    
    您可以看到我们建立了与信号服务器的套接字连接。当用户单击登录按钮时,应用程序将他的用户名发送到服务器。如果登录成功,应用程序创建 RTCPeerConnection 对象并设置 onicecandidate 处理程序,它将所有找到的icecandidates 发送到其他对等方。现在打开页面并尝试登录。您应该看到以下控制台输出 -
    建立连接
    下一步是为其他对等方创建报价。将以下代码添加到您的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处理程序。重新加载您的页面,在两个选项卡中打开它,使用两个用户登录并尝试在他们之间建立连接。您应该看到以下控制台输出 -
    控制台输出
    现在建立了对等连接。在接下来的教程中,我们将添加视频和音频流以及文本聊天支持。