En gÄng i de gamla och redan övergivna artiklarna skrev jag om hur enkelt och avslappnat man kan strömma video frÄn canvas via websockets. I den artikeln pratade jag ytligt om hur man fÄngar video frÄn kameran och ljud frÄn mikrofonen med hjÀlp av , hur man kodar den erhÄllna strömmen och skickar den via websockets till servern. Men i verkligheten gör man sÄ inte; för strömningar anvÀnds antingen speciell mjukvara som mÄste installeras och konfigureras: pÄ rak arm kan detta vara , eller sÄ anvÀnds WebRTC, som fungerar direkt ur lÄdan, det vill sÀga som inte krÀver installation av nÄgra plugins som Flash Player, som i december tas bort frÄn Chromium-webblÀsaren.
I dag ska vi prata om WebRTC.
Web Real-Time Communication (WebRTC) Àr inte en enda protokoll, utan en hel samling standarder, protokoll och JavaScript API:er som tillsammans möjliggör peer-to-peer video- och ljudkommunikation i realtid, och som ocksÄ kan anvÀndas för att överföra alla typer av binÀra data. Vanligtvis fungerar webblÀsare som peers, men det kan ocksÄ vara en mobilapp, till exempel. För att organisera p2p-kommunikation mellan klienterna krÀvs att webblÀsaren stöder olika typer av videouppspelning och ljudkodning, samt stöd för mÄnga nÀtverksprotokoll och sÀkerstÀller att hÄrdvaran fungerar med webblÀsaren (via OS-lager): webbkameror, ljudkort. All denna teknik Àr dolt bakom JavaScript API:er för utvecklarens bekvÀmlighet.
Allt sammanfattas till tre API:er:
â som vi diskuterade förra gĂ„ngen, jag kommer idag att skriva lite mer om det. Det anvĂ€nds för att fĂ„ video/audio-strömmar frĂ„n "hĂ„rdvaran"
â möjliggör kommunikation mellan tvĂ„ klienter (p2p)
â anvĂ€nds för att överföra godtyckliga data mellan tvĂ„ klienter
Förberedelse av ljud- och videoströmmar för överföring
Allt börjar med "fÄngsten" av medieströmmarna frÄn webbkamera och mikrofon. RÄa strömmar passar sjÀlvklart inte för att organisera videokonferenser; varje ström mÄste bearbetas: förbÀttra kvaliteten, synkronisera ljud med video, sÀtta synkroniseringsmarkeringar i videoströmmen och sÀkerstÀlla att bithastigheten motsvarar den stÀndigt förÀndrade bandbredden. Allt detta hanteras av webblÀsaren, sÄ utvecklaren behöver inte ens tÀnka pÄ att sÀkerstÀlla kodning av medieströmmarna. Inuti en modern webblÀsare finns det redan programskikt för fÄngst, kvalitetshöjning (ta bort eko och brus frÄn ljudet, förbÀttra bilden), samt kodning av video och ljud. Skissen av skikten visas i figur 1:
Figur 1. Skikt för ljud- och videobearbetning i webblÀsaren
All bearbetning sker direkt i webblÀsaren, inga extra plugins krÀvs. Men det Àr fortfarande inte helt ljust 2020. Det finns webblÀsare som fortfarande inte fullt ut stöder , du kan gÄ till lÀnken och titta lÀngst ner pÄ kompatibilitetstabellen. Speciellt IE överraskar igen med besvikelser.
Med de erhÄllna strömmarna kan man göra mycket intressanta saker: man kan klona, Àndra videoupplösning, manipulera ljudkvalitet, man kan ta och "koppla" Media Stream-strömmen till
â killarna jobbar med realtids-CV pĂ„ Javascript. De har ett helt av olika js-bibliotek för att arbeta med videoströmmar pĂ„ canvas: ansikts- och objektdetektering, applicering av filter (masker, som pĂ„ Instagram) osv. Ett utmĂ€rkt exempel pĂ„ hur man utan extra plugins kan bearbeta video i realtid direkt i webblĂ€saren.
â API-dokumentation för att fĂ„nga videoströmmen frĂ„n canvas. Redan stöds i Chrome, Opera och Firefox.
RTCPeerConnection
Nu har vi kommit till frÄgan; hur ska man egentligen skicka video till en annan anvÀndare? Först och frÀmst trÀder Om man ska vara kortfattad, sÄ behöver ni i stort sett skapa ett RTCPeerConnection-objekt vid det hÀr steget:
const peerConnection = new RTCPeerConnection({
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
});En av alternativen Ă€r att ange iceServers â det hĂ€r Ă€r servrar som hjĂ€lper till att sĂ€kerstĂ€lla anslutningen mellan tvĂ„ webblĂ€sare som befinner sig bakom NAT. Med andra ord, hur vet man IP-adressen till sin kontakt om denne Ă€r bakom hans internetleverantörs NAT? ICE-protokollet kommer till hjĂ€lp. För övrigt, ICE hör egentligen inte till WebRTC, men mer om det senare.
Tidigare fick vi Usermedia-strömmar:
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
// Usermedia-strömmar, vanligtvis video och ljud
const tracks = stream.getTracks();
for (const track of tracks) {
// varje spÄr kopplas till peerConnection
peerConnection.addTrack(track);
}
}).catch(console.error);DĂ€refter aktiveras hĂ€ndelsen onnegotiationneeded pĂ„ peerConnection, dĂ€r vi i hĂ€ndelsehanteraren mĂ„ste skapa ett offer (i SDP-termer â Session Description Protocol) och tilldela det i peerConnection via metoden setLocalDescription. Vi kommer att diskutera SDP, vad det Ă€r och om formaten offer och answer senare.
Efter att LocalDescription har tilldelats peerConnection samlar webblÀsaren ice-kandidater, vilket innebÀr att den hittar olika vÀgar för kommunikation genom NAT. HÀndelsen onicegatheringstatechange aktiveras. I hÀndelsehanteraren för onicegatheringstatechange tillÄter vi anslutning till webrtc-signaling-servern för att utbyta session description mellan peer-enheterna:
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-servern Àr en server som Àr nödvÀndig för att sÀkerstÀlla att session description överförs mellan bÄda peer-enheterna, detta kan vara en enkel websocket eller en xhr-server skriven i valfritt programmeringssprÄk. Dess uppgift Àr enkel: att ta emot session description frÄn en peer och skicka den till den andra.
Efter utbytet av Session descriptions Àr bÄda parter redo att sÀnda och ta emot videoströmmar. NÀr sidan som tar emot videoströmmen tar emot hÀndelsen ontrack pÄ peerConnection, kan vi i hÀndelsehanteraren tilldela de mottagna spÄren till
LĂ€nkar och litteratur:
â dokumentation
â implementering av WebRTC-protokoll pĂ„ Go
â en bok frĂ„n skaparna av pion
â boken High Perfomance Browser Networking. I detalj behandlar den frĂ„gor som rör hög prestanda för webbapplikationer. I slutet beskrivs WebRTC. Boken Ă€r förstĂ„s gammal (2013), men förlorar inte sin aktualitet.
I nÀsta del vill jag ge lite mer teoretisk bakgrund och praktiskt gÄ igenom hur man tar emot och bearbetar videoström pÄ servern med hjÀlp av pion, samt transcoding i HLS via ffmpeg för att sedan sÀnda till tittare i webblÀsaren.
För de otÄliga: (detta Àr bara ett experiment).
KĂ€lla: habr.com
