(Nästan) värdelös webbkameraströmning från en webbläsare. Del 2. WebRTC

På något sätt in одной från gamla och redan övergivna artiklar skrev jag om hur enkelt och naturligt man kan sända video från canvas via websockets. I den artikeln pratade jag kort om hur man fångar video från en kamera och ljud från en mikrofon med hjälp av MediaStream API, hur man kodar den mottagna strömmen och skickar den via websockets till servern. Men i verkligheten gör de inte detta, för sändningar använder de antingen speciell programvara som måste installeras och konfigureras: direkt, detta kan vara Open Broadcast Software, eller så använder de WebRTC, som fungerar direkt, det vill säga att det inte kräver installation av några plugins a la flash player, som kommer att klippas ut ur Chromium-webbläsaren redan i december.

Idag ska vi prata om WebRTC.

Web Real-Time Communication (WebRTC) är inte ett enda protokoll, det är en hel samling standarder, protokoll och JavaScript API:er som tillsammans tillhandahåller peer-to-peer video-ljudkommunikation i realtid, och kan även användas för att överföra alla binära data. Vanligtvis fungerar webbläsare som peers, men det kan till exempel också vara en mobilapplikation. För att organisera p2p-kommunikation mellan klienter krävs webbläsarstöd för olika typer av video- och ljudkodning, stöd för en mängd olika nätverksprotokoll, vilket säkerställer interaktion mellan hårdvara och webbläsare (genom OS-lager): webbkameror, ljudkort. Allt detta myller av teknologier är gömt bakom JavaScript API-abstraktionen för utvecklarens bekvämlighet.

Det hela kokar ner till tre API:er:

  • MediaStream API — demonterade förra gången, idag ska jag skriva lite mer om honom. Fungerar för att ta emot video-/ljudströmmar från hårdvaran

  • RTCPeerConnection — ger kommunikation mellan två klienter (p2p)

  • RTCDataChannel - tjänar till att överföra godtyckliga data mellan två klienter

Förbereder ljud- och videoströmmar för överföring

Allt börjar med att "fånga" webbkameran och mikrofonens mediaströmmar. Naturligtvis är råströmmar inte lämpliga för att organisera en telekonferens, varje ström måste bearbetas: förbättra kvaliteten, synkronisera ljud med video, placera synkroniseringsmärken i videoströmmen, och se till att bithastigheten motsvarar den ständigt föränderliga bandbredden på kanalen . Webbläsaren tar hand om allt detta, utvecklaren behöver inte ens oroa sig för att tillhandahålla kodning för mediaströmmar. Inuti en modern webbläsare finns det redan mjukvarulager för att fånga, förbättra kvaliteten (ta bort eko och brus från ljud, förbättra bilden), video- och ljudkodning. Lagerschemat visas i fig. 1:

(Nästan) värdelös webbkameraströmning från en webbläsare. Del 2. WebRTCRis. 1. Lager av ljud- och videobehandling i webbläsaren

All bearbetning sker direkt i själva webbläsaren, inga ytterligare. inga plugins krävs. Men det är fortfarande inte så ljust för 2020. Det finns webbläsare som ännu inte har fullt stöd MediaStream API, du kan följa länken och se kompatibilitetstabellen längst ner. Särskilt IE är återigen en besvikelse.

Du kan göra mycket intressanta saker med de mottagna strömmarna: du kan klona, ​​ändra videoupplösningen, manipulera ljudkvaliteten, du kan ta och "haka" Media Stream-strömmen till tagga och titta på dig själv på html-sidan. Eller så kan du rita en ström på canvas, och ställa in WebGL eller CSS3, och tillämpa olika filter på video, fånga den bearbetade videon från canvas och sedan skicka den över nätverket till servern, koda om och publicera till alla (hej bigo live, twitch och andra). Här kommer jag inte att analysera hur sådana saker går till, jag kommer att ge ett par exempel som finns på webben:

https://jeeliz.com/ - killarna gör realtids-CV i Javascript. De har en helhet arsenal olika js-bibliotek för att arbeta med en videoström på canvas: upptäcka ansikten, objekt, applicera filter (masker, som i Instagram), etc. Ett utmärkt exempel på hur du kan bearbeta realtidsvideo direkt i webbläsaren utan extra plugins.

Canvas captureStream API - API-dokumentation för att fånga en videoström från canvas. Stöds redan i Chrome, Opera och Firefox

RTCPeerConnection

Så vi kom till punkten, men hur överför man faktiskt videon till en annan användare? Kommer i förgrunden RTCPeerConnection. Kort sagt, i nästan detta steg måste du skapa ett RTCPeerConnection-objekt:

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

Vi anger iceServers som ett av alternativen - det här är en server som hjälper till att tillhandahålla en anslutning mellan två webbläsare bakom NAT'om. Det vill säga, problemet är löst här: hur får man reda på samtalspartnerns ip om han ligger bakom NAT från sin leverantör? ICE-protokollet kommer till undsättning, faktiskt, ICE gäller inte alls för WebRTC, men mer om det senare.

Tidigare fick vi Usermedia-strömmar:

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ärefter aktiveras händelsen som krävs för förhandling på peerConnection, i vars hanterare vi måste skapa ett erbjudande (i termer av SDP - Session Description Protocol) och tilldela det till peerConnection via metoden setLocalDescription. Om SDP - vad det är och om erbjudandet och svarsformaten - kommer vi att prata vidare.

Efter att ha tilldelats en LocalDescription peerConnection "sammansätter" webbläsaren iskandidater, det vill säga hittar olika sätt att kommunicera via NAT. Händelsen onicegatheringstatechange avfyras. I onicegatheringstatechange-hanteraren tillåter vi en anslutning till webrtc-signaling-server-strömmen för att utbyta sessionsbeskrivningen mellan peers:

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-servern är den server som krävs för att utbyta sessionsbeskrivningen mellan två peers, det kan vara den enklaste websocket eller xhr-servern på vilken PL som helst. Dess uppgift är enkel: att acceptera en sessionsbeskrivning från en kamrat och överföra den till en annan.

Efter utbytet av sessionsbeskrivningar är båda sidor redo att sända och ta emot videoströmmar, på den sida som tar emot videoströmmen utlöses ontrack-händelsen på peerConnection, i vars hanterare de mottagna spåren kan tilldelas till och titta på din favoritsamtalare. Ytterligare teori och detaljer.

Länkar och litteratur:

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

https://github.com/pion/webrtc - Implementering av WebRTC-protokoll på gång

https://webrtcforthecurious.com/ - en bok från skaparna av pion

https://hpbn.co/ - Boka högpresterande webbläsarnätverk. Frågan om att säkerställa hög prestanda för webbapplikationer diskuteras i detalj. I slutet beskrivs WebRTC. Boken är förvisso gammal (2013), men tappar inte sin relevans.

I nästa del vill jag ge lite mer del av teorin och i praktiken analysera mottagningen och bearbetningen av en videoström på servern med hjälp av pion, omkodning till HLS via ffmpeg för efterföljande sändning till tittare i webbläsaren.

För den otåliga: min väldigt grova prototyp av att sända video från en webbkamera för att reagera genom en pionbaserad server i twitch (det här är bara ett experiment).

Källa: will.com

Lägg en kommentar