Webrtc, рдкреАрдпрд░ рдХрдиреЗрдХреНрд╢рди - рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдПрдХ рдкреВрд░реНрдг рд╡реАрдбрд┐рдпреЛ рдЪреИрдЯ рдмрдирд╛ рд░рд╣рд╛ рд╣реИ


рдкрд░рд┐рдЪрдп


рд╣рдмрд▓ рдкрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдмрд╛рд░-рдмрд╛рд░ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдореИрдВ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рддрдХрдиреАрдХреА рд╣рд┐рд╕реНрд╕реЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдереЛрдбрд╝рд╛ рдмрддрд╛рдирд╛ рдФрд░ рдПрдХ рдЫреЛрдЯреЗ рд╡реАрдбрд┐рдпреЛ рдЪреИрдЯ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдкрд░ рдкреНрд░рдХрд╛рд╢ рдбрд╛рд▓рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ред рдореИрдВ рддреБрд░рдВрдд рдПрдХ рдЖрд░рдХреНрд╖рдг рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рд╡реЗрдмреНрд░рдЯ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд▓рдЧрд╛рддрд╛рд░ рдмрджрд▓ рд░рд╣рд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рдПрдкреАрдЖрдИ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдХреЗ рдирд╛рдо рдФрд░ рдЙрдирдХреЗ рдкреИрд░рд╛рдореАрдЯрд░ рд╢рд╛рдорд┐рд▓ рд╣реИрдВред
рд╣рд░ рдХреЛрдИ рдЬреЛ рд╕рд┐рд░реНрдл рдпрд╣ рджреЗрдЦрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╕рдм рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдЕрднреА рдпрд╣рд╛рдВ рд╣реИ: apprtc.appspot.com Google рдбреЗрдореЛ рдЖрдкрдХреЛ рд╕рднреА рдХреА рдЬрд░реВрд░рдд рд╣реИ рд▓рд┐рдВрдХ рдХрд╛ рдкрд╛рд▓рди рдХрд░реЗрдВ рдФрд░ рдЗрд╕реЗ рдХрдорд░реЗ рдХреЗ рдирдВрдмрд░ рдХреЗ рд╕рд╛рде рдХрд┐рд╕реА рдФрд░ рдХреЛ рднреЗрдЬреЗрдВред рдЕрдВрдд рдореЗрдВ, рдЖрдкрдХреЛ рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХреЛ рдмрджрд▓рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдпрджрд┐ рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рдХрдорд░рд╛ рднрд░рд╛ рд╣реБрдЖ рд╣реИред рдХреМрди рдкрд░рд╡рд╛рд╣ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╕рдм рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ? рдмрд┐рд▓реНрд▓реА рдореЗрдВ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИред


рд╕рд╛рдорд╛рдиреНрдп рднрд╛рдЧ


Webrtc рдПрдкреАрдЖрдИ рдореЗрдВ рд╣реА рддреАрди рднрд╛рдЧ рд╣реЛрддреЗ рд╣реИрдВ:
  1. getUserMedia (MediaStream), рдпрджрд┐ рд╕рд░рд▓реАрдХреГрдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдпрд╣ рдПрдХ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рд╡реАрдбрд┐рдпреЛ рд╕реНрдЯреНрд░реАрдо рдХреИрдкреНрдЪрд░ рдХрд░рдирд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдмрд╕ рдЕрдкрдиреЗ рдЖрдк рдХреЛ рджреЗрдЦреЗрдВ;)ред
    рдПрдХ рд╣рдмреНрд░ рдкрд░ рдПрдХ рдЕрдЪреНрдЫрд╛ рд▓реЗрдЦ рд╣реИ ред
  2. RTCPeerConnection рдХрд╛ рдЙрдкрдпреЛрдЧ рдмреНрд░рд╛рдЙрдЬрд╝рд░реЛрдВ рдХреЗ рдмреАрдЪ рд╕реАрдзреЗ рд╕рдВрд╡рд╛рдж рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рджрд░рдЕрд╕рд▓, RTCPeerConnection рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рдмрд╛рдд рдЖрдЧреЗ рдмрдврд╝реЗрдЧреАред
  3. RTCDataChannel: рд╡рд┐рднрд┐рдиреНрди рдбреЗрдЯрд╛ рдХреЗ рдЖрджрд╛рди-рдкреНрд░рджрд╛рди рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ: рдкрд╛рда, рдлрд╛рдЗрд▓реЗрдВ рдФрд░ рдЕрдиреНрдпред рдлрд┐рд▓рд╣рд╛рд▓, рд╡реЗ рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣ рдХреЗрд╡рд▓ рдкрд░реАрдХреНрд╖рдг рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ 25 рдХреНрд░реЛрдо рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╣реИ, рдЭрдВрдбреЗ рд╢рд╛рдорд┐рд▓ рдХрд┐рдП рдмрд┐рдирд╛ рдпрд╣ рдХреЗрд╡рд▓ 27 рдХреНрд░реЛрдо рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╣реЛ рдЬрд╛рдПрдЧрд╛ред


рдкреАрдпрд░ рдХрдиреЗрдХреНрд╢рди


рддреЛ рдЪрд▓рд┐рдП рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдкрд╣рд┐рдпрд╛ рдХреЛ рд╕реБрджреГрдврд╝ рдирд╣реАрдВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЗрд╕ рдбреЗрдореЛ рд╕реЗ рдХреЛрдб рд▓реЗрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдЗрд╕реЗ рдереЛрдбрд╝рд╛ рдФрд░ рд╕рд╛рд░реНрд╡рднреМрдорд┐рдХ рдмрдирд╛рдПрдВ (рдпрд╣ Google рдРрдк рдЗрдВрдЬрди рд╕реЗ рдмрдВрдзрд╛ рд╣реБрдЖ рд╣реИ) рдФрд░ рдХреБрдЫ рд╕реНрдерд╛рдиреЛрдВ рдореЗрдВ рд╕рд░рд▓ рд╣реИред рдпрд╣рд╛рдВ рдПрдХ рдФрд░ рдПрдбреЗрдкреНрдЯрд░ред рдЬреЗрдПрд╕ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдЬреБрдбрд╝рд╛ рд╣реБрдЖ рд╣реИ - рдпрд╣ рдХреЛрдб рдХреЗ рдХреБрдЫ рдПрдХреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдмрд╣реБрдд рдХреБрдЫ рдЕрднреА рднреА рдЙрдкрд╕рд░реНрдЧреЛрдВ рдХреЗ рд╕рд╛рде рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИ, рдФрд░ рдореБрдЦреНрдп рдмреНрд░рд╛рдЙрдЬрд╝рд░реЛрдВ рдХреЗ рд▓рд┐рдП рднреА рдЕрд▓рдЧ рд╣реИред

RTCPeerConnection рдХреЛ рд╣реА рдХрд╛рдлреА рд╕рд░рд▓рддрд╛ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ:
// Stun          ,    NAT,  , , google    . var pc_config = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]}; var pc_constraints = {"optional": [{"DtlsSrtpKeyAgreement": true}]}; pc = new RTCPeerConnection(pc_config, pc_constraints); pc.onicecandidate = onIceCandidate; pc.onaddstream = onRemoteStreamAdded; 

рдкреБрд░рд╛рдиреЗ рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ, RTCPeerConnection () рдореЗрдВ рдереЛрдбрд╝реЗ рдЕрд▓рдЧ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдкрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред

рдЖрд░рдЯреАрд╕реАрдкреАрд░реЗрдВрдХреНрд╢рди рд╢реБрд░реВ рдХрд░рдирд╛ рдФрд░ рд╢реБрд░реВ рдХрд░рдирд╛



 function initialize() { //          localVideo = document.getElementById("localVideo"); miniVideo = document.getElementById("miniVideo"); remoteVideo = document.getElementById("remoteVideo"); //                PeerConnection getUserMedia( {'audio':true, 'video':{"mandatory": {}, "optional": []}}, function(localVideo, stream){ //   PeerConnection. localVideo.src = window.URL.createObjectURL(stream); if (initiator) maybeStart(); }, function(error){console.log("Failed to get access to local media. Error code was " + error.code);} ); if (initiator) maybeStart(); sendMessage(); } 


рд╕рдВрджреЗрд╢ рд╕реЗрд╡рд╛


рдЗрд╕ рдмрд┐рдВрджреБ рдкрд░, рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдПрдХ рджреВрд╕рд░реЗ рд╕реЗ рд╕рдВрдкрд░реНрдХ рдХрд░рдиреЗ рдХрд╛ рддрд░реАрдХрд╛ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рднрд┐рдиреНрди рд╕рдВрджреЗрд╢реЛрдВ рдХрд╛ рдЖрджрд╛рди-рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВред рдЙрдореНрдореАрджрд╡рд╛рд░ рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рдВрджреЗрд╢реЛрдВ рдореЗрдВ, рд╡рд┐рднрд┐рдиреНрди рд╡рд┐рдХрд▓реНрдк рдЖрддреЗ рд╣реИрдВ, рдЬрд┐рдирдореЗрдВ рд╕рд░реНрд╡рд░ рд╕реНрдЯрди рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
 //           . S->C: {"type":"candidate","label":1,"id":"video","candidate":"a=candidate:2437072876 1 udp 2113937151 192.168.1.2 35191 typ host generation 0\r\n"} S->C: {"type":"candidate","label":0,"id":"audio","candidate":"a=candidate:941443129 1 udp 1845501695 111.222.111.222 35191 typ srflx raddr 192.168.1.2 rport 35191 generation 0\r\n"} 


 // CallBack ,    RTCPeerConnection     ,      . ,        -   websokets,  ajax. pc.onicecandidate = onIceCandidate; function onIceCandidate(event) { if (event.candidate) { sendMessage({type: 'candidate', label: event.candidate.sdpMLineIndex, id: event.candidate.sdpMid, candidate: event.candidate.candidate}); } else { console.log("End of candidates."); } } 


рд╕рд░реНрд╡рд░ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдПрдХ рд╕рдВрджреЗрд╢ рднреЗрдЬрдиреЗ рдХрд╛ рд╣рдорд╛рд░рд╛ рдХрд╛рд░реНрдп рдХрд╛рдлреА рд╕рд░рд▓ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЫреЛрдЯреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рдорд╛рдорд▓реЗ рдХреЛ рд▓рд┐рдЦрдиреЗ рдФрд░ рд╕рд░реНрд╡рд░ рдкрдХреНрд╖ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдЬрд╛рдХреНрд╕ рдХреЛ рдПрдХ рд╕рд░рд▓ рдФрд░ рдЕрдзрд┐рдХ рдХрд┐рдлрд╛рдпрддреА рд╡рд┐рдХрд▓реНрдк рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ рдЧрдпрд╛:

 function sendMessage(message) { var msgString = JSON.stringify(message); console.log('C->S: ' + msgString); $.ajax({ type: "POST", url: "/chat/tv", dataType: "json", data: { room:room, user_id:user_id, last:last, mess:msgString, is_new:is_new }, success: function(data){ console.log(['data.msg', data.msg]) if( data.last) last = data.last; for (var res in data.msg){ var msg = data.msg[res]; processSignalingMessage(msg[2]); } } }); is_new = 0; function repeat() { timeout = setTimeout(repeat, 5000); sendMessage(); } if (!timeout) repeat(); } 


рдпрджрд┐ рдЕрдиреБрд░реЛрдз рд╕рдлрд▓ рд░рд╣рд╛, рддреЛ рджреВрд╕рд░реЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рд╕реЗ рд╕рдВрдЪрд┐рдд рд╕рдВрджреЗрд╢ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЖрддреЗ рд╣реИрдВ:
 function processSignalingMessage(message) { //               . //        peeronnection var msg = JSON.parse(message); if (msg.type === 'offer') { if (!initiator && !started){ if (!started && localStream ) { createPeerConnection(); pc.addStream(localStream); started = true; if (initiator) pc.createOffer(setLocalAndSendMessage, null, {"optional": [], "mandatory": {"MozDontOfferDataChannel": true}}); } pc.setRemoteDescription(new RTCSessionDescription(msg)); pc.createAnswer(setLocalAndSendMessage, null, sdpConstraints); } else if (msg.type === 'answer' && started) { pc.setRemoteDescription(new RTCSessionDescription(msg)); } else if (msg.type === 'candidate' && started) { var candidate = new RTCIceCandidate({sdpMLineIndex:msg.label, candidate:msg.candidate}); pc.addIceCandidate(candidate); } else if (msg.type === 'bye' && started) { pc.close(); } } function setLocalAndSendMessage(sessionDescription) { //  preferOpus  . sessionDescription.sdp = preferOpus(sessionDescription.sdp); pc.setLocalDescription(sessionDescription); sendMessage(sessionDescription); } 


рджрд░рдЕрд╕рд▓, рдпрд╣ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд░реВрдк рд╕реЗ рд╕рднреА рд╣реИ, рдпрд╣ рд╡реАрдбрд┐рдпреЛ рд╕реНрдЯреНрд░реАрдо рддрддреНрд╡ рдХреЛ рдЕрд╕рд╛рдЗрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛ рд░рд╣рддрд╛ рд╣реИ рдФрд░ рдЙрд╕ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рди рднреВрд▓реЗрдВ рдЬреЛ рд╕рднреА рдХреЛрдб рдХреА рд╢реБрд░реБрдЖрдд рдХреЛ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИред
 pc.onaddstream = onRemoteStreamAdded; function onRemoteStreamAdded(event) { remoteVideo.src = window.URL.createObjectURL(event.stream); remoteStream = event.stream; } setTimeout(initialize, 1); 


рд╕рд░реНрд╡рд░ рд╕рд╛рдЗрдб


рд╣рдорд╛рд░рд╛ рд╕рд░реНрд╡рд░ рднрд╛рдЧ рдХрд╛рдлреА рд╕рд░рд▓ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рд╕рд░реНрд╡рд░ рдХреЛ рд╕реАрдзреЗ рд╕рдВрд╡рд╛рдж рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░реЛрдВ рдХрд╛ рд╕рдордиреНрд╡рдп рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред
рдФрд░ рдПрдХ рдЕрдиреНрдп рдмрд╛рд░реАрдХрд┐рдпреЛрдВ, var рд╕рд░реНрдЬрдХ = {{рдЖрд░рдВрднрдХрд░реНрддрд╛}} рдкреИрд░рд╛рдореАрдЯрд░ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдХреМрди рд╕рд╛ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдХрдиреЗрдХреНрдЯ рд╣реЛрдЧрд╛ рдФрд░ рдХреМрди рд╕рд╛ рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░ рд░рд╣рд╛ рд╣реИред
рдЕрд░реНрдерд╛рддреН, рдПрдХ рдХреЗ рдкрд╛рд╕ рдХреНрд░рдорд╢рдГ 0, рдЕрдиреНрдп 1 рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

рд╕рд░реНрд╡рд░ рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рдХрд╛рдлреА рд╕рд░рд▓ рд╣реИ, рдЬреАрдИрдЯреА рдЕрдиреБрд░реЛрдз рдкрд░ рд╣рдо рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдПрдХ рдХрдорд░рд╛ рдмрдирд╛рддреЗ рд╣реИрдВ, рдЗрд╕рдХреА рдЖрдИрдбреА рдХреЛ рдЯреЗрдореНрдкрд▓реЗрдЯ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдЕрдЧрд░ рдпрд╣ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдирд╣реАрдВ рд╣реИ, рддреЛ рдПрдХ рдирдпрд╛ рдмрдирд╛рдПрдВред
 def chat(room): doc = db.chat.find_one({'_id':room}) initiator = 1 if not doc: initiator = 0 doc = {'_id':room, 'mess': []} db.chat.save(doc) return templ('rtc.tpl', initiator = initiator, room=room) 


рдПрдХ рдкреЛрд╕реНрдЯ рдЕрдиреБрд░реЛрдз рдкрд░, рд╣рдо рдЧреНрд░рд╛рд╣рдХ рд╕реЗ рдбреЗрдЯрд╛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдпрджрд┐ рдЧреНрд░рд╛рд╣рдХ рдиреЗ рдПрдХ рдЧреИрд░-рд░рд┐рдХреНрдд рд╕рдВрджреЗрд╢ рднреЗрдЬрд╛ рд╣реИ, рддреЛ рд╣рдо рдЗрд╕рдХреА рд╕рд╛рдордЧреНрд░реА рдХреЛ рдХрдорд░реЗ рдореЗрдВ рд▓рд╛рддреЗ рд╣реИрдВ, рдлрд┐рд░ рдЕрдЧреНрд░рднреВрдорд┐ рдореЗрдВ рд╣рдо рдЬрд╛рдВрдЪрддреЗ рд╣реИрдВ рдХрд┐ рдкреНрд░рд╛рдкреНрдд рд╕рдВрджреЗрд╢ рдмрд┐рд▓реНрдХреБрд▓ "рдЪреИрдЯ рдореЗрдВ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рд╕реЗ" рд╣реИрдВ рдФрд░ рд╡реЗ рдирдП рд╣реИрдВ рддреЛ рд╣рдо рдЙрдиреНрд╣реЗрдВ рдЕрдкрдиреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдкрд░ рд╡рд╛рдкрд╕ рдХрд░рддреЗ рд╣реИрдВред

 def chat_post(): lst = 0.0; msg = [] room = get_post('room') user_id= get_post('user_id') last= float(get_post('last', 0)) mess= get_post('mess') doc = db.chat.find_one({'_id':room}) if mess: doc['mess'].append((time.time(), mess, user_id)) db.chat.save(doc) for i_time, i_msg, i_user in doc['mess']: if i_user != user_id and i_time > last: lst = i_time msg.append((i_time, i_user, i_msg)) if not lst: lst = last return json.dumps({'result': 'ok', 'last': lst, 'msg': msg}) 

рдЙрддреНрддрд░реА рднрд╛рдЧ рдХрд╛ рдпрд╣ рд╡рд┐рд╡рд░рдг рдкреВрд░рд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред

рд╕реВрддреНрд░реЛрдВ рдХрд╛ рдХрд╣рдирд╛ рд╣реИ:
Html5rocks.com рдкрд░ Webrtc рдХреА рдорджрдж
рдЖрдзрд┐рдХрд╛рд░рд┐рдХ рд╡реЗрдмрд╕рд╛рдЗрдЯ webrtc

Source: https://habr.com/ru/post/In171477/


All Articles