Criar os serviços de back-end necessários para um app WebRTC

O que é sinalização?

A sinalização é o processo de coordenação da comunicação. Para que um app do WebRTC configure uma chamada, os clientes precisam trocar as seguintes informações:

  • Mensagens de controle de sessão usadas para abrir ou fechar a comunicação
  • Mensagens de erro
  • Metadados de mídia, como codecs, configurações de codec, largura de banda e tipos de mídia
  • Dados de chave usados para estabelecer conexões seguras
  • Dados de rede, como o endereço IP e a porta de um host, conforme vistos pelo mundo externo

Esse processo de sinalização precisa de uma maneira de os clientes transmitirem mensagens. Esse mecanismo não é implementado pelas APIs do WebRTC. Você precisa criar por conta própria. Mais adiante neste artigo, você vai aprender a criar um serviço de sinalização. Primeiro, porém, você precisa de um pouco de contexto.

Por que a sinalização não é definida pelo WebRTC?

Para evitar redundância e maximizar a compatibilidade com tecnologias estabelecidas, os métodos e protocolos de sinalização não são especificados pelos padrões do WebRTC. Essa abordagem é descrita pelo Protocolo de estabelecimento de sessão do JavaScript (JSEP, na sigla em inglês):

A arquitetura do JSEP também evita que um navegador precise salvar o estado, ou seja, funcionar como uma máquina de estado de sinalização. Isso seria problemático se, por exemplo, os dados de sinalização fossem perdidos toda vez que uma página fosse recarregada. Em vez disso, o estado de sinalização pode ser salvo em um servidor.

Diagrama de arquitetura do JSEP
Arquitetura JSEP

A JSEP exige a troca entre pares de oferta e resposta, os metadados de mídia mencionados acima. As ofertas e respostas são comunicadas no formato do Protocolo de descrição de sessão (SDP), que tem este aspecto:

v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2

Quer saber o que significa toda essa confusão do SDP? Confira os exemplos do Internet Engineering Task Force (IETF).

O WebRTC foi projetado para que a oferta ou resposta possa ser ajustada antes de ser definida como a descrição local ou remota, editando os valores no texto SDP. Por exemplo, a função preferAudioCodec() em appr.tc pode ser usada para definir o codec e o bitrate padrão. O SDP é um pouco difícil de manipular com JavaScript, e há discussões sobre se as versões futuras do WebRTC devem usar JSON. No entanto, há algumas vantagens em usar o SDP.

API RTCPeerConnection e sinalização: oferta, resposta e candidato

RTCPeerConnection é a API usada pelos apps WebRTC para criar uma conexão entre pares e transmitir áudio e vídeo.

Para inicializar esse processo, RTCPeerConnection tem duas tarefas:

  • Determinar as condições da mídia local, como resolução e recursos do codec. Esses são os metadados usados para o mecanismo de oferta e resposta.
  • Receber possíveis endereços de rede para o host do app, conhecidos como candidatos.

Depois que esses dados locais forem confirmados, eles precisarão ser trocados com o par remoto por um mecanismo de sinalização.

Imagine que Alice está tentando ligar para Eva. Confira o mecanismo de oferta/resposta completo em todos os detalhes:

  1. Alice cria um objeto RTCPeerConnection.
  2. Alice cria uma oferta (uma descrição de sessão SDP) com o método createOffer() RTCPeerConnection.
  3. Alice liga para setLocalDescription() com a oferta.
  4. Alice transforma a oferta em string e usa um mecanismo de sinalização para enviá-la a Eve.
  5. Eve liga para setRemoteDescription() com a oferta de Alice para que a RTCPeerConnection saiba sobre a configuração de Alice.
  6. Eve chama createAnswer(), e o callback de sucesso é transmitido com uma descrição de sessão local, a resposta de Eve.
  7. Eve define a resposta como a descrição local chamando setLocalDescription().
  8. Em seguida, Eve usa o mecanismo de sinalização para enviar a resposta convertida em string para Alice.
  9. Alice define a resposta de Eva como a descrição da sessão remota usando setRemoteDescription().

Alice e Eve também precisam trocar informações de rede. A expressão "encontrar candidatos" se refere ao processo de encontrar interfaces de rede e portas usando o framework ICE.

  1. Alice cria um objeto RTCPeerConnection com um gerenciador onicecandidate.
  2. O gerenciador é chamado quando os candidatos de rede ficam disponíveis.
  3. No gerenciador, Alice envia dados de candidatos convertidos em string para Eve pelo canal de sinalização.
  4. Quando Eva recebe uma mensagem de candidato de Alice, ela chama addIceCandidate() para adicionar o candidato à descrição do peer remoto.

O JSEP oferece suporte ao ICE Candidate Trickling, que permite que o autor da chamada forneça candidatos incrementais ao chamado após a oferta inicial e que o chamado comece a agir na chamada e configure uma conexão sem esperar que todos os candidatos cheguem.

Programar o WebRTC para sinalização

O snippet de código abaixo é um exemplo de código do W3C que resume todo o processo de sinalização. O código pressupõe a existência de algum mecanismo de sinalização, SignalingChannel. Vamos falar sobre isso em mais detalhes mais tarde.

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});

// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    // send the offer to the other peer
    signaling.send({desc: pc.localDescription});
  } catch (err) {
    console.error(err);
  }
};

// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in self-view, and add it to be sent.
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) =>
      pc.addTrack(track, stream));
    selfView.srcObject = stream;
  } catch (err) {
    console.error(err);
  }
}

signaling.onmessage = async ({desc, candidate}) => {
  try {
    if (desc) {
      // If you get an offer, you need to reply with an answer.
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream =
          await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) =>
          pc.addTrack(track, stream));
        await pc.setLocalDescription(await pc.createAnswer());
        signaling.send({desc: pc.localDescription});
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc);
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
};

Para conferir os processos de oferta/resposta e troca de candidatos em ação, consulte simpl.info RTCPeerConnection e confira o registro do console para ver um exemplo de bate-papo por vídeo de uma página. Se quiser mais, faça o download de um despejo completo de sinalização e estatísticas do WebRTC na página about://webrtc-internals no Google Chrome ou na página opera://webrtc-internals no Opera.

Descoberta de semelhantes

Essa é uma maneira elegante de perguntar: "Como encontro alguém para conversar?"

Para ligações, você tem números de telefone e diretórios. Para o chat por vídeo e mensagens on-line, você precisa de sistemas de gerenciamento de identidade e presença e uma forma de os usuários iniciarem sessões. Os apps WebRTC precisam de uma maneira de os clientes se comunicarem entre si para iniciar ou participar de uma chamada.

Os mecanismos de descoberta de pares não são definidos pelo WebRTC, e você não vai encontrar as opções aqui. O processo pode ser tão simples quanto enviar um URL por e-mail ou mensagem. Para apps de videochamada, como Talky, tawk.to e Browser Meeting, você convida as pessoas para uma chamada compartilhando um link personalizado. O desenvolvedor Chris Ball criou um experimento interessante de WebRTC sem servidor que permite que os participantes da chamada do WebRTC troquem metadados por qualquer serviço de mensagens, como mensagens instantâneas, e-mail ou pombo-correio.

Como criar um serviço de sinalização?

Novamente, os protocolos e mecanismos de sinalização não são definidos pelos padrões do WebRTC. Seja qual for sua escolha, você vai precisar de um servidor intermediário para trocar mensagens de sinalização e dados do app entre os clientes. Infelizmente, um app da Web não pode simplesmente gritar na Internet: "Conecte-me ao meu amigo!"

Felizmente, as mensagens de sinalização são pequenas e geralmente trocadas no início de uma chamada. Em testes com o appr.tc para uma sessão de chat por vídeo, um total de cerca de 30 a 45 mensagens foram processadas pelo serviço de sinalização com um tamanho total de cerca de 10 KB.

Além de serem relativamente pouco exigentes em termos de largura de banda, os serviços de sinalização do WebRTC não consomem muito processamento ou memória porque precisam apenas retransmitir mensagens e reter uma pequena quantidade de dados de estado da sessão, como quais clientes estão conectados.

Enviar mensagens do servidor para o cliente

Um serviço de mensagem para sinalização precisa ser bidirecional: do cliente para o servidor e do servidor para o cliente. A comunicação bidirecional vai contra o modelo de solicitação/resposta do cliente/servidor HTTP, mas vários hacks, como a long polling, foram desenvolvidos ao longo dos anos para enviar dados de um serviço em execução em um servidor da Web para um app da Web em execução em um navegador.

Mais recentemente, a API EventSource foi amplamente implementada. Isso ativa os eventos enviados pelo servidor, ou seja, os dados enviados de um servidor da Web para um cliente do navegador por HTTP. O EventSource foi projetado para mensagens unidirecionais, mas pode ser usado em combinação com XHR para criar um serviço de troca de mensagens de sinalização. Um serviço de sinalização transmite uma mensagem de um autor da chamada, entregue por uma solicitação XHR, enviando-a por EventSource para o autor da chamada.

O WebSocket é uma solução mais natural, projetada para comunicação full-duplex entre cliente e servidor, ou seja, mensagens que podem fluir em ambas as direções ao mesmo tempo. Uma vantagem de um serviço de sinalização criado com WebSocket puro ou eventos enviados pelo servidor (EventSource) é que o back-end dessas APIs pode ser implementado em vários frameworks da Web comuns à maioria dos pacotes de hospedagem da Web para linguagens como PHP, Python e Ruby.

Todos os navegadores modernos, exceto o Opera Mini, são compatíveis com WebSocket e, mais importante, todos os navegadores compatíveis com WebRTC também são compatíveis com WebSocket, tanto em computadores quanto em dispositivos móveis. O TLS deve ser usado em todas as conexões para garantir que as mensagens não sejam interceptadas sem criptografia e também para reduzir problemas com a travessia de proxy. Para mais informações sobre WebSocket e traversal de proxy, consulte o capítulo sobre WebRTC em High Performance Browser Networking, de Ilya Grigorik.

Também é possível processar a sinalização fazendo com que os clientes do WebRTC consultem um servidor de mensagens repetidamente pelo Ajax, mas isso leva a muitas solicitações de rede redundantes, o que é especialmente problemático para dispositivos móveis. Mesmo depois que uma sessão é estabelecida, os peers precisam consultar mensagens de sinalização em caso de mudanças ou encerramento de sessão por outros peers. O exemplo de app WebRTC Book usa essa opção com algumas otimizações para a frequência de pesquisa.

Indicador de escala

Embora um serviço de sinalização consuma relativamente pouca largura de banda e CPU por cliente, os servidores de sinalização de um app popular podem precisar processar muitas mensagens de diferentes locais com altos níveis de simultaneidade. Os apps WebRTC que recebem muito tráfego precisam de servidores de sinalização capazes de processar uma carga considerável. Não é necessário entrar em detalhes aqui, mas há várias opções para mensagens de alto volume e alto desempenho, incluindo as seguintes:

  • Protocolo de presença e mensagem extensível (XMPP, na sigla em inglês), originalmente conhecido como Jabber, um protocolo desenvolvido para mensagens instantâneas que pode ser usado para sinalização. As implementações do servidor incluem ejabberd e Openfire. Os clientes JavaScript, como o Strophe.js, usam o BOSH para emular o streaming bidirecional, mas, por várias razões, o BOSH pode não ser tão eficiente quanto o WebSocket e, pelas mesmas razões, pode não ser bem dimensionado. (Ao falar sobre isso, o Jingle é uma extensão XMPP para ativar a voz e o vídeo. O projeto WebRTC usa componentes de rede e transporte da biblioteca libjingle, uma implementação C++ do Jingle.

  • Bibliotecas de código aberto, como ZeroMQ (usada pelo TokBox para o serviço Rumour) e OpenMQ (o NullMQ aplica os conceitos do ZeroMQ a plataformas da Web usando o protocolo STOMP por WebSocket).

  • Plataformas comerciais de mensagens em nuvem que usam WebSocket (embora possam recorrer à long polling), como Pusher, Kaazing e PubNub (a PubNub também tem uma API para WebRTC).

  • Plataformas comerciais do WebRTC, como o vLine

O Guia de tecnologias da Web em tempo real do desenvolvedor Phil Leggetter fornece uma lista abrangente de serviços e bibliotecas de mensagens.

Criar um serviço de sinalização com o Socket.io no Node

O código a seguir é para um app da Web simples que usa um serviço de sinalização criado com o Socket.io no Node. O design do Socket.io facilita a criação de um serviço para trocar mensagens, e o Socket.io é particularmente adequado para sinalização do WebRTC devido ao conceito integrado de salas. Este exemplo não foi projetado para ser dimensionado como um serviço de sinalização de produção, mas é simples de entender para um número relativamente pequeno de usuários.

O Socket.io usa o WebSocket com substitutos: pesquisa longa AJAX, streaming AJAX com vários segmentos, Iframe Forever e pesquisa JSONP. Ele foi transferido para vários back-ends, mas talvez seja mais conhecido pela versão do Node usada neste exemplo.

Não há WebRTC neste exemplo. Ele foi criado apenas para mostrar como criar sinalização em um app da Web. Confira o registro do console para saber o que está acontecendo quando os clientes entram em uma sala e trocam mensagens. Este codelab do WebRTC oferece instruções detalhadas sobre como integrar isso a um app de chat por vídeo completo do WebRTC.

Confira o index.html do cliente:

<!DOCTYPE html>
<html>
  <head>
    <title>WebRTC client</title>
  </head>
  <body>
    <script src='/socket.io/socket.io.js'></script>
    <script src='js/main.js'></script>
  </body>
</html>

Confira o arquivo JavaScript main.js referenciado no cliente:

const isInitiator;

room = prompt('Enter room name:');

const socket = io.connect();

if (room !== '') {
  console.log('Joining room ' + room);
  socket.emit('create or join', room);
}

socket.on('full', (room) => {
  console.log('Room ' + room + ' is full');
});

socket.on('empty', (room) => {
  isInitiator = true;
  console.log('Room ' + room + ' is empty');
});

socket.on('join', (room) => {
  console.log('Making request to join room ' + room);
  console.log('You are the initiator!');
});

socket.on('log', (array) => {
  console.log.apply(console, array);
});

Confira o app de servidor completo:

const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
  file.serve(req, res);
}).listen(2013);

const io = require('socket.io').listen(app);

io.sockets.on('connection', (socket) => {

  // Convenience function to log server messages to the client
  function log(){
    const array = ['>>> Message from server: '];
    for (const i = 0; i < arguments.length; i++) {
      array.push(arguments[i]);
    }
      socket.emit('log', array);
  }

  socket.on('message', (message) => {
    log('Got message:', message);
    // For a real app, would be room only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', (room) => {
    const numClients = io.sockets.clients(room).length;

    log('Room ' + room + ' has ' + numClients + ' client(s)');
    log('Request to create or join room ' + room);

    if (numClients === 0){
      socket.join(room);
      socket.emit('created', room);
    } else if (numClients === 1) {
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room);
    } else { // max two clients
      socket.emit('full', room);
    }
    socket.emit('emit(): client ' + socket.id +
      ' joined room ' + room);
    socket.broadcast.emit('broadcast(): client ' + socket.id +
      ' joined room ' + room);

  });

});

Você não precisa aprender sobre o node-static para isso. Ele é usado neste exemplo.)

