En Google, luchamos por la equidad racial de la comunidad negra. Más información

Comenzando con las conexiones entre pares

Las conexiones entre pares es la parte de las especificaciones de WebRTC que se ocupa de conectar dos aplicaciones en diferentes computadoras para comunicarse mediante un protocolo de igual a igual. La comunicación entre pares puede ser de vídeo, datos binarios de audio o arbitraria (para los clientes que apoyan la RTCDataChannel API). Para descubrir cómo se pueden conectar dos pares, ambos clientes deben proporcionar una configuración de servidor ICE. Este es un servidor STUN o TURN, y su función es proporcionar candidatos ICE a cada cliente que luego se transfieren al par remoto. Esta transferencia de candidatos ICE se denomina comúnmente señalización.

Señalización

La especificación WebRTC incluye API para comunicarse con un servidor ICE (establecimiento de conectividad a Internet), pero el componente de señalización no forma parte de él. La señalización es necesaria para que dos pares compartan cómo deben conectarse. Por lo general, esto se resuelve a través de una API web basada en HTTP (es decir, un servicio REST u otro mecanismo RPC) donde las aplicaciones web pueden transmitir la información necesaria antes de que se inicie la conexión entre pares.

El siguiente fragmento de código muestra cómo este servicio de señalización ficticio se puede utilizar para enviar y recibir mensajes de forma asincrónica. Esto se utilizará en los ejemplos restantes de esta guía cuando sea necesario.

// 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!');

La señalización se puede implementar de muchas formas diferentes y la especificación WebRTC no prefiere ninguna solución específica.

Iniciar conexiones entre pares

Cada conexión entre pares es manejado por un RTCPeerConnection objeto. El constructor de esta clase toma un único RTCConfiguration objeto como su parámetro. Este objeto define cómo se configura la conexión entre pares y debe contener información sobre los servidores ICE que se utilizarán.

Una vez que el RTCPeerConnection se crea que necesitamos para crear una oferta SDP o respuesta, dependiendo de si estamos llamando el par o la recepción de pares. Una vez que se crea la oferta o respuesta SDP, se debe enviar al par remoto a través de un canal diferente. Pasar objetos SDP a pares remotos se denomina señalización y no está cubierto por la especificación WebRTC.

Para iniciar el establecimiento de la comunicación entre pares desde el lado llamante, creamos un RTCPeerConnection objeto y luego llamamos createOffer() para crear un RTCSessionDescription objeto. Esta descripción de la sesión se establece como la descripción local utilizando setLocalDescription() y después se envía a través de nuestro canal de señalización al lado de recepción. También configuramos un oyente para nuestro canal de señalización para cuando se reciba una respuesta a la descripción de nuestra sesión ofrecida desde el 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});
}

En el lado receptor, esperamos a que una oferta entrante antes de que creamos nuestra RTCPeerConnection ejemplo. Una vez hecho esto nos propusimos la oferta recibida utilizando setRemoteDescription() . A continuación, se llama createAnswer() para crear una respuesta a la oferta recibida. Esta respuesta está configurado como la descripción local utilizando setLocalDescription() y luego se envían a un lado llamar a través de nuestro servidor de señalización.

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

Una vez que los dos pares han configurado las descripciones de sesión local y remota, conocen las capacidades del par remoto. Esto no significa que la conexión entre los pares esté lista. Para que esto funcione, necesitamos recopilar los candidatos ICE en cada par y transferirlos (a través del canal de señalización) al otro par.

Candidatos de ICE

Antes de que dos pares puedan comunicarse usando WebRTC, necesitan intercambiar información de conectividad. Dado que las condiciones de la red pueden variar según una serie de factores, generalmente se utiliza un servicio externo para descubrir los posibles candidatos para conectarse a un par. Este servicio se llama ICE y utiliza un servidor STUN o TURN. STUN son las siglas de Session Traversal Utilities para NAT, y generalmente se usa indirectamente en la mayoría de las aplicaciones WebRTC.

TURN (Traversal Using Relay NAT) es la solución más avanzada que incorpora los protocolos STUN y la mayoría de los servicios comerciales basados ​​en WebRTC utilizan un servidor TURN para establecer conexiones entre pares. La API de WebRTC admite STUN y TURN directamente, y se recopila bajo el término más completo Establecimiento de conectividad a Internet. Al crear una conexión WebRTC, por lo general proporcionar una o varios servidores de ICE en la configuración para el RTCPeerConnection objeto.

Goteo de hielo

Una vez que un RTCPeerConnection se crea objeto, el marco subyacente utiliza los servidores ICE previstos para reunir los candidatos para el establecimiento de conectividad (ICE) candidatos. El evento icegatheringstatechange en RTCPeerConnection señales en qué estado se encuentra la reunión ICE es ( new , gathering o complete ).

Si bien es posible que un par espere hasta que se complete la recopilación de ICE, generalmente es mucho más eficiente usar una técnica de "goteo de hielo" y transmitir cada candidato de ICE al par remoto a medida que se descubre. Esto reducirá significativamente el tiempo de configuración para la conectividad entre pares y permitirá que una videollamada comience con menos demoras.

Para reunir los candidatos ICE, basta con añadir un detector para el icecandidate evento. El RTCPeerConnectionIceEvent emite en ese oyente contendrá candidate propiedad que representa un nuevo candidato que debe ser enviado a la distancia entre pares (Ver 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);
        }
    }
});

Conexión establecida

Una vez que se reciben los candidatos de ICE, debemos esperar que el estado de nuestra conexión entre pares cambie eventualmente a un estado conectado. Para detectar esto, añadimos un oyente de nuestro RTCPeerConnection donde se escucha para connectionstatechange eventos.

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

Documentación de la API de RTCPeerConnection