(Casi) streaming inútil de una cámara web desde el navegador. Parte 2. WebRTC

En algún momento, uno en uno de mis artículos antiguos y ya olvidados, escribí sobre lo fácil y sencillo que es transmitir video desde un canvas a través de websockets. En ese artículo, hice un resumen de cómo capturar video de una cámara y audio de un micrófono usando API MediaStream, cómo codificar el flujo resultante y enviarlo a través de websockets a un servidor. Sin embargo, en la realidad no se hace así; para las transmisiones se utiliza software especializado que debe ser instalado y configurado: por ejemplo, se podría usar Open Broadcast Software, o se puede recurrir a WebRTC, que funciona directamente sin necesidad de instalar ningún tipo de plugins como Flash Player, el cual será eliminado del navegador Chromium en diciembre.

Hoy hablaremos sobre WebRTC.

La Comunicación en Tiempo Real en la Web (WebRTC) no es un único protocolo, sino un conjunto completo de estándares, protocolos y APIs de JavaScript que en conjunto permiten comunicación de video y audio entre pares en tiempo real, así como la transmisión de cualquier tipo de datos binarios. Generalmente, los pares son los navegadores, pero también puede ser una aplicación móvil, por ejemplo. Para organizar la comunicación p2p entre clientes, se requiere que el navegador soporte varios tipos de codificación de video y audio, soporte para múltiples protocolos de red, y asegure la interacción del hardware con el navegador (a través de capas del sistema operativo): cámaras web, tarjetas de sonido. Todo este conjunto de tecnologías está oculto detrás de una abstracción en la API de JavaScript para la comodidad del desarrollador.

Todo se reduce en última instancia a tres APIs:

  • API MediaStream — que discutimos la última vez, hoy escribiré un poco más sobre ello. Sirve para obtener flujos de video/audio del 'hardware'

  • RTCPeerConnection — facilita la comunicación entre dos clientes (p2p)

  • RTCDataChannel — se utiliza para la transmisión de datos arbitrarios entre dos clientes

Preparación de flujos de audio y video para la transmisión

Todo comienza con la "captura" de los flujos de medios de la cámara web y del micrófono. Los flujos en bruto, por supuesto, no son adecuados para organizar una teleconferencia; cada flujo necesita ser procesado: mejorar la calidad, sincronizar el audio con el video, colocar marcas de sincronización en el flujo de video y garantizar que el bitrate se ajuste a la variabilidad del ancho de banda. El navegador se encarga de todo esto, el desarrollador ni siquiera necesita preocuparse por proporcionar la codificación de los flujos de medios. Dentro de un navegador moderno ya existen capas de software para captura y mejora de calidad (eliminar eco y ruido del sonido, mejorar la imagen), y codificación de video y audio. El esquema de capas se muestra en la Fig. 1:

(Casi) streaming inútil de una cámara web desde el navegador. Parte 2. WebRTCFig. 1. Capas de procesamiento de audio y video en el navegador

Todo el procesamiento ocurre directamente en el navegador, no se requieren plugins adicionales. Sin embargo, en 2020, la situación aún no es alentadora. Existen navegadores que todavía no son totalmente compatibles API MediaStream, puedes seguir el enlace y al final ver la tabla de compatibilidad. En particular, IE decepciona nuevamente.

Con los flujos obtenidos se pueden hacer cosas muy interesantes: se pueden clonar, cambiar la resolución de video, manipular la calidad del audio, se puede 'adjuntar' el flujo de Media Stream a un

https://jeeliz.com/ — los chicos se dedican a CV en tiempo real usando Javascript. Tienen un completo arsenal de diversas bibliotecas js para trabajar con flujos de video en canvas: detección de rostros, objetos, aplicación de filtros (máscaras, como en Instagram) y más. Un gran ejemplo de cómo se puede procesar video en tiempo real directamente en el navegador sin plugins adicionales.

API de captureStream de Canvas — documentación de la API para capturar flujos de video desde canvas. Ya es compatible con Chrome, Opera y Firefox.

RTCPeerConnection

Y así llegamos a la pregunta de cómo transmitir video a otro usuario. En primer plano aparece RTCPeerConnectionSi hablamos brevemente, en esta etapa prácticamente necesitarás crear un objeto RTCPeerConnection:

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

Una de las opciones que indicamos es iceServers, que es un servidor que ayuda a establecer una conexión entre dos navegadores que se encuentran detrás de un NAT. Es decir, aquí se resuelve el problema: ¿cómo saber la IP del interlocutor si está detrás del NAT de su proveedor? El protocolo ICE viene al rescate; en realidad, ICE no está relacionado con WebRTC, pero de eso hablaremos más adelante.

Anteriormente, obtuvimos flujos de Usermedia:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Flujos de Usermedia, generalmente esto es video y audio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // cada pista se une a peerConnection
     peerConnection.addTrack(track);
   }
}).catch(console.error);

A continuación, en peerConnection se activa el evento onnegotiationneeded, en el manejador de este debemos crear una oferta (en términos de SDP - Session Description Protocol) y asignarla a peerConnection a través del método setLocalDescription. Sobre SDP - qué es y sobre los formatos de oferta y respuesta - hablaremos más adelante.

Después de asignar LocalDescription a peerConnection, el navegador "recoge" a los candidatos ice, es decir, encuentra diferentes caminos para la comunicación a través del NAT. Se activa el evento onicegatheringstatechange. En el manejador onicegatheringstatechange permitimos la conexión con el servidor de señalización webrtc para intercambiar la descripción de la sesión entre los 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);
      }
    };

El servidor de señalización webrtc es necesario para facilitar el intercambio de la descripción de la sesión entre dos pares; puede ser un websocket simple o un servidor xhr en cualquier lenguaje de programación. Su tarea es simple: recibir la descripción de la sesión de un par y pasarla al otro.

Después de intercambiar las descripciones de la sesión, ambas partes están listas para transmitir y recibir flujos de video; en el lado que recibe el flujo de video se activa el evento ontrack en peerConnection, en cuyo manejador se pueden asignar las pistas obtenidas al

Enlaces y literatura:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection — documentación RTCPeerConnection

https://github.com/pion/webrtc — implementación de protocolos WebRTC en go

https://webrtcforthecurious.com/ — libro de los creadores de pion

https://hpbn.co/ — libro High Performance Browser Networking. Detalla las cuestiones relacionadas con el rendimiento de aplicaciones web. Al final se describe WebRTC. El libro, aunque es un poco antiguo (2013), no pierde su relevancia.

En la siguiente parte, quiero ofrecer un poco más de teoría y en la práctica desglosar el método y el procesamiento de stream de video en el servidor a través de pion, la transcodificación a HLS mediante ffmpeg para la posterior transmisión a los espectadores en el navegador.

Para los impacientes: mi prototipo muy rudimentario de transmisión de video desde una webcam en react a través de un servidor basado en pion en twitch (esto es solo un experimento).

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster