// Muaz Khan - https://github.com/muaz-khan // MIT License - https://www.WebRTC-Experiment.com/licence/ // Source Code - https://github.com/muaz-khan/Chrome-Extensions // this page is using desktopCapture API to capture and share desktop // http://developer.chrome.com/extensions/desktopCapture.html // chrome.browserAction.onClicked.addListener(captureDesktop); var runtimePort; var websocket; var webRtcPeer; var socket; var roomHash; chrome.runtime.onConnect.addListener(function(port) { runtimePort = port; runtimePort.onMessage.addListener(function(message) { if (!message || !message.messageFromContentScript1234) { return; } if (message.startSharing || message.stopSharing) { captureDesktop(); return; } }); }); window.addEventListener('offline', function() { if (!connection || !connection.attachStreams.length) return; setDefaults(); chrome.runtime.reload(); }, false); window.addEventListener('online', function() { if (!connection) return; setDefaults(); chrome.runtime.reload(); }, false); function captureDesktop() { if (connection && connection.attachStreams[0]) { setDefaults(); connection.attachStreams.forEach(function(stream) { stream.getTracks().forEach(function(track) { track.stop(); }); }); chrome.storage.sync.set({ enableTabCaptureAPI: 'false', enableMicrophone: 'false', enableCamera: 'false', enableScreen: 'false', isSharingOn: 'false', enableSpeakers: 'false' }); return; } chrome.browserAction.setTitle({ title: 'Capturing Desktop' }); chrome.storage.sync.get(null, function(items) { var resolutions = {}; if (items['room_password']) { room_password = items['room_password']; } if (items['room_id']) { room_id = items['room_id']; } if (items['codecs']) { codecs = items['codecs']; } if (items['bandwidth']) { bandwidth = items['bandwidth']; } if (items['enableTabCaptureAPI'] == 'true') { enableTabCaptureAPI = items['enableTabCaptureAPI']; } if (items['enableMicrophone'] == 'true') { enableMicrophone = items['enableMicrophone']; } if (items['enableSpeakers'] == 'true') { enableSpeakers = items['enableSpeakers']; } if (items['enableCamera'] == 'true') { enableCamera = items['enableCamera']; } if (items['enableScreen'] == 'true') { enableScreen = items['enableScreen']; } if (items['enableTabCaptureAPI'] == 'true') { enableTabCaptureAPI = items['enableTabCaptureAPI']; } if (items['isSharingOn'] == 'true') { isSharingOn = items['isSharingOn']; } var _resolutions = items['resolutions']; if (!_resolutions) { _resolutions = 'fit-screen'; chrome.storage.sync.set({ resolutions: 'fit-screen' }, function() {}); } if (_resolutions === 'fit-screen') { // resolutions.maxWidth = screen.availWidth; // resolutions.maxHeight = screen.availHeight; resolutions.maxWidth = screen.width; resolutions.maxHeight = screen.height; } if (_resolutions === '4K') { resolutions.maxWidth = 3840; resolutions.maxHeight = 2160; } if (_resolutions === '1080p') { resolutions.maxWidth = 1920; resolutions.maxHeight = 1080; } if (_resolutions === '720p') { resolutions.maxWidth = 1280; resolutions.maxHeight = 720; } if (_resolutions === '360p') { resolutions.maxWidth = 640; resolutions.maxHeight = 360; } if (_resolutions === '4K') { alert('"4K" resolutions is not stable in Chrome. Please try "fit-screen" instead.'); } var sources = ['screen', 'window', 'tab']; if (enableSpeakers) { sources.push('audio'); } if (enableTabCaptureAPI) { captureTabUsingTabCapture(resolutions); return; } if (enableCamera || enableMicrophone) { captureCamera(function(stream) { if (!enableScreen) { gotCustomStream(stream); //CHANGES //gotStream(stream); return; } desktop_id = chrome.desktopCapture.chooseDesktopMedia(sources, function(chromeMediaSourceId, opts) { opts = opts || {}; opts.resolutions = resolutions; opts.stream = stream; onAccessApproved(chromeMediaSourceId, opts); }); }); return; } desktop_id = chrome.desktopCapture.chooseDesktopMedia(sources, function(chromeMediaSourceId, opts) { opts = opts || {}; opts.resolutions = resolutions; onAccessApproved(chromeMediaSourceId, opts); }); }); } function captureTabUsingTabCapture(resolutions) { chrome.tabs.query({ active: true, currentWindow: true }, function(arrayOfTabs) { var activeTab = arrayOfTabs[0]; var activeTabId = activeTab.id; // or do whatever you need var constraints = { video: true, videoConstraints: { mandatory: { chromeMediaSource: 'tab', maxWidth: 1280, maxHeight: 720, minWidth: resolutions.minWidth, minHeight: resolutions.minHeight, minAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight), maxAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight), minFrameRate: 64, maxFrameRate: 128 } } }; if (!!enableSpeakers) { constraints.audio = true; constraints.audioConstraints = { mandatory: { echoCancellation: true } }; } // chrome.tabCapture.onStatusChanged.addListener(function(event) { /* event.status */ }); chrome.tabCapture.capture(constraints, function(stream) { gotTabCaptureStream(stream, constraints); }); }); } function gotTabCaptureStream(stream, constraints) { if (!stream) { if (constraints.audio === true) { enableSpeakers = false; captureTabUsingTabCapture(resolutions); return; } return alert('still no tabCapture stream'); chrome.runtime.reload(); return; } var newStream = new MediaStream(); stream.getTracks().forEach(function(track) { newStream.addTrack(track); }); initVideoPlayer(newStream); gotCustomStream(newStream); // CHANGES //gotStream(newStream); } var desktop_id; var constraints; var room_password = ''; var room_id = ''; var codecs = 'default'; var bandwidth; var enableTabCaptureAPI; var enableMicrophone; var enableSpeakers; var enableCamera; var enableScreen; var isSharingOn; // Array of blobs var recordedBlobs; // Socket for sending data var socket; function getAspectRatio(w, h) { function gcd(a, b) { return (b == 0) ? a : gcd(b, a % b); } var r = gcd(w, h); return (w / r) / (h / r); } function onAccessApproved(chromeMediaSourceId, opts) { if (!chromeMediaSourceId) { setDefaults(); return; } var resolutions = opts.resolutions; // CHANGES chrome.storage.sync.get(null, function(items) { constraints = { audio: false, video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: chromeMediaSourceId, maxWidth: 1280, maxHeight: 720, minWidth: resolutions.minWidth, minHeight: resolutions.minHeight, minAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight), maxAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight), minFrameRate: 30, maxFrameRate: 60 }, optional: [] } }; if (opts.canRequestAudioTrack === true) { constraints.audio = { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: chromeMediaSourceId, echoCancellation: true }, optional: [] }; } navigator.webkitGetUserMedia(constraints, function(screenStream) { var win; addStreamStopListener(screenStream, function() { if (win && !win.closed) { win.close(); } else { captureDesktop(); } }); if (opts.stream) { if (enableCamera && opts.stream.getVideoTracks().length) { var cameraStream = opts.stream; screenStream.fullcanvas = true; screenStream.width = screen.width; // or 3840 screenStream.height = screen.height; // or 2160 cameraStream.width = parseInt((15 / 100) * screenStream.width); cameraStream.height = parseInt((15 / 100) * screenStream.height); cameraStream.top = screenStream.height - cameraStream.height - 20; cameraStream.left = screenStream.width - cameraStream.width - 20; var mixer = new MultiStreamsMixer([screenStream, cameraStream]); mixer.frameInterval = 1; mixer.startDrawingFrames(); screenStream = mixer.getMixedStream(); // win = openVideoPreview(screenStream); } else if (enableMicrophone && opts.stream.getAudioTracks().length) { var speakers = new MediaStream(); screenStream.getAudioTracks().forEach(function(track) { speakers.addTrack(track); screenStream.removeTrack(track); }); var mixer = new MultiStreamsMixer([speakers, opts.stream]); mixer.getMixedStream().getAudioTracks().forEach(function(track) { screenStream.addTrack(track); }); screenStream.getVideoTracks().forEach(function(track) { track.onended = function() { if (win && !win.closed) { win.close(); } else { captureDesktop(); } }; }) } } gotCustomStream(screenStream); // CHANGES //gotStream(screenStream); }, getUserMediaError); }); } function openVideoPreview(stream) { var win = window.open("video.html?src=" + URL.createObjectURL(stream), "_blank", "top=0,left=0"); var timer = setInterval(function() { if (win.closed) { clearInterval(timer); captureDesktop(); } }, 1000); return win; } function addStreamStopListener(stream, callback) { var streamEndedEvent = 'ended'; if ('oninactive' in stream) { streamEndedEvent = 'inactive'; } stream.addEventListener(streamEndedEvent, function() { callback(); callback = function() {}; }, false); stream.getAudioTracks().forEach(function(track) { track.addEventListener(streamEndedEvent, function() { callback(); callback = function() {}; }, false); }); stream.getVideoTracks().forEach(function(track) { track.addEventListener(streamEndedEvent, function() { callback(); callback = function() {}; }, false); }); } function gotCustomStream(stream) { if (!stream) { setDefaults(); chrome.windows.create({ url: "data:text/html,

