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

Nějak dovnitř jeden ze starých a již opuštěných článků jsem psal o tom, jak snadno a přirozeně můžete vysílat video z plátna přes websockets. V tomto článku jsem krátce hovořil o tom, jak zachytit video z kamery a zvuk z mikrofonu pomocí MediaStream API, jak zakódovat přijatý stream a odeslat jej přes websockets na server. Ve skutečnosti to však nedělají, pro vysílání používají buď speciální software, který je třeba nainstalovat a nakonfigurovat: mimochodem to lze Otevřený software pro vysílání, nebo používají WebRTC, který funguje hned po vybalení, to znamená, že nevyžaduje instalaci žádných pluginů a la flash player, který bude z prohlížeče Chromium vystřižen již v prosinci.

Dnes budeme mluvit o WebRTC.

Webová komunikace v reálném čase (WebRTC) není jediný protokol, je to celá sbírka standardů, protokolů a rozhraní API JavaScript, které společně poskytují video-audio komunikaci v reálném čase typu peer-to-peer a lze je také použít k přenosu jakéhokoli binární data. Obvykle prohlížeče fungují jako peers, ale může to být například i mobilní aplikace. Pro organizaci p2p komunikace mezi klienty je vyžadována podpora prohlížeče pro různé typy kódování videa a zvuku, podpora různých síťových protokolů, zajištění interakce hardwaru s prohlížečem (prostřednictvím vrstev OS): webové kamery, zvukové karty. Všechny tyto směsice technologií jsou skryty za abstrakcí JavaScript API pro pohodlí vývojáře.

Vše se scvrkává na tři API:

  • MediaStream API — minule rozebraný, dnes o něm napíšu něco víc. Slouží k příjmu video / audio streamů 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ů pro přenos

Vše začíná „zachycením“ mediálních toků z webové kamery a mikrofonu. Surové streamy samozřejmě nejsou vhodné pro pořádání telekonference, každý stream je potřeba zpracovat: zlepšit kvalitu, synchronizovat zvuk s videem, umístit synchronizační značky do video streamu a zajistit bitrate odpovídající neustále se měnící šířce pásma kanálu . O to vše se stará prohlížeč, vývojář se nemusí starat ani o zajištění kódování pro mediální streamy. Uvnitř moderního prohlížeče již existují softwarové vrstvy pro snímání, zlepšování kvality (odstranění ozvěny a šumu ze zvuku, vylepšení obrazu), kódování videa a zvuku. Schéma vrstev je znázorněno na Obr. 1:

(Téměř) zbytečné streamování z webové kamery z prohlížeče. Část 2. WebRTCRýže. 1. Vrstvy zpracování zvuku a videa v prohlížeči

Veškeré zpracování probíhá přímo v samotném prohlížeči, žádné další. nejsou potřeba žádné pluginy. Pro rok 2020 to však stále není tak růžové. Existují prohlížeče, které ještě plně nepodporují MediaStream API, můžete následovat odkaz a zobrazit tabulku kompatibility úplně dole. Zejména IE je opět zklamáním.

S přijatými streamy můžete dělat velmi zajímavé věci: můžete klonovat, měnit rozlišení videa, manipulovat s kvalitou zvuku, můžete stream Media Stream převzít a „zavěsit“ tag a podívejte se na sebe na html stránce. Nebo můžete nakreslit stream na plátno a nastavit WebGL nebo CSS3 a použít na video různé filtry, zachytit zpracované video z plátna a poté ho poslat přes síť na server, překódovat a publikovat všem (ahoj bigo live, twitch a další). Zde nebudu analyzovat, jak se takové věci dělají, uvedu několik příkladů nalezených na webu:

https://jeeliz.com/ - kluci dělají CV v reálném čase v Javascriptu. Mají celek arzenál různé knihovny js pro práci s video streamem na plátně: detekce tváří, objektů, aplikace filtrů (masky, jako na Instagramu) atd. Výborná ukázka toho, jak lze zpracovávat video v reálném čase přímo v prohlížeči bez dalších pluginů.

Canvas captureStream API - Dokumentace API pro zachycení video streamu z plátna. Již je podporováno v prohlížečích Chrome, Opera a Firefox

RTCPeerConnection

Došli jsme tedy k věci, ale jak vlastně video přenést na jiného uživatele? Do popředí se dostává RTCPeerConnection. Stručně řečeno, téměř v tomto kroku musíte vytvořit objekt RTCPeerConnection:

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

Jako jednu z možností uvádíme iceServers - jedná se o server, který pomáhá zajistit spojení mezi dvěma prohlížeči za NAT'om. To znamená, že problém je zde vyřešen: jak zjistit IP partnera, pokud je za NAT svého poskytovatele? Na pomoc přichází protokol ICE, ve skutečnosti se ICE na WebRTC vůbec nevztahuje, ale o tom později.

Dříve jsme měli streamy Usermedia:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Usermedia-потоки, обычно это видео и аудио 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // каждый трек присоединяем к peerConnection
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Dále se na peerConnection spustí událost onnegotiationneeded, v jejímž handleru musíme vytvořit nabídku (ve smyslu SDP - Session Description Protocol) a přiřadit ji k peerConnection pomocí metody setLocalDescription. O SDP - co to je a o formátech nabídek a odpovědí - si povíme dále.

Po přiřazení LocalDescription peerConnection prohlížeč „sestaví“ ledové kandidáty, to znamená, že najde různé způsoby komunikace přes NAT. Spustí se událost onicegatheringstatechange. V obslužné rutině onicegatheringstatechange umožňujeme připojení k streamu webrtc-signaling-server za účelem výměny popisu relace mezi partnery:

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 je server potřebný k výměně popisu relace mezi dvěma peery, může to být nejjednodušší websocket nebo xhr-server na jakémkoli PL. Jeho úkol je jednoduchý: přijmout popis relace od jednoho peer a přenést jej na druhého.

Po výměně popisů relace jsou obě strany připraveny vysílat a přijímat video streamy, na straně, která přijímá video stream, se událost ontrack spustí na peerConnection, v jehož handleru lze přijímané stopy přiřadit a podívejte se na svého oblíbeného partnera. Další teorie a detaily.

Odkazy a literatura:

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

https://github.com/pion/webrtc - implementace protokolů WebRTC na cestách

https://webrtcforthecurious.com/ - kniha od tvůrců pionu

https://hpbn.co/ - Kniha High Performance Browser Networking. Podrobně je diskutována problematika zajištění vysokého výkonu webových aplikací. Na konci je popsán WebRTC. Kniha je jistě stará (2013), ale neztrácí na aktuálnosti.

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

Pro netrpělivé: můj velmi hrubý prototyp vysílání videa z webové kamery na reakci přes server založený na pionu v škubnutí (toto je jen experiment).

Zdroj: www.habr.com

Přidat komentář