Para executar este app no localhost, é necessário ter o Node, o Socket.IO e o node-static instalados. O Node pode ser baixado no Node.js (a instalação é simples e rápida). Para instalar o Socket.IO e o node-static, execute o Node Package Manager em um terminal no diretório do app:

npm install socket.io
npm install node-static

Para iniciar o servidor, execute o seguinte comando em um terminal no diretório do app:

node server.js

No navegador, abra localhost:2013. Abra uma nova guia ou janela em qualquer navegador e abra o localhost:2013 novamente. Para conferir o que está acontecendo, verifique o console. No Chrome e no Opera, é possível acessar o console pelas Ferramentas para desenvolvedores do Google Chrome com Ctrl+Shift+J (ou Command+Option+J no Mac).

Seja qual for a abordagem escolhida para a sinalização, o app de back-end e o app cliente precisam fornecer serviços semelhantes a este exemplo.

Problemas de sinalização

  • O RTCPeerConnection não vai começar a coletar candidatos até que o setLocalDescription() seja chamado. Isso é obrigatório no rascunho do JSEP IETF.
  • Aproveite o Trickle ICE. Chame addIceCandidate() assim que os candidatos chegarem.

Servidores de sinalização prontos

Se você não quiser usar o seu próprio servidor, há vários servidores de sinalização do WebRTC disponíveis, que usam o Socket.IO como no exemplo anterior e são integrados às bibliotecas JavaScript do cliente do WebRTC:

  • webRTC.io é uma das primeiras bibliotecas de abstração para WebRTC.
  • O Signalmaster é um servidor de sinalização criado para uso com a biblioteca de cliente JavaScript SimpleWebRTC.

Se você não quiser escrever nenhum código, plataformas comerciais completas do WebRTC estarão disponíveis em empresas como vLine, OpenTok e Asterisk.

Para constar, a Ericsson criou um servidor de sinalização usando PHP no Apache nos primeiros dias do WebRTC. Isso está um pouco obsoleto, mas vale a pena conferir o código se você estiver considerando algo semelhante.

Segurança de sinalização

"A segurança é a arte de não deixar nada acontecer."

Salman Rushdie

A criptografia é obrigatória para todos os componentes do WebRTC.

No entanto, os mecanismos de sinalização não são definidos pelos padrões do WebRTC. Portanto, cabe a você garantir a segurança da sinalização. Se um invasor conseguir sequestrar a sinalização, ele poderá interromper sessões, redirecionar conexões e gravar, alterar ou injetar conteúdo.

O fator mais importante para proteger a sinalização é usar protocolos seguros, como HTTPS e WSS (por exemplo, TLS), que garantem que as mensagens não possam ser interceptadas sem criptografia. Além disso, tome cuidado para não transmitir mensagens de sinalização de forma que elas possam ser acessadas por outros autores da chamada usando o mesmo servidor de sinalização.

Após a sinalização: use o ICE para lidar com NATs e firewalls

Para a sinalização de metadados, os apps do WebRTC usam um servidor intermediário, mas para o streaming de mídia e dados real, depois que uma sessão é estabelecida, o RTCPeerConnection tenta conectar clientes diretamente ou ponto a ponto.

Em um mundo mais simples, cada endpoint do WebRTC teria um endereço exclusivo que poderia ser trocado com outros pares para se comunicar diretamente.

Conexão simples de peer to peer
Um mundo sem NATs e firewalls

Na realidade, a maioria dos dispositivos fica por trás de uma ou mais camadas de NAT, alguns têm software antivírus que bloqueia determinadas portas e protocolos, e muitos estão por trás de proxies e firewalls corporativos. Um firewall e um NAT podem ser implementados pelo mesmo dispositivo, como um roteador Wi-Fi doméstico.

Peers por trás de NATs e firewalls
O mundo real

Os apps WebRTC podem usar a estrutura ICE para superar as complexidades da rede do mundo real. Para que isso aconteça, o app precisa transmitir os URLs do servidor ICE para RTCPeerConnection, conforme descrito neste artigo.

O ICE tenta encontrar o melhor caminho para conectar pares. Ele tenta todas as possibilidades em paralelo e escolhe a opção mais eficiente que funciona. O ICE primeiro tenta fazer uma conexão usando o endereço do host obtido do sistema operacional e da placa de rede de um dispositivo. Se isso falhar (o que vai acontecer para dispositivos por trás de NATs), o ICE vai conseguir um endereço externo usando um servidor STUN e, se isso falhar, o tráfego será roteado por um servidor de retransmissão TURN.

Em outras palavras, um servidor STUN é usado para receber um endereço de rede externo, e os servidores TURN são usados para retransmitir o tráfego se a conexão direta (peer-to-peer) falhar.

Todos os servidores TURN oferecem suporte a STUN. Um servidor TURN é um servidor STUN com mais funcionalidades de retransmissão integradas. O ICE também lida com as complexidades das configurações de NAT. Na realidade, o NAT hole-punching pode exigir mais do que apenas um endereço IP público:porta.

Os URLs para servidores STUN e/ou TURN são especificados (opcionalmente) por um app do WebRTC no objeto de configuração iceServers, que é o primeiro argumento do construtor RTCPeerConnection. Para appr.tc, esse valor é assim:

{
  'iceServers': [
    {
      'urls': 'stun:stun.l.google.com:19302'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=udp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=tcp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    }
  ]
}

Quando RTCPeerConnection tiver essas informações, a magia do ICE vai acontecer automaticamente. O RTCPeerConnection usa a estrutura ICE para encontrar o melhor caminho entre os pares, trabalhando com servidores STUN e TURN conforme necessário.

STUN

As NATs fornecem a um dispositivo um endereço IP para uso em uma rede local particular, mas esse endereço não pode ser usado externamente. Sem um endereço público, não há como os pares do WebRTC se comunicarem. Para contornar esse problema, o WebRTC usa o STUN.

Os servidores STUN estão na Internet pública e têm uma tarefa simples: verificar o endereço IP:porta de uma solicitação recebida (de um app executado por NAT) e enviar esse endereço como resposta. Em outras palavras, o app usa um servidor STUN para descobrir o IP:porta de uma perspectiva pública. Esse processo permite que um peer do WebRTC receba um endereço acessível publicamente para si mesmo e o transmita a outro peer por um mecanismo de sinalização para configurar um link direto. Na prática, NATs diferentes funcionam de maneiras diferentes e podem ter várias camadas, mas o princípio é o mesmo.

Os servidores STUN não precisam fazer muito ou lembrar de muita coisa. Portanto, servidores STUN com especificações relativamente baixas podem processar um grande número de solicitações.

A maioria das chamadas do WebRTC estabelece uma conexão usando STUN: 86%, de acordo com o Webrtcstats.com. No entanto, esse número pode ser menor para chamadas entre pares por trás de firewalls e configurações complexas de NAT.

Conexão ponto a ponto usando um servidor STUN
Como usar servidores STUN para receber endereços IP:porta públicos

TURN

O RTCPeerConnection tenta configurar a comunicação direta entre pares pelo UDP. Se isso falhar, o RTCPeerConnection vai recorrer ao TCP. Se isso falhar, os servidores TURN podem ser usados como substitutos, retransmitindo dados entre os endpoints.

Só para reiterar, o TURN é usado para retransmitir streaming de áudio, vídeo e dados entre pares, não para sinalizar dados.

Os servidores TURN têm endereços públicos, então eles podem ser contatados por pares, mesmo que os pares estejam por trás de firewalls ou proxies. Os servidores TURN têm uma tarefa conceitualmente simples: retransmitir uma transmissão. No entanto, ao contrário dos servidores STUN, eles consomem muita largura de banda. Em outras palavras, os servidores TURN precisam ser mais robustos.

Conexão ponto a ponto usando um servidor STUN
O Monty completo: STUN, TURN e sinalização

