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

O que é a sinalização?

A sinalização é o processo de coordenação da comunicação. Para que um app WebRTC configure uma chamada, os clientes dele 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 importantes usados para estabelecer conexões seguras
  • Dados de rede, como porta e endereço IP de um host, como visto pelo mundo externo

Esse processo de sinalização precisa de uma maneira para que os clientes repassem mensagens. Esse mecanismo não é implementado pelas APIs WebRTC. Você precisa construí-lo por conta própria. Mais adiante neste artigo, você vai aprender maneiras de criar um serviço de sinalização. Primeiro, no entanto, 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, métodos e protocolos de sinalização não são especificados por padrões WebRTC. Essa abordagem é descrita pelo JavaScript Session Estament Protocol (JSEP):

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

Diagrama da arquitetura do JSEP
Arquitetura do JSEP

O JSEP exige a troca entre pares de offer e answer, os metadados de mídia mencionados acima. As ofertas e respostas são comunicadas no formato do protocolo de descrição da sessão (SDP, na sigla em inglês), que tem esta aparência:

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 todo esse conteúdo de SDP? Veja os exemplos da Internet Engineering Task Force (IETF).

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

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

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

Para inicializar esse processo, o RTCPeerConnection tem duas tarefas:

  • Verifique as condições da mídia local, como resolução e recursos de codec. Esses são os metadados usados para o mecanismo de oferta e resposta.
  • Busque 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 por um mecanismo de sinalização com o peering remoto.

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

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

Alice e Eva também precisam trocar informações sobre a rede. A expressão "encontrar candidatos" se refere ao processo de encontrar interfaces e portas de rede usando o framework ICE (em inglês).

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

O JSEP é compatível com Trickling de candidato a ICE, o que permite que o autor da chamada forneça gradualmente os candidatos ao recebedor da chamada após a oferta inicial, e o recebedor da chamada comece a agir na ligação e configure uma conexão sem esperar a chegada de todos os candidatos.

Criar código WebRTC para sinalização

O snippet de código abaixo é um exemplo de código W3C que resume todo o processo de sinalização. O código pressupõe a existência de algum mecanismo de sinalização, SignalingChannel. A sinalização vai ser abordada com mais detalhes posteriormente.

// 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 ver os processos de oferta/resposta e troca de candidatos em ação, consulte simpl.info RTCPeerConnection e veja um exemplo de chat por vídeo de página única no registro do console. Se você quiser mais, faça download de um despejo completo da sinalização e das estatísticas 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 sofisticada de perguntar: "Como encontrar alguém para conversar?"

Para chamadas telefônicas, existem números de telefone e diretórios. Para chat por vídeo e mensagens on-line, você precisa de sistemas de gerenciamento de identidade e presença e um meio para que os usuários iniciem sessões. Os apps WebRTC precisam de uma forma para que os clientes sinalizem uns aos outros que querem iniciar ou participar de uma chamada.

Os mecanismos de descoberta de pontos não são definidos pelo WebRTC e você não vai entrar nas opções aqui. O processo pode ser tão simples quanto enviar um e-mail ou enviar uma mensagem com um URL. Em apps de chat por vídeo, como o Talky, o tawk.to e o Browser Meeting, é possível convidar pessoas para uma chamada compartilhando um link personalizado. O desenvolvedor Chris Ball criou um intrigante experimento serverless-webrtc (em inglês) que permite aos participantes de chamadas WebRTC trocar metadados por qualquer serviço de mensagens, como mensagens instantâneas, e-mail ou pombo-home.

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

Reiterando, os protocolos e mecanismos de sinalização não são definidos por padrões WebRTC. Seja qual for sua escolha, você precisa de um servidor intermediário para trocar mensagens de sinalização e dados do app entre clientes. Infelizmente, um aplicativo da web não pode simplesmente gritar para a internet: "Conecte-me ao meu amigo!"

Felizmente, as mensagens de sinalização são pequenas e geralmente trocadas no início de uma ligação. Ao testar com appr.tc para uma sessão de chat por vídeo, o serviço de sinalização processou um total de 30 a 45 mensagens, com um tamanho total de cerca de 10 KB para todas as mensagens.

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

Mensagens push do servidor para o cliente

Um serviço de mensagens para sinalização precisa ser bidirecional: de cliente para servidor e de servidor para cliente. A comunicação bidirecional vai contra o modelo de solicitação/resposta de cliente/servidor HTTP, mas várias invasões, como a pesquisa longa, foram desenvolvidas ao longo de muitos anos para enviar dados de um serviço em execução em um servidor da Web para um app da Web executado em um navegador.

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

O WebSocket é uma solução mais natural, projetada para a comunicação cliente-servidor full-duplex, ou seja, mensagens que podem fluir nas duas 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 na 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 oferecem suporte a 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 possam ser interceptadas sem criptografia e para reduzir problemas com a travessia de proxy. Para mais informações sobre a travessia de WebSocket e proxy, consulte o capítulo WebRTC (em inglês) no artigo High Performance Browser Networking (link em inglês) de Ilya Grigorik.

Também é possível gerenciar a sinalização fazendo com que os clientes WebRTC pesquisem 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 após o estabelecimento de uma sessão, os pares precisam pesquisar as mensagens de sinalização em caso de alterações ou encerramento da sessão por outros pares. O exemplo do app WebRTC Book usa essa opção com algumas otimizações de frequência de pesquisa.

Dimensionar a sinalização

Embora um serviço de sinalização consuma relativamente pouca largura de banda e CPU por cliente, os servidores de sinalização de um aplicativo conhecido podem precisar lidar com muitas mensagens de diferentes locais com altos níveis de simultaneidade. Aplicativos WebRTC que recebem muito tráfego precisam de servidores de sinalização capazes de lidar com uma carga considerável. Você não vai 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 eXtensible (XMPP), originalmente conhecido como protocolo Jabber-a, desenvolvido para mensagens instantâneas que podem ser usadas para sinalização. As implementações do servidor incluem ejabberd e Openfire. Clientes JavaScript, como Strophe.js, usam BOSH para emular o streaming bidirecional. No entanto, por vários motivos, o BOSH pode não ser tão eficiente quanto o WebSocket e, pelos mesmos motivos, pode não ter bom escalonamento. Em relação à tangente, Jingle é uma extensão XMPP que permite voz e 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 (como usado pela TokBox para seu serviço Rumour) e OpenMQ (NullMQ aplica conceitos ZeroMQ a plataformas da Web usando o protocolo STOMP sobre WebSocket).

  • Plataformas comerciais de mensagens na nuvem que usam WebSocket (embora possam recorrer a pesquisas longas), como Pusher, Kaazing e PubNub. O PubNub também tem uma API para WebRTC.

  • Plataformas WebRTC comerciais, como vLine

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

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

Confira a seguir o código de 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 simplifica a criação de um serviço para troca de mensagens, e o Socket.io é particularmente adequado para sinalização WebRTC devido ao conceito integrado de salas. Este exemplo não foi projetado para ser escalonado 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 WebSocket com substitutos: sondagem longa AJAX, streaming de várias partes AJAX, Forever Iframe e pesquisa JSONP. Ele foi transferido para vários back-ends, mas talvez seja mais conhecido pela versão Node usada neste exemplo.

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

Este é o cliente index.html:

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

Este é 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 nós estáticos para isso. Ele será usado neste exemplo.)

Para executar este app no localhost, você precisa ter o Node, o Socket.IO e o node-static instalados. É possível fazer o download do nó a partir do Node.js. A instalação é simples e rápida. Para instalar o Socket.IO e o node-static, execute o Gerenciador de pacotes do Node 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 acessar o que está acontecendo, verifique o console. No Chrome e no Opera, você pode 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 sinalização, seu back-end e app cliente, no mínimo, precisam fornecer serviços semelhantes a este exemplo.

Pegadinhas de sinalização

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

Servidores de sinalização prontos

Se você não quiser implementar seu próprio modelo, há vários servidores de sinalização WebRTC disponíveis, que usam Socket.IO como o exemplo anterior e são integrados às bibliotecas JavaScript do cliente 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, empresas como vLine, OpenTok e Asterisk disponibilizam plataformas comerciais completas de WebRTC.

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

