desktopCapture-p2p/desktop-capturing.js
2018-06-07 18:03:26 +05:00

1046 lines
29 KiB
JavaScript
Executable File

// 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,<h1>Internal error occurred while capturing the screen.</h1>",
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,<title>Unique Room URL</title><h1 style='text-align:center'>Copy following private URL:</h1><input type='text' value='https://stream.gto.to/?room=" + roomHash + "' style='text-align:center;width:100%;font-size:1.2em;'><p style='text-align:center'>You can share this private-session URI with fellows using email or social networks.</p>",
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,<h1>Internal error occurred while capturing the screen.</h1>",
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,<h1>getUserMediaError: " + JSON.stringify(e, null, '<br>') + "</h1><br>Constraints used:<br><pre>" + JSON.stringify(constraints, null, '<br>') + '</pre>',
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,<h1>A user tried to join your room with invalid password. His request is rejected. He tried password: " + request.extra.password + " </h2>",
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,<h1>Failed connecting the WebSockets server. Please click screen icon to try again.</h1>",
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,<title>Unique Room URL</title><h1 style='text-align:center'>Copy following private URL:</h1><input type='text' value='" + resultingURL + "' style='text-align:center;width:100%;font-size:1.2em;'><p style='text-align:center'>You can share this private-session URI with fellows using email or social networks.</p>",
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'
});
});
}