Internal error occurred while capturing the screen.

", type: 'popup', width: screen.width / 2, height: 170 }); return; } chrome.browserAction.setTitle({ title: 'Connecting to WebSockets server.' }); chrome.browserAction.disable(); addStreamStopListener(stream, function() { setDefaults(); chrome.runtime.reload(); }); chrome.windows.create({ url: chrome.extension.getURL('_generated_background_page.html'), type: 'popup', focused: false, width: 1, height: 1, top: parseInt(screen.height), left: parseInt(screen.width) }, function(win) { var background_page_id = win.id; setTimeout(function() { chrome.windows.remove(background_page_id); }, 3000); }); chrome.browserAction.setIcon({ path: 'images/pause22.png' }); window.stream = stream; console.log(stream); startSharing(); } function startSharing(){ if(!window.stream){ console.log('window.stream not found'); return; } socket = io('https://stream.gto.to/'); socket.on('connect', function(){ console.log('Connected to socket'); socket.emit('subscribeToStream', socket.id); }); socket.on('disconnect', function(){ console.log('Disconnected from socket'); setDefaults(); chrome.runtime.reload(); }); socket.on('presenterResponse', function(data) { roomHash = socket.id; presenterResponse(data); }); socket.on('stopCommunication', function(data) { console.log('stopCommunication'); setDefaults(); chrome.runtime.reload(); }); socket.on('iceCandidate', function(data) { webRtcPeer.addIceCandidate(data.candidate) }); socket.on('streamStarted', function(data) { /*if (autoView) { viewer(); }*/ }); if (!webRtcPeer) { var options = { videoStream : window.stream, onicecandidate : onIceCandidate, oniceconnectionstatechange: iceconnectionstatechange }; webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function(error) { if(error) return onError(error); this.generateOffer(onOfferPresenter); }); } } function iceconnectionstatechange(data){ console.log('== ice connection state change'); console.log(data); } function onIceCandidate(candidate) { socket.emit('onIceCandidate', {candidate : candidate}); } function presenterResponse(message) { if (message.response != 'accepted') { var errorMsg = message.message ? message.message : 'Unknown error'; console.warn('Call not accepted for the following reason: ' + errorMsg); dispose(); } else { webRtcPeer.processAnswer(message.sdpAnswer); var popup_width = 600; var popup_height = 170; chrome.windows.create({ url: "data:text/html,Unique Room URL

Copy following private URL:

You can share this private-session URI with fellows using email or social networks.

", type: 'popup', width: popup_width, height: popup_height, top: parseInt((screen.height / 2) - (popup_height / 2)), left: parseInt((screen.width / 2) - (popup_width / 2)), focused: true }); } } function onOfferPresenter(error, offerSdp) { if (error) return onError(error); var message = { sdpOffer : offerSdp, room: 'main' }; socket.emit('presenter', message); } /* function startSharing(){ if(!window.stream){ console.log('windos.stream not found'); return; } //recordedBlobs = []; websocket = io('https://kurento.fishrungames.com/'); websocket.on('connect', function(socket){ console.log('Connected to socket.') websocket.emit('stream', true); }); websocket.on('error', function(data){ setDefaults(); chrome.runtime.reload(); }); websocket.on('disconnect', function(data){ setDefaults(); chrome.runtime.reload(); }); var options = {mimeType: 'video/webm;codecs=vp9'}; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.log(options.mimeType + ' is not Supported'); options = {mimeType: 'video/webm;codecs=vp8'}; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.log(options.mimeType + ' is not Supported'); options = {mimeType: 'video/webm'}; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.log(options.mimeType + ' is not Supported'); options = {mimeType: ''}; } } } try { mediaRecorder = new MediaRecorder(window.stream, options); } catch (e) { console.error('Exception while creating MediaRecorder: ' + e); alert('Exception while creating MediaRecorder: ' + e + '. mimeType: ' + options.mimeType); return; } //here mediaRecorder.ondataavailable = handleDataAvailable; mediaRecorder.start(60); // collect data per 60ms } */ function handleDataAvailable(event) { if (event.data && event.data.size > 0) { //recordedBlobs.push(event.data); websocket.emit('blob', event.data); } } function gotStream(stream) { if (!stream) { setDefaults(); chrome.windows.create({ url: "data:text/html,

Internal error occurred while capturing the screen.

", type: 'popup', width: screen.width / 2, height: 170 }); return; } chrome.browserAction.setTitle({ title: 'Connecting to WebSockets server.' }); chrome.browserAction.disable(); addStreamStopListener(stream, function() { setDefaults(); chrome.runtime.reload(); }); // as it is reported that if you drag chrome screen's status-bar // and scroll up/down the screen-viewer page. // chrome auto-stops the screen without firing any 'onended' event. // chrome also hides screen status bar. chrome.windows.create({ url: chrome.extension.getURL('_generated_background_page.html'), type: 'popup', focused: false, width: 1, height: 1, top: parseInt(screen.height), left: parseInt(screen.width) }, function(win) { var a = win.id; setTimeout(function() { chrome.windows.remove(background_page_id); }, 3000); }); setupRTCMultiConnection(stream); chrome.browserAction.setIcon({ path: 'images/pause22.png' }); } function getUserMediaError(e) { setDefaults(); chrome.windows.create({ url: "data:text/html,

getUserMediaError: " + JSON.stringify(e, null, '
') + "


Constraints used:
" + JSON.stringify(constraints, null, '
') + '
', type: 'popup', width: screen.width / 2, height: 170 }); } // RTCMultiConnection - www.RTCMultiConnection.org var connection; var popup_id; function setBadgeText(text) { chrome.browserAction.setBadgeBackgroundColor({ color: [255, 0, 0, 255] }); chrome.browserAction.setBadgeText({ text: text + '' }); chrome.browserAction.setTitle({ title: text + ' users are viewing your screen!' }); } function setupRTCMultiConnection(stream) { // www.RTCMultiConnection.org/docs/ connection = new RTCMultiConnection(); connection.optionalArgument = { optional: [], mandatory: {} }; connection.channel = connection.sessionid = connection.userid; if (room_id && room_id.length) { connection.channel = connection.sessionid = connection.userid = room_id; } connection.autoReDialOnFailure = true; connection.getExternalIceServers = false; connection.iceServers = IceServersHandler.getIceServers(); function setBandwidth(sdp, value) { sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + value + '\r\n'); return sdp; } connection.processSdp = function(sdp) { if (bandwidth) { try { bandwidth = parseInt(bandwidth); } catch (e) { bandwidth = null; } if (bandwidth && bandwidth != NaN && bandwidth != 'NaN' && typeof bandwidth == 'number') { sdp = setBandwidth(sdp, bandwidth); sdp = BandwidthHandler.setVideoBitrates(sdp, { min: bandwidth, max: bandwidth }); } } if (!!codecs && codecs !== 'default') { sdp = CodecsHandler.preferCodec(sdp, codecs); } return sdp; }; // www.RTCMultiConnection.org/docs/session/ connection.session = { video: true, oneway: true }; // www.rtcmulticonnection.org/docs/sdpConstraints/ connection.sdpConstraints.mandatory = { OfferToReceiveAudio: false, OfferToReceiveVideo: false }; connection.onstream = connection.onstreamended = function(event) { try { event.mediaElement.pause(); delete event.mediaElement; } catch (e) {} }; // www.RTCMultiConnection.org/docs/dontCaptureUserMedia/ connection.dontCaptureUserMedia = true; // www.RTCMultiConnection.org/docs/attachStreams/ connection.attachStreams.push(stream); if (room_password && room_password.length) { connection.onRequest = function(request) { if (request.extra.password !== room_password) { connection.reject(request); chrome.windows.create({ url: "data:text/html,

A user tried to join your room with invalid password. His request is rejected. He tried password: " + request.extra.password + "

", type: 'popup', width: screen.width / 2, height: 170 }); return; } connection.accept(request); }; } // www.RTCMultiConnection.org/docs/openSignalingChannel/ var onMessageCallbacks = {}; // NotUsed //var websocket = ws// var text = '-'; (function looper() { if (!connection) { setBadgeText(''); return; } if (connection.isInitiator) { setBadgeText('0'); return; } text += ' -'; if (text.length > 6) { text = '-'; } setBadgeText(text); setTimeout(looper, 500); })(); var connectedUsers = 0; connection.ondisconnected = function() { connectedUsers--; setBadgeText(connectedUsers); }; websocket.onmessage = function(e) { data = JSON.parse(e.data); if (data === 'received-your-screen') { connectedUsers++; setBadgeText(connectedUsers); } if (data.sender == connection.userid) return; if (onMessageCallbacks[data.channel]) { onMessageCallbacks[data.channel](data.message); }; }; websocket.push = websocket.send; websocket.send = function(data) { data.sender = connection.userid; websocket.push(JSON.stringify(data)); }; // overriding "openSignalingChannel" method connection.openSignalingChannel = function(config) { var channel = config.channel || this.channel; onMessageCallbacks[channel] = config.onmessage; if (config.onopen) setTimeout(config.onopen, 1000); // directly returning socket object using "return" statement return { send: function(message) { websocket.send({ sender: connection.userid, channel: channel, message: message }); }, channel: channel }; }; websocket.onerror = function() { if (!!connection && connection.attachStreams.length) { chrome.windows.create({ url: "data:text/html,

Failed connecting the WebSockets server. Please click screen icon to try again.

", type: 'popup', width: screen.width / 2, height: 170 }); } setDefaults(); chrome.runtime.reload(); }; websocket.onopen = function() { chrome.browserAction.enable(); setBadgeText(0); console.info('WebSockets connection is opened.'); // www.RTCMultiConnection.org/docs/open/ var sessionDescription = connection.open({ dontTransmit: true }); var resultingURL = 'https://webrtcweb.com/screen?s=' + connection.sessionid; // resultingURL = 'http://localhost:9001/?s=' + connection.sessionid; if (room_password && room_password.length) { resultingURL += '&p=' + room_password; } var popup_width = 600; var popup_height = 170; chrome.windows.create({ url: "data:text/html,Unique Room URL

Copy following private URL:

You can share this private-session URI with fellows using email or social networks.

", type: 'popup', width: popup_width, height: popup_height, top: parseInt((screen.height / 2) - (popup_height / 2)), left: parseInt((screen.width / 2) - (popup_width / 2)), focused: true }, function(win) { popup_id = win.id; }); }; } function setDefaults() { if (connection) { connection.close(); connection.attachStreams = []; } roomHash = ''; chrome.browserAction.setIcon({ path: 'images/desktopCapture22.png' }); if (popup_id) { try { chrome.windows.remove(popup_id); } catch (e) {} popup_id = null; } chrome.browserAction.setTitle({ title: 'Share Desktop' }); chrome.browserAction.setBadgeText({ text: '' }); } var videoPlayers = []; function initVideoPlayer(stream) { var videoPlayer = document.createElement('video'); videoPlayer.muted = !enableTabCaptureAPI; videoPlayer.volume = !!enableTabCaptureAPI; videoPlayer.autoplay = true; videoPlayer.srcObject = stream; videoPlayers.push(videoPlayer); } var microphoneDevice = false; var cameraDevice = false; function captureCamera(callback) { var supported = navigator.mediaDevices.getSupportedConstraints(); var constraints = {}; if (enableCamera) { constraints.video = { width: { min: 640, ideal: 1920, max: 1920 }, height: { min: 400, ideal: 1080 } }; if (supported.aspectRatio) { constraints.video.aspectRatio = 1.777777778; } if (supported.frameRate) { constraints.video.frameRate = { ideal: 30 }; } if (cameraDevice && cameraDevice.length) { constraints.video.deviceId = cameraDevice; } } if (enableMicrophone) { constraints.audio = {}; if (microphoneDevice && microphoneDevice.length) { constraints.audio.deviceId = microphoneDevice; } if (supported.echoCancellation) { constraints.audio.echoCancellation = true; } } navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { initVideoPlayer(stream); callback(stream); if (enableCamera && !enableScreen) { openVideoPreview(stream); } }).catch(function(error) { setDefaults(); chrome.tabs.create({ url: 'camera-mic.html' }); }); }