Este diagrama mostra o TURN em ação. O STUN puro não teve sucesso, então cada peer recorre ao uso de um servidor TURN.

Como implantar servidores STUN e TURN

Para testes, o Google executa um servidor STUN público, stun.l.google.com:19302, usado por appr.tc. Para um serviço STUN/TURN de produção, use o rfc5766-turn-server. O código-fonte para servidores STUN e TURN está disponível no GitHub, onde você também encontra links para várias fontes de informações sobre a instalação do servidor. Uma imagem de VM para Amazon Web Services também está disponível.

Um servidor TURN alternativo é restund, disponível como código-fonte e também para a AWS. Confira as instruções para configurar o reembolso no Compute Engine.

  1. Abra o firewall conforme necessário para tcp=443, udp/tcp=3478.
  2. Crie quatro instâncias, uma para cada IP público, imagem padrão do Ubuntu 12.06.
  3. Configure a configuração de firewall local (permita QUALQUER coisa de QUALQUER lugar).
  4. Instalar ferramentas: shell sudo apt-get install make sudo apt-get install gcc
  5. Instale o libre em creytiv.com/re.html.
  6. Extraia o restund de creytiv.com/restund.html.
  7. wget hancke.name/restund-auth.patch e aplique com patch -p1 < restund-auth.patch.
  8. Execute make, sudo make install para libre e restund.
  9. Adapte restund.conf às suas necessidades (substitua os endereços IP e verifique se ele contém a mesma senha secreta) e copie para /etc.
  10. Copie restund/etc/restund para /etc/init.d/.
  11. Configurar o reembolso:
    1. Defina LD_LIBRARY_PATH.
    2. Copie restund.conf para /etc/restund.conf.
    3. Defina restund.conf para usar o 10 certo. ele vai receber um novo endereço IP.
  12. Executar restund
  13. Teste usando o cliente de teste da máquina remota: ./client IP:port

Além do modelo um a um: WebRTC para várias partes

Você também pode conferir o padrão IETF proposto por Justin Uberti para uma API REST para acesso a serviços TURN.

É fácil imaginar casos de uso de streaming de mídia que vão além de uma simples chamada individual. Por exemplo, videoconferências entre um grupo de colegas ou um evento público com um palestrante e centenas ou milhões de espectadores.

Um app WebRTC pode usar várias RTCPeerConnections para que cada endpoint se conecte a todos os outros em uma configuração de malha. Essa é a abordagem adotada por apps como o talky.io e funciona muito bem para um pequeno grupo de pares. Além disso, o processamento e o consumo de largura de banda se tornam excessivos, especialmente para clientes móveis.

Mesh: chamada N-way pequena
Topologia de malha completa: todos conectados a todos

Como alternativa, um app WebRTC pode escolher um endpoint para distribuir streams para todos os outros em uma configuração de estrela. Também seria possível executar um endpoint do WebRTC em um servidor e criar seu próprio mecanismo de redistribuição (um exemplo de app cliente é fornecido pelo webrtc.org).

Desde o Chrome 31 e o Opera 18, uma MediaStream de uma RTCPeerConnection pode ser usada como entrada para outra. Isso pode permitir arquiteturas mais flexíveis, porque permite que um app da Web processe o roteamento de chamadas escolhendo a qual outro peer se conectar. Para conferir isso em ação, consulte Exemplos de WebRTC para retransmissão de conexão de peer e Exemplos de WebRTC para várias conexões de peer.

Unidade de controle multiponto

Uma opção melhor para um grande número de endpoints é usar uma Unidade de Controle Multiponto (MCU). É um servidor que funciona como uma ponte para distribuir mídia entre um grande número de participantes. As MCUs podem lidar com diferentes resoluções, codecs e taxas de frames em uma videoconferência, processar a transcodificação, fazer encaminhamento de stream seletivo e mixar ou gravar áudio e vídeo. Para chamadas com várias pessoas, há várias questões a serem consideradas, principalmente como mostrar várias entradas de vídeo e misturar áudio de várias fontes. Plataformas de nuvem, como o vLine, também tentam otimizar o roteamento de tráfego.

