WebRTC RTCPeerConnection:新视频通话应用中对等连接的秘密

WebRTC RTCPeerConnection 是一种 API,用于连接不同计算机上的两个应用程序,使用点对点协议进行通信。对等方之间的通信可以是视频、音频或任意二进制数据(对于支持 RTCD DataChannel API 的客户端)。

为了发现两个对等设备之间的连接方式,两个客户端都需要连接到共同的信令服务器,并提供 ICE 服务器配置。ICE 服务器可以是 STUN 或 TURN 服务器,其作用是向每个客户端提供 ICE 候选配置,然后将其传输给远程对等设备。这种候选 ICE 的传输通常称为信令。所有这些术语在开始时可能听起来很陌生,但这正是仅使用浏览器就能在两台电脑间成功连接视频通话的秘诀所在。

为了让两个对等设备共享连接方式,需要发出信令。通常,这是通过基于 HTTP 的常规Web API(即 REST 服务或其他 RPC 机制,如websocket)来解决的,Web 应用程序可以在启动对等连接之前转发必要的信息。信令可以通过多种不同方式实现,WebRTC 规范并不偏好任何特定的解决方案。

启动对等连接

RTCPeerConnection API 负责通过实例化 RTCPeerConnection 对象来创建 RTCPeerConnection 对象,如下文代码片段所述。该类的构造函数将一个 RTCConfiguration 对象作为参数。该对象定义了如何设置对等连接,并应包含要使用的 ICE 服务器的相关信息。

创建 RTCPeerConnection 后,我们需要创建 SDP 要约或应答,具体取决于我们是呼叫对等方还是接收对等方。一旦创建了 SDP 要约或应答,就必须通过不同的通道将其发送给远程对等设备。将 SDP 对象传递给远程对等设备被称为信令(signalling),这在 WebRTC 规范中并没有明确规定。

为了从调用方启动对等连接设置,我们先创建一个 RTCPeerConnection 对象,然后调用 createOffer() 创建一个 RTCSessionDescription 对象。使用 setLocalDescription() 将此会话描述设置为本地描述,然后通过我们的信令通道发送到接收端。我们还为信令信道设置了一个监听器,以便接收端收到对我们提供的会话描述的应答。

简单的信令服务器

// 设置一个异步通信通道,该通道将在对等连接设置期间使用
const signalingChannel = new SignalingChannel(remoteClientId); 
signalingChannel.addEventListener('message', message => { 
    // 收到来自远程客户端的新消息
});
// 向远程客户端发送异步消息
signalingChannel.send('Hello!');

从浏览器 A 发起呼叫

async function makeCall() {
    const configuration {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
    const peerConnection = new RTCPeerConnection(configuration);
    signalingChannel.addEventListener('message', async message => {
        if (message.answer) {
            const remoteDesc = new RTCSessionDescription(message.answer);
            await peerConnection.setRemoteDescription(remoteDesc);
        }
    });
    const offer = await peerConnection.createOffer();
    await peerConnection.setLocalDescription(offer);
    signalingChannel.send({'offer': offer});
}

在接收端,我们在创建 RTCPeerConnection 实例之前会等待接收到要约。创建完成后,我们使用 setRemoteDescription() 设置接收到的要约。接下来,我们调用 createAnswer() 来为收到的要约创建一个应答。使用 setLocalDescription() 将此应答设置为本地描述,然后通过我们的信令服务器发送给呼叫方。

const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
    if (message.offer) {
        peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);
        signalingChannel.send({'answer': answer});
    }
});

一旦两个对等端设置了本地和远程会话描述,它们就会知道各自的远程对等端功能。这并不意味着对等设备之间的连接已经建立。为此,我们需要在每个对等端收集 ICE Candidates ,并(通过信令信道)传输给另一个对等端,以建立它们之间的连接。

ICE Candidates

ICE 指互联网连接建立。在两个对等点使用 WebRTC 通信之前,它们需要交换连接信息。由于网络条件会因多种因素而变化,因此通常使用外部服务来发现可能与对等端连接的候选对象。这种服务称为 ICE,使用 STUN 或 TURN 服务器。STUN 是用户数据报协议会话遍历的缩写,通常在大多数 WebRTC 应用中间接使用。

TURN(使用中继 NAT 进行遍历)是更先进的解决方案,它结合了 STUN 协议,大多数基于 WebRTC 的商业服务都使用 TURN 服务器在对等端之间建立连接。WebRTC API 直接支持 STUN 和 TURN,它被归类为更完整的术语“Internet 连接建立”。创建 WebRTC 连接时,我们通常在 RTCPeerConnection 对象的配置中提供一个或多个 ICE 服务器。

Trickle ICE

Trickle ICE 是一种用于缩短两个对等设备之间呼叫建立时间的技术。一旦创建了 RTCPeerConnection 对象,底层框架就会使用所提供的 ICE 服务器收集候选对象,以便根据 ICE 候选对象建立连接。RTCPeerConnection 上的事件 icegatheringstatechange 表明 ICE 收集处于何种状态(新收集、正在收集或已完成)。

虽然对等设备可以等待 ICE 采集完成,但使用这种技术并在发现每个 ICE 候选设备时将其传输给远程对等设备通常会更有效。这大大减少了对等连接的设置时间,使视频通话的开始延迟更少。

要收集候选 ICE,只需为 icecandidate 事件添加一个监听器。该监听器发出的 RTCPeerConnectionIceEvent 将包含候选属性,该属性代表应使用上述信令机制发送给远程对等设备的新候选设备。

// 在本地 RTCPeerConnection 上监听本地 ICE 候选
peerConnection.addEventListener( 'icecandidate' , event => { 
    if ( event .candidate) { 
        signalingChannel.send({ 'new-ice-candidate' : event .candidate}); 
    } 
});
// 监听远程 ICE 候选并将其添加到本地 RTCPeerConnection 
signalingChannel.addEventListener('message', async message => { 
    if (message.iceCandidate) { 
        try { 
            await peerConnection.addIceCandidate(message.iceCandidate); 
        } catch (e) { 
            console.error('Error adding received ice candidate', e); 
        } 
    } 
});

一旦接收到 ICE candidates,我们就可以预期对等连接的状态最终会变为连接状态。为了检测这一点,我们在 RTCPeerConnection 中添加了一个监听器,用于监听连接状态变化事件。

// 在本地 RTCPeerConnection 上监听连接状态改变
peerConnection.addEventListener( 'connectionstatechange' , event => { 
    if (peerConnection.connectionState === 'connected' ) { 
        // 对等体已连接!
     } 
});

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/49763.html

(1)

相关推荐

发表回复

登录后才能评论