áá
áºáááºážáááºážáá²á·
ááá±á·áá»áœááºá¯ááºááá¯á·ááẠWebRTC á¡ááŒá±á¬ááºážááŒá±á¬áá«áááºá
Web Real-Time Communication (WebRTC) ááẠáá áºáá¯áááºážáá±á¬ áááá¯ááá¯áá±á¬ááá¯ááºáá«á áááºážááẠá á¶ááŸá¯ááºážáá»á¬ážá áááá¯ááá¯áá±á¬áá»á¬ážááŸáá·áº JavaScript API áá»á¬ážá á¡á á¯á¡áá±ážáá áºáá¯ááŒá áºááŒá®áž áá áºáŠážááŸáá áºáŠáž á¡áá»áááºááŸáá·áºáááŒá±ážáá® áá®áá®ááá¯-á¡áᶠáááºááœááºááŸá¯ááᯠáá¶á·ááá¯ážáá±ážááá·áº á á¶ááŸá¯ááºážáá»á¬ážá áááá¯ááá¯áá±á¬áá»á¬ážááŸáá·áº JavaScript API áá»á¬áž á á¯á ááºážááŸá¯ááŒá áºááŒá®áž áááºááá·áºá¡áá¬ááá¯áááᯠááœáŸá²ááŒá±á¬ááºážáááºá¡ááœááºáááºáž á¡áá¯á¶ážááŒá¯ááá¯ááºáááºá ááœááá±áá¬á á¡áá»á¬ážá¡á¬ážááŒáá·áº ááá±á¬ááºáá¬áá»á¬ážááẠááœááºáá°áá»á¬ážá¡ááŒá Ạáá¯ááºáá±á¬ááºááŒáá±á¬áºáááºážá á¥ááá¬á¡á¬ážááŒáá·áº áááºážááẠááá¯ááá¯ááºážá¡ááá®áá±ážááŸááºážáá áºáá¯áááºáž ááŒá áºááá¯ááºáááºá áá±á¬ááºáááºáá»á¬ážá¡ááŒá¬áž p2p áááºááœááºáá±ážááᯠá á¯á ááºážáááºá¡ááœááºá áá®áá®ááá¯ááŸáá·áº á¡áá¶áá¯ááºááŒá±á¬ááºážááŒááºáž á¡áá»áá¯ážá¡á á¬ážá¡áá»áá¯ážáá»áá¯ážá¡ááœáẠááá±á¬ááºáá¬áá¶á·ááá¯ážááŸá¯á ááœááºáááºáááá¯ááá¯áá±á¬á¡áá»áá¯ážáá»áá¯ážá¡ááœáẠáá¶á·ááá¯ážááŸá¯á ááá±á¬ááºáá¬ááŸáá·áº áá¬á·ááºáá²ááºá á¡ááŒááºá¡ááŸááºáááºáá¶áá±áž (OS á¡ááœáŸá¬áá»á¬ážááŸáááá·áº)- áááºáááºááá¬áá»á¬ážá á¡áá¶áááºáá»á¬ážá developer áá¡áááºááŒá±á á±áááºá¡ááœáẠဠhodgepodge á¡á¬ážáá¯á¶ážááᯠJavaScript API abstraction ááá±á¬ááºááœááºááœááºááŸááºáá¬ážáááºá
áááºážááẠAPI áá¯á¶ážáá¯á¡áá áá»áááºážááœá¬ážáááº-
-
MediaStream API - áá±á¬ááºáá¯á¶ážá¡ááŒááẠáá»ááºááá¯ááºá áá®áá±á·áá±á¬á· áá°á·á¡ááŒá±á¬ááºáž áááºážáááºážáá±ážáááºá áá¬á·ááºáá²á០áá®áá®ááá¯/á¡áá¶á á®ážááŒá±á¬ááºážáá»á¬ážááᯠáááºáá¶áááŸáááẠáá±á¬ááºááœááºáá«áááºá -
RTCPeerConnection - áá±á¬ááºáááºááŸá áºáŠážá¡ááŒá¬áž áááºááœááºááŸá¯ááᯠáá¶á·ááá¯ážáá±ážááẠ(p2p) -
RTCDataChannel - áá±á¬ááºáááºááŸá áºáŠážááŒá¬ážá០áááá¬ážáá±áá¬ááœáŸá²ááŒá±á¬ááºážááẠáá±á¬ááºááœááºáááºá
áá¯ááºááœáŸáá·áºááŸá¯á¡ááœáẠá¡áá¶ááŸáá·áº áá®áá®ááá¯á á®ážááŒá±á¬ááºážáá»á¬ážááᯠááŒááºáááºáá±áá«áááºá
áááºážááẠáááºáááºááá¬ááŸáá·áº ááá¯ááºáááá¯áá¯ááºáž áá®áá®áá¬áá¯ááºááœáŸáá·áºááŸá¯áá»á¬ážááᯠ"áááºážáá°ááŒááºáž" ááŒáá·áº á áááºáááºá áá¯ááºáá«áááºá áá¯ááºááŒááºážá á®ážááŒá±á¬ááºážáá»á¬ážááẠáááºáá®ááœááºáááá·áºáá áºáá¯áá»ááºážááááºá¡ááœáẠáááá·áºáá»á±á¬áºáá«á áá¯ááºááœáŸáá·áºááŸá¯áá áºáá¯á á®ááá¯ááºážááᯠá á®áá¶áá±á¬ááºááœááºáááºááá¯á¡ááºáááº- á¡áááºá¡ááœá±ážááŒáŸáá·áºáááºáááºá áá®áá®ááá¯ááŸáá·áº á¡áá¶ááᯠáááºáá°ááŒá¯ááŒááºážá áá®áá®ááá¯á á®ážááŒá±á¬ááºážááœáẠáááºáá°ááŒá¯ááŒááºážá¡ááŸááºá¡áá¬ážáá»á¬ážááᯠáá¬ážááŸááᬠáá»ááºáááºá á¡áááºáááŒááºááŒá±á¬ááºážáá²áá±áá±á¬ áááºážáááºááŸáá·áº ááá¯ááºáá®áá±á¬ áá áºááŸá¯ááºážááᯠáá±áá»á¬á á±áá«á . ááá±á¬ááºáá¬ááẠá€á¡áá¬á¡á¬ážáá¯á¶ážááᯠááá¯á áá¯ááºáááºá áá±á¬á·ááºáá²áá±ážáá¬ážáá°ááẠáá®áá®áá¬á á®ážááŒá±á¬ááºážáá»á¬ážá¡ááœáẠáá¯ááºáá¯ááºááᯠáá¶á·ááá¯ážáá±ážáááºá¡ááœááºááẠá áááºáá°á áá¬áááá¯áá«á áá±ááºáá®ááá±á¬ááºáá¬áá áºáá¯ááœááºá áááºážáá°áááºá á¡áááºá¡ááœá±ážááŒáŸáá·áºáááºááẠ(áá¶áá±á¬ááºááŸáá·áº áá°áá¶áá¶áá»á¬ážááᯠáááºááŸá¬ážáááºá áá¯ááºáá¯á¶ááá¯ážáááºá á±áááº)á áá®áá®ááá¯ááŸáá·áº á¡áá¶áá¯ááºááŒá±á¬ááºážááŒááºážá¡ááœáẠáá±á¬á·ááºáá²á¡ááœáŸá¬áá»á¬áž ááŸáááŸáá·áºááŒá®ážáá¬ážááŒá áºáááºá á¡ááœáŸá¬áá¯á¶á á¶ááᯠáá¯á¶ááœáẠááŒáá¬ážáááºá á-
ááááºážá 1. ááá±á¬ááºáá¬ááŸá á¡áá¶ááŸáá·áº áá®áá®ááᯠáá¯ááºáá±á¬ááºááŒááºážá á¡ááœáŸá¬áá»á¬áž
áá¯ááºáá±á¬ááºááŸá¯á¡á¬ážáá¯á¶ážááẠááá±á¬ááºáá¬ááá¯ááºááá¯áẠááá¯ááºááá¯ááºáá¯ááºáá±á¬ááºáááºá áááºáá±á¬ááºážáááŸááá«á plugin áá»á¬ážáááá¯á¡ááºáá«á ááá¯á·áá±á¬áº 2020 ááœáẠá¡áá¬áá»á¬ážááẠááŸááºážáá®áááºážáááŸáá±ážáá«á á¡ááŒáá·áºá¡á ááá¶á·ááá¯ážááá±ážáá±á¬ browser áá»á¬ážááŸááá«áááºá
áááºáá¶áááŸááá±á¬ áá¯ááºááœáŸáá·áºááŸá¯áá»á¬ážááŒáá·áº á¡ááœááºá áááºáááºá á¬ážá áá¬áá±á¬ááºážáá±á¬á¡áá¬áá»á¬ážááᯠáááºáá¯ááºáá±á¬ááºááá¯ááºáááº- áááºááẠáá¯á¶áá°áá°ážáááºá áá®áá®ááá¯ááŒááºáááºááŒááºáá¬ážááŸá¯ááᯠááŒá±á¬ááºážáá²ááá¯ááºáááºá á¡áá¶á¡áááºá¡ááœá±ážááᯠááŒááºááŸááºááá¯ááºáááºá áááºáá°ááá¯ááºááŒá®áž Media Stream áá¯ááºááœáŸáá·áºááŸá¯ááᯠáá°ážááœá²áá« tag áá¯ááºááŒá®áž html á á¬áá»ááºááŸá¬ááŸá¬ ááá¯áá·áºááá¯ááá¯áẠááŒáá·áºááá¯ááºáá«á ááá¯á·ááá¯áẠáááºááẠáááºážáááºá áºáá±á«áºááœáẠá á®ážááŒá±á¬ááºážáá áºáá¯ááœá²ááá¯ááºááŒá®áž WebGL ááá¯á·ááá¯áẠCSS3 ááᯠáááºááŸááºááá¯ááºááŒá®ážá áá®áá®ááá¯ááœáẠá¡áá»áá¯ážáá»áá¯ážáá±á¬ á á áºáá¯ááºááŸá¯áá»á¬ážááᯠá¡áá¯á¶ážáá»áá¬á áá¯ááºáá±á¬ááºááŒá®ážáá¬áž áá®áá®ááá¯ááᯠáááºážáááºá áºá០ááá¯ááºáá°ážáᬠáááºážááᯠááœááºáááºáá±á«áºá០áá¬áá¬ááá¯á· áá±ážááá¯á·ááá¯ááºááŒá®ážá áá°ááá¯ááºážáᶠáá¬áá¬ááŒááºááŒá®áž áá¯ááºáá±ááá¯ááºááẠ(áááºá¹ááá¬áá« bigo liveá twitch ááŸáá·áº á¡ááŒá¬áž)á á€ááœáẠáá»áœááºá¯ááºááẠááá¯á¡áá¬áá»á¬ážááᯠáááºááá¯á·áá¯ááºáá±á¬ááºáááºááᯠááœá²ááŒááºážá áááºááŒá¬áááºááá¯ááºáá«á áááºáá±á«áºááœááºááœá±á·ááá±á¬ á¥ááá¬á¡áá»áá¯á·ááᯠáá»áœááºá¯ááºááŒá±á¬ááŒáá«áááºá
RTCPeerConnection
ááá¯á·ááŒá±á¬áá·áº áá»áœááºá¯ááºááá¯á·ááẠá¡ááŸááºááá¬áááºá ááá¯á·áá±á¬áº áá®áá®ááá¯ááᯠá¡ááŒá¬ážá¡áá¯á¶ážááŒá¯áá°áᶠáááºááá¯á·ááœáŸá²ááŒá±á¬ááºážáááºáááºážá ááŸá±á·ááá¯áá±á¬ááº
const peerConnection = new RTCPeerConnection({
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
});
ááœá±ážáá»ááºá áá¬áá»á¬ážáá²ááŸáá áºáá¯á¡ááŒá ẠiceServers ááá¯áá»áœááºá¯ááºááá¯á·áááºááŸááºááẠ- áááºážááẠNAT'om áá±á¬ááºááœááºááŸáááá±á¬ááºáá¬ááŸá áºáá¯ááŒá¬ážáá»áááºáááºááŸá¯ááá¯áá¶á·ááá¯ážáá±ážááá·áºáá¬áá¬áá áºáá¯ááŒá áºáááºá ááá¯ááá¯áááºááŸá¬á ááŒá¿áá¬ááᯠá€áá±áá¬ááœáẠááŒá±ááŸááºážááá¯ááºáááº- áá°ááẠáááºážááááºáá±á¬ááºááŸá¯áá±ážáá°á NAT áá±á¬ááºááœááºááœáẠááŸááá±áá«á á¡ááŒááºá¡ááŸááºááŒá±á¬ááá¯áá°á ip ááᯠáááºááá¯á·ááŸá¬ááœá±ááááºáááºážá ICE áááá¯ááá¯áá±á¬ááẠá¡ááŸááºááááºá¡á¬ážááŒáá·áº ICE ááẠWebRTC ááŸáá·áº áá¯á¶ážááááºááá¯ááºááŒááºážáááŸááá±á¬áºáááºáž áá±á¬ááºááá¯ááºážááœáẠááá¯ááá¯áá¯ááºáá±á¬ááºááá¯ááºáá²á·áááºá
ááááºá áá»áœááºá¯ááºááá¯á·ááẠ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);
ááá¯á·áá±á¬ááºá áá»áœááºá¯ááºááá¯á·ááẠáááºážááŸááºážáá»ááºáá áºáá¯áááºáá®ážááááºááŒá áºááŒá®áž (SDP - Session áá±á¬áºááŒáá»ááºáááá¯ááá¯áá±á¬) á ááá¯ááºááœááºááŒá±ááŸááºážáá°ááœáẠpeerConnection ááœáẠááŸáááŸáá¯ááºážááŸá¯ááá¯á¡ááºáá±á¬ ááŒá áºáááºááẠá áááºááŒá®áž setLocalDescription áááºážáááºážááŸáá áºááá·áº áááºážááᯠpeerConnection ááá¯á· áááºááŸááºáá±ážáááºááŒá áºáááºá SDP á¡ááŒá±á¬ááºáž - áááºážááẠáá¬áá²á áááºážááŸááºážáá»ááºááŸáá·áº á¡ááŒá±áá±á¬áºáááºáá»á¬ážá¡ááŒá±á¬ááºáž - áá»áœááºá¯ááºááá¯á· áááºáááºááœá±ážááœá±ážáá«áááºá
LocalDescription peerConnection ááᯠáá¬áááºáá±ážááŒá®ážáá±á¬ááºá ááá±á¬ááºáá¬ááẠ"áá±áá²" ááá¯ááºá á¬ážááŸááºáá»á¬ážááᯠá á¯á ááºážáá±ážáááºá ááá¯ááá¯áááºááŸá¬ NAT ááŸáááá·áº áááºááœááºááẠáááºážáááºážá¡áá»áá¯ážáá»áá¯ážááᯠááŸá¬ááœá±áááºá á ááºážáá¯á¶ážáá®ááœááºááŸá¯á¡ááŒá±á¡áá± ááŒá±á¬ááºážáá²ááŸá¯ ááŒá áºáááºá áá®ážáá±á¬ááºáááºá onicegatheringstatechange handler ááœááºá áá»áœááºá¯ááºááá¯á·ááẠáááºáá°ááœááºáá°áá»á¬ážá¡ááŒá¬áž Session áá±á¬áºááŒáá»ááºááᯠáááŸááºáááºá¡ááœáẠwebrtc-signaling-server á á®ážááŒá±á¬ááºážááá¯á· áá»áááºáááºááŸá¯ááᯠááœáá·áºááŒá¯áááº-
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 ááẠáááºáá°ááœááºáá°ááŸá áºáŠážááŒá¬áž á ááºááŸááºáá±á¬áºááŒáá»ááºááᯠáááŸááºáááºá¡ááœáẠááá¯á¡ááºáá±á¬áá¬áá¬ááŒá áºááŒá®áž áááºážááẠáááºááá·áº PL ááœááºáááᯠá¡ááá¯ážááŸááºážáá¯á¶áž websocket ááá¯á·ááá¯áẠxhr-server ááŒá áºááá¯ááºáááºá áááºážááá¬áááºááŸá¬ ááá¯ážááŸááºážáááº- ááááºáááºáá áºáŠážá០á ááºááŸááºáá±á¬áºááŒáá»ááºááᯠáááºáá¶ááŒá®áž á¡ááŒá¬ážáá áºáá¯ááá¯á· ááœáŸá²ááŒá±á¬ááºážáááºá
Session áá±á¬áºááŒáá»ááºáá»á¬ážá¡á¬áž áááŸááºááŒá®ážáá±á¬ááºá ááŸá áºáááºá áá¯á¶ážááẠáá®áá®ááá¯á á®ážááŒá±á¬ááºážáá»á¬ážááᯠáá¯ááºááœáŸáá·áºááŒááºážááŸáá·áº áááºáá¶ááẠá¡áááºááá·áºááŒá áºáá±áá«ááŒá®á áá®áá®ááá¯á á®ážááŒá±á¬ááºážááᯠáááºáá¶áááŸáááá·áºáááºááœááºá ontrack event ááᯠpeerConnection ááœáẠá¡á áá»áá¯ážáá¬ážááŒá®áž ááá¯ááºááœááºáá°ááœááºá áááºáá¶áááŸááá±á¬áá®áá»ááºážáá»á¬ážááᯠáááºááŸááºáá±ážááá¯ááºáá«áááºá ááŒá®ážáá±á¬á· áááºážá¡ááŒáá¯ááºáá¯á¶áž á áá¬ážááá¯ááºážááᯠááŒáá·áºáá«á áá±á¬ááºáááºáá®á¡áá¯áá®ááŸáá·áºá¡áá±ážá áááºá
ááá·áºáá»á¬ážááŸáá·áº á á¬áá±áá»á¬áž-
áá±á¬ááºá¡ááá¯ááºážááœááºá pion ááᯠá¡áá¯á¶ážááŒá¯á áá¬áá¬ááŸá áá®áá®ááá¯á á®ážááŒá±á¬ááºážá áááºáá¶ááŸá¯ááŸáá·áº á á®áá¶áá±á¬ááºááœááºááŸá¯ááᯠááœá²ááŒááºážá áááºááŒá¬ááẠáá®á¡áá¯áá®á áá±á¬ááºáááºá¡ááá¯ááºážá¡áááºážáááºááŸáá·áº áááºááœá±á·ááœáẠpion ááᯠá¡áá¯á¶ážááŒá¯á ááá±á¬ááºáá¬ááŸá ááŒáá·áºááŸá¯áá°áá»á¬ážáᶠffmpeg ááŸáá áºááá·áº HLS ááá¯á· ááŒá±á¬ááºážááá·áºáá«áááºá
á
áááºáááŸááºáá°áá»á¬ážá¡ááœááº
source: www.habr.com