(Fast) nutzloses Streaming von Webcams aus dem Browser. Teil 2. WebRTC

Eines Tages in einem der alten und bereits verlassenen Artikel habe ich darüber geschrieben, wie einfach und unkompliziert es ist, Video von einem Canvas über Websockets zu streamen. In diesem Artikel habe ich oberflächlich erläutert, wie man Video von der Kamera und den Ton vom Mikrofon mittels MediaStream API, wie man den erhaltenen Stream kodiert und über Websockets an den Server sendet. In der Realität wird das jedoch nicht so gemacht, für Streams verwendet man entweder spezielle Software, die man installieren und einstellen muss, wie zum Beispiel Open Broadcast Software, oder man nutzt WebRTC, das sofort einsatzbereit ist, das heißt, es erfordert keine Installation von Plugins wie Flash Player, der im Dezember aus dem Chromium-Browser entfernt wird.

Heute sprechen wir über WebRTC.

Web Real-Time Communication (WebRTC) ist nicht nur ein Protokoll, sondern eine gesamte Sammlung von Standards, Protokollen und JavaScript-APIs, die zusammen Peer-to-Peer-Video- und Audio-Kommunikation in Echtzeit ermöglichen und auch zur Übertragung beliebiger binärer Daten verwendet werden können. In der Regel agieren Browser als Teilnehmer, aber auch Mobile Anwendungen können beteiligt sein. Um eine p2p-Kommunikation zwischen Clients zu organisieren, bedarf es der Unterstützung verschiedener Video- und Audio-Codierungsarten durch den Browser, der Unterstützung zahlreicher Netzwerkprotokolle und der Gewährleistung der Interaktion zwischen der Hardware und dem Browser (über OS-Schichten): Webcams und Soundkarten. Alle diese Technologien werden durch die Abstraktion der JavaScript-API für die Bequemlichkeit des Entwicklers verborgen.

Letztendlich läuft alles auf drei APIs hinaus:

  • MediaStream API — wir haben das beim letzten Mal behandelt, heute schreibe ich noch ein wenig darüber. Dient zur Erfassung von Video-/Audio-Streams von der Hardware.

  • RTCPeerConnection — ermöglicht die Kommunikation zwischen zwei Clients (p2p)

  • RTCDataChannel — dient zur Übertragung beliebiger Daten zwischen zwei Clients.

Vorbereitung von Audio- und Video-Streams zur Übertragung.

Alles beginnt mit der "Erfassung" von Medienströmen der Webcam und des Mikrofons. Rohströme sind natürlich nicht geeignet, um eine Videokonferenz zu organisieren; jeder Stream muss bearbeitet werden: die Qualität verbessern, Audio mit Video synchronisieren, Synchronisierungspunkte im Video-Stream setzen und eine an die sich ständig ändernde Bandbreite angepasste Bitrate sicherstellen. Der Browser übernimmt das alles, der Entwickler muss sich nicht um die Kodierung der Medienströme kümmern. Im modernen Browser sind bereits Software-Schichten für die Erfassung, Qualitätsverbesserung (Echo und Rauschen reduzieren, Bildqualität verbessern), sowie für die Kodierung von Video und Audio enthalten. Das Schema der Schichten ist in Abb. 1 dargestellt:

(Fast) nutzloses Streaming von Webcams aus dem Browser. Teil 2. WebRTCAbb. 1. Schichten der Audio- und Videoverarbeitung im Browser

Die gesamte Verarbeitung erfolgt direkt im Browser, es sind keine zusätzlichen Plugins erforderlich. Allerdings ist die Lage im Jahr 2020 noch nicht so rosig. Es gibt immer noch Browser, die nicht vollständig unterstützen MediaStream API, Sie können dem Link folgen und ganz unten die Kompatibilitätstabelle ansehen. Besonders IE enttäuscht wieder.

Mit den erhaltenen Streams lassen sich interessante Dinge anstellen: Man kann Videos klonen, die Auflösung ändern, die Audioqualität manipulieren, und man kann den Media Stream an ein

https://jeeliz.com/ — Eine Gruppe von Leuten beschäftigt sich mit real-time CV in Javascript. Sie haben ein ganzes Arsenal verschiedener Javascript-Bibliotheken für die Arbeit mit Videostreams auf dem Canvas: Gesichtserkennung, Objekterkennung, Filteranwendung (Masken, wie bei Instagram) usw. Ein hervorragendes Beispiel dafür, wie man ohne zusätzliche Plugins direkt im Browser Videos in Echtzeit verarbeiten kann.

Canvas captureStream API — API-Dokumentation zum Erfassen von Videostreams vom Canvas. Wird bereits in Chrome, Opera und Firefox unterstützt.

RTCPeerConnection

Nun stellen sich die Fragen, wie man das Video eigentlich an einen anderen Nutzer überträgt? Im Vordergrund steht RTCPeerConnection. Kurz gesagt, in diesem Schritt müssen Sie ein RTCPeerConnection-Objekt erstellen:

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

Eine der Optionen, die wir angeben, sind iceServers – dies ist der Server, der hilft, eine Verbindung zwischen zwei Browsern herzustellen, die sich hinter einem NAT befinden. Hier wird also das Problem gelöst: Wie erfährt man die IP des Gegenübers, wenn er sich hinter dem NAT seines Anbieters befindet? Hier kommt das ICE-Protokoll ins Spiel; eigentlich gehört ICE nicht zum WebRTC, aber dazu später mehr.

Zuvor haben wir die Usermedia-Streams erhalten:

navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
  // Usermedia-Streams, normalerweise Video und Audio 
  const tracks = stream.getTracks();

   for (const track of tracks) {
     // jeden Track fügen wir zur peerConnection hinzu
     peerConnection.addTrack(track);
   }
}).catch(console.error);

Als nächstes wird bei der peerConnection das Ereignis onnegotiationneeded ausgelöst, in dessen Handler wir ein Angebot (in den Begriffen von SDP – Session Description Protocol) erstellen und dieses über die Methode setLocalDescription in der peerConnection festlegen. Was SDP ist und welche Formate Angebot und Antwort haben – dazu später mehr.

Nachdem die LocalDescription für die PeerConnection festgelegt wurde, „sammelt“ der Browser ICE-Kandidaten, das heißt, er findet verschiedene Wege zur Kommunikation über NAT. Das Ereignis onicegatheringstatechange wird ausgelöst. Im Handler onicegatheringstatechange erlauben wir die Verbindung zum WebRTC-Signalisierungsserver stream für den Austausch der Session Description zwischen den 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);
      }
    };

Der WebRTC-Signalisierungsserver ist ein Server, der notwendig ist, um den Austausch von Session Descriptions zwischen zwei Peers zu gewährleisten. Dies kann ein einfacher WebSocket- oder XHR-Server in jeder Programmiersprache sein. Seine Aufgabe ist einfach: die Session Description von einem Peer zu empfangen und an den anderen weiterzuleiten.

Nach dem Austausch der Session Descriptions sind beide Parteien bereit, Video-Streams zu senden und zu empfangen. Auf der Seite, die den Video-Stream empfängt, wird das Ereignis ontrack auf der PeerConnection ausgelöst. Im Handler dafür können die empfangenen Tracks dem

Links und Literatur:

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

https://github.com/pion/webrtc — Implementierung von WebRTC-Protokollen auf Go

https://webrtcforthecurious.com/ — kleines Buch von den Machern von Pion

https://hpbn.co/ — Buch "High Performance Browser Networking". Es werden ausführlich Fragen zur Gewährleistung einer hohen Performance von Webanwendungen behandelt. Am Ende wird WebRTC beschrieben. Das Buch ist zwar schon älter (2013), aber verliert nicht an Aktualität.

Im nächsten Teil möchte ich noch etwas Theorie geben und praktisch die Aufnahme und Verarbeitung von Videostreams auf einem Server mit Pion sowie die Transkodierung in HLS über FFmpeg für die anschließende Übertragung an die Zuschauer im Browser erläutern.

Für Ungeduldige: mein sehr rudimentärer Prototyp zum Streamen von Videos von der Webcam über einen Pion-basierten Server auf React zu Twitch (das ist einfach nur ein Experiment).

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster