var Janus = require('../externals/janus.nojquery'); var Promise = require('bluebird/js/release/bluebird'); var DataChannel = require('../modules/datachannel'); /** * SplitClient usecase * @class * @classdesc SplitClient usecase * @param {object} onEvents Event handlers: onCalling, onRegisteredFailed, onHangup, onReadyLocalAudio, onReadyLocalStream, onAcceptedAudio, onAcceptedVideo, onAcceptedData, onDataReceived, onStartRecording, onStopRecording, onScreensharingAccepted, onScreensharingClosed, onFileTransferOk, onFileTransferKo, onJoined, onNotified, onVideoClosed * @param {object} domElements DOM elements: local, remote, screenRemote, screenLocal * @param {object} options Available options: dataChannel, stream * @return {Promise} SplitClient methods: call, getLicense, hangup, sendData, sendDtmf, toggleAudio, toggleVideo * @example * var onEvents = { * onCalling: function() { * // Calling * }, * onRegisteredFailed: function(cause) { * // Register failed * console.log(cause); * }, * onHangup: function(code, cause) { * // HangUp * }, * onAcceptedAudio: function(data, stream) { * // Accepted Audio * console.log(data.userName); * console.log(data.sessionId); * }, * onReadyLocalAudio: function() { * // Ready Local Audio * }, * onReadyLocalStream: function(isVideo) { * // Ready Local Stream * console.log("Video is ready: " + isVideo); * }, * onAcceptedVideo: function(stream) { * // Accepted Video * }, * onAcceptedData: function() { * // Accepted Data * }, * onDataReceived: function(type, data, filename) { * // Data received * if(type === 'application/x-chat') { } * else if(type === 'text/plain') { } * else if(type === 'application/pdf') { } * else if(type === 'application/zip') { } * else if(type === 'application/x-rar') { } * else if(type === 'image/jpeg') {} * else if(type === 'image/png') {} * else if(type === 'application/x-docx') {} * else if(type === 'application/x-pptx') {} * else if(type === 'application/x-xlsx') {} * else if(type === 'application/vnd.oasis.opendocument.text') {} * }, * onStartRecording: function() { * // Start recording * }, * onStopRecording: function() { * // Stop recording * }, * onScreensharingAccepted: function() { * // ScreenSharing Accepted * }, * onScreensharingClosed: function() { * // ScreenSharing Closed * }, * onJoined: function(screenRoom) { * // Joined * }, * onFileTransferOk: function(fileId) { * // File Transfer OK * }, * onFileTransferKo: function(fileId) { * // File Transfer KO * }, * onNotified: function(action, data) { * // Notification * } * }; * * var domElements = { * local: document.getElementById('localvideo'), * remote: document.getElementById('remotevideo'), * screenRemote: document.getElementById('remotescreen'), * screenLocal: document.getElementById('localscreen') * }; * * var options = { * dataChannel: { * dataEnabled: true, * allowedTypes: ['application/x-chat', 'image/jpeg', 'image/png', 'application/pdf'], * maxSize: 5, // In MB * fileTransmission: { * timeout: 5, // Minutes * retransmissionDelay: 15 // Seconds * } * }, * stream: { * audioEnabled: true, * videoEnabled: true, * aDeviceId: null, * vDeviceId: null, * audioStream: null, * videoStream: null, * recordPrefix: '__default__' * } * }; * * usecases.splitClient(onEvents, domElements, options) * .then(function(action) { * // Use Case has been atacched succesfully * ... * }) * .catch(function(cause) { * // Error attaching the Use Case * console.log("Error Attach " + cause ); * }) */ var splitClient = function(onEvents, domElements, options) { if(!domElements || !domElements.local || !domElements.remote || domElements.local.nodeType !== 1 || domElements.remote.nodeType !== 1) return null; // Stream options var streamOptions = (options && options.stream) ? options.stream : {}; var isDataEnabled = (options && options.dataChannel && options.dataChannel.dataEnabled) ? options.dataChannel.dataEnabled : false; if(!streamOptions.audioEnabled) streamOptions.audioEnabled = false; if(!streamOptions.videoEnabled) streamOptions.videoEnabled = false; if(!streamOptions.aDeviceId) streamOptions.aDeviceId = undefined; if(!streamOptions.vDeviceId) streamOptions.vDeviceId = undefined; if(!streamOptions.audioStream) streamOptions.audioStream = undefined; if(!streamOptions.videoStream) streamOptions.videoStream = undefined; if(!streamOptions.recordPrefix) streamOptions.recordPrefix = '__default__'; // Vars var audioHandle = null; var videoHandle = null; var userName = null; var sessionId = null; var feeds = []; var bitrateTimer = []; var screenRoomHandle = null; var screenid = null; var screenFeed = null; var screenRoom = null; /** * Close the current UseCase. It's recommended combine with disconnect method * @return {nothing} * @example * action.closeUsecase(); * myVideoApp.disconnect(); // Recommended */ var closeUsecase = function() { if(domElements) { for (var element in domElements) { if (!domElements.hasOwnProperty(element)) continue; var obj = domElements[element]; if(obj && obj.innerHTML) obj.innerHTML = ''; } } }; /** * Send a DTMF tone * @param {string} dtmf Digit * @return {nothing} * @example * action.sendDtmf("5"); // Also "*" and "#" */ var sendDtmf = function(dtmf) { audioHandle.dtmf({dtmf: { tones: dtmf }}); // You can also send DTMF tones using SIP INFO //audioHandle.send({"message": {"request": "dtmf_info", "digit": tone }}); }; /** * Sends a message (Chat or File) through the DataChannel * @param {string} type MIME Type (e.g: 'application/x-chat', 'text/plain', 'application/pdf', 'application/zip', 'application/x-rar', 'image/jpeg', 'image/png', 'application/x-docx', 'application/x-pptx', 'application/x-xlsx', 'application/vnd.oasis.opendocument.text') * @param {string} data Data Content * @param {function} cOk Callback success function * @param {function} cKo Callback failed function * @param {string} filename File Name (e.g: file.zip) (Optional) * @return {nothing} * @example * action.sendData('application/x-chat', 'Hello Mike!', * function(cOk) { * // Success * }, * function(error) { * // Error * console.log(error); * } * ) */ var sendData = function(type, data, cOk, cKo, filename) { DataChannel.send(type, data, cOk, cKo, filename); }; /** * Toggle local Audio stream (Mute/Unmute) * @return {boolean} Is audio muted? * @example * action.toggleAudio(); // true or false */ var toggleAudio = function() { if (audioHandle.isAudioMuted()) audioHandle.unmuteAudio(); else audioHandle.muteAudio(); return audioHandle.isAudioMuted(); }; /** * Toggle local Video stream (Mute/Unmute) * @return {boolean} Is video muted? * @example * action.toggleVideo(); // true or false */ var toggleVideo = function() { if (videoHandle.isVideoMuted()) videoHandle.unmuteVideo(); else videoHandle.muteVideo(); return videoHandle.isVideoMuted(); }; /** * Join Video Session * @return {nothing} * @private */ var joinVideo = function() { var register = { "request": "join", "room": sessionId, "ptype": "publisher", "display": userName }; videoHandle.send({"message": register}); }; /** * Publish the local stream * @return {nothing} * @private */ var publishStream = function() { videoHandle.createOffer( { media: { // Publishers are sendonly audioRecv: false, audioSend: false, videoRecv: false, videoSend: streamOptions.videoEnabled, data: isDataEnabled, video: { deviceId: { exact: streamOptions.vDeviceId || undefined } } }, stream: streamOptions.videoStream, success: function(jsep) { Janus.debug("Got publisher SDP!"); Janus.debug(jsep); var publish = { "request": "configure", "audio": false, "video": streamOptions.videoEnabled }; videoHandle.send({"message": publish, "jsep": jsep}); }, error: function(error) { Janus.error("WebRTC error:", error); publishStream(); } }); }; /** * Unpublished a Video Stream * @return {nothing} * @private */ var unPublishStream = function() { var unpublish = { "request": "unpublish" }; videoHandle.send({"message": unpublish}); }; /** * Start Recording * @param {integer} id Session Id * @return {nothing} * @example * action.startRecording(1234); * @private */ var startRecording = function(id, recordId) { var req = { "request": "configure", "room": id, "record": true, "prefixtheme": streamOptions.recordPrefix, "record_id": recordId, "role": "client" }; videoHandle.send({"message": req}); var recording = { "request": "recording", "action": "start", "audio": true, "video": false, "peer_audio": true, "peer_video": false, "record_id": recordId }; audioHandle.send({"message": recording}); if(onEvents && onEvents.onStartRecording) onEvents.onStartRecording(); }; /** * Stop Recording * @param {integer} id Session Id * @return {nothing} * @example * action.stopRecording(1234); * @private */ var stopRecording = function(id) { var req = { "request": "configure", "room": id, "record": false }; videoHandle.send({"message": req}); var recording = { "request": "recording", "action": "stop", "audio": true, "video": false, "peer_audio": true, "peer_video": false }; audioHandle.send({"message": recording}); if(onEvents && onEvents.onStopRecording) onEvents.onStopRecording(); }; /** * Client sends a request to start a call to an Agent * @param {string} callerId Caller ID * @param {integer} videoRate Video Bitrate (kbps) (Optional) * @param {integer} screenRate Screen Bitrate (kbps) (Optional) * @return {nothing} * @example * action.call('IVRPowers', 128000, 256000); */ var call = function(callerId, videoRate, screenRate) { audioHandle.createOffer( { media: { audioSend: streamOptions.audioEnabled, audioRecv: true, // streamOptions.audioEnabled, videoSend: false, videoRecv: false, audio: { deviceId: { exact: streamOptions.aDeviceId || undefined } }, data: false }, stream: streamOptions.audioStream, success: function(jsep) { var body = { request: "call_split", username: callerId, videorate: videoRate || undefined, screenrate: screenRate || undefined }; audioHandle.send({"message": body, "jsep": jsep}); }, error: function(error) { Janus.error("WebRTC error...", error); } }); }; /** * Client sends a request to hangup the call * @return {nothing} * @example * action.hangup(); */ var hangup = function() { var hangup = { "request": "hangup" }; audioHandle.send({"message": hangup}); audioHandle.hangup(); }; /** * The VideoGateway is requested about the features of the contracted license * @return {Object} License Information (Screensharing, Livechat and VideoRecording ) * @example * var myLicense = action.getLicense(); * console.log(myLicense); */ var getLicense = function() { return window.VideoRTC.license; }; /** * New Remote Feed * @param {integer} id Id * @param {string} display Name * @return {nothing} * @private */ var newRemoteFeed = function(id, display) { // A new feed has been published, create a new plugin handle and attach to it as a listener var remoteFeed = null; window.VideoRTC.connection.handle.attach( { plugin: "janus.plugin.split", success: function(pluginHandle) { remoteFeed = pluginHandle; Janus.log("Plugin attached! (" + remoteFeed.getPlugin() + ", id=" + remoteFeed.getId() + ")"); Janus.log(" -- This is a subscriber"); // We wait for the plugin to send us an offer var listen = { "request": "join", "room": sessionId, "ptype": "listener", "feed": id }; remoteFeed.send({"message": listen}); }, error: function(error) { Janus.error(" -- Error attaching plugin...", error); }, onmessage: function(msg, jsep) { Janus.debug(" ::: Got a message (listener) :::"); var event = msg["split"]; if(event !== undefined && event !== null) { if(event === "attached") { // Subscriber created and attached for(var i=1;i<6;i++) { if(feeds[i] === undefined || feeds[i] === null) { feeds[i] = remoteFeed; remoteFeed.rfindex = i; break; } } remoteFeed.rfid = msg["id"]; remoteFeed.rfdisplay = msg["display"]; Janus.log("Successfully attached to feed " + remoteFeed.rfid + " (" + remoteFeed.rfdisplay + ") in room " + msg["room"]); } else if(msg["error"] !== undefined && msg["error"] !== null) { Janus.error(msg["error"]); } else { // What has just happened? } } if(jsep !== undefined && jsep !== null) { Janus.debug("Handling SDP as well..."); Janus.debug(jsep); // Answer and attach remoteFeed.createAnswer( { jsep: jsep, media: { audioSend: false, videoSend: false, audioRecv: true, //streamOptions.audioEnabled, videoRecv: true, //streamOptions.videoEnabled, data: isDataEnabled }, // We want recvonly audio/video success: function(jsep) { Janus.debug("Got SDP!"); Janus.debug(jsep); var body = { "request": "start", "room": sessionId }; remoteFeed.send({"message": body, "jsep": jsep}); }, error: function(error) { Janus.error("WebRTC error:", error); } }); } }, onlocalstream: function(stream) { // The subscriber stream is recvonly, we don't expect anything here }, onremotestream: function(stream) { Janus.debug("Remote feed #" + remoteFeed.rfindex); var generateLabel = function(id, content, backgroundColor, position) { var output = ''; output += '<span id="'+id+'" style="'; output += ' position:absolute;margin:10px;padding:3px 5px;color:white;font-weight:bold;border-radius:5px;'; output += 'background:'+backgroundColor+';'; output += position; output += '">'+content+'</span>'; return output; }; var container = domElements.remote; var tpl = ''; tpl += '<div id="container-remote1">'; tpl += '<video autoplay poster="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" id="videoremote1"></video>'; tpl += generateLabel('remote-name', remoteFeed.rfdisplay, '#55D8D5', 'left:0; margin-top: 10px;'); tpl += generateLabel('remote-resolution', '', '#8DCC6D', 'left:0; margin-top: 40px;'); tpl += generateLabel('remote-bitrate', '', '#FF6E5F', 'left:0; margin-top: 70px;'); tpl += '</div>'; container.innerHTML += tpl; var remoteContainer = document.getElementById('container-remote1'); var videoElem = document.getElementById('videoremote1'); videoElem.muted = true; videoElem.onplaying = function() { var width = this.videoWidth; var height = this.videoHeight; document.getElementById('remote-resolution').innerHTML = width+'x'+height; if(window.VideoRTC.getBrowser() === 'firefox') { // Firefox Stable has a bug: width and height are not immediately available after a playing setTimeout(function() { var width = videoElem.videoWidth; var height = videoElem.videoHeight; document.getElementById('remote-resolution').innerHTML = width+'x'+height; }, 2000); } }; Janus.attachMediaStream(videoElem, stream); //videoElem.muted = true; var videoTracks = stream.getVideoTracks(); if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0 || videoTracks[0].muted) { // No remote video var nodeNoCamera = document.createElement('div'); nodeNoCamera.innerHTML = 'No camera available'; nodeNoCamera.style.position = "relative"; nodeNoCamera.style.height = "100%"; nodeNoCamera.style.width = "100%"; nodeNoCamera.style.top = "100%"; nodeNoCamera.style.left = "50%"; nodeNoCamera.style.transform = "translate(-50%,-50%)"; nodeNoCamera.className = "no-camera-available"; remoteContainer.insertBefore(nodeNoCamera, remoteContainer.firstChild); // We Hide Bitrate and Resolution labels for(var i = 0; i < remoteContainer.childNodes.length; i++) { if(remoteContainer.childNodes[i].id && (remoteContainer.childNodes[i].id.indexOf('remote-resolution') >= 0 || remoteContainer.childNodes[i].id.indexOf('remote-bitrate') >= 0 )) { remoteContainer.childNodes[i].style.display = 'none'; } } } bitrateTimer[remoteFeed.rfindex] = setInterval(function() { // Display updated bitrate, if supported var bitrate = remoteFeed.getBitrate(); document.getElementById('remote-bitrate').innerHTML = bitrate; }, 1000); if(onEvents && onEvents.onAcceptedVideo) onEvents.onAcceptedVideo(stream); }, oncleanup: function() { Janus.log(" ::: Got a cleanup notification (remote feed " + id + ") :::"); if(bitrateTimer[remoteFeed.rfindex] !== null && bitrateTimer[remoteFeed.rfindex] !== null) clearInterval(bitrateTimer[remoteFeed.rfindex]); bitrateTimer[remoteFeed.rfindex] = null; }, ondataopen: function () { Janus.log("The DataChannel is available!"); if(onEvents && onEvents.onAcceptedData) onEvents.onAcceptedData(); }, ondata: function (data) { Janus.debug("We got data from the DataChannel! " + data); DataChannel.receive(data, onEvents.onDataReceived); } }); }; /** * Join Screen Room * @param {integer} screenRoom Screen Room ID * @return {nothing} * @private */ var joinScreenroom = function(screenRoom) { window.VideoRTC.connection.handle.attach({ plugin: "janus.plugin.split", success: function(pluginHandle) { // Plugin attached! 'pluginHandle' is our handle screenRoomHandle = pluginHandle; var register = { "request": "join", "room": screenRoom, "ptype": "publisher", "display": 'ScreenClient' }; screenRoomHandle.send({"message": register}); }, error: function(cause) { }, consentDialog: function(on) { }, onmessage: function(msg, jsep) { var event = msg.split; if(event !== undefined && event !== null) { if(event === "joined") { if(msg["publishers"] !== undefined && msg["publishers"] !== null) { var list = msg["publishers"]; Janus.debug("Got a list of available publishers/feeds:"); Janus.debug(list); for(var f in list) { var id = list[f]["id"]; var display = list[f]["display"]; Janus.debug(" >> [" + id + "] " + display); listenScreenSharing(screenRoom, id); } } } else if(event === "destroyed") { Janus.warn("The room has been destroyed!"); } else if(event === "event") { if(msg["publishers"] !== undefined && msg["publishers"] !== null) { var list = msg["publishers"]; Janus.debug("Got a list of available publishers/feeds:"); Janus.debug(list); for(var f in list) { var id = list[f]["id"]; var display = list[f]["display"]; Janus.debug(" >> [" + id + "] " + display); listenScreenSharing(screenRoom, id); } } else if(msg["leaving"] !== undefined && msg["leaving"] !== null) { var leaving = msg["leaving"]; Janus.log("Publisher left: " + leaving); var container = domElements.screenRemote; container.innerHTML = ''; container.style = ''; //container.className = ''; screenFeed.detach(); if(onEvents && onEvents.onScreensharingClosed) onEvents.onScreensharingClosed(); } else if(msg["unpublished"] !== undefined && msg["unpublished"] !== null) { var unpublished = msg["unpublished"]; Janus.log("Publisher left: " + unpublished); var container = domElements.screenRemote; container.innerHTML = ''; container.style = ''; //container.className = ''; screenFeed.detach(); } else if(msg["error"] !== undefined && msg["error"] !== null) { Janus.error(msg["error"]); } } } if(jsep !== undefined && jsep !== null) { Janus.debug("Handling SDP as well..."); Janus.debug(jsep); screenRoomHandle.handleRemoteJsep({jsep: jsep}); } }, onlocalstream: function(stream) { // We have a local stream (getUserMedia worked!) to display var container = domElements.screenLocal; container.innerHTML = '<video autoplay poster="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" id="screenClient"></video>'; var videoElem = document.getElementById('screenClient'); Janus.attachMediaStream(videoElem, stream); videoElem.muted = true; }, onremotestream: function(stream) { }, oncleanup: function() { }, detached: function() { } }); }; /** * Listen ScreenSharing * @param {integer} screenRoom Screen Room ID * @param {integer} peerId Peer ID * @return {nothing} * @private */ var listenScreenSharing = function(screenRoom, peerId) { window.VideoRTC.connection.handle.attach( { plugin: "janus.plugin.split", success: function(pluginHandle) { //if(screenFeed) screenFeed.detach(); screenFeed = pluginHandle; var listen = { "request": "join", "room": screenRoom, "ptype": "listener", "feed": peerId }; screenFeed.send({"message": listen}); }, error: function(error) { Janus.error(" -- Error attaching plugin...", error); }, onmessage: function(msg, jsep) { Janus.debug(" ::: Got a message (listener) :::"); var event = msg["split"]; if(event !== undefined && event !== null) { if(event === "attached") { } else if(msg["error"] !== undefined && msg["error"] !== null) { Janus.error(msg["error"]); } else { // What has just happened? } } if(jsep !== undefined && jsep !== null) { Janus.debug("Handling SDP as well..."); Janus.debug(jsep); // Answer and attach screenFeed.createAnswer( { jsep: jsep, media: { audioSend: false, videoSend: false, audioRecv: false, videoRecv: true, data: false }, // We want recvonly audio/video success: function(jsep) { Janus.debug("Got SDP!"); Janus.debug(jsep); var body = { "request": "start", "room": sessionId }; screenFeed.send({"message": body, "jsep": jsep}); }, error: function(error) { Janus.error("WebRTC error:", error); } }); } }, onlocalstream: function(stream) { // The subscriber stream is recvonly, we don't expect anything here }, onremotestream: function(stream) { var container = domElements.screenRemote; var tpl = ''; tpl += '<div id="container-screenremote">'; tpl += '<video autoplay poster="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" id="screenremote"></video>'; tpl += '</div>'; container.innerHTML = tpl; var videoElem = document.getElementById('screenremote'); Janus.attachMediaStream(videoElem, stream); videoElem.muted = true; if(onEvents && onEvents.onScreensharingAccepted) onEvents.onScreensharingAccepted(); }, oncleanup: function() { } }); }; /** * The Client sends a request to start Screensharing * @param {integer} screenId Screen Id * @param {function} cOk Callback Ok * @param {function} cKo Callback Error * @return {nothing} * @example * action.startScreensharing(1234, function() { * // Success * }, function(cause) { * // Error * }) */ var startScreensharing = function(screenId, cOk, cKo) { if(!getLicense().screensharing) { cKo ('Screensharing is not a feature of your Gateway'); return false; } if(window.location.protocol !== 'https:') { cKo ('Screensharing requires HTTPS'); return false; } if(window.isMobile) { cKo ('Screensharing is not available on Mobile devices'); return false; } if(!Janus.isExtensionEnabled()) { cKo("You're using a recent version of Chrome but don't have the screensharing extension installed: click <b><a href='https://chrome.google.com/webstore/detail/videortc-screensharing/pkilckpboojemoogepfpkgbihkfkikel' target='_blank'>here</a></b> to do so"); return false; } screenid = screenId; var capture = 'screen'; screenRoomHandle.createOffer( { //media: { audioRecv: false, videoRecv: false, audioSend: useAudio, videoSend: true/*, video:"hires"*/}, // Publishers are sendonly media: { video: capture, audio: false, videoRecv: false }, success: function(jsep) { var publish = { "request": "configure", "audio": false, "video": true }; screenRoomHandle.send({"message": publish, "jsep": jsep}); cOk(); }, error: function(error) { console.log(error); if(error && error.name === 'NotAllowedError' && window.getBrowser() === 'firefox') { cKo('Firefox needs that the domain this web application is from is listed in Allowed domains.'); } Janus.error("WebRTC error:", error); } } ); }; /** * The Client sends a request to stop Screensharing * @param {function} cOk Callback Ok * @param {function} cKo Callback Error * @return {nothing} * @example * action.stopScreensharing(function() { * // Success * }, function(cause) { * // Error * }) */ var stopScreensharing = function(cOk, cKo) { var leave = { "request": "leave" }; screenRoomHandle.send({"message": leave}); screenRoomHandle.detach(); joinScreenroom(screenRoom); var container = domElements.screenLocal; container.innerHTML = ''; container.style = ''; cOk(); }; /** * Send a notification to the other peer via the VideoGateway * @param {string} action Identifies the notification type * @param {string} data Useful for sending extra data (Optional) * @example * action.notify('open-map'); */ var notify = function(action, data) { if(!action) return; var not = { "request": "notify", "action": action, "room": sessionId, "origin": "client" }; if(data) not.data = data; videoHandle.send({"message": not}); }; var p1 = new Promise(function (resolve, reject) { window.VideoRTC.connection.handle.attach({ plugin: "janus.plugin.split", success: function(pluginHandle) { // Plugin attached! 'pluginHandle' is our handle audioHandle = pluginHandle; resolve(); }, error: function(cause) { // Couldn't attach to the plugin if(cause && cause.indexOf('482') >= 0) cause = 'Sorry, all channels are busy'; if(cause && cause.indexOf('480') >= 0) cause = 'License has been expired. Please, contact with an Administrator'; reject(cause); }, consentDialog: function(on) { // e.g., Darken the screen if on=true (getUserMedia incoming), restore it otherwise }, onmessage: function(msg, jsep) { // We got a message/event (msg) from the plugin // If jsep is not null, this involves a WebRTC negotiation var error = msg["error"]; if(error != null && error != undefined) { Janus.error(error); return; } var result = msg["result"]; if(result !== null && result !== undefined && result["event"] !== undefined && result["event"] !== null) { var event = result["event"]; if(event === 'registration_failed') { Janus.warn("Registration failed: " + result["code"] + " " + result["reason"]); if(onEvents && onEvents.onRegisteredFailed) onEvents.onRegisteredFailed(result["reason"]); return; } else if(event === 'calling') { Janus.log("Waiting for the peer to answer..."); if(onEvents && onEvents.onCalling) onEvents.onCalling(); } else if(event === 'accepted') { Janus.log("Accepted..."); if(jsep !== null && jsep !== undefined) { audioHandle.handleRemoteJsep({jsep: jsep, error: hangup}); } userName = result.caller_username; sessionId = result.id_videoroom; joinVideo(); } else if(event === 'hangup') { Janus.log("Call hung up (" + result["code"] + " " + result["reason"] + ")!"); audioHandle.hangup(); if(result["code"] === 200) window.VideoRTC.connection.handle.destroy(); if(onEvents && onEvents.onHangup) onEvents.onHangup(result["code"], result["reason"]); } } }, onlocalstream: function(stream) { // We have a local stream (getUserMedia worked!) to display Janus.debug(" ::: Got a local stream :::"); if(onEvents && onEvents.onReadyLocalAudio) onEvents.onReadyLocalAudio(); }, onremotestream: function(stream) { // We have a remote stream (working PeerConnection!) to display Janus.debug(" ::: Got a remote stream :::"); var container = domElements.remote; var newNode = document.createElement('div'); newNode.style.display = 'none'; // Safari iOS: https://github.com/webrtc/samples/issues/929 newNode.innerHTML = '<video poster="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" playsinline="true" controls="true" style="display:none;" id="remote-audio" autoplay></video>'; container.parentNode.insertBefore(newNode, container.nextSibling); Janus.attachMediaStream(document.getElementById('remote-audio'), stream); if(onEvents && onEvents.onAcceptedAudio) onEvents.onAcceptedAudio({ userName: userName, sessionId: sessionId }, stream); }, oncleanup: function() { // PeerConnection with the plugin closed, clean the UI // The plugin handle is still valid so we can create a new one }, detached: function() { // Connection with the plugin closed, get rid of its features // The plugin handle is not valid anymore } }); }); var p2 = new Promise(function (resolve, reject) { window.VideoRTC.connection.handle.attach({ plugin: "janus.plugin.split", success: function(pluginHandle) { // Plugin attached! 'pluginHandle' is our handle videoHandle = pluginHandle; // DataChannel options var optionsDataChannel = (options && options.dataChannel) ? options.dataChannel : {}; var cbks = { fileOk: onEvents.onFileTransferOk || function() {}, fileKo: onEvents.onFileTransferKo || function() {} }; var result = DataChannel.initialize(optionsDataChannel, pluginHandle, cbks); if(!result) console.log("Datachannel options can't be loaded:"); videoHandle.send({ message: { request: "license_info" }, success: function(data) { if(data && data["error_code"]){ reject(data["error"]); } else { //resolve(); } } }); }, error: function(cause) { // Couldn't attach to the plugin reject(); }, consentDialog: function(on) { // e.g., Darken the screen if on=true (getUserMedia incoming), restore it otherwise }, onmessage: function(msg, jsep) { // We got a message/event (msg) from the plugin // If jsep is not null, this involves a WebRTC negotiation var event = msg.split; if(event !== undefined && event !== null) { if(event === "joined") { Janus.log("Successfully joined room " + msg["room"] + " with ID " + msg["id"]); publishStream(); // Any new feed to attach to? if(msg["publishers"] !== undefined && msg["publishers"] !== null) { var list = msg["publishers"]; Janus.debug("Got a list of available publishers/feeds:"); Janus.debug(list); for(var f in list) { var id = list[f]["id"]; var display = list[f]["display"]; Janus.debug(" >> [" + id + "] " + display); newRemoteFeed(id, display) } } screenRoom = msg["screenroom"]; if(onEvents && onEvents.onJoined) onEvents.onJoined(screenRoom); if(screenRoom && screenRoom > 0 && domElements.screenRemote) joinScreenroom(screenRoom); } else if(event === "destroyed") { Janus.warn("The room has been destroyed!"); } else if(event === "event") { if(msg["publishers"] !== undefined && msg["publishers"] !== null) { var list = msg["publishers"]; Janus.debug("Got a list of available publishers/feeds:"); Janus.debug(list); for(var f in list) { var id = list[f]["id"]; var display = list[f]["display"]; Janus.debug(" >> [" + id + "] " + display); newRemoteFeed(id, display) } } else if(msg["leaving"] !== undefined && msg["leaving"] !== null) { // One of the publishers has gone away? var leaving = msg["leaving"]; Janus.log("Publisher left: " + leaving); var remoteFeed = null; for(var i=1; i<6; i++) { if(feeds[i] != null && feeds[i] != undefined && feeds[i].rfid == leaving) { remoteFeed = feeds[i]; break; } } if(remoteFeed != null) { Janus.debug("Feed " + remoteFeed.rfid + " (" + remoteFeed.rfdisplay + ") has left the room, detaching"); remoteFeed.detach(); var container = domElements.remote; container.innerHTML = ''; container.style = ''; if(onEvents && onEvents.onVideoClosed) onEvents.onVideoClosed(); } } else if(msg["unpublished"] !== undefined && msg["unpublished"] !== null) { // One of the publishers has unpublished? var unpublished = msg["unpublished"]; Janus.log("Publisher left: " + unpublished); if(unpublished === 'ok') { // That's us videoHandle.hangup(); return; } var remoteFeed = null; for(var i=1; i<6; i++) { if(feeds[i] != null && feeds[i] != undefined && feeds[i].rfid == unpublished) { remoteFeed = feeds[i]; break; } } if(remoteFeed != null) { Janus.debug("Feed " + remoteFeed.rfid + " (" + remoteFeed.rfdisplay + ") has left the room, detaching"); feeds[remoteFeed.rfindex] = null; remoteFeed.detach(); } } else if(msg["startedRec"] !== undefined && msg["startedRec"] !== null) { startRecording(sessionId, msg["recordId"]); } else if(msg["stoppedRec"] !== undefined && msg["stoppedRec"] !== null) { stopRecording(sessionId); } else if(msg["notified"] !== undefined && msg["notified"] !== null) { if(onEvents && onEvents.onNotified && msg["origin"] !== 'client') { Janus.debug("Notification received: " + msg["action"]); onEvents.onNotified(msg["action"], msg["data"]); } } else if(msg["error"] !== undefined && msg["error"] !== null) { Janus.error(msg["error"]); } else if (msg['result'] !== undefined && msg['result'] !== null) { var result = msg['result']; if (result['event'] == 'license_info') { window.VideoRTC.license = { screensharing: (result['screensharing'] == 'true'), livechat: (result['livechat'] == 'true'), videorecording: (result['videorecording'] == 'true') }; Janus.debug("License", window.VideoRTC.license); resolve(); } } } } if(jsep !== undefined && jsep !== null) { Janus.debug("Handling SDP as well..."); Janus.debug(jsep); videoHandle.handleRemoteJsep({jsep: jsep}); } }, onlocalstream: function(stream) { // We have a local stream (getUserMedia worked!) to display var container = domElements.local; container.innerHTML = '<video poster="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" autoplay id="videolocal1"></video>'; var videoElem = document.getElementById('videolocal1'); Janus.attachMediaStream(videoElem, stream); videoElem.muted = true; var videoTracks = stream.getVideoTracks(); if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0) { // No remote video var nodeNoCamera = document.createElement('div'); nodeNoCamera.innerHTML = 'No camera available'; nodeNoCamera.style.position = "relative"; nodeNoCamera.style.height = "100%"; nodeNoCamera.style.width = "100%"; nodeNoCamera.style.top = "100%"; nodeNoCamera.style.left = "50%"; nodeNoCamera.style.transform = "translate(-50%,-50%)"; nodeNoCamera.className = "no-camera-available"; container.insertBefore(nodeNoCamera, container.firstChild); if(onEvents && onEvents.onReadyLocalStream) onEvents.onReadyLocalStream(false); } else { if(onEvents && onEvents.onReadyLocalStream) onEvents.onReadyLocalStream(true); } }, onremotestream: function(stream) { // We have a remote stream (working PeerConnection!) to display }, oncleanup: function() { // PeerConnection with the plugin closed, clean the UI // The plugin handle is still valid so we can create a new one }, detached: function() { // Connection with the plugin closed, get rid of its features // The plugin handle is not valid anymore } }); }); return Promise.all([ p1, p2 ]) .then(function() { return { call: call, closeUsecase: closeUsecase, getLicense: getLicense, hangup: hangup, notify: notify, sendData: sendData, sendDtmf: sendDtmf, startScreensharing: startScreensharing, stopScreensharing: stopScreensharing, toggleAudio: toggleAudio, toggleVideo: toggleVideo }; }); }; exports.splitClient = splitClient;