WebRTC audio/video call and conferencing server.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

314 lines
9.1 KiB

/*
* 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(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials/screensharepeer.html', 'bigscreen'], function($, _, template, templatePeer, BigScreen) {
return ["$window", "mediaStream", "$compile", "safeApply", "videoWaiter", "$timeout", "alertify", "translation", function($window, mediaStream, $compile, safeApply, videoWaiter, $timeout, alertify, translation) {
var peerTemplate = $compile(templatePeer);
var controller = ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
var screenCount = 0;
var screens = {};
var pane = $element.find(".screensharepane");
$scope.layout.screenshare = false;
$scope.usermedia = null;
$scope.connected = false;
$scope.hideOptionsBar = true;
$scope.fitScreen = true;
var handleRequest = function(event, currenttoken, to, data, type, to2, from, peerscreenshare) {
console.log("Screen share answer message", currenttoken, data, type);
if (typeof data === "string") {
if (data.charCodeAt(0) === 2) {
// Ignore whatever shit is sent us by Firefox.
return;
}
// Control data request.
var msg;
try {
msg = JSON.parse(data);
} catch (e) {
// Invalid JSON.
console.warn("Invalid JSON received from screen share channel.", data);
peerscreenshare.close();
return;
}
switch (msg.m) {
case "bye":
// Close this screen share.
peerscreenshare.close();
break;
default:
console.log("Unknown screen share control request", msg.m, msg);
break;
}
} else {
console.warn("Unkown data type received -> ignored", typeof data, [data]);
peerscreenshare.close();
}
};
mediaStream.api.e.on("received.screenshare", function(event, id, from, data, p2p) {
if (!p2p) {
console.warn("Received screensharing info without p2p. This should not happen!");
return;
}
var token = data.id;
// Bind token.
var handler = mediaStream.tokens.on(token, handleRequest, "screenshare");
// Subscribe to peers screensharing.
mediaStream.webrtc.doSubscribeScreenshare(from, token, {
created: function(peerscreenshare) {
peerscreenshare.e.on("remoteStreamAdded", function(event, stream) {
$scope.$apply(function(scope) {
scope.addRemoteStream(stream, peerscreenshare);
});
});
peerscreenshare.e.on("remoteStreamRemoved", function(event, stream) {
safeApply($scope, function(scope) {
scope.removeRemoteStream(stream, peerscreenshare);
});
});
},
connected: function(peerscreenshare) {
console.log("PeerScreenshare connected", peerscreenshare);
$scope.$apply(function(scope) {
scope.connected = true;
});
},
closed: function(peerscreenshare) {
console.log("PeerScreenshare closed", peerscreenshare);
safeApply($scope, function(scope) {
scope.removeRemoteStream(null, peerscreenshare);
scope.connected = false;
});
mediaStream.tokens.off(token, handler);
handler = null;
}
});
});
$scope.addRemoteStream = function(stream, currentscreenshare) {
$scope.$emit("mainview", "screenshare", true);
var subscope = $scope.$new(true);
var peerid = subscope.peerid = currentscreenshare.id;
peerTemplate(subscope, function(clonedElement, scope) {
pane.append(clonedElement);
scope.element = clonedElement;
var video = clonedElement.find("video").get(0);
$window.attachMediaStream(video, stream);
videoWaiter.wait(video, stream, function() {
console.log("Screensharing size: ", video.videoWidth, video.videoHeight);
}, function() {
console.warn("We did not receive screen sharing video data", currentscreenshare, stream, video);
});
screens[peerid] = scope;
});
};
$scope.removeRemoteStream = function(stream, currentscreenshare) {
var subscope = screens[currentscreenshare.id];
if (subscope) {
delete screens[currentscreenshare.id];
if (subscope.element) {
subscope.element.remove();
}
subscope.$destroy();
}
if (_.isEmpty(screens)) {
$scope.$emit("mainview", "screenshare", false);
}
};
$scope.doScreenshare = function() {
if ($scope.layout.screenshare) {
$scope.stopScreenshare();
}
$scope.layout.screenshare = true;
// Create userMedia with screen share type.
var usermedia = mediaStream.webrtc.doScreenshare();
var handler;
var peers = {};
var screenshares = [];
var connector = function(token, peercall) {
if (peers.hasOwnProperty(peercall.id)) {
// Already got a connection.
return;
}
peers[peercall.id] = true;
mediaStream.api.apply("sendScreenshare", {
send: function(type, data) {
//console.log("sent screenshare", data, peercall);
return peercall.peerconnection.send(data);
}
})(peercall.from, token);
};
usermedia.e.one("mediasuccess", function(event, usermedia) {
$scope.$apply(function(scope) {
scope.usermedia = usermedia;
// Create token to register with us and send token out to all peers.
// Peers when connect to us with the token and we answer.
var token = "screenshare_" + scope.id + "_" + (screenCount++);
// Updater function to bring in new calls.
var updater = function(event, state, currentcall) {
switch (state) {
case "completed":
case "connected":
connector(token, currentcall);
break;
}
};
// Create callbacks are called for each incoming connections.
handler = mediaStream.tokens.create(token, function(event, currenttoken, to, data, type, to2, from, peerscreenshare) {
console.log("Screen share create", currenttoken, data, type, peerscreenshare);
screenshares.push(peerscreenshare);
usermedia.addToPeerConnection(peerscreenshare.peerconnection);
}, "screenshare");
// Connect all current calls.
mediaStream.webrtc.callForEachCall(function(peercall) {
connector(token, peercall);
});
// Catch later calls too.
mediaStream.webrtc.e.on("statechange", updater);
// Cleanup on stop of media.
usermedia.e.one("stopped", function() {
mediaStream.tokens.off(token, handler);
mediaStream.webrtc.e.off("statechange", updater);
handler = null;
updated = null;
// Send by to all connected peers.
_.each(screenshares, function(peerscreenshare) {
peerscreenshare.send({
m: "bye"
});
$timeout(function() {
peerscreenshare.close();
}, 0);
});
peers = {};
screenshares = [];
// Make sure to clean up.
safeApply(scope, function(scope) {
scope.stopScreenshare();
});
});
});
});
usermedia.e.one("mediaerror", function(event, usermedia, error) {
$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."));
}
});
};
$scope.stopScreenshare = function() {
if ($scope.usermedia) {
$scope.usermedia.stop()
$scope.usermedia = null;
console.log("Screen share stopped.");
}
$scope.layout.screenshare = false;
};
$scope.toggleFullscreen = function(elem) {
if (BigScreen.enabled) {
if (elem) {
BigScreen.toggle(elem);
} else {
BigScreen.toggle(pane.get(0));
}
}
};
$scope.$watch("layout.screenshare", function(newval, oldval) {
if (newval && !oldval) {
$scope.doScreenshare();
} else if (!newval && oldval) {
$scope.stopScreenshare();
}
});
}];
var compile = function(tElement, tAttr) {
return function(scope, iElement, iAttrs, controller) {
$(iElement).on("dblclick", ".remotescreen", _.debounce(function(event) {
scope.toggleFullscreen(event.delegateTarget);
}, 100, true));
}
};
return {
restrict: 'E',
replace: true,
scope: true,
template: template,
controller: controller,
compile: compile
}
}];
});