(Næsten) ubrugelig streaming af webcam fra browseren. Del 2. WebRTC

Engang i en af de gamle og allerede glemte artikler skrev jeg om, hvor let og ubesværet man kan streame video fra canvas via websockets. I den artikel fortalte jeg overfladisk om, hvordan man kan optage video fra kameraet og lyd fra mikrofonen ved hjælp af , hvordan man kan kode den modtagne strøm og sende den gennem websockets til serveren. Men i virkeligheden gør man ikke sådan; til streaming bruger man enten særlig software, som man skal installere og konfigurere: en hurtig eksempel kunne være MediaStream APIOpen Broadcast Software , eller man bruger WebRTC, som fungerer lige ud af boksen, uden at kræve installation af plugins ala flash player, som i december vil blive fjernet fra Chromium-browseren.I dag taler vi om WebRTC.

Web Real-Time Communication (WebRTC) er ikke bare et protokol, det er en hel samling af standarder, protokoller og JavaScript API'er, der samlet set muliggør peer-to-peer video-lyd kommunikation i realtid og kan bruges til overførsel af enhver binær data. Normalt fungerer browsere som peering-enheder, men det kan også være en mobilapplikation, for eksempel. For at organisere p2p kommunikation mellem klienter kræves det, at browseren understøtter forskellige typer video- og lydkodning, understøttelse af mange netværksprotokoller samt sikring af interaktion mellem hardware og browser (gennem OS-lag): webcams, lydkort. Hele denne blanding af teknologier er skjult bag en abstraktion af JavaScript API for udviklerens bekvemmelighed.

Det hele ender i sidste ende med tre API'er:

— som vi dækkede sidste gang, i dag vil jeg skrive lidt mere om det. Det bruges til at modtage video/lyd streams fra 'hardwaren'

Подготовка аудио и видео потоков к передаче

Det hele starter med "fangsten" af medie-strømme fra webcam og mikrofon. Rå strømme er naturligvis ikke egnede til at organisere telekonferencer, hver strøm skal behandles: forbedre kvaliteten, synkronisere lyd med video, placere synkroniseringsmærker i videostrømmen, og sikre en passende bitrate, der konstant tilpasser sig kanalens båndbredde. Browseren håndterer alt dette, udviklere behøver ikke bekymre sig om at sikre kodning af mediestrømme. Inden i en moderne browser findes der allerede softwarelag til optagelse, kvalitetsforbedring (fjerne ekko og støj fra lyden, forbedre billedet), samt kodning af video og lyd. Lag-diagrammet er vist i fig. 1:

(Næsten) ubrugelig streaming af webcam fra browseren. Del 2. WebRTCFig. 1. Lag af audio og video behandling i browseren

Al behandling foregår direkte i selve browseren, ingen yderligere plugins er nødvendige. Men det er stadig ikke så rosenrødt i 2020. Der er stadig browsere, som ikke helt understøtter MediaStream API, du kan klikke på linket og se kompatibilitetstabellen helt nederst. Især skuffer IE igen.

Med de indhentede strømme kan man lave meget interessante ting: man kan klone, ændre videaufløsningen, manipulere lydkvaliteten, man kan "hæfte" en Media Stream strøm til

https://jeeliz.com/ — folk arbejder med realtime CV på Javascript. De har en hel arsenal af forskellige js-biblioteker til at arbejde med videostrømmen på canvas: ansigts- og objektgenkendelse, påføring af filtre (masker, som på Instagram) osv. Et fremragende eksempel på, hvordan man kan behandle video i realtid direkte i browseren uden yderligere plugins.

Canvas captureStream API — API-dokumentation til optagelse af videostrøm fra canvas. Allerede understøttet i Chrome, Opera og Firefox.

— muliggør kommunikation mellem to klienter (p2p)

Nu er vi nået frem til, hvordan man egentlig sender video til en anden bruger? Det første skridt er — muliggør kommunikation mellem to klienter (p2p)For a brief overview, at this stage, you need to create a RTCPeerConnection object:

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

One of the options is to specify iceServers — this is a server that helps establish a connection between two browsers behind a NAT. This addresses the issue of how to find out the IP of the dialog partner if they are behind their provider's NAT. The ICE protocol comes to the rescue; in fact, ICE is not directly related to WebRTC, but more on that later.

Previously, we obtained Usermedia streams:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Usermedia streams, usually this is video and audio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // each track is added to peerConnection
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Next, the onnegotiationneeded event is triggered on peerConnection, where we need to create an offer (in terms of SDP — Session Description Protocol) and set it in peerConnection using the setLocalDescription method. We'll discuss what SDP is and the formats of offer and answer later.

After assigning LocalDescription to peerConnection, the browser 'gathers' ICE candidates, which means it finds different paths for communication through NAT. The onicegatheringstatechange event is triggered. In the onicegatheringstatechange handler, we allow the connection with the webrtc-signaling server stream to exchange Session Description between 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);
      }
    };

The webrtc-signaling server is required to facilitate the exchange of session descriptions between two peers. This can be a simple websocket or XHR server in any programming language. Its task is straightforward: to receive a session description from one peer and relay it to the other.

After exchanging Session descriptions, both parties are ready to transmit and receive video streams. On the receiving side, when receiving the video stream, the ontrack event is triggered on peerConnection, and in its handler, the received tracks can be assigned to

References and literature:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection — documentation — muliggør kommunikation mellem to klienter (p2p)

https://github.com/pion/webrtc — implementation of WebRTC protocols in Go

https://webrtcforthecurious.com/ — booklet from the creators of Pion

https://hpbn.co/ — the book High Performance Browser Networking. It delves into issues of ensuring high performance in web applications. At the end, it discusses WebRTC. The book is somewhat dated (2013), but still relevant.

I den næste del vil jeg gerne give lidt mere teori og praktisk opdeling af metoden til håndtering af videostrømmen på serveren ved hjælp af pion, transkodning til HLS via ffmpeg til efterfølgende transmission til seerne i browseren.

For de utålmodige: Min meget rå prototype til videostreaming fra et webkamera til react via en server baseret på pion til twitch. (det er bare et eksperiment).

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster