Quellcode durchsuchen

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

master
Lyubomir Marinov vor 10 Jahren
Ursprung
Commit
ae4dafb06d
3 geänderte Dateien mit 177 neuen und 53 gelöschten Zeilen
  1. 51
    17
      app.js
  2. 40
    22
      data_channels.js
  3. 86
    14
      libs/strophe/strophe.jingle.sessionbase.js

+ 51
- 17
app.js Datei anzeigen

@@ -1035,26 +1035,60 @@ function getConferenceHandler() {
1035 1035
     return activecall;
1036 1036
 }
1037 1037
 
1038
+/**
1039
+ * Mutes/unmutes the local video.
1040
+ *
1041
+ * @param mute <tt>true</tt> to mute the local video; otherwise, <tt>false</tt>
1042
+ * @param options an object which specifies optional arguments such as the
1043
+ * <tt>boolean</tt> key <tt>byUser</tt> with default value <tt>true</tt> which
1044
+ * specifies whether the method was initiated in response to a user command (in
1045
+ * contrast to an automatic decision taken by the application logic)
1046
+ */
1047
+function setVideoMute(mute, options) {
1048
+    if (connection && connection.jingle.localVideo) {
1049
+        var session = getConferenceHandler();
1050
+
1051
+        if (session) {
1052
+            session.setVideoMute(
1053
+                mute,
1054
+                function (mute) {
1055
+                    var video = $('#video');
1056
+                    var communicativeClass = "icon-camera";
1057
+                    var muteClass = "icon-camera icon-camera-disabled";
1058
+
1059
+                    if (mute) {
1060
+                        video.removeClass(communicativeClass);
1061
+                        video.addClass(muteClass);
1062
+                    } else {
1063
+                        video.removeClass(muteClass);
1064
+                        video.addClass(communicativeClass);
1065
+                    }
1066
+                    connection.emuc.addVideoInfoToPresence(mute);
1067
+                    connection.emuc.sendPresence();
1068
+                },
1069
+                options);
1070
+        }
1071
+    }
1072
+}
1073
+
1074
+$(document).on('inlastnchanged', function (event, oldValue, newValue) {
1075
+    if (config.muteLocalVideoIfNotInLastN) {
1076
+        setVideoMute(!newValue, { 'byUser': false });
1077
+    }
1078
+});
1079
+
1080
+/**
1081
+ * Mutes/unmutes the local video.
1082
+ */
1038 1083
 function toggleVideo() {
1039 1084
     buttonClick("#video", "icon-camera icon-camera-disabled");
1040
-    if (!(connection && connection.jingle.localVideo))
1041
-        return;
1042 1085
 
1043
-    var sess = getConferenceHandler();
1044
-    if (sess) {
1045
-        sess.toggleVideoMute(
1046
-            function (isMuted) {
1047
-                if (isMuted) {
1048
-                    $('#video').removeClass("icon-camera");
1049
-                    $('#video').addClass("icon-camera icon-camera-disabled");
1050
-                } else {
1051
-                    $('#video').removeClass("icon-camera icon-camera-disabled");
1052
-                    $('#video').addClass("icon-camera");
1053
-                }
1054
-                connection.emuc.addVideoInfoToPresence(isMuted);
1055
-                connection.emuc.sendPresence();
1056
-            }
1057
-        );
1086
+    if (connection && connection.jingle.localVideo) {
1087
+        var session = getConferenceHandler();
1088
+
1089
+        if (session) {
1090
+            setVideoMute(!session.isVideoMute());
1091
+        }
1058 1092
     }
1059 1093
 }
1060 1094
 

+ 40
- 22
data_channels.js Datei anzeigen

@@ -70,46 +70,65 @@ function onDataChannel(event)
70 70
                     'dominantspeakerchanged',
71 71
                     [dominantSpeakerEndpoint]);
72 72
             }
73
+            else if ("InLastNChangeEvent" === colibriClass)
74
+            {
75
+                var oldValue = obj.oldValue;
76
+                var newValue = obj.newValue;
77
+                // Make sure that oldValue and newValue are of type boolean.
78
+                var type;
79
+
80
+                if ((type = typeof oldValue) !== 'boolean') {
81
+                    if (type === 'string') {
82
+                        oldValue = (oldValue == "true");
83
+                    } else {
84
+                        oldValue = new Boolean(oldValue).valueOf();
85
+                    }
86
+                }
87
+                if ((type = typeof newValue) !== 'boolean') {
88
+                    if (type === 'string') {
89
+                        newValue = (newValue == "true");
90
+                    } else {
91
+                        newValue = new Boolean(newValue).valueOf();
92
+                    }
93
+                }
94
+                $(document).trigger('inlastnchanged', [oldValue, newValue]);
95
+            }
73 96
             else if ("LastNEndpointsChangeEvent" === colibriClass)
74 97
             {
75 98
                 // The new/latest list of last-n endpoint IDs.
76 99
                 var lastNEndpoints = obj.lastNEndpoints;
77
-                /*
78
-                 * The list of endpoint IDs which are entering the list of
79
-                 * last-n at this time i.e. were not in the old list of last-n
80
-                 * endpoint IDs.
81
-                 */
100
+                // The list of endpoint IDs which are entering the list of
101
+                // last-n at this time i.e. were not in the old list of last-n
102
+                // endpoint IDs.
82 103
                 var endpointsEnteringLastN = obj.endpointsEnteringLastN;
83
-
84 104
                 var stream = obj.stream;
85 105
 
86 106
                 console.log(
87 107
                     "Data channel new last-n event: ",
88 108
                     lastNEndpoints, endpointsEnteringLastN, obj);
89
-
90 109
                 $(document).trigger(
91
-                        'lastnchanged',
92
-                        [lastNEndpoints, endpointsEnteringLastN, stream]);
110
+                    'lastnchanged',
111
+                    [lastNEndpoints, endpointsEnteringLastN, stream]);
93 112
             }
94 113
             else if ("SimulcastLayersChangedEvent" === colibriClass)
95 114
             {
96
-                var endpointSimulcastLayers = obj.endpointSimulcastLayers;
97
-                $(document).trigger('simulcastlayerschanged', [endpointSimulcastLayers]);
115
+                $(document).trigger(
116
+                    'simulcastlayerschanged',
117
+                    [obj.endpointSimulcastLayers]);
98 118
             }
99 119
             else if ("SimulcastLayersChangingEvent" === colibriClass)
100 120
             {
101
-                var endpointSimulcastLayers = obj.endpointSimulcastLayers;
102
-                $(document).trigger('simulcastlayerschanging', [endpointSimulcastLayers]);
121
+                $(document).trigger(
122
+                    'simulcastlayerschanging',
123
+                    [obj.endpointSimulcastLayers]);
103 124
             }
104 125
             else if ("StartSimulcastLayerEvent" === colibriClass)
105 126
             {
106
-                var simulcastLayer = obj.simulcastLayer;
107
-                $(document).trigger('startsimulcastlayer', simulcastLayer);
127
+                $(document).trigger('startsimulcastlayer', obj.simulcastLayer);
108 128
             }
109 129
             else if ("StopSimulcastLayerEvent" === colibriClass)
110 130
             {
111
-                var simulcastLayer = obj.simulcastLayer;
112
-                $(document).trigger('stopsimulcastlayer', simulcastLayer);
131
+                $(document).trigger('stopsimulcastlayer', obj.simulcastLayer);
113 132
             }
114 133
             else
115 134
             {
@@ -141,10 +160,8 @@ function bindDataChannelListener(peerConnection)
141 160
     // and peer as single channel can be used for sending and receiving data.
142 161
     // So either channel opened by the bridge or the one opened here is enough
143 162
     // for communication with the bridge.
144
-    /*var dataChannelOptions =
145
-    {
146
-        reliable: true
147
-    };
163
+/*
164
+    var dataChannelOptions = { reliable: true };
148 165
     var dataChannel
149 166
        = peerConnection.createDataChannel("myChannel", dataChannelOptions);
150 167
 
@@ -157,6 +174,7 @@ function bindDataChannelListener(peerConnection)
157 174
     {
158 175
         var msgData = event.data;
159 176
         console.info("Got My Data Channel Message:", msgData, dataChannel);
160
-    };*/
177
+    };
178
+*/
161 179
 }
162 180
 

+ 86
- 14
libs/strophe/strophe.jingle.sessionbase.js Datei anzeigen

@@ -4,10 +4,16 @@
4 4
  * @param sid my session identifier(resource)
5 5
  * @constructor
6 6
  */
7
-function SessionBase(connection, sid){
8
-
7
+function SessionBase(connection, sid) {
9 8
     this.connection = connection;
10 9
     this.sid = sid;
10
+
11
+    /**
12
+     * The indicator which determines whether the (local) video has been muted
13
+     * in response to a user command in contrast to an automatic decision made
14
+     * by the application logic.
15
+     */
16
+    this.muteByUser = true;
11 17
 }
12 18
 
13 19
 
@@ -63,6 +69,7 @@ SessionBase.prototype.removeSource = function (elem, fromJid) {
63 69
 
64 70
     this.modifySources();
65 71
 };
72
+
66 73
 /**
67 74
  * Switches video streams.
68 75
  * @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,
230 237
     }
231 238
 };
232 239
 
233
-// SDP-based mute by going recvonly/sendrecv
234
-// FIXME: should probably black out the screen as well
235
-SessionBase.prototype.toggleVideoMute = function (callback) {
240
+/**
241
+ * Determines whether the (local) video is mute i.e. all video tracks are
242
+ * disabled.
243
+ *
244
+ * @return <tt>true</tt> if the (local) video is mute i.e. all video tracks are
245
+ * disabled; otherwise, <tt>false</tt>
246
+ */
247
+SessionBase.prototype.isVideoMute = function () {
248
+    var tracks = connection.jingle.localVideo.getVideoTracks();
249
+    var mute = true;
250
+
251
+    for (var i = 0; i < tracks.length; ++i) {
252
+        if (tracks[i].enabled) {
253
+            mute = false;
254
+            break;
255
+        }
256
+    }
257
+    return mute;
258
+};
236 259
 
237
-    var ismuted = false;
238
-    var localVideo = connection.jingle.localVideo;
239
-    for (var idx = 0; idx < localVideo.getVideoTracks().length; idx++) {
240
-        ismuted = !localVideo.getVideoTracks()[idx].enabled;
260
+/**
261
+ * Mutes/unmutes the (local) video i.e. enables/disables all video tracks.
262
+ *
263
+ * @param mute <tt>true</tt> to mute the (local) video i.e. to disable all video
264
+ * tracks; otherwise, <tt>false</tt>
265
+ * @param callback a function to be invoked with <tt>mute</tt> after all video
266
+ * tracks have been enabled/disabled. The function may, optionally, return
267
+ * another function which is to be invoked after the whole mute/unmute operation
268
+ * has completed successfully.
269
+ * @param options an object which specifies optional arguments such as the
270
+ * <tt>boolean</tt> key <tt>byUser</tt> with default value <tt>true</tt> which
271
+ * specifies whether the method was initiated in response to a user command (in
272
+ * contrast to an automatic decision made by the application logic)
273
+ */
274
+SessionBase.prototype.setVideoMute = function (mute, callback, options) {
275
+    var byUser;
276
+
277
+    if (options) {
278
+        byUser = options.byUser;
279
+        if (typeof byUser === 'undefined') {
280
+            byUser = true;
281
+        } 
282
+    } else {
283
+        byUser = true;
284
+    }
285
+    // The user's command to mute the (local) video takes precedence over any
286
+    // automatic decision made by the application logic.
287
+    if (byUser) {
288
+        this.muteByUser = mute;
289
+    } else if (this.muteByUser) {
290
+        return;
241 291
     }
242
-    for (var idx = 0; idx < localVideo.getVideoTracks().length; idx++) {
243
-        localVideo.getVideoTracks()[idx].enabled = !localVideo.getVideoTracks()[idx].enabled;
292
+    if (mute == this.isVideoMute())
293
+    {
294
+        // Even if no change occurs, the specified callback is to be executed.
295
+        // The specified callback may, optionally, return a successCallback
296
+        // which is to be executed as well.
297
+        var successCallback = callback(mute);
298
+
299
+        if (successCallback) {
300
+            successCallback();
301
+        }
302
+    } else {
303
+        var tracks = connection.jingle.localVideo.getVideoTracks();
304
+
305
+        for (var i = 0; i < tracks.length; ++i) {
306
+            tracks[i].enabled = !mute;
307
+        }
308
+
309
+        if (this.peerconnection) {
310
+            this.peerconnection.hardMuteVideo(mute);
311
+        }
312
+
313
+        this.modifySources(callback(mute));
244 314
     }
315
+};
245 316
 
246
-    if(this.peerconnection)
247
-        this.peerconnection.hardMuteVideo(!ismuted);
248
-    this.modifySources(callback(!ismuted));
317
+// SDP-based mute by going recvonly/sendrecv
318
+// FIXME: should probably black out the screen as well
319
+SessionBase.prototype.toggleVideoMute = function (callback) {
320
+    setVideoMute(isVideoMute(), callback);
249 321
 };

Laden…
Abbrechen
Speichern