-
-
Save mvenkatesh431/23eea38a8f356aaf9e02 to your computer and use it in GitHub Desktop.
Simple webrtc demo script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <h1>WebRTC</h1> | |
| <script> | |
| // This is only needed for browser testing | |
| window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection; | |
| window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; | |
| window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate; | |
| navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia; | |
| // window.URL = window.webkitURL || window.URL; | |
| </script> | |
| <script> | |
| // Globals that will be object properties once we wrap this up as a module | |
| var localStream; | |
| var localStreamView; | |
| var clients; | |
| var remoteVideoViews = []; | |
| var ourClientId = '' + randomInt(1,1000); | |
| console.log('ourClientId', ourClientId); | |
| // Actual RTCPeerConnections based on clients | |
| var peers = {}; | |
| var mediaConstraints = { | |
| optional: [], | |
| mandatory: { | |
| OfferToReceiveAudio: true, | |
| OfferToReceiveVideo: true | |
| } | |
| }; | |
| // http://www.w3.org/TR/webrtc/#idl-def-RTCOfferOptions | |
| var RTC_Offer_Options = { | |
| offerToReceiveVideo: true, | |
| offerToReceiveAudio: true | |
| }; | |
| var peerconnectionOptions = { | |
| 'optional': [ | |
| {'DtlsSrtpKeyAgreement': true}, | |
| {'RtpDataChannels': true } | |
| ] | |
| }; | |
| // http://w3c.github.io/webrtc-pc/#idl-def-RTCConfiguration | |
| var RTC_Configuration = { | |
| // more options here | |
| // ... | |
| iceServers: [ | |
| // {url: 'stun:stun.services.mozilla.com'}, | |
| {url: 'stun:stun.l.google.com:19302'} | |
| ] | |
| }; | |
| function randomInt(min, max) { | |
| return Math.floor(Math.random() * (max - min + 1)) + min; | |
| } | |
| </script> | |
| <!-- Sample signaling server using socket.io --> | |
| <script src="socket.io.js"></script> | |
| <script> | |
| var signaling = io.connect('http://localhost:8000/'); | |
| signaling.on('clients', function(clients) { | |
| console.log('clients', clients); | |
| }); | |
| signaling.on('call list', function(room, usernames) { | |
| clients = usernames; | |
| startCalling(); | |
| }); | |
| signaling.emit('username', ourClientId, function(error) { | |
| console.log('username', error); | |
| signaling.emit('join', 'testing', function(error) { | |
| console.log('join', error); | |
| }); | |
| }); | |
| function send(name, message) { | |
| console.log('send', name, message); | |
| signaling.emit('sendMessage', name, message); | |
| } | |
| </script> | |
| <script> | |
| // And and remove videos as needed | |
| var remoteVideoViews = []; | |
| function addStream(stream, me) { | |
| var videoView = document.createElement('video'); | |
| videoView.autoplay = true; | |
| videoView.setAttribute('muted', true); | |
| videoView.setAttribute('autoplay', true); | |
| videoView.setAttribute('controls', true); | |
| videoView.style.width = '300px'; | |
| // videoView.id = me || +new Date(); | |
| videoView.className = me ? 'me' : 'them'; | |
| videoView.src = URL.createObjectURL(stream); | |
| videoView.load(); | |
| remoteVideoViews.push(videoView); | |
| document.body.appendChild(videoView); | |
| return videoView; | |
| } | |
| function removeStream(videoView) { | |
| document.body.removeChild(videoView); | |
| remoteVideoViews.splice(videoView, 1); | |
| } | |
| var startCalling = function() { | |
| console.log('startCalling', clients, !!localStream); | |
| // Wait to recive a client list and access to the local video view | |
| if(! clients || !localStream) { | |
| return; | |
| } | |
| signaling.on('messageReceived', function(name, message) { | |
| console.log('messageReceived from', name, message); | |
| if(message.type === 'offer') { | |
| var peer = peerConnectionTo(name); | |
| var state = peer.signalingState; | |
| var offerSDP = new RTCSessionDescription(message.data); | |
| peer.setRemoteDescription(offerSDP); | |
| // We have been called so we need to send the SDP and ICE Candidate | |
| if(state === 'stable') { | |
| console.log('state', state); | |
| peer.createAnswer(function (answerSDP) { | |
| // Upgrade bandwidth from 30k to 5000k | |
| // console.log('answerSDP', answerSDP.sdp); | |
| // var Bandwidth = 5000; | |
| // answerSDP.sdp = answerSDP.sdp.replace(/b=AS:([0-9]+)/g, 'b=AS:'+Bandwidth); | |
| // console.log('answerSDP', answerSDP.sdp); | |
| peer.setLocalDescription(answerSDP); | |
| // use XHR/WebSocket/etc. to exchange answer-sdp with "offerer" | |
| signaling.emit('sendMessage', name, { | |
| type: 'offer', | |
| data: answerSDP | |
| }); | |
| }, console.log, mediaConstraints); | |
| peer.onicecandidate = function (event) { | |
| if (!event || !event.candidate) return; | |
| // answerer.addIceCandidate(event.candidate); | |
| signaling.emit('sendMessage', client, { | |
| type: 'candidate', | |
| data: event.candidate | |
| }); | |
| // We only need the first candidate...? | |
| peer.onicecandidate = null; | |
| }; | |
| } | |
| return; | |
| } | |
| if(message.type === 'candidate') { | |
| var peer = peerConnectionTo(name); | |
| peer.addIceCandidate(new RTCIceCandidate(message.data)); | |
| } | |
| }); | |
| for (var i = 0; i < clients.length; i++) { | |
| var client = clients[i]; | |
| // Skip ourselves | |
| if(client === ourClientId) continue; | |
| if(peers[client]) { | |
| console.log('already have a peer with', client); | |
| continue; | |
| } | |
| call(client); | |
| }; | |
| } | |
| // We are calling something | |
| function call(name) { | |
| if(peers[name]) { | |
| console.log('already have a peer with', name); | |
| return; | |
| } | |
| console.log('call', name); | |
| var peer = peerConnectionTo(name); | |
| // let the "negotiationneeded" event trigger offer generation | |
| peer.onnegotiationneeded = function () { | |
| console.log('peer.negotiationneeded for ', name); | |
| peer.createOffer(function (offer) { | |
| console.log('createOffer', name, offer); | |
| peer.setLocalDescription(offer); | |
| signaling.emit('sendMessage', name, { | |
| type: 'offer', | |
| data: offer // peer.localDescription | |
| }); | |
| }, console.log, RTC_Offer_Options); | |
| }; | |
| peer.onicecandidate = function (event) { | |
| console.log('onicecandidate for', name, event.candidate); | |
| if (!event || !event.candidate) return; | |
| signaling.emit('sendMessage', name, { | |
| type: 'candidate', | |
| data: event.candidate | |
| }); | |
| // We only need the first candidate...? | |
| // peer.onicecandidate = null; | |
| }; | |
| // Caller creates the datachannel | |
| var dc = peer.createDataChannel("chats"); | |
| peer.dc = dc; // save for later | |
| console.log("created DataChannel", dc.id, name); | |
| dc.onmessage = function (event) { | |
| console.log("dc.received: " + event.data, name); | |
| }; | |
| dc.onopen = function () { | |
| console.log("dc.open", name); | |
| var readyState = dc.readyState; | |
| console.log('dc.readyState', dc.readyState); | |
| if (readyState == "open") { | |
| dc.send('dc.send to', name, 'from', ourClientId); | |
| } else { | |
| console.log('channel not open yet, dc.send fail'); | |
| } | |
| // setTimeout(function() { | |
| // dc.send('dc.send to', name, 'from', ourClientId); | |
| // }, 3000); | |
| }; | |
| dc.onclose = function () { | |
| console.log("dc.close", name); | |
| }; | |
| } | |
| function peerConnectionTo(name) { | |
| if( ! peers[name]) { | |
| console.log('creating peerConnectionTo', name); | |
| // http://www.w3.org/TR/webrtc/#rtcpeerconnection-interface | |
| peers[name] = new RTCPeerConnection(RTC_Configuration, peerconnectionOptions); | |
| // Add our video stream | |
| peers[name].addStream(localStream); | |
| // When we get access to their stream show it | |
| peers[name].onaddstream = function (event) { | |
| console.log('adding stream', name); | |
| addStream(event.stream); | |
| }; | |
| // When we get access to their stream show it | |
| peers[name].onremovestream = function (event) { | |
| console.log('removing stream', name); | |
| removeStream(event.stream); | |
| }; | |
| // Both caller and callee get this but only callee uses it | |
| // since the caller creates the channel in call | |
| peers[name].ondatachannel = function (evt) { | |
| console.log('ondatachannel', evt); | |
| peers[name].dc = evt.channel; | |
| peers[name].dc.send('First Message'); | |
| }; | |
| } | |
| return peers[name]; | |
| } | |
| function getUserMedia(callback) { | |
| navigator.getUserMedia({ | |
| audio: true, | |
| video: true | |
| }, callback, function(e) { | |
| console.log(e); | |
| }); | |
| } | |
| getUserMedia(function (stream) { | |
| localStreamView = addStream(stream); | |
| localStream = stream; | |
| startCalling(); | |
| }); | |
| function waitUntilRemoteStreamStartsFlowing(remote_video, callback) | |
| { | |
| if (!(remote_video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA | |
| || remote_video.paused || remote_video.currentTime <= 0)) | |
| { | |
| // remote stream started flowing! | |
| callback(); | |
| return; | |
| } | |
| setTimeout(function() { | |
| waitUntilRemoteStreamStartsFlowing(remote_video, callback) | |
| }, 50); | |
| } | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment