(Peaaegu) kasutu veebikaamera voogedastus brauserist. Osa 2. WebRTC

Kordagi ühes vanas ja juba mahajäetud artiklis mainisin, kui lihtne ja muretu on edastada videot canvas'ilt veebikanalite kaudu. Selles artiklis rääkisin pinnapealselt, kuidas haarata video kaamerast ja heli mikrofonist kasutades MediaStream API, kuidas saadud voogu kodeerida ja edastada veebikanalite kaudu serverisse. Kuid tegelikult nii ei tehta, edastamiseks kasutatakse kas spetsiaalset tarkvara, mida on vaja installida ja seadistada: näiteks Open Broadcast Software, või kasutatakse WebRTC-d, mis töötab otse välja kastist, see tähendab, et see ei nõua mingite pluginade nagu flash player installimist, mis detsembris Chromiumi brauserist eemaldatakse.

Täna räägime WebRTC-st.

Veebireaalajas suhtlemine (WebRTC) ei ole üks protokoll, vaid terve standardite, protokollide ja JavaScripti API kogum, mis kõik koos võimaldavad omavahelist videot ja heli reaalajas, samuti võivad need olla kasutatud teiste binaarsete andmete edastamiseks. Tüüpiliselt on osalised brauserid, kuid näiteks võib see olla ka mobiilirakendus. Et korraldada p2p suhtlust klientide vahel, peab brauser toetama erinevaid video- ja helikodeerimisviise, paljusid võrguprotokolle ning tagama riistvara interaktsiooni brauseriga (operatsioonisüsteemi kihtide kaudu): veebikaamera ja helikaardid. Kogu see tehnoloogia segu on varjatud JavaScripti API abstraktsiooni taha, et hõlbustada arendajate tööd.

Kõik viib lõpuks kolme API-ni:

  • MediaStream API — rääkisime sellest eelmisel korral, täna kirjutan veel veidi sellest. See teenib eesmärgiks video/heli voogude saamist „riistvaralt“

  • RTCPeerConnection — tagab suhtluse kahe kliendi vahel (p2p)

  • RTCDataChannel — teenib kahe kliendi vahel suvaliste andmete edastamiseks

Audio ja video voogude ettevalmistamine edastamiseks

Kõik algab veebikaamera ja mikrofoni meediavoogude „püüdmisest“. Toored vood ei sobi tõepoolest videokonverentsi korraldamiseks, iga voog tuleb töödelda: parandada kvaliteeti, sünkroonida heli ja video, paigutada sünkroonimismärgid videosse ning tagada vastavus pidevalt muutuvatele kanalite bitrate'i laiustele. Kõik selle teeb brauser, arendaja ei pea muretsema meediavoogude kodeerimise korraldamise pärast. Kaasaegses brauseris on juba olemas programmikihid, mis tegelevad püüdmisega, kvaliteedi parandamisega (kõnest müra ja kaja eemaldamine, pildi parandamine), video ja heli kodeerimisega. Kihtide skeem on näidatud joonisel 1:

(Peaaegu) kasutu veebikaamera voogedastus brauserist. Osa 2. WebRTCJoonis 1. Audio ja video töötlemise kihid brauseris

Kogu töötlemine toimub otse brauseris, lisapalke ei ole vaja. Siiski ei ole 2020. aasta seisuga olukord veel nii roosiline. On brausereid, mis ei toeta täielikult MediaStream API, saate minna lingile ja vaadata tabelit ühilduvusest allosas. Eelkõige pettumust valmistab jälle IE.

Saadud voogudega saab teha väga huvitavaid asju: saab kloonida, muuda video eraldusvõimet, manipuleerida helikvaliteediga, saame võtta ja "kinnitada" Media Stream voogu <video> sildi külge ning vaadata ennast lehe html-is. Samuti saab voogu joonistada canvas'ile, suunata WebGL-i või CSS3, kanda video peale erinevaid filtreid, kinni püüda töödeldud videod canvas'ilt ning seejärel saata serverisse, transkodeerida ja avaldada kõigile soovijatele (tere bigo live, twitch ja teised). Siin ma ei hakka selgitama, kuidas selliseid asju teha, toon välja paar näidet, mis olen leidnud internetist:

https://jeeliz.com/ — poisid tegelevad reaalajas CV-ga Javascriptis. Neil on terve arsenal erinevaid js-raamatukogusid videovoogude töötlemiseks canvas'il: näotuvastus, objektide tuvastamine, filtrite (maskide, nagu Insta) pealekandmine jne. Suurepärane näide sellest, kuidas ilma lisapluginate kasutamiseta saab videoid brauseris reaalajas töödelda.

Canvas captureStream API — API dokumentatsioon video voogude töötlemiseks canvas'il. Juba on toetatud Chrome'is, Operas ja Firefoxis.

RTCPeerConnection

Nüüd oleme jõudnud küsimuseni, kuidas ikkagi video teisele kasutajale edastada? Esiplaanile tõuseb RTCPeerConnection. Kui rääkida lühidalt, peaaegu et sellel sammul peate looma RTCPeerConnection objekti:

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

Ühe valikuna määrame iceServers — see on server, mis aitab tagada ühenduse kahe brauseri vahel, mis asuvad NAT'i taga. See tähendab, et siin lahendatakse probleem: kuidas teada saada kõnekaaslase IP, kui ta on oma pakkuja NAT'i taga? ICE protokoll tuleb appi, tegelikult ei seostu ICE WebRTC-ga üldse, aga sellest räägime hiljem.

Varem saime Usermedia vooge:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Usermedia-voog, tavaliselt on need video ja audio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // iga rada lisame peerConnection'i
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Seejärel toimub peerConnection'il sündmus onnegotiationneeded, mille käitlejas peame looma pakkumise (SDP - Session Description Protocol terminoloogia järgi) ja määrama selle peerConnection'isse meetodi setLocalDescription kaudu. SDP-st — mis see on ja pakkumise ja vastuse formaatidest — räägime hiljem.

Pärast LocalDescription määramist peerConnection, kogub brauser ice-kandidaate, st otsib erinevaid teid suhtlemiseks läbi NAT-i. Käivitub sündmus onicegatheringstatechange. Sündmuse onicegatheringstatechange käitlejas lubame ühenduse webrtc-signaling-serveriga stream, et vahetada Session Description'it kahe poe vahel:

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 on server, mis on vajalik kahe poe vahel session description'i vahetamiseks; see võib olla lihtne websocket või xhr-server mis tahes programmeerimiskeeles. Selle ülesanne on lihtne: võtta session description ühest paarist ja edastada see teisele.

Pärast Session description’ide vahetamist on mõlemad pooled valmis edastama ja vastu võtma videovooge; poole, kes võtab vastu videovoo, korraldatakse событие ontrack peerConnection'is, mille käitlejas saavad saadud rajad määrata <video> ja vaatama oma lemmikvestluspartnerit. Edasi tulevad teooria ja detailid.

Lingid ja kirjandus:

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

https://github.com/pion/webrtc — WebRTC protokollide rakendamine go-s

https://webrtcforthecurious.com/ — raamat vähetutjatelt pion

https://hpbn.co/ — raamat High Perfomance Browser Networking. Detailide kaupa käsitletakse küsimusi, mis on seotud web-rakenduste kõrge jõudluse tagamisega. Lõpus käsitletakse WebRTC-d. Raamat on muidugi vana (2013), kuid ei kaota oma aktuaalsust.

Järgmises osas tahan anda veel natuke teooriat ja praktiliselt käsitleda video voogesituse vastuvõttu ja töötlemist serveris Pioni abil, HLS-i transkodeerimist ffmpeg'i kaudu, et seejärel edastada vaatajatele brauseris.

Kannatamatutele: minu väga toore prototüüp videostreaming'ist veebikaamerast react'i kaudu serveris, mis põhineb pionil, twitch'is (see on lihtsalt eksperiment).

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster