Nějak v jednom ze starých a již opuštěných článků jsem psal o tom, jak snadno a bez námahy můžeme streamovat video z canvasu přes websockets. V tom článku jsem povrchně popisoval, jak zachytit video z kamery a zvuk z mikrofonu pomocí , jak získaný stream kódovat a posílat přes websockets na server. Ve skutečnosti se však takto nepostupuje, pro streamování se používá buď speciální software, který je třeba nainstalovat a nastavit: tak na rychlo to může být , nebo se využívá WebRTC, který funguje přímo z krabice, tedy nevyžaduje instalaci žádných pluginů jako Flash Player, který již v prosinci odstraní z prohlížeče Chromium.
Dnes se budeme bavit o WebRTC.
Web Real-Time Communication (WebRTC) není jen jeden protokol, ale celá kolekce standardů, protokolů a JavaScript API, které společně poskytují peer-to-peer video-audio komunikaci v reálném čase a mohou být také použity pro přenos jakýchkoli binárních dat. Obvykle jsou peerovými stranami prohlížeče, ale může to být i mobilní aplikace, například. Pro organizaci p2p komunikace mezi klienty je zapotřebí podpora různých typů kódování videa a audia ze strany prohlížeče, podpora mnoha síťových protokolů, zajištění interakce hardwaru s prohlížečem (přes vrstvy OS): webkamerami, zvukovými kartami. Celá tato směs technologických komponentů je skryta za abstrakcí JavaScript API pro usnadnění vývoje.
Vše se nakonec shrnuje do třech API:
— rozebírali jsme minule, dnes o něm napíšu ještě trochu víc. Slouží k získávání video/audiostreamů z 'hardwaru'
— zajišťuje komunikaci mezi dvěma klienty (p2p)
— slouží k přenosu libovolných dat mezi dvěma klienty
Příprava audio a video streamů k přenosu
Vše začíná „zachycením“ mediálních toků z webové kamery a mikrofonu. Nezpracované toky samozřejmě nejsou vhodné pro organizaci videokonference; každý tok je třeba zpracovat: zlepšit kvalitu, synchronizovat zvuk s obrazem, umístit synchronizační značky do videostreamu a zajistit odpovídající bitrate v neustále se měnící šířce pásma kanálu. To vše zvládne prohlížeč, vývojář se nemusí starat o zajištění kódování mediálních toků. Uvnitř moderního prohlížeče už jsou přítomny softwarové vrstvy pro zachycení, zlepšení kvality (odstranění ozvěny a šumu ze zvuku, vylepšení obrazu), kódování videa a audia. Schéma vrstev je zobrazeno na obr. 1:
Obr. 1. Vrstvy zpracování audia a videa v prohlížeči
Veškeré zpracování probíhá přímo v prohlížeči, žádné další pluginy nejsou vyžadovány. Nicméně v roce 2020 to stále není tak růžové. Některé prohlížeče dosud plně nepodporují , můžete přejít na odkaz a úplně dole si prohlédnout tabulku kompatibility. Zejména IE opět zklamává.
S přijatými toky lze dělat velmi zajímavé věci: můžete klonovat, měnit rozlišení videa, manipulovat s kvalitou audia, můžete vzít a „připojit“ Media Stream k <video> tagu a vykouknout na sebe na html stránce. Tok můžete také vykreslit na canvas, aplikovat WebGL nebo CSS3, a dokonce na video aplikovat různé filtry, zachytit zpracované video z canvasu a poté je posílat přes síť na server, transcódovat a publikovat pro všechny zájemce (ahoj bigo live, twitch a další). Zde nebudu rozebírat, jak se takové věci dělají, uvedu pár příkladů nalezených na webu:
— kluci se zabývají realtime CV na Javascriptu. Mají celou různých js-knihoven pro práci s videostreamem na canvasu: detekce tváří, objektů, aplikace filtrů (masky, jako v instagramu) atd. Skvělý příklad toho, jak bez dodatečných pluginů lze přímo v prohlížeči zpracovávat video v reálném čase.
— dokumentace API pro zachycení videostreamu z canvasu. Již podporováno v Chrome, Opeře a Firefoxu.
RTCPeerConnection
A tak jsme se dostali k tomu, jak vlastně přenést video jinému uživateli? Na scénu vystupuje Pokud to shrneme, na tomto kroku je potřeba vytvořit objekt RTCPeerConnection:
const peerConnection = new RTCPeerConnection({
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
});Jednou z voleb, kterou uvádíme, je iceServers — server, který pomáhá zajistit spojení mezi dvěma prohlížeči nacházejícími se za NAT. Tedy zde se řeší problém: jak zjistit IP adresu udržovatele, pokud je za NAT jeho poskytovatele? S pomocí přichází protokol ICE, který vlastně vůbec nespadá do WebRTC, ale o tom později.
Dříve jsme získali toky Usermedia:
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
// Toky Usermedia, obvykle video a audio
const tracks = stream.getTracks();
for (const track of tracks) {
// každý track připojujeme k peerConnection
peerConnection.addTrack(track);
}
}).catch(console.error);Poté na peerConnection dojde k události onnegotiationneeded, ve zpracovatelovi této události musíme vytvořit nabídku (v termínech SDP — Session Description Protocol) a přiřadit ji k peerConnection pomocí metody setLocalDescription. O SDP — co to je a o formátech nabídka a odpověď — si promluvíme později.
Po přiřazení LocalDescription k peerConnection, prohlížeč "shromažďuje" ice kandidáty, tedy hledá různé cesty pro komunikaci přes NAT. Nastává událost onicegatheringstatechange. V zpracovateli onicegatheringstatechange umožníme spojení s webrtc-signaling serverem pro výměnu Session Description mezi peer.
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 — to je server, který je potřebný k zajištění výměny session description mezi dvěma peer, což může být nejjednodušší websocket nebo xhr server na jakémkoliv programu. Jeho úkolem je jednoduché: přijmout session description od jednoho peer a předat ji druhému.
Po výměně Session descriptions jsou obě strany připraveny streamovat a přijímat video toky, na straně, která přijímá video tok, dochází k události ontrack na peerConnection, v zpracovateli této události můžeme přiřadit příchozí tracky k
Odkazy a literatura:
— dokumentace
— implementace protokolů WebRTC v go
— knížečka od tvůrců pion
— kniha High Performance Browser Networking. V podrobnostech se zabývá otázkami zajištění vysokého výkonu webových aplikací. Na konci se zabývá WebRTC. Kniha je sice stará (2013), ale neztrácí na aktuálnosti.
V další části chci poskytnout ještě trochu teorie a v praxi rozebrat příjem a zpracování video toku na serveru pomocí pion, transkódování do HLS přes ffmpeg pro následné vysílání divákům v prohlížeči.
Pro nedočkavé: (to je jen experiment).
Zdroj: habr.com
