Eines Tages in der alten und bereits aufgegebenen Artikel habe ich darüber geschrieben, wie einfach und unverkrampft man Video von einem Canvas über Websockets streamen kann. In diesem Artikel habe ich oberflächlich erklärt, wie man Video von einer Kamera und Audio von einem Mikrofon über , wie man den erhaltenen Stream kodiert und über Websockets an den Server sendet. In der Realität wird das jedoch so nicht gemacht, für Übertragungen verwendet man entweder spezielle Software, die installiert und konfiguriert werden muss: aus dem Stegreif könnte das sein , oder man nutzt WebRTC, das sofort funktioniert, das heißt, es erfordert keine Installation von Plugins wie z.B. Flash Player, der im Dezember aus dem Chromium-Browser entfernt wird.
Heute sprechen wir über WebRTC.
Web Real-Time Communication (WebRTC) ist kein einzelnes Protokoll, sondern eine Sammlung von Standards, Protokollen und JavaScript-APIs, die zusammen die Peer-to-Peer-Videokonferenz und -Audiokommunikation in Echtzeit ermöglichen und auch für die Übertragung beliebiger binärer Daten verwendet werden können. Normalerweise sind die Peers Browser, es könnte jedoch auch eine mobile Anwendung sein. Um eine P2P-Kommunikation zwischen Clients zu organisieren, ist es erforderlich, dass der Browser verschiedene Arten der Video- und Audiokodierung unterstützt, eine Vielzahl von Netzwerkprotokollen und die Interaktion von Hardware mit dem Browser (über die Betriebssystemschichten): Webcams, Soundkarten. All diese technische Mischmasch ist hinter der Abstraktion der JavaScript-API verborgen, um den Entwicklern die Arbeit zu erleichtern.
Letztendlich läuft alles auf drei APIs hinaus:
— haben wir beim letzten Mal behandelt, heute werde ich noch ein wenig darüber schreiben. Dient zur Aufnahme von Video-/Audiostreams von der „Hardware“
— ermöglicht die Kommunikation zwischen zwei Clients (P2P)
— dient zur Übertragung beliebiger Daten zwischen zwei Clients
Vorbereitung von Audio- und Video-Streams zur Übertragung
Alles beginnt mit der "Erfassung" der Medienströme von Webcam und Mikrofon. Rohströme sind natürlich nicht geeignet für die Organisation von Videokonferenzen, jeder Stream muss bearbeitet werden: die Qualität verbessern, Audio mit Video synchronisieren, Synchronisationsmarken im Videostream setzen, den Bitrate entsprechend der sich ständig ändernden Bandbreite des Kanals anpassen. Der Browser übernimmt all dies, der Entwickler muss sich nicht um die Kodierung der Medienströme kümmern. In modernen Browsern sind bereits Software-Schichten für die Erfassung, Qualitätsverbesserung (Echo und Rauschen aus dem Ton entfernen, das Bild verbessern), Videound Audio-Kodierung vorhanden. Das Schichtenmodell ist in Abb. 1 dargestellt:
Abb. 1. Schichten der Audio- und Videoverarbeitung im Browser
Alle Verarbeitungen erfolgen direkt im Browser, keine zusätzlichen Plugins sind erforderlich. Allerdings sieht die Lage im Jahr 2020 nicht so rosig aus. Es gibt Browser, die noch nicht vollständig unterstützen , Sie können über den Link gehen und ganz unten die Tabelle der Kompatibilität ansehen. Insbesondere enttäuscht IE erneut.
Mit den erhaltenen Streams kann man sehr interessante Dinge machen: man kann klonen, die Videoauflösung ändern, die Audioqualität manipulieren, man kann den Media Stream Stream an einem
— Die Jungs beschäftigen sich mit real-time CV in Javascript. Sie haben ein ganzes verschiedene JS-Bibliotheken zur Arbeit mit Videostreams im Canvas: Gesichtserkennung, Objekterkennung, das Anwenden von Filtern (Masken, wie in Instagram) u.v.m. Ein hervorragendes Beispiel dafür, wie man ganz ohne zusätzliche Plugins direkt im Browser Video in Echtzeit verarbeiten kann.
— API-Dokumentation zur Erfassung von Videostreams vom Canvas. Bereits in Chrome, Opera und Firefox unterstützt.
RTCPeerConnection
Nun sind wir an dem Punkt, wie man das Video eigentlich an einen anderen Benutzer überträgt. Im Vordergrund steht Kurz gesagt, an diesem Punkt müssen Sie praktisch das RTCPeerConnection-Objekt erstellen:
const peerConnection = new RTCPeerConnection({
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
});Eine der Optionen, die wir angeben, sind die iceServers – der Server, der hilft, die Verbindung zwischen zwei Browsern herzustellen, die sich hinter einem NAT befinden. Das heißt, hier wird das Problem gelöst: Wie erfährt man die IP-Adresse des Gesprächspartners, wenn dieser sich hinter dem NAT seines Anbieters befindet? Der ICE-Protokoll hilft dabei; tatsächlich gehört ICE überhaupt nicht zu WebRTC, aber dazu später mehr.
Früher 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 zu peerConnection hinzu
peerConnection.addTrack(track);
}
}).catch(console.error);Anschließend wird in peerConnection das Ereignis onnegotiationneeded ausgelöst, in dessen Handler wir ein Angebot (in den Begriffen von SDP – Session Description Protocol) erstellen und es in peerConnection über die Methode setLocalDescription zuweisen müssen. Über SDP – was ist das und über die Formate Angebot und Antwort – sprechen wir später.
Nach der Zuordnung der LocalDescription zu peerConnection „sammelt“ der Browser ICE-Kandidaten, das heißt, er findet verschiedene Wege für die Kommunikation über NAT. Das Ereignis onicegatheringstatechange wird ausgelöst. Im Handler von onicegatheringstatechange erlauben wir die Verbindung mit dem webrtc-signaling-Server stream, um die Session Description zwischen den Peers auszutauschen:
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-signaling-Server ist ein Server, der notwendig ist, um den Austausch der Session Description zwischen zwei Peers zu gewährleisten. Dies kann ein einfacher Websocket oder ein XHR-Server in jeder Programmiersprache sein. Seine Aufgabe ist einfach: die Session Description von einem Peer anzunehmen und an den anderen weiterzuleiten.
Nach dem Austausch der Session Descriptions sind beide Seiten bereit, Video-Streams zu übertragen und zu empfangen. Auf der Seite, die den Video-Stream empfängt, wird das Ereignis ontrack an peerConnection ausgelöst, in dessen Handler die empfangenen Tracks einem
Links und Literatur:
— Dokumentation
— Implementierung der WebRTC-Protokolle in Go
— kleines Buch von den Machern von Pion
— Buch "High Performance Browser Networking". Es werden die Fragen zur Sicherstellung einer hohen Leistung von Webanwendungen im Detail behandelt. Am Ende wird WebRTC beschrieben. Das Buch ist zwar alt (2013), verliert jedoch nicht an Relevanz.
Im nächsten Abschnitt möchte ich noch etwas Theorie geben und praktisch die Aufnahme und Verarbeitung von Videostreams auf dem Server mit Hilfe von Pion, das Transcodieren in HLS über ffmpeg für die anschließende Übertragung an die Zuschauer im Browser behandeln.
Für die Ungeduldigen: (das ist einfach ein Experiment).
Quelle: habr.com
