O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Primeiros passos com conexões de mesmo nível

As conexões ponto a ponto são a parte das especificações WebRTC que trata da conexão de dois aplicativos em computadores diferentes para se comunicarem usando um protocolo ponto a ponto. A comunicação entre os pares podem ser de vídeo, dados binários de áudio ou arbitrária (para clientes que apoiam o RTCDataChannel API). Para descobrir como dois pares podem se conectar, ambos os clientes precisam fornecer uma configuração do ICE Server. Este é um servidor STUN ou TURN, e sua função é fornecer candidatos ICE a cada cliente, que é então transferido para o par remoto. Essa transferência de candidatos ICE é comumente chamada de sinalização.

Sinalização

A especificação WebRTC inclui APIs para comunicação com um servidor ICE (Internet Connectivity Establishment), mas o componente de sinalização não faz parte dele. A sinalização é necessária para que dois pontos compartilhem como devem se conectar. Normalmente, isso é resolvido por meio de uma API da Web baseada em HTTP regular (ou seja, um serviço REST ou outro mecanismo RPC), onde os aplicativos da Web podem retransmitir as informações necessárias antes que a conexão de mesmo nível seja iniciada.

O trecho de código a seguir mostra como esse serviço de sinalização fictício pode ser usado para enviar e receber mensagens de forma assíncrona. Isso será usado nos exemplos restantes neste guia, quando necessário.

// Set up an asynchronous communication channel that will be
// used during the peer connection setup
const signalingChannel = new SignalingChannel(remoteClientId);
signalingChannel.addEventListener('message', message => {
    // New message from remote client received
});

// Send an asynchronous message to the remote client
signalingChannel.send('Hello!');

A sinalização pode ser implementada de muitas maneiras diferentes e a especificação WebRTC não dá preferência a nenhuma solução específica.

Iniciando conexões de pares

Cada conexão ponto é tratado por um RTCPeerConnection objeto. O construtor para esta classe tem um único RTCConfiguration objeto como parâmetro. Este objeto define como a conexão de mesmo nível é configurada e deve conter informações sobre os servidores ICE a serem usados.

Uma vez que o RTCPeerConnection é criado precisamos criar uma oferta SDP ou resposta, dependendo se estamos a ponto de chamada ou recepção de pares. Depois que a oferta ou resposta SDP é criada, ela deve ser enviada ao peer remoto por meio de um canal diferente. A passagem de objetos SDP para pontos remotos é chamada de sinalização e não é coberta pela especificação WebRTC.

Para iniciar a configuração da conexão de pares do lado chamando, criamos um RTCPeerConnection objeto e, em seguida, chamar createOffer() para criar uma RTCSessionDescription objeto. Esta sessão descrição é definida como a descrição local usando setLocalDescription() e é então enviado através de nosso canal de sinalização para o lado de recebimento. Também configuramos um ouvinte para nosso canal de sinalização para quando uma resposta à nossa descrição de sessão oferecida for recebida do lado receptor.

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});
}

No lado receptor, esperamos por uma oferta de entrada antes de criar o nosso RTCPeerConnection instância. Uma vez feito isto vamos definir a oferta recebida usando setRemoteDescription() . Em seguida, chamamos createAnswer() para criar uma resposta para a proposta recebida. Essa resposta é definido como a descrição local usando setLocalDescription() e, em seguida, enviado para o lado chamando sobre o nosso servidor de sinalização.

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});
    }
});

Depois que os dois pares tiverem definido as descrições da sessão local e remota, eles conhecerão os recursos do par remoto. Isso não significa que a conexão entre os pares esteja pronta. Para que isso funcione, precisamos coletar os candidatos ICE em cada par e transferir (pelo canal de sinalização) para o outro par.

Candidatos ICE

Antes que dois pares possam se comunicar usando WebRTC, eles precisam trocar informações de conectividade. Uma vez que as condições da rede podem variar dependendo de vários fatores, um serviço externo é geralmente usado para descobrir os possíveis candidatos para se conectar a um par. Este serviço é denominado ICE e está usando um servidor STUN ou TURN. STUN significa Session Traversal Utilities for NAT e geralmente é usado indiretamente na maioria dos aplicativos WebRTC.

TURN (Traversal Using Relay NAT) é a solução mais avançada que incorpora os protocolos STUN e a maioria dos serviços comerciais baseados em WebRTC usam um servidor TURN para estabelecer conexões entre pares. A API WebRTC suporta STUN e TURN diretamente e é reunida sob o termo mais completo Estabelecimento de Conectividade com a Internet. Ao criar uma conexão WebRTC, que geralmente oferecem um ou vários servidores ICE na configuração para o RTCPeerConnection objeto.

Trickle ICE

Uma vez que um RTCPeerConnection objeto é criado, a estrutura subjacente usa os servidores ICE previstos para reunir candidatos para o estabelecimento de conectividade (candidatos ICE). O evento icegatheringstatechange em RTCPeerConnection sinais em que estado o encontro ICE é ( new , gathering ou complete ).

Embora seja possível para um par esperar até que a coleta de ICE seja concluída, geralmente é muito mais eficiente usar uma técnica de "gelo residual" e transmitir cada candidato ICE ao par remoto à medida que é descoberto. Isso reduzirá significativamente o tempo de configuração da conectividade de pares e permitirá que uma chamada de vídeo seja iniciada com menos atrasos.

Para reunir candidatos ICE, basta adicionar um ouvinte para o icecandidate evento. O RTCPeerConnectionIceEvent emitida em que ouvinte irá conter candidate propriedade que representa um novo candidato que deve ser enviado para o ponto remoto (consulte Signaling).

// Listen for local ICE candidates on the local RTCPeerConnection
peerConnection.addEventListener('icecandidate', event => {
    if (event.candidate) {
        signalingChannel.send({'new-ice-candidate': event.candidate});
    }
});

// Listen for remote ICE candidates and add them to the local 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);
        }
    }
});

Conexão estabelecida

Assim que os candidatos ICE estiverem sendo recebidos, devemos esperar que o estado de nossa conexão de mesmo nível mude para um estado conectado. Para detectar isso, adicionar um ouvinte para o nosso RTCPeerConnection onde nós ouvir connectionstatechange eventos.

// Listen for connectionstatechange on the local RTCPeerConnection
peerConnection.addEventListener('connectionstatechange', event => {
    if (peerConnection.connectionState === 'connected') {
        // Peers connected!
    }
});

Documentação da API RTCPeerConnection