var callerIdName, conferenceVoiceBridge, userAgent, userMicMedia, userWebcamMedia, currentSession, callTimeout, callActive, callICEConnected, callFailCounter, callPurposefullyEnded, uaConnected; function callIntoConference(voiceBridge, callback) { if (!callerIdName) { BBB.getMyUserInfo(function(userInfo) { console.log("User info callback [myUserID=" + userInfo.myUserID + ",myUsername=" + userInfo.myUsername + ",myAvatarURL=" + userInfo.myAvatarURL + ",myRole=" + userInfo.myRole + ",amIPresenter=" + userInfo.amIPresenter + ",dialNumber=" + userInfo.dialNumber + ",voiceBridge=" + userInfo.voiceBridge + "]."); callerIdName = userInfo.myUserID + "-bbbID-" + userInfo.myUsername; conferenceVoiceBridge = userInfo.voiceBridge voiceBridge = voiceBridge || conferenceVoiceBridge; console.log(callerIdName); webrtc_call(callerIdName, voiceBridge, callback); }); } else { voiceBridge = voiceBridge || conferenceVoiceBridge; webrtc_call(callerIdName, voiceBridge, callback); } } function joinWebRTCVoiceConference() { console.log("Joining to the voice conference"); var callback = function(message) { switch (message.status) { case 'failed': BBB.webRTCConferenceCallFailed(message.errorcode); break; case 'ended': BBB.webRTCConferenceCallEnded(); break; case 'started': BBB.webRTCConferenceCallStarted(); break; case 'connecting': BBB.webRTCConferenceCallConnecting(); break; case 'waitingforice': BBB.webRTCConferenceCallWaitingForICE(); break; case 'mediarequest': BBB.webRTCMediaRequest(); break; case 'mediasuccess': BBB.webRTCMediaSuccess(); break; case 'mediafail': BBB.webRTCMediaFail(); break; case 'websocketSucceded': BBB.webRTCConferenceCallWebsocketSucceeded(); break; case 'websocketFailed': BBB.webRTCConferenceCallWebsocketFailed(message.errorcode); break; } } // Set proper callbacks to previously created user agent if(userAgent) { setUserAgentListeners(callback); } callIntoConference(conferenceVoiceBridge, callback); } function leaveWebRTCVoiceConference() { console.log("Leaving the voice conference"); webrtc_hangup(); } function startWebRTCAudioTest(){ console.log("Testing webrtc audio"); var callback = function(message) { switch(message.status) { case 'failed': BBB.webRTCEchoTestFailed(message.errorcode); break; case 'ended': BBB.webRTCEchoTestEnded(); break; case 'started': BBB.webRTCEchoTestStarted(); break; case 'connecting': BBB.webRTCEchoTestConnecting(); break; case 'waitingforice': BBB.webRTCEchoTestWaitingForICE(); break; case 'mediarequest': BBB.webRTCMediaRequest(); break; case 'mediasuccess': BBB.webRTCMediaSuccess(); break; case 'mediafail': BBB.webRTCMediaFail(); break; case 'websocketSucceded': BBB.webRTCEchoTestWebsocketSucceeded(); break; case 'websocketFailed': BBB.webRTCEchoTestWebsocketFailed(message.errorcode); break; } } callIntoConference("9196", callback); } function stopWebRTCAudioTest(){ console.log("Stopping webrtc audio test"); webrtc_hangup(); } function stopWebRTCAudioTestJoinConference(){ console.log("Stopping webrtc audio test and joining conference afterwards"); var callback = function(request) { joinWebRTCVoiceConference(); } webrtc_hangup(callback); } function requestWebRTCWebcam(){ var callback = function(message) { switch(message.status) { case 'mediarequest': BBB.webRTCWebcamRequest(); break; case 'mediasuccess': BBB.webRTCWebcamRequestSuccess(); break; case 'mediafail': BBB.webRTCWebcamRequestFail(message.cause); break; } } makeWebRTCWebcamRequest(callback); } function makeWebRTCWebcamRequest(callback) { console.log("Requesting webcam permissions on Chrome "); callback({'status':'mediarequest'}); getUserWebcamMedia(function(stream) { console.log("getUserWebcamMedia: success"); userWebcamMedia = stream; callback({'status':'mediasuccess'}); }, function(error) { console.error("getUserWebcamMedia: failure - " + error.name); callback({'status':'mediafail', 'cause': error.name}); } ); } function createUA(username, server, callback) { if (userAgent) { console.log("User agent already created"); return; } console.log("Creating new user agent"); /* VERY IMPORTANT * - You must escape the username because spaces will cause the connection to fail * - We are connecting to the websocket through an nginx redirect instead of directly to 5066 */ var configuration = { uri: 'sip:' + encodeURIComponent(username) + '@' + server, wsServers: 'ws://' + server + '/ws', displayName: username, register: false, traceSip: true, autostart: false, userAgentString: "BigBlueButton", stunServers: "stun:stun.freeswitch.org" }; uaConnected = false; userAgent = new SIP.UA(configuration); setUserAgentListeners(callback); userAgent.start(); }; function setUserAgentListeners(callback) { console.log("reseting UA CALLBACKS"); userAgent.off('connected'); userAgent.on('connected', function() { uaConnected = true; callback({'status':'websocketSucceded'}); }); userAgent.off('disconnected'); userAgent.on('disconnected', function() { if (userAgent) { userAgent.stop(); userAgent = null; if (uaConnected) { callback({'status':'websocketFailed', 'errorcode': 1001}); // WebSocket disconnected } else { callback({'status':'websocketFailed', 'errorcode': 1002}); // Could not make a WebSocket connection } } }); } function getUserWebcamMedia(getUserWebcamMediaSuccess, getUserWebcamMediaFailure) { if (userWebcamMedia == undefined) { if (SIP.WebRTC.isSupported()) { SIP.WebRTC.getUserMedia({audio:false, video:true}, getUserWebcamMediaSuccess, getUserWebcamMediaFailure); } else { console.log("getUserWebcamMedia: webrtc not supported"); getUserWebcamMediaFailure("WebRTC is not supported"); } } else { console.log("getUserWebcamMedia: webcam already set"); getUserWebcamMediaSuccess(userWebcamMedia); } }; function getUserMicMedia(getUserMicMediaSuccess, getUserMicMediaFailure) { if (userMicMedia == undefined) { if (SIP.WebRTC.isSupported()) { SIP.WebRTC.getUserMedia({audio:true, video:false}, getUserMicMediaSuccess, getUserMicMediaFailure); } else { console.log("getUserMicMedia: webrtc not supported"); getUserMicMediaFailure("WebRTC is not supported"); } } else { console.log("getUserMicMedia: mic already set"); getUserMicMediaSuccess(userMicMedia); } }; function webrtc_call(username, voiceBridge, callback) { if (!isWebRTCAvailable()) { callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported return; } var server = window.document.location.hostname; console.log("user " + username + " calling to " + voiceBridge); if (!userAgent) { createUA(username, server, callback); } if (userMicMedia !== undefined) { make_call(username, voiceBridge, server, callback, false); } else { callback({'status':'mediarequest'}); getUserMicMedia(function(stream) { console.log("getUserMicMedia: success"); userMicMedia = stream; callback({'status':'mediasuccess'}); make_call(username, voiceBridge, server, callback, false); }, function(e) { console.error("getUserMicMedia: failure - " + e); callback({'status':'mediafail', 'cause': e}); } ); } } function make_call(username, voiceBridge, server, callback, recall) { if (!userAgent.isConnected()) { console.log("Trying to make call, but UserAgent hasn't connected yet. Delaying call"); userAgent.once('connected', function() { console.log("UserAgent has now connected, retrying the call"); make_call(username, voiceBridge, server, callback, recall); }); return; } if (currentSession) { console.log('Active call detected ignoring second make_call'); return; } // Make an audio/video call: console.log("Setting options.. "); var options = { media: { stream: userMicMedia, render: { remote: { audio: document.getElementById('remote-media') } } } }; callTimeout = setTimeout(function() { console.log('Ten seconds without updates sending timeout code'); callback({'status':'failed', 'errorcode': 1006}); // Failure on call currentSession = null; var userAgentTemp = userAgent; userAgent = null; userAgentTemp.stop(); }, 10000); callActive = false; callICEConnected = false; callPurposefullyEnded = false; callFailCounter = 0; console.log("Calling to " + voiceBridge + "...."); currentSession = userAgent.invite('sip:' + voiceBridge + '@' + server, options); // Only send the callback if it's the first try if (recall === false) { console.log('call connecting'); callback({'status':'connecting'}); } else { console.log('call connecting again'); } // The connecting event fires before the listener can be added currentSession.on('connecting', function(){ clearTimeout(callTimeout); }); currentSession.on('progress', function(response){ console.log('call progress: ' + response); clearTimeout(callTimeout); }); currentSession.on('failed', function(response, cause){ console.log('call failed with cause: '+ cause); if (currentSession) { if (callActive === false) { callback({'status':'failed', 'errorcode': 1004}); // Failure on call currentSession = null; var userAgentTemp = userAgent; userAgent = null; userAgentTemp.stop(); } else { callActive = false; //currentSession.bye(); currentSession = null; userAgent.stop(); } } clearTimeout(callTimeout); }); currentSession.on('bye', function(request){ callActive = false; if (currentSession) { console.log('call ended ' + currentSession.endTime); if (callPurposefullyEnded === true) { callback({'status':'ended'}); } else { callback({'status':'failed', 'errorcode': 1005}); // Call ended unexpectedly } clearTimeout(callTimeout); currentSession = null; } else { console.log('bye event already received'); } }); currentSession.on('cancel', function(request){ callActive = false; if (currentSession) { console.log('call canceled'); clearTimeout(callTimeout); currentSession = null; } else { console.log('cancel event already received'); } }); currentSession.on('accepted', function(data){ callActive = true; console.log('BigBlueButton call accepted'); if (callICEConnected === true) { callback({'status':'started'}); } else { callback({'status':'waitingforice'}); } clearTimeout(callTimeout); }); currentSession.mediaHandler.on('iceFailed', function() { console.log('received ice negotiation failed'); callback({'status':'failed', 'errorcode': 1007}); // Failure on call currentSession = null; var userAgentTemp = userAgent; userAgent = null; userAgentTemp.stop(); clearTimeout(callTimeout); }); // Some browsers use status of 'connected', others use 'completed', and a couple use both currentSession.mediaHandler.on('iceConnected', function() { console.log('Received ICE status changed to connected'); if (callICEConnected === false) { callICEConnected = true; if (callActive === true) { callback({'status':'started'}); } clearTimeout(callTimeout); } }); currentSession.mediaHandler.on('iceCompleted', function() { console.log('Received ICE status changed to completed'); if (callICEConnected === false) { callICEConnected = true; if (callActive === true) { callback({'status':'started'}); } clearTimeout(callTimeout); } }); } function webrtc_hangup(callback) { callPurposefullyEnded = true; console.log("Hanging up current session"); if (callback) { currentSession.on('bye', callback); } try { currentSession.bye(); } catch (err) { console.log("Forcing to cancel current session"); currentSession.cancel(); } } function isWebRTCAvailable() { return SIP.WebRTC.isSupported(); }