(Quase) streaming inútil de webcam pelo navegador. Parte 2. WebRTC

Certa vez, em um dos artigos antigos e já abandonados, escrevi sobre como é fácil e despretensiosamente transmitir vídeo de um canvas através de websockets. Naquele artigo, falei superficialmente sobre como capturar vídeo da câmera e áudio do microfone usando MediaStream API, como codificar o fluxo obtido e enviá-lo via websockets para o servidor. No entanto, na realidade, isso não é feito; para transmissões, utiliza-se ou software especial que precisa ser instalado e configurado, como Open Broadcast Software, ou emprega-se o WebRTC, que funciona direto da caixa, ou seja, não requer a instalação de nenhum plugin como o flash player, que será removido do navegador Chromium em dezembro.

Hoje, vamos falar sobre WebRTC.

A Comunicação em Tempo Real pela Web (WebRTC) não é um protocolo único, mas uma coleção inteira de padrões, protocolos e APIs JavaScript que juntos garantem comunicações de vídeo e áudio em tempo real peer-to-peer, além de poderem ser usados para transferir quaisquer dados binários. Normalmente, os pares são navegadores, mas isso também pode ser um aplicativo móvel, por exemplo. Para organizar a comunicação p2p entre os clientes, é necessária a compatibilidade do navegador com vários tipos de codificação de vídeo e áudio, suporte a muitos protocolos de rede e interação do hardware com o navegador (através de camadas do sistema operacional): webcams e placas de som. Todo esse emaranhado de tecnologias é ocultado por uma abstração da API JavaScript para conveniência do desenvolvedor.

Tudo se resume a três APIs:

  • MediaStream API — que discutimos na última vez, hoje vou escrever um pouco mais sobre isso. Serve para obter fluxos de vídeo/aúdio do ‘hardware’

  • RTCPeerConnection — assegura comunicações entre dois clientes (p2p)

  • RTCDataChannel — serve para transmitir dados arbitrários entre dois clientes

Preparação de fluxos de áudio e vídeo para transmissão

Tudo começa com a "captura" dos fluxos de mídia da webcam e do microfone. Os fluxos brutos, é claro, não são adequados para uma videoconferência, cada fluxo precisa ser processado: melhorar a qualidade, sincronizar o áudio com o vídeo, marcar pontos de sincronização no fluxo de vídeo, garantir que a taxa de bits corresponda à largura de banda variável. O navegador cuida de tudo isso, o desenvolvedor não precisa se preocupar em codificar os fluxos de mídia. Dentro de um navegador moderno, já existem camadas de software para captura, melhoria da qualidade (eliminar eco e ruído do som, melhorar a imagem), codificação de vídeo e áudio. O esquema das camadas está mostrado na Fig. 1:

(Quase) streaming inútil de webcam pelo navegador. Parte 2. WebRTCFig. 1. Camadas de processamento de áudio e vídeo no navegador

Todo o processamento ocorre diretamente no navegador, nenhum plugin adicional é necessário. No entanto, a situação ainda não é tão otimista em 2020. Existem navegadores que ainda não suportam completamente MediaStream API, você pode seguir o link e, na parte inferior, ver a tabela de compatibilidade. Em particular, o IE desaponta novamente.

Com os fluxos obtidos, é possível fazer coisas muito interessantes: você pode clonar, mudar a resolução do vídeo, manipular a qualidade do áudio, pode "anexar" o fluxo Media Stream ao <video> tag e ver a si mesmo na página HTML. Você também pode desenhar o fluxo no canvas, usar WebGL ou CSS3, aplicar vários filtros ao vídeo, capturar o vídeo processado do canvas e enviá-lo pela rede para o servidor, transcodificar e publicar para todos interessados (olá bigo live, twitch, e outros). Aqui, não vou detalhar como essas coisas são feitas, apenas vou dar alguns exemplos encontrados na web:

https://jeeliz.com/ — um grupo está trabalhando em CV em tempo real com Javascript. Eles têm um verdadeiro arsenal de várias bibliotecas js para trabalhar com fluxos de vídeo no canvas: detecção de rostos, objetos, aplicação de filtros (máscaras, como no Instagram), etc. Um ótimo exemplo de como é possível processar vídeo em tempo real diretamente no navegador sem plugins adicionais.

Canvas captureStream API — documentação da API para captura de fluxos de vídeo do canvas. Já é suportada no Chrome, Opera e Firefox

RTCPeerConnection

Chegamos à parte de como transferir o vídeo para outro usuário? Surge a questão RTCPeerConnectionSe formos resumir, nesta etapa, você precisa criar um objeto RTCPeerConnection:

const peerConnection = new RTCPeerConnection({
  iceServers: [{
    urls: 'stun:stun.l.google.com:19302'
  }]
});

Uma das opções que especificamos é iceServers - é um servidor que ajuda a garantir a conexão entre dois navegadores que estão atrás de um NAT. Ou seja, aqui resolve-se o problema: como descobrir o IP do interlocutor se ele estiver atrás do NAT de seu provedor? O protocolo ICE vem em auxílio, na verdade, o ICE não pertence exatamente ao WebRTC, mas falaremos sobre isso mais adiante.

Anteriormente, obtivemos fluxos de Usermedia:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Fluxos de Usermedia, geralmente são vídeo e áudio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // cada trilha é adicionada ao peerConnection
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Em seguida, no peerConnection, o evento onnegotiationneeded é acionado, no manipulador, devemos criar uma oferta (em termos de SDP - Session Description Protocol) e atribuí-la ao peerConnection através do método setLocalDescription. Sobre o SDP - o que é isso e sobre os formatos de oferta e resposta - falaremos em breve.

Após a designação de LocalDescription no peerConnection, o navegador "coleta" candidatos ice, ou seja, encontra diferentes caminhos para comunicação através do NAT. O evento onicegatheringstatechange é acionado. No manipulador onicegatheringstatechange, permitimos a conexão com o servidor de sinalização webrtc para trocar a Session Description entre os pares:

peerConnection.oniceconnectionstatechange = (event) => {
      console.log('Connection state: ', peerConnection.iceConnectionState);

      if (peerConnection.iceConnectionState === 'connected') {
        // Можем активировать кнопку Start broadcast
        setBroadcasting(true);
        setBroadcastingBtnActive(true);
      }
    };

// Событие срабатывает сразу, как только добавился медаиапоток в peerConnection
peerConnection.onnegotiationneeded = (event) => {
      // Создаем и назначаем SDP offer
      peerConnection.createOffer().
        then((offer) => peerConnection.setLocalDescription(offer)).
        catch(console.error);
    };

// Событие срабатывает каждый раз, как появляется ICE кандидат
peerConnection.onicegatheringstatechange = (ev) => {
      let connection = ev.target;

      // Now we can activate broadcast button
      if (connection.iceGatheringState === 'complete') {
        let delay = 50;
        let tries = 0;
        let maxTries = 3;

        let timerId = setTimeout(function allowStreaming() {
          if (isOnline) {
            setBroadcastingBtnActive(true);
            return;
          }

          if (tries < maxTries) {
            tries += 1;
            delay *= 2;
            timerId = setTimeout(allowStreaming, delay);
          } else {
            // TODO: show user notification
            console.error("Can't connect to server");

            alert("Can't connect to server");
          }
        }, delay);
      }
    };

O servidor de sinalização webrtc é um servidor necessário para garantir a troca da descrição da sessão entre dois pares, pode ser um websocket simples ou um servidor xhr em qualquer linguagem de programação. Sua tarefa é simples: aceitar a descrição da sessão de um par e transmitir a outro.

Após a troca de descrições de sessão, ambas as partes estão prontas para transmitir e receber fluxos de vídeo; do lado que recebe o fluxo de vídeo, o evento ontrack é acionado no peerConnection, no manipulador deste evento, as trilhas recebidas podem ser atribuídas ao

Links e literatura:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection — documentação RTCPeerConnection

https://github.com/pion/webrtc — implementação dos protocolos WebRTC em Go

https://webrtcforthecurious.com/ — um livrinho dos criadores do pion

https://hpbn.co/ — livro High Performance Browser Networking. Detalha questões relacionadas à garantia de alto desempenho de aplicativos da web. No final, descreve sobre WebRTC. É claro que o livro é antigo (2013), mas ainda se mantém relevante.

Na próxima parte, quero fornecer um pouco mais de teoria e analisar na prática a recepção e o processamento de fluxo de vídeo no servidor usando o pion, realizando transcodificação em HLS através do ffmpeg para a transmissão subsequente para os espectadores no navegador.

Para os impacientes: meu protótipo muito rudimentar de transmissão de vídeo de uma webcam no react através de um servidor baseado em pion no twitch (é apenas um experimento).

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster