|
|
@@ -11,6 +11,7 @@ import * as RemoteControlEvents
|
|
11
|
11
|
from './service/remotecontrol/RemoteControlEvents';
|
|
12
|
12
|
import UIEvents from './service/UI/UIEvents';
|
|
13
|
13
|
import UIUtil from './modules/UI/util/UIUtil';
|
|
|
14
|
+import { createTaskQueue } from './modules/util/helpers';
|
|
14
|
15
|
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
|
15
|
16
|
|
|
16
|
17
|
import {
|
|
|
@@ -274,6 +275,27 @@ function redirectToStaticPage(pathname) {
|
|
274
|
275
|
windowLocation.pathname = newPathname;
|
|
275
|
276
|
}
|
|
276
|
277
|
|
|
|
278
|
+/**
|
|
|
279
|
+ * A queue for the async replaceLocalTrack action so that multiple audio
|
|
|
280
|
+ * replacements cannot happen simultaneously. This solves the issue where
|
|
|
281
|
+ * replaceLocalTrack is called multiple times with an oldTrack of null, causing
|
|
|
282
|
+ * multiple local tracks of the same type to be used.
|
|
|
283
|
+ *
|
|
|
284
|
+ * @private
|
|
|
285
|
+ * @type {Object}
|
|
|
286
|
+ */
|
|
|
287
|
+const _replaceLocalAudioTrackQueue = createTaskQueue();
|
|
|
288
|
+
|
|
|
289
|
+/**
|
|
|
290
|
+ * A task queue for replacement local video tracks. This separate queue exists
|
|
|
291
|
+ * so video replacement is not blocked by audio replacement tasks in the queue
|
|
|
292
|
+ * {@link _replaceLocalAudioTrackQueue}.
|
|
|
293
|
+ *
|
|
|
294
|
+ * @private
|
|
|
295
|
+ * @type {Object}
|
|
|
296
|
+ */
|
|
|
297
|
+const _replaceLocalVideoTrackQueue = createTaskQueue();
|
|
|
298
|
+
|
|
277
|
299
|
/**
|
|
278
|
300
|
*
|
|
279
|
301
|
*/
|
|
|
@@ -856,9 +878,6 @@ export default {
|
|
856
|
878
|
return;
|
|
857
|
879
|
}
|
|
858
|
880
|
|
|
859
|
|
- // FIXME it is possible to queue this task twice, but it's not causing
|
|
860
|
|
- // any issues. Specifically this can happen when the previous
|
|
861
|
|
- // get user media call is blocked on "ask user for permissions" dialog.
|
|
862
|
881
|
if (!this.localVideo && !mute) {
|
|
863
|
882
|
const maybeShowErrorDialog = error => {
|
|
864
|
883
|
showUI && APP.UI.showCameraErrorNotification(error);
|
|
|
@@ -1261,16 +1280,23 @@ export default {
|
|
1261
|
1280
|
* @returns {Promise}
|
|
1262
|
1281
|
*/
|
|
1263
|
1282
|
useVideoStream(newStream) {
|
|
1264
|
|
- return APP.store.dispatch(
|
|
1265
|
|
- replaceLocalTrack(this.localVideo, newStream, room))
|
|
1266
|
|
- .then(() => {
|
|
1267
|
|
- this.localVideo = newStream;
|
|
1268
|
|
- this._setSharingScreen(newStream);
|
|
1269
|
|
- if (newStream) {
|
|
1270
|
|
- APP.UI.addLocalStream(newStream);
|
|
1271
|
|
- }
|
|
1272
|
|
- this.setVideoMuteStatus(this.isLocalVideoMuted());
|
|
|
1283
|
+ return new Promise((resolve, reject) => {
|
|
|
1284
|
+ _replaceLocalVideoTrackQueue.enqueue(onFinish => {
|
|
|
1285
|
+ APP.store.dispatch(
|
|
|
1286
|
+ replaceLocalTrack(this.localVideo, newStream, room))
|
|
|
1287
|
+ .then(() => {
|
|
|
1288
|
+ this.localVideo = newStream;
|
|
|
1289
|
+ this._setSharingScreen(newStream);
|
|
|
1290
|
+ if (newStream) {
|
|
|
1291
|
+ APP.UI.addLocalStream(newStream);
|
|
|
1292
|
+ }
|
|
|
1293
|
+ this.setVideoMuteStatus(this.isLocalVideoMuted());
|
|
|
1294
|
+ })
|
|
|
1295
|
+ .then(resolve)
|
|
|
1296
|
+ .catch(reject)
|
|
|
1297
|
+ .then(onFinish);
|
|
1273
|
1298
|
});
|
|
|
1299
|
+ });
|
|
1274
|
1300
|
},
|
|
1275
|
1301
|
|
|
1276
|
1302
|
/**
|
|
|
@@ -1300,15 +1326,22 @@ export default {
|
|
1300
|
1326
|
* @returns {Promise}
|
|
1301
|
1327
|
*/
|
|
1302
|
1328
|
useAudioStream(newStream) {
|
|
1303
|
|
- return APP.store.dispatch(
|
|
1304
|
|
- replaceLocalTrack(this.localAudio, newStream, room))
|
|
1305
|
|
- .then(() => {
|
|
1306
|
|
- this.localAudio = newStream;
|
|
1307
|
|
- if (newStream) {
|
|
1308
|
|
- APP.UI.addLocalStream(newStream);
|
|
1309
|
|
- }
|
|
1310
|
|
- this.setAudioMuteStatus(this.isLocalAudioMuted());
|
|
|
1329
|
+ return new Promise((resolve, reject) => {
|
|
|
1330
|
+ _replaceLocalAudioTrackQueue.enqueue(onFinish => {
|
|
|
1331
|
+ APP.store.dispatch(
|
|
|
1332
|
+ replaceLocalTrack(this.localAudio, newStream, room))
|
|
|
1333
|
+ .then(() => {
|
|
|
1334
|
+ this.localAudio = newStream;
|
|
|
1335
|
+ if (newStream) {
|
|
|
1336
|
+ APP.UI.addLocalStream(newStream);
|
|
|
1337
|
+ }
|
|
|
1338
|
+ this.setAudioMuteStatus(this.isLocalAudioMuted());
|
|
|
1339
|
+ })
|
|
|
1340
|
+ .then(resolve)
|
|
|
1341
|
+ .catch(reject)
|
|
|
1342
|
+ .then(onFinish);
|
|
1311
|
1343
|
});
|
|
|
1344
|
+ });
|
|
1312
|
1345
|
},
|
|
1313
|
1346
|
|
|
1314
|
1347
|
/**
|
|
|
@@ -2375,11 +2408,24 @@ export default {
|
|
2375
|
2408
|
createLocalTracksF,
|
|
2376
|
2409
|
newDevices.videoinput,
|
|
2377
|
2410
|
newDevices.audioinput)
|
|
2378
|
|
- .then(tracks =>
|
|
2379
|
|
- Promise.all(this._setLocalAudioVideoStreams(tracks)))
|
|
|
2411
|
+ .then(tracks => {
|
|
|
2412
|
+ // If audio or video muted before, or we unplugged current
|
|
|
2413
|
+ // device and selected new one, then mute new track.
|
|
|
2414
|
+ const muteSyncPromises = tracks.map(track => {
|
|
|
2415
|
+ if ((track.isVideoTrack() && videoWasMuted)
|
|
|
2416
|
+ || (track.isAudioTrack() && audioWasMuted)) {
|
|
|
2417
|
+ return track.mute();
|
|
|
2418
|
+ }
|
|
|
2419
|
+
|
|
|
2420
|
+ return Promise.resolve();
|
|
|
2421
|
+ });
|
|
|
2422
|
+
|
|
|
2423
|
+ return Promise.all(muteSyncPromises)
|
|
|
2424
|
+ .then(() => Promise.all(
|
|
|
2425
|
+ this._setLocalAudioVideoStreams(tracks)));
|
|
|
2426
|
+ })
|
|
2380
|
2427
|
.then(() => {
|
|
2381
|
|
- // If audio was muted before, or we unplugged current device
|
|
2382
|
|
- // and selected new one, then mute new audio track.
|
|
|
2428
|
+ // Log and sync known mute state.
|
|
2383
|
2429
|
if (audioWasMuted) {
|
|
2384
|
2430
|
sendAnalytics(createTrackMutedEvent(
|
|
2385
|
2431
|
'audio',
|
|
|
@@ -2388,8 +2434,6 @@ export default {
|
|
2388
|
2434
|
muteLocalAudio(true);
|
|
2389
|
2435
|
}
|
|
2390
|
2436
|
|
|
2391
|
|
- // If video was muted before, or we unplugged current device
|
|
2392
|
|
- // and selected new one, then mute new video track.
|
|
2393
|
2437
|
if (!this.isSharingScreen && videoWasMuted) {
|
|
2394
|
2438
|
sendAnalytics(createTrackMutedEvent(
|
|
2395
|
2439
|
'video',
|