diff --git a/app.js b/app.js
index 33d27de85..4555f979a 100644
--- a/app.js
+++ b/app.js
@@ -1035,26 +1035,60 @@ function getConferenceHandler() {
return activecall;
}
+/**
+ * Mutes/unmutes the local video.
+ *
+ * @param mute true to mute the local video; otherwise, false
+ * @param options an object which specifies optional arguments such as the
+ * boolean key byUser with default value true which
+ * specifies whether the method was initiated in response to a user command (in
+ * contrast to an automatic decision taken by the application logic)
+ */
+function setVideoMute(mute, options) {
+ if (connection && connection.jingle.localVideo) {
+ var session = getConferenceHandler();
+
+ if (session) {
+ session.setVideoMute(
+ mute,
+ function (mute) {
+ var video = $('#video');
+ var communicativeClass = "icon-camera";
+ var muteClass = "icon-camera icon-camera-disabled";
+
+ if (mute) {
+ video.removeClass(communicativeClass);
+ video.addClass(muteClass);
+ } else {
+ video.removeClass(muteClass);
+ video.addClass(communicativeClass);
+ }
+ connection.emuc.addVideoInfoToPresence(mute);
+ connection.emuc.sendPresence();
+ },
+ options);
+ }
+ }
+}
+
+$(document).on('inlastnchanged', function (event, oldValue, newValue) {
+ if (config.muteLocalVideoIfNotInLastN) {
+ setVideoMute(!newValue, { 'byUser': false });
+ }
+});
+
+/**
+ * Mutes/unmutes the local video.
+ */
function toggleVideo() {
buttonClick("#video", "icon-camera icon-camera-disabled");
- if (!(connection && connection.jingle.localVideo))
- return;
- var sess = getConferenceHandler();
- if (sess) {
- sess.toggleVideoMute(
- function (isMuted) {
- if (isMuted) {
- $('#video').removeClass("icon-camera");
- $('#video').addClass("icon-camera icon-camera-disabled");
- } else {
- $('#video').removeClass("icon-camera icon-camera-disabled");
- $('#video').addClass("icon-camera");
- }
- connection.emuc.addVideoInfoToPresence(isMuted);
- connection.emuc.sendPresence();
- }
- );
+ if (connection && connection.jingle.localVideo) {
+ var session = getConferenceHandler();
+
+ if (session) {
+ setVideoMute(!session.isVideoMute());
+ }
}
}
diff --git a/data_channels.js b/data_channels.js
index 8348c7653..1787e968e 100644
--- a/data_channels.js
+++ b/data_channels.js
@@ -70,46 +70,65 @@ function onDataChannel(event)
'dominantspeakerchanged',
[dominantSpeakerEndpoint]);
}
+ else if ("InLastNChangeEvent" === colibriClass)
+ {
+ var oldValue = obj.oldValue;
+ var newValue = obj.newValue;
+ // Make sure that oldValue and newValue are of type boolean.
+ var type;
+
+ if ((type = typeof oldValue) !== 'boolean') {
+ if (type === 'string') {
+ oldValue = (oldValue == "true");
+ } else {
+ oldValue = new Boolean(oldValue).valueOf();
+ }
+ }
+ if ((type = typeof newValue) !== 'boolean') {
+ if (type === 'string') {
+ newValue = (newValue == "true");
+ } else {
+ newValue = new Boolean(newValue).valueOf();
+ }
+ }
+ $(document).trigger('inlastnchanged', [oldValue, newValue]);
+ }
else if ("LastNEndpointsChangeEvent" === colibriClass)
{
// The new/latest list of last-n endpoint IDs.
var lastNEndpoints = obj.lastNEndpoints;
- /*
- * The list of endpoint IDs which are entering the list of
- * last-n at this time i.e. were not in the old list of last-n
- * endpoint IDs.
- */
+ // The list of endpoint IDs which are entering the list of
+ // last-n at this time i.e. were not in the old list of last-n
+ // endpoint IDs.
var endpointsEnteringLastN = obj.endpointsEnteringLastN;
-
var stream = obj.stream;
console.log(
"Data channel new last-n event: ",
lastNEndpoints, endpointsEnteringLastN, obj);
-
$(document).trigger(
- 'lastnchanged',
- [lastNEndpoints, endpointsEnteringLastN, stream]);
+ 'lastnchanged',
+ [lastNEndpoints, endpointsEnteringLastN, stream]);
}
else if ("SimulcastLayersChangedEvent" === colibriClass)
{
- var endpointSimulcastLayers = obj.endpointSimulcastLayers;
- $(document).trigger('simulcastlayerschanged', [endpointSimulcastLayers]);
+ $(document).trigger(
+ 'simulcastlayerschanged',
+ [obj.endpointSimulcastLayers]);
}
else if ("SimulcastLayersChangingEvent" === colibriClass)
{
- var endpointSimulcastLayers = obj.endpointSimulcastLayers;
- $(document).trigger('simulcastlayerschanging', [endpointSimulcastLayers]);
+ $(document).trigger(
+ 'simulcastlayerschanging',
+ [obj.endpointSimulcastLayers]);
}
else if ("StartSimulcastLayerEvent" === colibriClass)
{
- var simulcastLayer = obj.simulcastLayer;
- $(document).trigger('startsimulcastlayer', simulcastLayer);
+ $(document).trigger('startsimulcastlayer', obj.simulcastLayer);
}
else if ("StopSimulcastLayerEvent" === colibriClass)
{
- var simulcastLayer = obj.simulcastLayer;
- $(document).trigger('stopsimulcastlayer', simulcastLayer);
+ $(document).trigger('stopsimulcastlayer', obj.simulcastLayer);
}
else
{
@@ -141,10 +160,8 @@ function bindDataChannelListener(peerConnection)
// and peer as single channel can be used for sending and receiving data.
// So either channel opened by the bridge or the one opened here is enough
// for communication with the bridge.
- /*var dataChannelOptions =
- {
- reliable: true
- };
+/*
+ var dataChannelOptions = { reliable: true };
var dataChannel
= peerConnection.createDataChannel("myChannel", dataChannelOptions);
@@ -157,6 +174,7 @@ function bindDataChannelListener(peerConnection)
{
var msgData = event.data;
console.info("Got My Data Channel Message:", msgData, dataChannel);
- };*/
+ };
+*/
}
diff --git a/libs/strophe/strophe.jingle.sessionbase.js b/libs/strophe/strophe.jingle.sessionbase.js
index 230092121..87e338a01 100644
--- a/libs/strophe/strophe.jingle.sessionbase.js
+++ b/libs/strophe/strophe.jingle.sessionbase.js
@@ -4,10 +4,16 @@
* @param sid my session identifier(resource)
* @constructor
*/
-function SessionBase(connection, sid){
-
+function SessionBase(connection, sid) {
this.connection = connection;
this.sid = sid;
+
+ /**
+ * The indicator which determines whether the (local) video has been muted
+ * in response to a user command in contrast to an automatic decision made
+ * by the application logic.
+ */
+ this.muteByUser = true;
}
@@ -63,6 +69,7 @@ SessionBase.prototype.removeSource = function (elem, fromJid) {
this.modifySources();
};
+
/**
* Switches video streams.
* @param new_stream new stream that will be used as video of this session.
@@ -230,20 +237,85 @@ SessionBase.prototype.sendSSRCUpdateIq = function(sdpMediaSsrcs, sid, initiator,
}
};
+/**
+ * Determines whether the (local) video is mute i.e. all video tracks are
+ * disabled.
+ *
+ * @return true if the (local) video is mute i.e. all video tracks are
+ * disabled; otherwise, false
+ */
+SessionBase.prototype.isVideoMute = function () {
+ var tracks = connection.jingle.localVideo.getVideoTracks();
+ var mute = true;
+
+ for (var i = 0; i < tracks.length; ++i) {
+ if (tracks[i].enabled) {
+ mute = false;
+ break;
+ }
+ }
+ return mute;
+};
+
+/**
+ * Mutes/unmutes the (local) video i.e. enables/disables all video tracks.
+ *
+ * @param mute true to mute the (local) video i.e. to disable all video
+ * tracks; otherwise, false
+ * @param callback a function to be invoked with mute after all video
+ * tracks have been enabled/disabled. The function may, optionally, return
+ * another function which is to be invoked after the whole mute/unmute operation
+ * has completed successfully.
+ * @param options an object which specifies optional arguments such as the
+ * boolean key byUser with default value true which
+ * specifies whether the method was initiated in response to a user command (in
+ * contrast to an automatic decision made by the application logic)
+ */
+SessionBase.prototype.setVideoMute = function (mute, callback, options) {
+ var byUser;
+
+ if (options) {
+ byUser = options.byUser;
+ if (typeof byUser === 'undefined') {
+ byUser = true;
+ }
+ } else {
+ byUser = true;
+ }
+ // The user's command to mute the (local) video takes precedence over any
+ // automatic decision made by the application logic.
+ if (byUser) {
+ this.muteByUser = mute;
+ } else if (this.muteByUser) {
+ return;
+ }
+ if (mute == this.isVideoMute())
+ {
+ // Even if no change occurs, the specified callback is to be executed.
+ // The specified callback may, optionally, return a successCallback
+ // which is to be executed as well.
+ var successCallback = callback(mute);
+
+ if (successCallback) {
+ successCallback();
+ }
+ } else {
+ var tracks = connection.jingle.localVideo.getVideoTracks();
+
+ for (var i = 0; i < tracks.length; ++i) {
+ tracks[i].enabled = !mute;
+ }
+
+ if (this.peerconnection) {
+ this.peerconnection.hardMuteVideo(mute);
+ }
+
+ this.modifySources(callback(mute));
+ }
+};
+
// SDP-based mute by going recvonly/sendrecv
// FIXME: should probably black out the screen as well
SessionBase.prototype.toggleVideoMute = function (callback) {
-
- var ismuted = false;
- var localVideo = connection.jingle.localVideo;
- for (var idx = 0; idx < localVideo.getVideoTracks().length; idx++) {
- ismuted = !localVideo.getVideoTracks()[idx].enabled;
- }
- for (var idx = 0; idx < localVideo.getVideoTracks().length; idx++) {
- localVideo.getVideoTracks()[idx].enabled = !localVideo.getVideoTracks()[idx].enabled;
- }
-
- if(this.peerconnection)
- this.peerconnection.hardMuteVideo(!ismuted);
- this.modifySources(callback(!ismuted));
+ setVideoMute(isVideoMute(), callback);
};