Segurança de sinalização

"Segurança é a arte de fazer nada acontecer."

Salman Rushdie

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

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

O fator mais importante na proteção da 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 maneira que elas possam ser acessadas por outros autores de chamadas usando o mesmo servidor de sinalização.

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

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

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

Conexão ponto a ponto simples
Um mundo sem NATs e firewalls

Na realidade, a maioria dos dispositivos reside 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 atrás de proxies e firewalls corporativos. Um firewall e NAT podem, de fato, ser implementados pelo mesmo dispositivo, como um roteador Wi-Fi doméstico.

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

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

A ICE tenta encontrar o melhor caminho para conectar os colegas. Ele testa todas as possibilidades em paralelo e escolhe a opção mais eficiente que funcionar. Primeiro, a ICE 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 acontecerá para dispositivos com NATs), a ICE obterá um endereço externo usando um servidor STUN e, se isso falhar, o tráfego será roteado através de um servidor de redirecionamento TURN.

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

Todos os servidores TURN são compatíveis com STUN. Um servidor TURN é um servidor STUN com funcionalidade adicional de retransmissão integrada. A ICE também lida com as complexidades das configurações NAT. Na realidade, a perfuração do NAT pode exigir mais do que apenas um endereço IP:porta público.

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

{
  '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'
    }
  ]
}

Depois que o RTCPeerConnection tiver essas informações, a mágica do ICE vai acontecer automaticamente. O RTCPeerConnection usa o framework ICE para descobrir o melhor caminho entre pares, trabalhando com servidores STUN e TURN conforme necessário.

DESPERTADO

As NATs fornecem um endereço IP para uso dentro de uma rede local privada, mas esse endereço não pode ser usado externamente. Sem um endereço público, os colegas WebRTC não podem se comunicar. Para contornar esse problema, o WebRTC usa STUN.

Os servidores STUN ficam na Internet pública e têm uma tarefa simples: verificar o endereço IP:port de uma solicitação recebida (de um app executado com um NAT) e enviar esse endereço de volta como resposta. Em outras palavras, o aplicativo usa um servidor STUN para descobrir a porta IP dele de uma perspectiva pública. Esse processo permite que um peer de WebRTC receba um endereço de acesso público para si e, em seguida, passe-o para outro ponto por um mecanismo de sinalização a fim de configurar um link direto. Na prática, NATs diferentes funcionam de maneiras diferentes e pode haver várias camadas de NAT, mas o princípio ainda é o mesmo.

Servidores STUN não precisam fazer muito nem se lembrar de muito, portanto, servidores STUN de especificações relativamente baixas podem lidar com um grande número de solicitações.

A maioria das chamadas WebRTC faz uma conexão usando STUN, 86%, de acordo com Webrtcstats.com, embora isso possa ser menos para chamadas entre pares atrás de firewalls e configurações NAT complexas.

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

TURN

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

Apenas para reiterar, o TURN é usado para retransmitir áudio, vídeo e streaming de dados entre pares, sem sinalizar dados.

Os servidores TURN têm endereços públicos, portanto, podem ser contatados por pares mesmo se os pares estiverem atrás de firewalls ou proxies. Os servidores TURN têm uma tarefa conceitualmente simples: redirecionar um stream. 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, portanto cada par recorre ao uso de um servidor TURN.

Implantação de servidores STUN e TURN

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

Um servidor TURN alternativo é restaurado, disponível como código-fonte e também para a AWS. Veja as instruções para configurar o REST 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. Definir a configuração do firewall local (permitir QUALQUER de QUALQUER).
  4. Instalar ferramentas: shell sudo apt-get install make sudo apt-get install gcc
  5. Instale o libre em creytiv.com/re.html.
  6. Busque o restante em creytiv.com/restund.html e descompacte./
  7. wget hancke.name/restund-auth.patch e aplicar com patch -p1 < restund-auth.patch.
  8. Execute make, sudo make install para libre e restund.
  9. Adapte o restund.conf às suas necessidades (substitua os endereços IP e verifique se ele contém a mesma chave secreta compartilhada) e copie para /etc.
  10. Copie restund/etc/restund para /etc/init.d/.
  11. Configurar o restante:
    1. Defina LD_LIBRARY_PATH.
    2. Copie restund.conf para /etc/restund.conf.
    3. Defina restund.conf para usar os 10 valores corretos. endereço IP.
  12. Executar resto
  13. Teste usando o cliente stund da máquina remota: ./client IP:port

Além de um para um: WebRTC de várias partes

Confira também o padrão IETF proposto por Justin Uberti para uma API REST para acesso aos serviços TURN (link em inglês).

É fácil imaginar casos de uso para streaming de mídia que vão além de uma simples chamada individual. Por exemplo, uma videoconferência 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ários RTCPeerConnections para que cada endpoint se conecte a outros endpoints em uma configuração de malha. Essa é a abordagem adotada por apps, como o talky.io, e funciona muito bem para poucos colegas. Além disso, o processamento e o consumo de largura de banda se tornam excessivos, especialmente para clientes móveis.

Malha: pequena chamada N-way
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 em estrela. Também seria possível executar um endpoint WebRTC em um servidor e construir seu próprio mecanismo de redistribuição (um exemplo de app cliente é fornecido pelo webrtc.org).

Desde o Chrome 31 e Opera 18, um MediaStream de um RTCPeerConnection pode ser usado como a entrada de outro. Isso permite arquiteturas mais flexíveis, pois permite que um app da Web lide com o roteamento de chamadas escolhendo a que outro ponto se conectar. Para ver isso em ação, consulte WebRTC exemplos de redirecionamento de conexão de peering e WebRTC exemplos de várias conexões de peering.

Unidade de controle multiponto

Uma opção melhor para um grande número de endpoints é usar uma Unidade de controle multiponto (MCU, na sigla em inglês). Esse é um servidor que funciona como uma ponte para distribuir mídia entre um grande número de participantes. Os MCUs podem lidar com diferentes resoluções, codecs e frame rates em uma videoconferência; lidar com a transcodificação; fazer o encaminhamento seletivo de stream; e mixar ou gravar áudio e vídeo. Para chamadas de várias partes, há uma série de questões a serem consideradas, especialmente como exibir várias entradas de vídeo e mixar áudio de várias fontes. As plataformas em nuvem, como 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 um 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 WebRTC. O OpenTok tem o Mantis.

Além dos navegadores: VoIP, telefones e mensagens

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

SIP é um protocolo de sinalização usado por sistemas de VoIP e videoconferência. Para permitir a comunicação entre um app da Web 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 ponto a ponto.

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

Vários aplicativos, bibliotecas e plataformas usam a capacidade do WebRTC de comunicação com o mundo exterior:

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

Os desenvolvedores do sipML5 também criaram o gateway webrtc2sip. A Tethr e o Tropo demonstraram um framework para comunicações de desastres "em uma pasta" usando uma célula OpenBTS (link em inglês) para permitir a comunicação entre feature phone e computadores pelo WebRTC. É a comunicação telefônica sem operadora!

Saiba mais

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

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

Apresentação de Chris Wilson sobre SFHTML5 - Introdução a apps WebRTC

O livro de 350 páginas WebRTC: APIs e RTCWEB Protocols of the HTML5 Real-Time Web apresenta muitos detalhes sobre caminhos de dados e sinalização e inclui vários diagramas detalhados de topologia de rede.

WebRTC e Signaling: o que dois anos nos ensinaram (em inglês): postagem do blog da TokBox sobre por que foi uma boa ideia deixar a sinalização de fora das especificações

Um guia prático para a criação de apps WebRTC, de Ben Strong, fornece muitas informações sobre topologias e infraestrutura WebRTC.

O Capítulo WebRTC aborda a arquitetura, os casos de uso e o desempenho do WebRTC de Ilya Grigorik..

Exceto em caso de indicação contrária, o conteúdo desta página é licenciado de acordo com a Licença de atribuição 4.0 do Creative Commons, e as amostras de código são licenciadas de acordo com a Licença Apache 2.0. Para mais detalhes, consulte as políticas do site do Google Developers. Java é uma marca registrada da Oracle e/ou afiliadas.

Última atualização 2013-11-04 UTC.