Browse Source

Merge pull request #77 from longsleep/chromeextension

Chrome extension for screen sharing
pull/79/head
Simon Eisenmann 11 years ago
parent
commit
f8200fdc5b
  1. 12
      html/main.html
  2. 15
      src/styles/components/_bar.scss
  3. 2
      static/css/main.min.css
  4. 46
      static/js/directives/screenshare.js
  5. 9
      static/js/mediastream/peerscreenshare.js
  6. 2
      static/js/mediastream/webrtc.js
  7. 122
      static/js/services/chromeextension.js
  8. 179
      static/js/services/screensharing.js
  9. 9
      static/js/services/services.js

12
html/main.html

@ -22,12 +22,12 @@ @@ -22,12 +22,12 @@
<div class="navbar-collapse collapse" collapse="isCollapsed">
<ul class="nav navbar-nav navbar-right right">
<li class="ng-cloak">
<button title="{{_('Present a file')}}" class="btn aenablebtn" ng-show="status=='connected' || status=='conference' || layout.presentation" ng-model="layout.presentation" btn-checkbox><i class="fa fa-folder-open-o"></i></button>
<button title="{{_('Share your screen')}}" class="btn aenablebtn" ng-disabled="!supported.screensharing" ng-show="status=='connected' || status=='conference' || layout.screenshare" ng-model="layout.screenshare" btn-checkbox><i class="fa fa-desktop"></i></button>
<button title="{{_('Chat')}}" class="btn" ng-class="{messagesunseen: chatMessagesUnseen>0}" ng-model="layout.chat" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-comments-o"></i><span class="badge" ng-show="chatMessagesUnseen" ng-bind="chatMessagesUnseen"></span></button>
<button title="{{_('Mute microphone')}}" class="btn amutebtn" ng-model="microphoneMute" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-microphone-slash"></i></button>
<button title="{{_('Turn camera off')}}" class="btn amutebtn" ng-model="cameraMute" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-eye-slash"></i></button>
<button title="{{_('Settings')}}" class="btn" ng-model="layout.settings" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-cog"></i></button>
<button title="{{_('Present a file')}}" class="btn aenablebtn btn-presentation" ng-show="status=='connected' || status=='conference' || layout.presentation" ng-model="layout.presentation" btn-checkbox><i class="fa fa-folder-open-o"></i></button>
<button title="{{_('Share your screen')}}" class="btn aenablebtn btn-screenshare" ng-disabled="!supported.screensharing" ng-show="status=='connected' || status=='conference' || layout.screenshare" ng-model="layout.screenshare" btn-checkbox><i class="fa fa-desktop"></i></button>
<button title="{{_('Chat')}}" class="btn btn-chat" ng-class="{messagesunseen: chatMessagesUnseen>0}" ng-model="layout.chat" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-comments-o"></i><span class="badge" ng-show="chatMessagesUnseen" ng-bind="chatMessagesUnseen"></span></button>
<button title="{{_('Mute microphone')}}" class="btn btn-mutemicrophone amutebtn" ng-model="microphoneMute" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-microphone-slash"></i></button>
<button title="{{_('Turn camera off')}}" class="btn btn-mutecamera amutebtn" ng-model="cameraMute" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-eye-slash"></i></button>
<button title="{{_('Settings')}}" class="btn btn-settings" ng-model="layout.settings" btn-checkbox btn-checkbox-true="true" btn-checkbox-false="false"><i class="fa fa-cog"></i></button>
</li>
</ul>
</div>

15
src/styles/components/_bar.scss

@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
text-align: center;
touch-callout: none;
user-select: none;
z-index: 10;
z-index: 60;
}
.bar .left {
@ -50,13 +50,24 @@ @@ -50,13 +50,24 @@
span a {
color: $bar-logo-text-desc;
}
@include breakpt($breakpoint-medium) {
background: $scalable-logo no-repeat center;
width: 46px;
height: 46px;
span {
display: none;
}
}
}
@include breakpt($breakpoint-medium) {
padding: 2px 5px 0px 11px;
}
}
.bar .middle {
position: absolute;
left: 0px;
right: 0px;
right: 60px;
top: 0px;
pointer-events: none;
text-align: center;

2
static/css/main.min.css vendored

File diff suppressed because one or more lines are too long

46
static/js/directives/screenshare.js

@ -18,9 +18,9 @@ @@ -18,9 +18,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials/screensharepeer.html', 'bigscreen'], function($, _, template, templatePeer, BigScreen) {
define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials/screensharepeer.html', 'bigscreen', 'webrtc.adapter'], function($, _, template, templatePeer, BigScreen) {
return ["$window", "mediaStream", "$compile", "safeApply", "videoWaiter", "$timeout", "alertify", "translation", function($window, mediaStream, $compile, safeApply, videoWaiter, $timeout, alertify, translation) {
return ["$window", "mediaStream", "$compile", "safeApply", "videoWaiter", "$timeout", "alertify", "translation", "screensharing", function($window, mediaStream, $compile, safeApply, videoWaiter, $timeout, alertify, translation, screensharing) {
var peerTemplate = $compile(templatePeer);
@ -168,9 +168,24 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials @@ -168,9 +168,24 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials
}
$scope.layout.screenshare = true;
screensharing.getScreen().then(function(options) {
if (options) {
$scope.startScreenshare(options);
} else {
// No options received - assume cancel.
$scope.stopScreenshare();
}
}, function(err) {
console.log("Screen sharing request returned error", err);
$scope.stopScreenshare();
});
};
$scope.startScreenshare = function(options) {
// Create userMedia with screen share type.
var usermedia = mediaStream.webrtc.doScreenshare();
var usermedia = mediaStream.webrtc.doScreenshare(options);
var handler;
var peers = {};
var screenshares = [];
@ -251,8 +266,20 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials @@ -251,8 +266,20 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials
$scope.$apply(function(scope) {
scope.stopScreenshare();
});
if (error && error.name === "PermissionDeniedError") {
alertify.dialog.alert(translation._("Permission to start screen sharing was denied. Make sure to have enabled screen sharing access for your browser. Copy chrome://flags/#enable-usermedia-screen-capture and open it with your browser and enable the flag on top. Then restart the browser and you are ready to go."));
if (error && error.name) {
switch (error.name) {
case "PermissionDeniedError":
if ($window.webrtcDetectedVersion >= 32 &&
$window.webrtcDetectedVersion < 37) {
alertify.dialog.alert(translation._("Permission to start screen sharing was denied. Make sure to have enabled screen sharing access for your browser. Copy chrome://flags/#enable-usermedia-screen-capture and open it with your browser and enable the flag on top. Then restart the browser and you are ready to go."));
} else {
alertify.dialog.alert(translation._("Permission to start screen sharing was denied."));
}
break;
default:
alertify.dialog.alert(translation._("Failed to start screen sharing (%s).", error.name));
break
}
}
});
@ -266,7 +293,10 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials @@ -266,7 +293,10 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials
console.log("Screen share stopped.");
}
$scope.layout.screenshare = false;
if ($scope.layout.screenshare) {
screensharing.cancelGetScreen();
$scope.layout.screenshare = false;
}
};
@ -282,6 +312,10 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials @@ -282,6 +312,10 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials
};
mediaStream.webrtc.e.on("done", function() {
$scope.stopScreenshare();
});
$scope.$watch("layout.screenshare", function(newval, oldval) {
if (newval && !oldval) {
$scope.doScreenshare();

9
static/js/mediastream/peerscreenshare.js

@ -71,19 +71,16 @@ define(['jquery', 'underscore', 'mediastream/peercall', 'mediastream/tokens'], f @@ -71,19 +71,16 @@ define(['jquery', 'underscore', 'mediastream/peercall', 'mediastream/tokens'], f
var screenWidth = window.screen.width;
var screenHeight = window.screen.height;
// NOTE(longsleep): This generates constrains for the experiental Screen sharing
// support in Chrome 31+. This needs to be enabled in flags:
// chrome://flags/#enable-usermedia-screen-capture
// Constraints which define what actually gets shared need to
// be provided in options.
var mandatoryVideoConstraints = $.extend({
chromeMediaSource: 'screen',
maxWidth: screenWidth,
maxHeight: screenHeight
}, options);
var mediaConstraints = {
audio: false,
video: {
mandatory: mandatoryVideoConstraints,
optional: []
mandatory: mandatoryVideoConstraints
}
}
console.log("Setting screen sharing media constraints", mandatoryVideoConstraints);

2
static/js/mediastream/webrtc.js

@ -481,7 +481,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -481,7 +481,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
var usermedia = new UserMedia({
noaudio: true
});
var ok = usermedia.doGetUserMedia(null, PeerScreenshare.getMediaContraints());
var ok = usermedia.doGetUserMedia(null, PeerScreenshare.getMediaContraints(options));
if (ok) {
this.e.one("done", function() {
usermedia.stop();

122
static/js/services/chromeextension.js

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
define(["underscore", "jquery"], function(_, $) {
// chromeExtension
return ["$window", "$q", function($window, $q) {
var ChromeExtension = function() {
this.available = false;
this.registry = {};
this.count = 0;
this.e = $({});
this.autoinstall = {};
this.initialize();
};
ChromeExtension.prototype.initialize = function() {
var marker = $window.document.getElementById("chromeextension-available");
if (marker) {
this.available = true;
console.log("Chrome extension is available.");
this.e.triggerHandler("available", true);
} else if (!marker && this.available) {
this.available = false;
console.log("Chrome extension is no longer available.");
this.e.triggerHandler("available", false);
}
};
ChromeExtension.prototype.call = function(data) {
var deferred = $q.defer();
var n = this.count++;
this.registry[n] = deferred;
var msg = {
Type: "Call",
Call: data,
n: n
}
$window.postMessage(msg, $window.document.URL);
return deferred.promise;
};
ChromeExtension.prototype.onMessage = function(event) {
var data = event.data;
switch (data.Type) {
case "Call":
var deferred = this.registry[data.n];
if (deferred) {
var call = data.Call;
switch (call.Type) {
case "Result":
delete this.registry[data.n];
//console.log("Call complete with result", call);
deferred.resolve(call.Result);
break;
case "Notify":
//console.log("Notify", call);
deferred.notify(call.Notify);
break;
case "Error":
delete this.registry[data.n];
//console.log("Call failed with error", call);
deferred.reject(call.Error);
break
}
} else {
console.warn("Unknown call reference received", data, this.registry, this);
}
break;
case "Upgrade":
console.log("Extension installed or upgraded", data);
this.initialize();
break;
default:
console.log("Unknown message type", data.Type, data);
break;
}
};
ChromeExtension.prototype.registerAutoInstall = function(installFunc, cancelInstallFunc) {
this.autoinstall.install = installFunc;
this.autoinstall.cancel = cancelInstallFunc;
if (!this.available && installFunc) {
this.e.triggerHandler("available", true);
}
};
var extension = new ChromeExtension();
$window.addEventListener("message", function(event) {
//console.log("message", event.origin, event.source === window, event);
if (event.source === window && event.data.answer) {
// Only process answers to avoid loops.
extension.onMessage(event);
}
});
// Expose.
return extension;
}];
});

179
static/js/services/screensharing.js

@ -18,33 +18,164 @@ @@ -18,33 +18,164 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
define(['webrtc.adapter'], function() {
define(['underscore', 'webrtc.adapter'], function(_) {
// screensharing
return ["$window", function($window) {
// Check if we can do screensharing.
var supported = false;
if ($window.webrtcDetectedBrowser === "chrome") {
if ($window.webrtcDetectedVersion >= 32 &&
$window.webrtcDetectedVersion < 37) {
// Support for flag based developer screen sharing came in Chrome 32.
// It was removed in Chrome 37 in favour of chrome.chooseDesktopMedia
// https://code.google.com/p/chromium/issues/detail?id=347641
supported = true;
} else if ($window.webrtcDetectedVersion >= 37) {
// We need a extension to support screen sharing. See
// https://developer.chrome.com/extensions/desktopCapture#method-chooseDesktopMedia
// for details.
return ["$window", "$q", "chromeExtension", function($window, $q, chromeExtension) {
var Screensharing = function() {
this.autoinstall = false;
this.initialize();
chromeExtension.e.on("available", _.bind(function() {
this.initialize();
}, this));
};
Screensharing.prototype.initialize = function() {
// Check if we can do screensharing.
this.supported = false;
// Define our helpers.
this.prepare = null;
this.cancel = null;
// Chrome support.
if ($window.webrtcDetectedBrowser === "chrome") {
if ($window.webrtcDetectedVersion >= 32 &&
$window.webrtcDetectedVersion < 37) {
// Support for flag based developer screen sharing came in Chrome 32.
// It was removed in Chrome 37 in favour of chrome.chooseDesktopMedia
// https://code.google.com/p/chromium/issues/detail?id=347641
this.supported = true;
this.prepare = function(options) {
// This generates constrains for the flag based screen screensharing
// support in Chrome 31+ to 36. Flag to be enabled is found at:
// chrome://flags/#enable-usermedia-screen-capture
var d = $q.defer()
var opts = _.extend({
chromeMediaSource: "screen"
}, options);
d.resolve(opts);
return d.promise;
};
} else if ($window.webrtcDetectedVersion >= 37) {
// We need a extension to support screen sharing. See
// https://developer.chrome.com/extensions/desktopCapture#method-chooseDesktopMedia
// for details.
}
if (chromeExtension.available) {
this.supported = true;
var pending = null;
this.prepare = function(options) {
var select = chromeExtension.call({
Type: "Action",
Action: "chooseDesktopMedia"
});
var d = $q.defer();
select.then(function(id) {
// Success with id.
pending = null;
if (id) {
var opts = _.extend({
chromeMediaSource: "desktop",
chromeMediaSourceId: id
}, options);
d.resolve(opts);
} else {
d.resolve(null);
}
}, function(err) {
// Error.
pending = null;
console.log("Failed to prepare screensharing", err);
d.reject(err);
}, function(data) {
// Notify.
pending = data;
});
return d.promise;
};
this.cancel = function() {
if (pending !== null) {
chromeExtension.call({
Type: "Action",
Action: "cancelChooseDesktopMedia",
Args: pending
});
pending = null;
}
};
}
} else {
// Currently Chrome only - sorry.
// Firefox 33 might get screen sharing support.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=923225
}
// Auto install support.
if (!this.supported && chromeExtension.autoinstall.install) {
this.supported = this.autoinstall = true;
var that = this;
this.prepare = function(options) {
var d = $q.defer();
var install = chromeExtension.autoinstall.install();
install.then(function() {
that.initialize();
if (that.autoinstall) {
// We are still on auto install - must have failed.
d.reject("Auto install failed");
} else {
// Seems we can do it now.
var prepare = that.prepare(options);
prepare.then(function(id) {
d.resolve(id);
}, function(err) {
d.reject(err);
});
}
}, function(err) {
d.reject(err);
});
return d.promise;
};
this.cancel = function() {
if (chromeExtension.autoinstall.cancel) {
chromeExtension.autoinstall.cancel();
}
};
} else {
this.autoinstall = false;
}
} else {
// Currently Chrome only.
}
// public API.
return {
supported: supported
}
console.log("Screensharing support", this.supported, this.autoinstall ? "autoinstall" : "");
};
Screensharing.prototype.getScreen = function(options) {
if (this.prepare) {
return this.prepare(options);
} else {
var d = $q.defer()
d.reject("No implementation to get screen.");
return d.promise;
}
};
Screensharing.prototype.cancelGetScreen = function() {
if (this.cancel) {
this.cancel();
}
};
// Expose.
var screensharing = new Screensharing();
return screensharing;
}];

9
static/js/services/services.js

@ -53,7 +53,8 @@ define([ @@ -53,7 +53,8 @@ define([
'services/dialogs',
'services/geolocation',
'services/screensharing',
'services/continueconnector'], function(_,
'services/continueconnector',
'services/chromeextension'], function(_,
desktopNotify,
playSound,
safeApply,
@ -86,7 +87,8 @@ animationFrame, @@ -86,7 +87,8 @@ animationFrame,
dialogs,
geolocation,
screensharing,
continueConnector) {
continueConnector,
chromeExtension) {
var services = {
desktopNotify: desktopNotify,
@ -121,7 +123,8 @@ continueConnector) { @@ -121,7 +123,8 @@ continueConnector) {
dialogs: dialogs,
geolocation: geolocation,
screensharing: screensharing,
continueConnector: continueConnector
continueConnector: continueConnector,
chromeExtension: chromeExtension
};
var initialize = function(angModule) {

Loading…
Cancel
Save