Eines Tages in 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 , 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 , 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:
— 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.
— 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" 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:
Abb. 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 , 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
— Eine Gruppe von Leuten beschäftigt sich mit real-time CV in Javascript. Sie haben ein ganzes 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.
— 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 . 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:
— Dokumentation
— Implementierung von WebRTC-Protokollen auf Go
— kleines Buch von den Machern von Pion
— 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: (das ist einfach nur ein Experiment).
Quelle: habr.com
