(Téměř) bezcenné streamování webcam z prohlížeče. Část 2. WebRTC

Nějak v jedné jednom ze starých a již opuštěných článků jsem psal o tom, jak snadno a bez námahy můžeme streamovat video z canvasu přes websockets. V tom článku jsem povrchně popisoval, jak zachytit video z kamery a zvuk z mikrofonu pomocí MediaStream API, jak získaný stream kódovat a posílat přes websockets na server. Ve skutečnosti se však takto nepostupuje, pro streamování se používá buď speciální software, který je třeba nainstalovat a nastavit: tak na rychlo to může být Open Broadcast Software, nebo se využívá WebRTC, který funguje přímo z krabice, tedy nevyžaduje instalaci žádných pluginů jako Flash Player, který již v prosinci odstraní z prohlížeče Chromium.

Dnes se budeme bavit o WebRTC.

Web Real-Time Communication (WebRTC) není jen jeden protokol, ale celá kolekce standardů, protokolů a JavaScript API, které společně poskytují peer-to-peer video-audio komunikaci v reálném čase a mohou být také použity pro přenos jakýchkoli binárních dat. Obvykle jsou peerovými stranami prohlížeče, ale může to být i mobilní aplikace, například. Pro organizaci p2p komunikace mezi klienty je zapotřebí podpora různých typů kódování videa a audia ze strany prohlížeče, podpora mnoha síťových protokolů, zajištění interakce hardwaru s prohlížečem (přes vrstvy OS): webkamerami, zvukovými kartami. Celá tato směs technologických komponentů je skryta za abstrakcí JavaScript API pro usnadnění vývoje.

Vše se nakonec shrnuje do třech API:

  • MediaStream API — rozebírali jsme minule, dnes o něm napíšu ještě trochu víc. Slouží k získávání video/audiostreamů z 'hardwaru'

  • RTCPeerConnection — zajišťuje komunikaci mezi dvěma klienty (p2p)

  • RTCDataChannel — slouží k přenosu libovolných dat mezi dvěma klienty

Příprava audio a video streamů k přenosu

Vše začíná „zachycením“ mediálních toků z webové kamery a mikrofonu. Nezpracované toky samozřejmě nejsou vhodné pro organizaci videokonference; každý tok je třeba zpracovat: zlepšit kvalitu, synchronizovat zvuk s obrazem, umístit synchronizační značky do videostreamu a zajistit odpovídající bitrate v neustále se měnící šířce pásma kanálu. To vše zvládne prohlížeč, vývojář se nemusí starat o zajištění kódování mediálních toků. Uvnitř moderního prohlížeče už jsou přítomny softwarové vrstvy pro zachycení, zlepšení kvality (odstranění ozvěny a šumu ze zvuku, vylepšení obrazu), kódování videa a audia. Schéma vrstev je zobrazeno na obr. 1:

(Téměř) bezcenné streamování webcam z prohlížeče. Část 2. WebRTCObr. 1. Vrstvy zpracování audia a videa v prohlížeči

Veškeré zpracování probíhá přímo v prohlížeči, žádné další pluginy nejsou vyžadovány. Nicméně v roce 2020 to stále není tak růžové. Některé prohlížeče dosud plně nepodporují MediaStream API, můžete přejít na odkaz a úplně dole si prohlédnout tabulku kompatibility. Zejména IE opět zklamává.

S přijatými toky lze dělat velmi zajímavé věci: můžete klonovat, měnit rozlišení videa, manipulovat s kvalitou audia, můžete vzít a „připojit“ Media Stream k <video> tagu a vykouknout na sebe na html stránce. Tok můžete také vykreslit na canvas, aplikovat WebGL nebo CSS3, a dokonce na video aplikovat různé filtry, zachytit zpracované video z canvasu a poté je posílat přes síť na server, transcódovat a publikovat pro všechny zájemce (ahoj bigo live, twitch a další). Zde nebudu rozebírat, jak se takové věci dělají, uvedu pár příkladů nalezených na webu:

https://jeeliz.com/ — kluci se zabývají realtime CV na Javascriptu. Mají celou arsenál různých js-knihoven pro práci s videostreamem na canvasu: detekce tváří, objektů, aplikace filtrů (masky, jako v instagramu) atd. Skvělý příklad toho, jak bez dodatečných pluginů lze přímo v prohlížeči zpracovávat video v reálném čase.

Canvas captureStream API — dokumentace API pro zachycení videostreamu z canvasu. Již podporováno v Chrome, Opeře a Firefoxu.

RTCPeerConnection

A tak jsme se dostali k tomu, jak vlastně přenést video jinému uživateli? Na scénu vystupuje RTCPeerConnectionPokud to shrneme, na tomto kroku je potřeba vytvořit objekt RTCPeerConnection:

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

Jednou z voleb, kterou uvádíme, je iceServers — server, který pomáhá zajistit spojení mezi dvěma prohlížeči nacházejícími se za NAT. Tedy zde se řeší problém: jak zjistit IP adresu udržovatele, pokud je za NAT jeho poskytovatele? S pomocí přichází protokol ICE, který vlastně vůbec nespadá do WebRTC, ale o tom později.

Dříve jsme získali toky Usermedia:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Toky Usermedia, obvykle video a audio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // každý track připojujeme k peerConnection
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Poté na peerConnection dojde k události onnegotiationneeded, ve zpracovatelovi této události musíme vytvořit nabídku (v termínech SDP — Session Description Protocol) a přiřadit ji k peerConnection pomocí metody setLocalDescription. O SDP — co to je a o formátech nabídka a odpověď — si promluvíme později.

Po přiřazení LocalDescription k peerConnection, prohlížeč "shromažďuje" ice kandidáty, tedy hledá různé cesty pro komunikaci přes NAT. Nastává událost onicegatheringstatechange. V zpracovateli onicegatheringstatechange umožníme spojení s webrtc-signaling serverem pro výměnu Session Description mezi 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);
      }
    };

webrtc-signaling server — to je server, který je potřebný k zajištění výměny session description mezi dvěma peer, což může být nejjednodušší websocket nebo xhr server na jakémkoliv programu. Jeho úkolem je jednoduché: přijmout session description od jednoho peer a předat ji druhému.

Po výměně Session descriptions jsou obě strany připraveny streamovat a přijímat video toky, na straně, která přijímá video tok, dochází k události ontrack na peerConnection, v zpracovateli této události můžeme přiřadit příchozí tracky k

Odkazy a literatura:

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

https://github.com/pion/webrtc — implementace protokolů WebRTC v go

https://webrtcforthecurious.com/ — knížečka od tvůrců pion

https://hpbn.co/ — kniha High Performance Browser Networking. V podrobnostech se zabývá otázkami zajištění vysokého výkonu webových aplikací. Na konci se zabývá WebRTC. Kniha je sice stará (2013), ale neztrácí na aktuálnosti.

V další části chci poskytnout ještě trochu teorie a v praxi rozebrat příjem a zpracování video toku na serveru pomocí pion, transkódování do HLS přes ffmpeg pro následné vysílání divákům v prohlížeči.

Pro nedočkavé: můj velmi experimentální prototyp streamování videa z webové kamery na react přes server založený na pion v twitchi (to je jen experiment).

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster