@@ -1,7 +1,7 @@
< h1 > WebRTC Demo </ h1 >
< h1 > WebRTC</ h1 >
< script >
< script >
// This is only needed for browser testing
window . RTCPeerConnection = window . mozRTCPeerConnection || window . webkitRTCPeerConnection ;
window . RTCSessionDescription = window . mozRTCSessionDescription || window . RTCSessionDescription ;
@@ -21,29 +21,40 @@ <h1>WebRTC Demo</h1>
var clients ;
var remoteVideoViews = [ ] ;
var ourClientId = '' + randomInt ( 1 , 1000 ) ;
console . log ( 'ourClientId' , ourClientId ) ;
console . log ( 'ourClientId' , ourClientId ) ;
// Actual RTCPeerConnections based on clients
var peers = { } ;
var mediaConstraints = {
optional : [ ] ,
mandatory : {
OfferToReceiveAudio : true ,
OfferToReceiveVideo : true
}
optional : [ ] ,
mandatory : {
OfferToReceiveAudio : true ,
OfferToReceiveVideo : true
}
} ;
// http://www.w3.org/TR/webrtc/#idl-def-RTCOfferOptions
var RTC_Offer_Options = {
offerToReceiveVideo :true ,
offerToReceiveAudio : true
offerToReceiveVideo : true ,
offerToReceiveAudio : true
} ;
var peerconnectionOptions = {
'optional' : [
{ 'DtlsSrtpKeyAgreement' : true } ,
{ 'RtpDataChannels' : true }
]
} ;
window . iceServers = {
iceServers : [
{ url : 'stun:stun.services.mozilla.com' } ,
{ url : 'stun:stun.l.google.com:19302' }
]
// 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 ) {
@@ -59,238 +70,298 @@ <h1>WebRTC Demo</h1>
var signaling = io . connect ( 'http://localhost:8000/' ) ;
signaling . on ( 'clients' , function ( clients ) {
console . log ( 'clients' , clients ) ;
console . log ( 'clients' , clients ) ;
} ) ;
signaling . on ( 'call list' , function ( room , usernames ) {
clients = usernames ;
startCalling ( ) ;
clients = usernames ;
startCalling ( ) ;
} ) ;
signaling . emit ( 'username' , ourClientId , function ( error ) {
console . log ( 'username' , error ) ;
signaling . emit ( 'join' , 'testing' , function ( error ) {
console . log ( 'join' , 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 ) ;
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 ;
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 ) ;
document . body . removeChild ( videoView ) ;
remoteVideoViews . splice ( videoView , 1 ) ;
}
var startCalling = function ( ) {
console . log ( 'startCalling' , clients , ! ! localStream ) ;
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 ) ;
// Wait to recive a client list and access to the local video view
if ( ! clients || ! localStream ) {
return ;
}
var state = peer . signalingState ;
signaling . on ( 'messageReceived' , function ( name , message ) {
var offerSDP = new RTCSessionDescription ( message . data ) ;
peer . setRemoteDescription ( offerSDP ) ;
console . log ( 'messageReceived from' , name , message ) ;
// We have been called so we need to send the SDP and ICE Candidate
if ( state === 'stable' ) {
if ( message . type === 'offer' ) {
var peer = peerConnectionTo ( name ) ;
console . log ( 'state' , state ) ;
var state = peer . signalingState ;
peer . createAnswer ( function ( answerSDP ) {
var offerSDP = new RTCSessionDescription ( message . data ) ;
peer . setRemoteDescription ( offerSDP ) ;
// 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);
// We have been called so we need to send the SDP and ICE Candidate
if ( state === 'stable' ) {
console . log ( 'state' , state ) ;
peer . setLocalDescription ( answerSDP ) ;
peer . createAnswer ( function ( answerSDP ) {
peer . setLocalDescription ( answerSDP ) ;
// use XHR/WebSocket/etc. to exchange answer-sdp with "offerer"
signaling . emit ( 'sendMessage' , name , {
type : 'offer' ,
data : answerSDP
} ) ;
// use XHR/WebSocket/etc. to exchange answer-sdp with "offerer"
signaling . emit ( 'sendMessage' , name , {
type : 'offer' ,
data : answerSDP
} ) ;
} , console . log , mediaConstraints ) ;
} , console . log , mediaConstraints ) ;
peer . onicecandidate = function ( event ) {
if ( ! event || ! event . candidate ) return ;
// answerer.addIceCandidate(event.candidate);
peer . onicecandidate = function ( event ) {
if ( ! event || ! event . candidate ) return ;
// answerer.addIceCandidate(event.candidate);
signaling . emit ( 'sendMessage' , client , {
type : 'candidate' ,
data : event . candidate
} ) ;
signaling . emit ( 'sendMessage' , client , {
type : 'candidate' ,
data : event . candidate
} ) ;
// We only need the first candidate...?
peer . onicecandidate = null ;
} ;
// We only need the first candidate...?
peer . onicecandidate = null ;
} ;
}
}
return ;
}
return ;
}
if ( message . type === 'candidate' ) {
var peer = peerConnectionTo ( name ) ;
peer . addIceCandidate ( new RTCIceCandidate ( message . data ) ) ;
}
} ) ;
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 ] ;
for ( var i = 0 ; i < clients . length ; i ++ ) {
var client = clients [ i ] ;
// Skip ourselves
if ( client === ourClientId ) continue ;
// Skip ourselves
if ( client === ourClientId ) continue ;
if ( peers [ client ] ) {
console . log ( 'already have a peer with' , client ) ;
continue ;
}
if ( peers [ client ] ) {
console . log ( 'already have a peer with' , client ) ;
continue ;
}
call ( client ) ;
call ( client ) ;
} ;
} ;
}
// We are calling something
function call ( name ) {
if ( peers [ name ] ) {
console . log ( 'already have a peer with' , name ) ;
return ;
}
if ( peers [ name ] ) {
console . log ( 'already have a peer with' , name ) ;
return ;
}
console . log ( 'call' , name ) ;
console . log ( 'call' , name ) ;
var peer = peerConnectionTo ( name ) ;
// let the "negotiationneeded" event trigger offer generation
peer . onnegotiationneeded = function ( ) {
console . log ( 'peer.negotiationneeded for ' , name ) ;
var peer = peerConnectionTo ( name ) ;
peer . createOffer ( function ( offer ) {
// let the "negotiationneeded" event trigger offer generation
peer . onnegotiationneeded = function ( ) {
console . log ( 'peer.negotiationneeded for ' , name ) ;
console . log ( 'createOffer' , name , offer ) ;
peer . setLocalDescription ( offer ) ;
peer . createOffer ( function ( offer ) {
signaling . emit ( 'sendMessage' , name , {
type : 'offer' ,
data : offer // peer.localDescription
} ) ;
console . log ( 'createOffer' , name , offer ) ;
peer . setLocalDescription ( offer ) ;
} , console . log , RTC_Offer_Options ) ;
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
} ) ;
peer . onicecandidate = function ( event ) {
console . log ( 'onicecandidate for' , name , event . candidate ) ;
// We only need the first candidate...?
// peer.onicecandidate = null;
} ;
if ( ! event || ! event . candidate ) return ;
signaling . emit ( 'sendMessage' , name , {
type : 'candidate' ,
data : event . candidate
} ) ;
// 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 ) ;
} ;
// We only need the first candidate...?
// peer.onicecandidate = null;
} ;
}
function peerConnectionTo ( name ) {
if ( ! peers [ name ] ) {
console . log ( 'creating peerConnectionTo' , name ) ;
if ( ! peers [ name ] ) {
console . log ( 'creating peerConnectionTo' , name ) ;
peers [ name ] = new RTCPeerConnection ( window . iceServers ) ;
// http://www.w3.org/TR/webrtc/#rtcpeerconnection-interface
peers [ name ] = new RTCPeerConnection ( RTC_Configuration , peerconnectionOptions ) ;
// Add our video stream
peers [ name ] . addStream ( localStream ) ;
// Add our video stream
peers [ name ] . addStream ( localStream ) ;
// When we get access to their stream show it
peers [ name ] . onaddstream = function ( event ) {
addStream ( event . stream ) ;
} ;
// 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 ] ;
return peers [ name ] ;
}
function getUserMedia ( callback ) {
navigator . getUserMedia ( {
audio : true ,
video : true
} , callback , function ( e ) {
console . log ( e ) ;
} ) ;
navigator . getUserMedia ( {
audio : true ,
video : true
} , callback , function ( e ) {
console . log ( e ) ;
} ) ;
}
getUserMedia ( function ( stream ) {
localStreamView = addStream ( stream ) ;
localStream = stream ;
startCalling ( ) ;
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 ) )
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 )
waitUntilRemoteStreamStartsFlowing ( remote_video , callback )
} , 50 ) ;
}
</ script >
</ script >