É possível comprar um pacote de hardware MCU completo ou criar o seu.

Vista traseira do Cisco MCU5300
A parte de trás de uma MCU da Cisco

Várias opções de software MCU de código aberto estão disponíveis. Por exemplo, o Licode (anteriormente conhecido como Lynckia) produz um MCU de código aberto para o WebRTC. O OpenTok tem o Mantis.

Além dos navegadores: VoIP, telefones e mensagens

A natureza padronizada do WebRTC permite estabelecer a comunicação entre um app WebRTC em execução em um navegador e um dispositivo ou plataforma em execução em outra plataforma de comunicação, como um telefone ou um sistema de videoconferência.

O SIP é um protocolo de sinalização usado por sistemas de VoIP e videoconferência. Para ativar a comunicação entre um app da WebRTC e um cliente SIP, como um sistema de videoconferência, o WebRTC precisa de um servidor proxy para mediar a sinalização. A sinalização precisa fluir pelo gateway, mas, depois que a comunicação é estabelecida, o tráfego SRTP (vídeo e áudio) pode fluir diretamente de um peer para outro.

A rede pública de telefonia comutada (PSTN, na sigla em inglês) é a rede comutada por circuitos de todos os telefones analógicos "simples e antigos". Para ligações entre apps da Web e telefones WebRTC, o tráfego precisa passar por um gateway PSTN. Da mesma forma, os apps da Web do WebRTC precisam de um servidor XMPP intermediário para se comunicar com endpoints Jingle, como clientes de mensagens instantâneas. O Jingle foi desenvolvido pelo Google como uma extensão do XMPP para permitir a voz e o vídeo em serviços de mensagens. As implementações atuais do WebRTC são baseadas na biblioteca C++ libjingle, uma implementação do Jingle inicialmente desenvolvida para o Talk.

Vários apps, bibliotecas e plataformas usam a capacidade do WebRTC de se comunicar com o mundo externo:

  • sipML5: um cliente SIP JavaScript de código aberto
  • jsSIP (link em inglês): biblioteca SIP JavaScript
  • Phono: API de smartphone JavaScript de código aberto criada como um plug-in
  • Zingaya (link em inglês): um widget de telefone que pode ser incorporado
  • Twilio: voz e mensagens
  • Uberconference: videoconferência

Os desenvolvedores do sipML5 também criaram o gateway webrtc2sip. A Tethr e a Tropo demonstraram um framework para comunicações em caso de desastre usando uma célula OpenBTS para permitir a comunicação entre feature phones e computadores pelo WebRTC. É a comunicação telefônica sem uma operadora.

Saiba mais

O codelab do WebRTC fornece instruções detalhadas sobre como criar um app de chat por vídeo e texto usando um serviço de sinalização Socket.io em execução no Node.

Apresentação do WebRTC do Google I/O de 2013 com o líder técnico do WebRTC, Justin Uberti

Apresentação de Chris Wilson sobre SFHTML5: Introdução aos apps WebRTC

O livro de 350 páginas WebRTC: APIs e protocolos RTCWEB da Web em tempo real do HTML5 fornece muitos detalhes sobre dados e caminhos de sinalização e inclui vários diagramas de topologia de rede detalhados.

WebRTC and Signaling: What Two Years Has Taught Us: postagem do blog da TokBox sobre por que deixar a sinalização de fora da especificação foi uma boa ideia

O guia prático de Ben Strong A Practical Guide to Building WebRTC Apps (em inglês) oferece muitas informações sobre as topologias e a infraestrutura do WebRTC.

O capítulo sobre WebRTC em High Performance Browser Networking, de Ilya Grigorik, aborda em detalhes a arquitetura, os casos de uso e a performance do WebRTC.