(Quasi) streaming inutile della webcam dal browser. Parte 2. WebRTC

Qualche tempo fa uno in uno dei vecchi e già abbandonati articoli parlavo di quanto sia facile e spontaneo trasmettere video da un canvas tramite websockets. In quell'articolo ho accennato superficialmente a come catturare il video dalla camera e l'audio dal microfono tramite API MediaStream, come codificare il flusso ottenuto e inviarlo tramite websockets al server. Tuttavia, in realtà non si fa così, per le trasmissioni si utilizzano software speciali da installare e configurare: ad esempio, potrebbe essere Open Broadcast Software, oppure si utilizza WebRTC, che funziona direttamente out-of-the-box, ossia non richiede l'installazione di plugin come flash player, che sarà rimosso da Chromium a dicembre.

Oggi parleremo di WebRTC.

Web Real-Time Communication (WebRTC) non è un singolo protocollo, ma un'intera collezione di standard, protocolli e JavaScript API che insieme forniscono comunicazioni video-audio peer-to-peer in tempo reale e possono essere utilizzati per la trasmissione di qualsiasi tipo di dati binari. Di solito i peer sono i browser, ma può essere anche un'applicazione mobile, per esempio. Per organizzare la comunicazione p2p tra i client è necessaria la compatibilità del browser con diversi formati di codifica video e audio, supporto a numerosi protocolli di rete, e l'interazione della parte hardware con il browser (tramite i layer del SO): webcam, schede audio. Tutta questa mescolanza di tecnologie è nascosta dietro l'astrazione dell'API JavaScript per la comodità dello sviluppatore.

In definitiva si riduce a tre API:

  • API MediaStream — analizzate l'ultima volta, oggi scriverò ancora un po' su di essa. Serve per ricevere flussi video/audio dall'hardware

  • RTCPeerConnection — consente comunicazioni tra due client (p2p)

  • RTCDataChannel — serve per la trasmissione di dati arbitrari tra due client

Preparazione dei flussi audio e video per la trasmissione

Tutto inizia con il "catturare" i flussi video della webcam e del microfono. I flussi grezzi non sono adatti per l'organizzazione di videoconferenze, ogni flusso deve essere elaborato: migliorare la qualità, sincronizzare l'audio con il video, posizionare i marker di sincronizzazione nel flusso video, garantire che il bitrate si adatti continuamente alla larghezza di banda del canale. Il browser si occupa di tutto questo, il programmatore non deve nemmeno preoccuparsi di garantire la codifica dei flussi multimediali. All'interno di un moderno browser ci sono già strati software per la cattura, il miglioramento della qualità (eliminare l'eco e il rumore dal suono, migliorare l'immagine), la codifica video e audio. Lo schema degli strati è mostrato nella figura 1:

(Quasi) streaming inutile della webcam dal browser. Parte 2. WebRTCFig. 1. Strati di elaborazione audio e video nel browser

Tutta l'elaborazione avviene direttamente nel browser, non sono necessari plugin aggiuntivi. Tuttavia, nel 2020 non è ancora tutto roseo. Alcuni browser non supportano ancora completamente API MediaStream, puoi cliccare sul link e vedere in fondo la tabella di compatibilità. In particolare, IE delude ancora una volta.

Con i flussi ottenuti si possono fare cose molto interessanti: si può clonare, cambiare la risoluzione del video, manipolare la qualità dell'audio; si può prendere e "attaccare" il flusso Media Stream a un tag <video> e osservare sé stessi sulla pagina html. E si può anche disegnare il flusso su canvas, e applicare WebGL o CSS3, e sovrapporre vari filtri al video, catturare il video elaborato dal canvas e poi inviarlo su rete al server, transcodificarlo e pubblicarlo a chiunque lo desideri (ciao bigo live, twitch e altri). Qui non esplorerò come fare tali cose, fornirò un paio di esempi trovati in rete:

https://jeeliz.com/ — ragazzi che si occupano di CV in tempo reale su Javascript. Hanno un vero e proprio arsenale di varie librerie js per lavorare con flussi video su canvas: rilevamento di volti, oggetti, applicazione di filtri (maschere, come su Instagram) e altro. Ottimo esempio di come sia possibile elaborare video in tempo reale direttamente nel browser senza plugin aggiuntivi.

Canvas captureStream API — documentazione API per la cattura dei flussi video da canvas. Già supportato in Chrome, Opera e Firefox

RTCPeerConnection

Siamo quindi giunti al punto cruciale: come trasferire effettivamente il video a un altro utente? All'orizzonte si presenta RTCPeerConnection. Parlando brevemente, in questa fase è necessario creare un oggetto RTCPeerConnection:

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

Una delle opzioni è iceServers: un server che aiuta a stabilire una connessione tra due browser che si trovano dietro a un NAT. Quindi qui si risolve il problema: come scoprire l'ip dell'interlocutore se si trova dietro il NAT del suo provider? Interviene il protocollo ICE, che in realtà non è specificamente legato a WebRTC, ma ne parleremo più avanti.

In precedenza abbiamo ottenuto dei flussi Usermedia:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // I flussi Usermedia, di solito sono video e audio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // ciascun tracciato viene aggiunto a peerConnection
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Successivamente, sull'oggetto peerConnection si attiva l'evento onnegotiationneeded, nel gestore del quale dobbiamo creare un'offerta (in termini di SDP — Session Description Protocol) e impostarla in peerConnection tramite il metodo setLocalDescription. Parleremo di SDP: cos'è e dei formati dell'offerta e della risposta in seguito.

Dopo aver assegnato LocalDescription a peerConnection, il browser 'raccoglie' i candidati ice, cioè trova vari percorsi per la comunicazione attraverso il NAT. Si attiva l'evento onicegatheringstatechange. Nel gestore onicegatheringstatechange consentiamo la connessione al server di segnalazione webrtc per scambiare la Session Description tra i peer:

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

Il server di segnalazione webrtc è un server necessario per facilitare lo scambio della session description tra due peer; può essere un semplice server websocket o xhr in qualsiasi linguaggio di programmazione. Il suo compito è semplice: ricevere la session description da un peer e passarla all'altro.

Dopo lo scambio delle Session descriptions, entrambe le parti sono pronte a trasmettere e ricevere flussi video; sul lato che riceve il flusso video si attiva l'evento ontrack su peerConnection, nel gestore del quale i tracciati ricevuti possono essere assegnati a

Riferimenti e letteratura:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection — documentazione RTCPeerConnection

https://github.com/pion/webrtc — implementazione dei protocolli WebRTC in go

https://webrtcforthecurious.com/ — un libretto dagli autori di pion

https://hpbn.co/ — libro High Performance Browser Networking. Vengono esaminati in dettaglio i problemi di alta prestazione delle applicazioni web. Alla fine si parla di WebRTC. Il libro è infatti vecchio (2013), ma non ha perso la sua attualità.

Nella prossima parte voglio fornire un'altra dose di teoria e analizzare praticamente la ricezione e l'elaborazione del flusso video sul server con pivot, la transcoding in HLS attraverso ffmpeg per la successiva trasmissione agli spettatori nel browser.

Per i più impazienti: il mio prototipo molto grezzo per la trasmissione video dalla webcam su react attraverso un server basato su pivot in twitch (è solo un esperimento).

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster