Optionally automatically mutes the local video if it is not in any "last N".

This commit is contained in:
Lyubomir Marinov 2014-12-11 21:50:08 +02:00
parent 4c95921b06
commit ae4dafb06d
3 changed files with 178 additions and 54 deletions

68
app.js
View File

@ -1035,26 +1035,60 @@ function getConferenceHandler() {
return activecall;
}
/**
* Mutes/unmutes the local video.
*
* @param mute <tt>true</tt> to mute the local video; otherwise, <tt>false</tt>
* @param options an object which specifies optional arguments such as the
* <tt>boolean</tt> key <tt>byUser</tt> with default value <tt>true</tt> 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());
}
}
}

View File

@ -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);
};*/
};
*/
}

View File

@ -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 <tt>true</tt> if the (local) video is mute i.e. all video tracks are
* disabled; otherwise, <tt>false</tt>
*/
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 <tt>true</tt> to mute the (local) video i.e. to disable all video
* tracks; otherwise, <tt>false</tt>
* @param callback a function to be invoked with <tt>mute</tt> 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
* <tt>boolean</tt> key <tt>byUser</tt> with default value <tt>true</tt> 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);
};