diff --git a/assets/3rdparty/getScreenId.js b/assets/3rdparty/getScreenId.js new file mode 100644 index 00000000..a8abaa42 --- /dev/null +++ b/assets/3rdparty/getScreenId.js @@ -0,0 +1,222 @@ +// Last time updated on June 08, 2018 + +// Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js + +// Muaz Khan - www.MuazKhan.com +// MIT License - www.WebRTC-Experiment.com/licence +// Documentation - https://github.com/muaz-khan/getScreenId. + +// ______________ +// getScreenId.js + +/* +getScreenId(function (error, sourceId, screen_constraints) { + // error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome' + // sourceId == null || 'string' || 'firefox' + + if(navigator.getDisplayMedia) { + navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure); + } + else { + navigator.mediaDevices.getUserMedia(screen_constraints).then(onSuccess)catch(onFailure); + } + +}, 'pass second parameter only if you want system audio'); +*/ + +(function() { + window.getScreenId = function(callback, custom_parameter) { + if(navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob)) { + // microsoft edge => navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure); + callback({ + video: true + }); + return; + } + + // for Firefox: + // sourceId == 'firefox' + // screen_constraints = {...} + if (!!navigator.mozGetUserMedia) { + callback(null, 'firefox', { + video: { + mozMediaSource: 'window', + mediaSource: 'window' + } + }); + return; + } + + window.addEventListener('message', onIFrameCallback); + + function onIFrameCallback(event) { + if (!event.data) return; + + if (event.data.chromeMediaSourceId) { + if (event.data.chromeMediaSourceId === 'PermissionDeniedError') { + callback('permission-denied'); + } else { + callback(null, event.data.chromeMediaSourceId, getScreenConstraints(null, event.data.chromeMediaSourceId, event.data.canRequestAudioTrack)); + } + + // this event listener is no more needed + window.removeEventListener('message', onIFrameCallback); + } + + if (event.data.chromeExtensionStatus) { + callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus)); + + // this event listener is no more needed + window.removeEventListener('message', onIFrameCallback); + } + } + + if(!custom_parameter) { + setTimeout(postGetSourceIdMessage, 100); + } + else { + setTimeout(function() { + postGetSourceIdMessage(custom_parameter); + }, 100); + } + }; + + function getScreenConstraints(error, sourceId, canRequestAudioTrack) { + var screen_constraints = { + audio: false, + video: { + mandatory: { + chromeMediaSource: error ? 'screen' : 'desktop', + maxWidth: window.screen.width > 1920 ? window.screen.width : 1920, + maxHeight: window.screen.height > 1080 ? window.screen.height : 1080 + }, + optional: [] + } + }; + + if(!!canRequestAudioTrack) { + screen_constraints.audio = { + mandatory: { + chromeMediaSource: error ? 'screen' : 'desktop', + // echoCancellation: true + }, + optional: [] + }; + } + + if (sourceId) { + screen_constraints.video.mandatory.chromeMediaSourceId = sourceId; + + if(screen_constraints.audio && screen_constraints.audio.mandatory) { + screen_constraints.audio.mandatory.chromeMediaSourceId = sourceId; + } + } + + return screen_constraints; + } + + function postGetSourceIdMessage(custom_parameter) { + if (!iframe) { + loadIFrame(function() { + postGetSourceIdMessage(custom_parameter); + }); + return; + } + + if (!iframe.isLoaded) { + setTimeout(function() { + postGetSourceIdMessage(custom_parameter); + }, 100); + return; + } + + if(!custom_parameter) { + iframe.contentWindow.postMessage({ + captureSourceId: true + }, '*'); + } + else if(!!custom_parameter.forEach) { + iframe.contentWindow.postMessage({ + captureCustomSourceId: custom_parameter + }, '*'); + } + else { + iframe.contentWindow.postMessage({ + captureSourceIdWithAudio: true + }, '*'); + } + } + + var iframe; + + // this function is used in RTCMultiConnection v3 + window.getScreenConstraints = function(callback) { + loadIFrame(function() { + getScreenId(function(error, sourceId, screen_constraints) { + if(!screen_constraints) { + screen_constraints = { + video: true + }; + } + + callback(error, screen_constraints.video); + }); + }); + }; + + function loadIFrame(loadCallback) { + if (iframe) { + loadCallback(); + return; + } + + iframe = document.createElement('iframe'); + iframe.onload = function() { + iframe.isLoaded = true; + + loadCallback(); + }; + iframe.src = 'https://www.webrtc-experiment.com/getSourceId/'; // https://wwww.yourdomain.com/getScreenId.html + iframe.style.display = 'none'; + (document.body || document.documentElement).appendChild(iframe); + } + + window.getChromeExtensionStatus = function(callback) { + // for Firefox: + if (!!navigator.mozGetUserMedia) { + callback('installed-enabled'); + return; + } + + window.addEventListener('message', onIFrameCallback); + + function onIFrameCallback(event) { + if (!event.data) return; + + if (event.data.chromeExtensionStatus) { + callback(event.data.chromeExtensionStatus); + + // this event listener is no more needed + window.removeEventListener('message', onIFrameCallback); + } + } + + setTimeout(postGetChromeExtensionStatusMessage, 100); + }; + + function postGetChromeExtensionStatusMessage() { + if (!iframe) { + loadIFrame(postGetChromeExtensionStatusMessage); + return; + } + + if (!iframe.isLoaded) { + setTimeout(postGetChromeExtensionStatusMessage, 100); + return; + } + + iframe.contentWindow.postMessage({ + getChromeExtensionStatus: true + }, '*'); + } +})(); diff --git a/assets/js/common/utils.js b/assets/js/common/utils.js index df869f06..318cc2b6 100644 --- a/assets/js/common/utils.js +++ b/assets/js/common/utils.js @@ -791,4 +791,31 @@ function IsFullScreen(){ */ function checkEmojiCode(s) { return s.match(/^:[a-zA-Z0-9]+:$/) != null +} + +/** + * Request user screen as stream + * + * @return {Promise} + */ +function requestUserScreen() { + return new Promise((onSuccess, onFailure) => { + getScreenId(function (error, sourceId, screen_constraints) { + + if(error != null) + return onFailure(error) + + // error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome' + // sourceId == null || 'string' || 'firefox' + + if(navigator.getDisplayMedia) { + navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure); + } + else { + navigator.mediaDevices.getUserMedia(screen_constraints).then(onSuccess).catch(onFailure); + } + + }); + }) + } \ No newline at end of file diff --git a/assets/js/components/calls/window.js b/assets/js/components/calls/window.js index 1a9490d7..78150b50 100644 --- a/assets/js/components/calls/window.js +++ b/assets/js/components/calls/window.js @@ -254,6 +254,9 @@ class CallWindow extends CustomEvents { // Parse list of menu entries for(const entry of menuEntries) { + if(entry.needVideo && !this.allowVideo) + continue + const a = createElem2({ appendTo: menuEntriesTarget, type: "li", @@ -666,14 +669,33 @@ class CallWindow extends CustomEvents { * Start to send this client audio & video * * @param {boolean} includeVideo + * @param {boolean} shareScreen */ - async startStreaming(includeVideo) { + async startStreaming(includeVideo, shareScreen = false) { - // First, query user media - const stream = await navigator.mediaDevices.getUserMedia({ - video: this.conv.can_have_video_call && includeVideo, - audio: true - }) + let videoConstraints = this.conv.can_have_video_call && includeVideo; + + let stream; + + // Get user screen + if(includeVideo && shareScreen) { + stream = await requestUserScreen(true) + + const second_stream = await navigator.mediaDevices.getUserMedia({ + audio: true + }) + + stream.addTrack(second_stream.getAudioTracks()[0]) + } + + // Use regular webcam + else { + // First, query user media + stream = await navigator.mediaDevices.getUserMedia({ + video: videoConstraints, + audio: true, + }) + } this.mainStream = stream; if(includeVideo) diff --git a/system/config/dev.config.php b/system/config/dev.config.php index 56db6a43..b4acaa6d 100644 --- a/system/config/dev.config.php +++ b/system/config/dev.config.php @@ -162,6 +162,9 @@ class Dev { //Simple peer array("path" => "3rdparty/simplepeer/simplepeer.min.js", "uglifyjs" => false), + + // Share screen + "3rdparty/getScreenId.js", ); /**