ProHoster > Blog > podávání > (Téměř) zbytečné streamování z webové kamery z prohlížeče. Část 2. WebRTC
(Téměř) zbytečné streamování z webové kamery z prohlížeče. Část 2. WebRTC
Nějak dovnitř jeden ze starých a již opuštěných článků jsem psal o tom, jak snadno a přirozeně můžete vysílat video z plátna přes websockets. V tomto článku jsem krátce hovořil o tom, jak zachytit video z kamery a zvuk z mikrofonu pomocí MediaStream API, jak zakódovat přijatý stream a odeslat jej přes websockets na server. Ve skutečnosti to však nedělají, pro vysílání používají buď speciální software, který je třeba nainstalovat a nakonfigurovat: mimochodem to lze Otevřený software pro vysílání, nebo používají WebRTC, který funguje hned po vybalení, to znamená, že nevyžaduje instalaci žádných pluginů a la flash player, který bude z prohlížeče Chromium vystřižen již v prosinci.
Dnes budeme mluvit o WebRTC.
Webová komunikace v reálném čase (WebRTC) není jediný protokol, je to celá sbírka standardů, protokolů a rozhraní API JavaScript, které společně poskytují video-audio komunikaci v reálném čase typu peer-to-peer a lze je také použít k přenosu jakéhokoli binární data. Obvykle prohlížeče fungují jako peers, ale může to být například i mobilní aplikace. Pro organizaci p2p komunikace mezi klienty je vyžadována podpora prohlížeče pro různé typy kódování videa a zvuku, podpora různých síťových protokolů, zajištění interakce hardwaru s prohlížečem (prostřednictvím vrstev OS): webové kamery, zvukové karty. Všechny tyto směsice technologií jsou skryty za abstrakcí JavaScript API pro pohodlí vývojáře.
Vše se scvrkává na tři API:
MediaStream API — minule rozebraný, dnes o něm napíšu něco víc. Slouží k příjmu video / audio streamů z hardwaru
RTCDataChannel - slouží k přenosu libovolných dat mezi dvěma klienty
Příprava audio a video streamů pro přenos
Vše začíná „zachycením“ mediálních toků z webové kamery a mikrofonu. Surové streamy samozřejmě nejsou vhodné pro pořádání telekonference, každý stream je potřeba zpracovat: zlepšit kvalitu, synchronizovat zvuk s videem, umístit synchronizační značky do video streamu a zajistit bitrate odpovídající neustále se měnící šířce pásma kanálu . O to vše se stará prohlížeč, vývojář se nemusí starat ani o zajištění kódování pro mediální streamy. Uvnitř moderního prohlížeče již existují softwarové vrstvy pro snímání, zlepšování kvality (odstranění ozvěny a šumu ze zvuku, vylepšení obrazu), kódování videa a zvuku. Schéma vrstev je znázorněno na Obr. 1:
Rýže. 1. Vrstvy zpracování zvuku a videa v prohlížeči
Veškeré zpracování probíhá přímo v samotném prohlížeči, žádné další. nejsou potřeba žádné pluginy. Pro rok 2020 to však stále není tak růžové. Existují prohlížeče, které ještě plně nepodporují MediaStream API, můžete následovat odkaz a zobrazit tabulku kompatibility úplně dole. Zejména IE je opět zklamáním.
S přijatými streamy můžete dělat velmi zajímavé věci: můžete klonovat, měnit rozlišení videa, manipulovat s kvalitou zvuku, můžete stream Media Stream převzít a „zavěsit“ tag a podívejte se na sebe na html stránce. Nebo můžete nakreslit stream na plátno a nastavit WebGL nebo CSS3 a použít na video různé filtry, zachytit zpracované video z plátna a poté ho poslat přes síť na server, překódovat a publikovat všem (ahoj bigo live, twitch a další). Zde nebudu analyzovat, jak se takové věci dělají, uvedu několik příkladů nalezených na webu:
https://jeeliz.com/ - kluci dělají CV v reálném čase v Javascriptu. Mají celek arzenál různé knihovny js pro práci s video streamem na plátně: detekce tváří, objektů, aplikace filtrů (masky, jako na Instagramu) atd. Výborná ukázka toho, jak lze zpracovávat video v reálném čase přímo v prohlížeči bez dalších pluginů.
Canvas captureStream API - Dokumentace API pro zachycení video streamu z plátna. Již je podporováno v prohlížečích Chrome, Opera a Firefox
RTCPeerConnection
Došli jsme tedy k věci, ale jak vlastně video přenést na jiného uživatele? Do popředí se dostává RTCPeerConnection. Stručně řečeno, téměř v tomto kroku musíte vytvořit objekt RTCPeerConnection:
Jako jednu z možností uvádíme iceServers - jedná se o server, který pomáhá zajistit spojení mezi dvěma prohlížeči za NAT'om. To znamená, že problém je zde vyřešen: jak zjistit IP partnera, pokud je za NAT svého poskytovatele? Na pomoc přichází protokol ICE, ve skutečnosti se ICE na WebRTC vůbec nevztahuje, ale o tom později.
Dříve jsme měli streamy Usermedia:
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
// Usermedia-потоки, обычно это видео и аудио
const tracks = stream.getTracks();
for (const track of tracks) {
// каждый трек присоединяем к peerConnection
peerConnection.addTrack(track);
}
}).catch(console.error);
Dále se na peerConnection spustí událost onnegotiationneeded, v jejímž handleru musíme vytvořit nabídku (ve smyslu SDP - Session Description Protocol) a přiřadit ji k peerConnection pomocí metody setLocalDescription. O SDP - co to je a o formátech nabídek a odpovědí - si povíme dále.
Po přiřazení LocalDescription peerConnection prohlížeč „sestaví“ ledové kandidáty, to znamená, že najde různé způsoby komunikace přes NAT. Spustí se událost onicegatheringstatechange. V obslužné rutině onicegatheringstatechange umožňujeme připojení k streamu webrtc-signaling-server za účelem výměny popisu relace mezi partnery:
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 je server potřebný k výměně popisu relace mezi dvěma peery, může to být nejjednodušší websocket nebo xhr-server na jakémkoli PL. Jeho úkol je jednoduchý: přijmout popis relace od jednoho peer a přenést jej na druhého.
Po výměně popisů relace jsou obě strany připraveny vysílat a přijímat video streamy, na straně, která přijímá video stream, se událost ontrack spustí na peerConnection, v jehož handleru lze přijímané stopy přiřadit a podívejte se na svého oblíbeného partnera. Další teorie a detaily.
https://hpbn.co/ - Kniha High Performance Browser Networking. Podrobně je diskutována problematika zajištění vysokého výkonu webových aplikací. Na konci je popsán WebRTC. Kniha je jistě stará (2013), ale neztrácí na aktuálnosti.
V další části chci dát trochu větší porci teorie a v praxi analyzovat příjem a zpracování video streamu na serveru pomocí pion, překódování do HLS přes ffmpeg pro následné vysílání divákům v prohlížeči.