(Cea mai aproape) de streaming inutil de webcam din browser. Partea 2. WebRTC

Odată, în una unele dintre articolele vechi și deja abandonate, am scris despre cât de simplu și fără efort se poate transmite video din canvas prin websockets. În acel articol, am discutat superficial despre cum să captezi video de la cameră și sunet de la microfon prin MediaStream API, cum să codifici fluxul obținut și să-l trimiți prin websockets pe server. Totuși, în realitate, nu se face așa, pentru transmisiuni se folosește fie un software special, care trebuie instalat și configurat: din memorie, acesta poate fi Open Broadcast Software, fie se utilizează WebRTC, care funcționează direct din cutie, adică nu necesită instalarea unor pluginuri precum flash player, care va fi eliminat din browserul Chromium în decembrie.

Astăzi vom vorbi despre WebRTC.

Web Real-Time Communication (WebRTC) nu este un singur protocol, ci o întreagă colecție de standarde, protocoale și API-uri JavaScript care, împreună, asigură comunicații video-audio peer-to-peer în timp real și pot fi utilizate pentru transmiterea oricăror date binare. De obicei, piesele de legătură sunt browserele, dar ar putea fi și o aplicație mobilă, de exemplu. Pentru a organiza comunicarea p2p între clienți, browserul trebuie să suporte diferite tipuri de codificare video și audio, suport pentru numeroase protocoale de rețea și să asigure interacțiunea hardware-ului cu browserul (prin straturile sistemului de operare): camere web, plăci de sunet. Toată această aglomerare de tehnologii este ascunsă în spatele unei abstracții a API-ului JavaScript pentru confortul dezvoltatorului.

Totul se rezumă în cele din urmă la trei API-uri:

  • MediaStream API — am discutat data trecută, astăzi voi scrie puțin mai mult despre el. Este folosit pentru a obține fluxuri video/audio de la «hard»

  • RTCPeerConnection — asigură comunicațiile între doi clienți (p2p)

  • RTCDataChannel — servește pentru transmiterea de date arbitrare între doi clienți

Pregătirea fluxurilor audio și video pentru transmitere

Totul începe cu „captarea” fluxurilor media de la webcam și microfon. Fluxurile brute nu sunt potrivite pentru organizarea conferințelor video; fiecare flux trebuie procesat: îmbunătățirea calității, sincronizarea audio cu video, adăugarea de marcaje de sincronizare în fluxul video, asigurându-se că debitele se adaptează constant la lățimea de bandă a canalului. Browserul se ocupă de toate acestea; dezvoltatorul nu trebuie să își facă griji cu privire la codarea fluxurilor media. În interiorul unui browser modern există deja straturi software pentru captare, îmbunătățirea calității (îndepărtarea ecoului și a zgomotului din sunet, îmbunătățirea imaginii), codarea video și audio. Schema straturilor este prezentată în fig. 1:

(Cea mai aproape) de streaming inutil de webcam din browser. Partea 2. WebRTCFig. 1. Straturile de procesare audio și video în browser

Toată procesarea are loc chiar în browser, fără a necesita pluginuri suplimentare. Totuși, situația nu este atât de roz în 2020. Există încă browsere care nu suportă complet MediaStream API, puteți accesa linkul și vedea tabelul de compatibilitate în partea de jos. În special, IE dezamăgește din nou.

Cu fluxurile obținute, se pot face lucruri foarte interesante: se poate clona, schimba rezoluția video, manipula calitatea audio, se poate „agenția” fluxul Media Stream la eticheta <video> și privi la sine pe pagina HTML. De asemenea, fluxul poate fi redat pe un canvas, fiind utilizat WebGL sau CSS3, aplicând diverse filtre video, captând video procesat de pe canvas și apoi trimis pe rețea către server, transcodificat și publicat celor interesați ( Salut bigo live, twitch și altele). Aici nu voi detalia cum se fac aceste lucruri, ci voi aduce câteva exemple găsite pe internet:

https://jeeliz.com/ — băieții se ocupă cu CV în timp real pe Javascript. Aceștia au un întreg arsenal de biblioteci js diferite pentru lucrul cu fluxul video pe canvas: detectarea fețelor, obiectelor, aplicarea filtrelor (măști, ca în Instagram) etc. Un exemplu excelent de procesare a video-ului în timp real direct în browser, fără pluginuri suplimentare.

API canvas captureStream — documentația API pentru captarea fluxului video de pe canvas. Este deja suportat în Chrome, Opera și Firefox

RTCPeerConnection

Și așa am ajuns la întrebarea, cum să transmitem video altui utilizator? Întrebarea este acum în prim-plan. RTCPeerConnection. Dacă vorbim pe scurt, practic în acest pas trebuie să creați un obiect RTCPeerConnection:

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

Una dintre opțiuni pe care le specificăm este iceServers — acesta este un server care ajută la realizarea conexiunii între două browsere, aflate în spatele unui NAT. Cu alte cuvinte, rezolvăm problema: cum să aflăm adresa IP a interlocutorului, dacă el se află în spatele NAT-ului provider-ului său? Protocolul ICE vine în ajutor, de fapt, ICE nu se leagă de WebRTC, dar despre asta mai târziu.

Anterior am obținut fluxurile Usermedia:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Fluxurile Usermedia, de obicei acestea sunt video și audio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // fiecare track îl adăugăm la peerConnection
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Apoi, pe peerConnection, se declanșează evenimentul onnegotiationneeded, în handlerul căruia trebuie să creăm o ofertă (în termenii SDP — Protocolul de Descriere a Session-ului) și să o atribuim în peerConnection prin metoda setLocalDescription. Vom discuta despre SDP — ce este și despre formatele ofertă și răspuns — mai târziu.

După atribuirea LocalDescription peerConnection, browserul „colectează” candidații ice, adică găsește diverse căi pentru comunicare prin NAT. Se declanșează evenimentul onicegatheringstatechange. În handlerul onicegatheringstatechange, permitem conexiunea cu serverul de semnalizare webrtc pentru a schimba Descrierile Session între perechi:

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

Serverul de semnalizare webrtc — este un server necesar pentru a asigura schimbul de descrieri de sesiune între două perechi, acesta poate fi un simplu server websocket sau xhr pe orice limbaj de programare. Sarcina sa este simplă: să primească descrierea sesiunii de la o pereche și să o transmită celeilalte.

După schimbul de Descrieri ale sesiunii, ambele părți sunt gata să transmită și să primească fluxuri video, pe partea care primește fluxul video se declanșează evenimentul ontrack pe peerConnection, în handlerul căruia, track-urile primite pot fi atribuite la

Linkuri și literatură:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection — documentație RTCPeerConnection

https://github.com/pion/webrtc — implementarea protocoalelor WebRTC în go

https://webrtcforthecurious.com/ — o brosură de la creatorii pion

https://hpbn.co/ — cartea High Performance Browser Networking. Aceasta detaliază aspectele asigurării unei performanțe ridicate a aplicațiilor web. La final sunt discutate WebRTC. Cartea este, desigur, veche (2013), dar își menține relevanța.

În următoarea parte, vreau să ofer o porție suplimentară de teorie și să analizez practic metoda de preluare și procesare a fluxului video pe server folosind pion, transcodarea în HLS prin ffmpeg pentru transmiterea ulterioară spectatorilor în browser.

Pentru nerăbdători: prototipul meu foarte brut pentru transmiterea video de la cameră web pe react printr-un server bazat pe pion în twitch (acesta este doar un experiment).

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster