Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code
-
Upload
amitesh-madhur -
Category
Technology
-
view
1.742 -
download
1
description
Transcript of Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code
![Page 1: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/1.jpg)
WebRTCAmitesh Madhur (@amiteshawa)
Cisco Systems
![Page 2: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/2.jpg)
Agenda
1. Media Stream 2. Constraints3. RTCPeerConnection 4. Network (STUN, TURN)5. DataChannel6. Ephemeral password
![Page 3: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/3.jpg)
Peer to peer, plugin free!
ServerX
![Page 4: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/4.jpg)
WebRTC1. MediaStream
2. RTCPeerConnection
3. DataChannel
![Page 5: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/5.jpg)
Media Stream
1. getUserMedia2. Collects audio, video, screen inputs3. Synchronizes Audio & Video4. Noise Cancellation5. Image Enhancement
![Page 6: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/6.jpg)
<video id=“me" autoplay></video>
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia; if (navigator.getUserMedia) navigator.getUserMedia(video: true, onSuccess, onError);
window.URL = window.URL || window.webkitURL; var me = document.getElementById('me');
function onSuccess(stream) me.src = window.URL.createObjectURL(stream); function onError(e) // error
![Page 7: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/7.jpg)
Demo
![Page 8: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/8.jpg)
<style> video
-webkit-transform: scaleX(-1);
</style>
<video id=“me" autoplay></video>
Confused Left/Right?
canvasContext.translate(width, 0); canvasContext.scale(-1, 1);
![Page 9: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/9.jpg)
<video id=“me" autoplay></video>
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia; if (navigator.getUserMedia) navigator.getUserMedia(video: true, onSuccess, onError);
window.URL = window.URL || window.webkitURL; var me = document.getElementById('me');
function onSuccess(stream) me.src = window.URL.createObjectURL(stream); function onError(e) // error
navigator.getUserMedia(video: true,
onSuccess, onError);
Constraints
![Page 10: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/10.jpg)
audio: true, video: true
Constraints (audio, video)
![Page 11: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/11.jpg)
audio: true,video:
mandatory: maxWidth: 320, maxHeight: 180
Constraints (video height, width)
![Page 12: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/12.jpg)
getUserMedia(video: mandatory:
chromeMediaSource: 'screen'
, audio: false, onSuccess, onError);
Constraints for screen capture
![Page 13: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/13.jpg)
1. Works only on https 2. chrome://flags/#enable-usermedia-screen-
capture
chromeMediaSource: 'screen'
![Page 14: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/14.jpg)
Peer Connection
![Page 15: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/15.jpg)
1. Establish a connection though Signaling2. Pass the user media stream3. Other side gets the stream4. Add the received stream to <video> tag
![Page 16: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/16.jpg)
Demo
![Page 17: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/17.jpg)
github.com/amiteshawa/learn-rtc
![Page 18: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/18.jpg)
<video id=“me" autoplay></video> <video id=“other" autoplay></video>
peer = new RTCPeerConnection(servers);peer.onaddstream = gotRemoteStream;peer.addStream(localStream);
if(host) peer.createOffer(callGotOffer, null, mandatory:
OfferToReceiveAudio: true, OfferToReceiveVideo: true);
else peer.createAnswer(peer.remoteDescription, callGotOffer);function callGotOffer(sd) peer.setLocalDescription(sd); function gotAnswer(desc) peer.setRemoteDescription(new RTCSessionDescription(desc)); function gotRemoteStream(e) attachMediaStream(remoteVideo, e.stream);
![Page 19: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/19.jpg)
<video id=“me" autoplay></video> <video id=“other" autoplay></video>
peer = new RTCPeerConnection(servers);peer.onaddstream = gotRemoteStream;peer.addStream(localStream);
if(host) peer.createOffer(callGotOffer, null, mandatory:
OfferToReceiveAudio: true, OfferToReceiveVideo: true);
else peer.createAnswer(peer.remoteDescription, callGotOffer);function callGotOffer(sd) peer.setLocalDescription(sd); function gotAnswer(desc) peer.setRemoteDescription(new RTCSessionDescription(desc)); function gotRemoteStream(e) attachMediaStream(remoteVideo, e.stream);
var STUN, TURN, config = ;STUN = url: “stun:stun.l.google.com:19302”;TURN = username: “turn-user", credential: "NGFmNGRlMOWZmMTVmZGZiNg==",
url: "turn:10.5.7.13:3333?transport=udp“ ;config.iceServers = [STUN, TURN];
![Page 20: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/20.jpg)
User 1 User 2Page Load
Page Load
Websocket
Renders Invite Button
Renders Invite Button
![Page 21: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/21.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
![Page 22: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/22.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create InviteClicks Invite Button
GUM
![Page 23: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/23.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create Invite Broadcast Invite
Broadcast Invite
GUM
![Page 24: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/24.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create Invite Broadcast Invite
Broadcast Invite
Invite Button Changed to “Join”Ignored Invite
GUM
![Page 25: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/25.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create Invite Broadcast Invite
Broadcast InviteInvite Button Changed to “Join”
Ignored Invite Clicked Join Button
GUMOffer SDPOffer SDP
GUM
![Page 26: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/26.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create Invite Broadcast Invite
Broadcast InviteInvite Button Changed to “Join”
Ignored Invite Clicked Join ButtonOffer SDPOffer SDP
Answer SDP Answer SDP
Remote Desc
GUM
GUM
Remote Desc
![Page 27: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/27.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create Invite Broadcast Invite
Broadcast InviteInvite Button Changed to “Join”
Ignored Invite Clicked Join ButtonOffer SDPOffer SDP
Answer SDP Answer SDP
Remote DescICE CandidateICE Candidate
GUM
Remote Desc
GUM
![Page 28: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/28.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create Invite Broadcast Invite
Broadcast InviteInvite Button Changed to “Join”
Ignored Invite Clicked Join ButtonOffer SDPOffer SDP
Answer SDP Answer SDP
Remote DescICE CandidateICE Candidate
ICE Candidate ICE Candidate
Add StreamAdd Stream
GUM
Remote Desc
GUM
![Page 29: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/29.jpg)
User 1 User 2Page Load Page Load
Websocket
Open connection Open connection
Create Invite Broadcast Invite
Broadcast InviteInvite Button Changed to “Join”
Ignored Invite Clicked Join ButtonOffer SDPOffer SDP
Answer SDP Answer SDP
Remote DescICE CandidateICE Candidate
ICE Candidate ICE Candidate
Add Stream
Add Stream
GUM
Remote Desc
GUM
![Page 30: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/30.jpg)
STUN/TURN and ICE
![Page 31: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/31.jpg)
NAT
STUN
NAT
![Page 32: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/32.jpg)
NAT
STUN
NAT
Not 100% Reliable
![Page 33: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/33.jpg)
NAT
TURN
SECURE
NAT
![Page 34: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/34.jpg)
NAT
TURN
SECURE
NAT
Expensive & Slow
![Page 35: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/35.jpg)
SECURE
NAT
ICE
NAT
STUNTURN
![Page 36: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/36.jpg)
Public STUNstun.l.google.com:19302stun1.l.google.com:19302stun2.l.google.com:19302stun3.l.google.com:19302stun4.l.google.com:19302
![Page 37: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/37.jpg)
slideshare.net/amiteshawa/web-rtc-media-stra
Wanna Setup your own?
![Page 38: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/38.jpg)
Data Channel
![Page 39: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/39.jpg)
RTCDataChannel
1. Peer to peer data sharing2. Works with RTCPeerConnection3. Secure4. Websocket like APIs
![Page 40: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/40.jpg)
createOffer = function() peer = new webkitRTCPeerConnection(config,
optional: [RtpDataChannels: true]); channel = peer.createDataChannel("sendDataChannel",
reliable: false); channel.onopen = manageChannel; channel.onclose = manageChannel; , sendData = function(data) channel.send(data); , onMsg = function(e) obj.innerHTML += 'He: ' + e.data ; , manageChannel = function() if (channel.readyState === "open") channel.onmessage = onMsg; ;
![Page 41: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/41.jpg)
Demo
![Page 42: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/42.jpg)
Ephemeral password
![Page 43: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/43.jpg)
IN JAVASCRIPTvar turn; turn = url: 'turn:<user-name>@<IP>:<PORT>',
credential: ‘password‘;
// for chrome 28 and aboveturn = url: 'turn:<IP-address>:<PORT>', username: ‘<user-name>‘, credential: ‘<password>' ;
![Page 44: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/44.jpg)
Ephemeral password
1. Limited time TURN credentials2. Based on REST Service3. Webserver creates password4. Shared secret5. TURN Server does NOT implement the REST API6. Based on long-term credentials mechanism
![Page 45: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/45.jpg)
long-term credentials
User TURNUser sends request to TURN without password
![Page 46: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/46.jpg)
long-term credentials
User TURNUser sends request to TURN without password
TURN send Error 401, with realm and nonce
![Page 47: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/47.jpg)
long-term credentials
User TURNUser sends request to TURN without password
TURN send Error 401, with realm and nonce
User Checks 401 and extracts realm and nonce
![Page 48: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/48.jpg)
long-term credentials
User TURNUser sends request to TURN without password
TURN send Error 401, with realm and nonce
User Checks 401 and extracts realm and nonceUser generates MD5 key with user, realm
![Page 49: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/49.jpg)
long-term credentials
User TURNUser sends request to TURN without password
TURN send Error 401, with realm and nonce
User Checks 401 and extracts realm and nonceUser generates MD5 key with user, realm
User sends new request to TURN with password
![Page 50: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/50.jpg)
long-term credentials
User TURNUser sends request to TURN without password
TURN send Error 401, with realm and nonce
User Checks 401 and extracts realm and nonceUser generates MD5 key with user, realm
User sends new request to TURN with password
TURN Validates
Matches? Then connected
![Page 51: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/51.jpg)
Long-term password alone does not solve the problem for WebRTC
![Page 52: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/52.jpg)
Ephemeral credentials
User TURN
my-svc/get-turn-auth
REST Server
MySQL
Shared SecretCreates username, password
Sends username, password
Send connection request to TURN
Shared Secret
After this it same as long term auth
![Page 53: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/53.jpg)
Create Tables
CREATE TABLE turnusers_lt ( name varchar(512) PRIMARY KEY, hmackey char(32));
CREATE TABLE turn_secret ( value varchar(512));
CREATE TABLE allowed_peer_ip ( ip_range varchar(256));
CREATE TABLE denied_peer_ip ( ip_range varchar(256));
![Page 54: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/54.jpg)
Create REST API
Generate username and credential username= <USER NAME> + ":" + <timestamp>password = base64(hmac-sha1(secret, username))
![Page 55: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/55.jpg)
"username" : "user1:1393412082", "password" : "NGFmNzUzZDIxOWE1OWI0NTBmZGZiNg==", "ttl" : 86400, "uris" : [ "turn:1.2.3.4:3333?transport=udp" ]var i, uri, iceServer, config = "iceServers": [];for (i = 0; i < response.uris.length; ++i) uri = response.uris[i]; iceServer = "username":response.username, "credential":response.password, "uri":uri ; config.iceServers.push(iceServer);var pc = new PeerConnection(config);
![Page 56: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/56.jpg)
Config
1. In turnserver.conf Uncomment/Enable 1. lt-cred-mech2. use-auth-secret3. static-auth-secret4. mysql-userdb="host=127.0.0.1 dbname=turn
user=root password= port=3306 connect_timeout=60"
5. realm=foobar6. fingerprint7. Start turn: turnserver -c /usr/local/etc/turnserver.conf
![Page 57: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/57.jpg)
Create User & Secret
Create Secret$ turnadmin --mysql-userdb="host=127.0.0.1 dbname=turn user=root password= connect_timeout=10" --set-secret=no1knows
![Page 58: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/58.jpg)
>= 21 >= 20 >= 12
![Page 59: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/59.jpg)
WebRTC4all…
![Page 60: Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code](https://reader031.fdocuments.us/reader031/viewer/2022012922/554b9cf7b4c905b8618b46a1/html5/thumbnails/60.jpg)
Thank youTwitter: @amiteshawa