Bladeren bron

Refactored conference.js code. Moved almost all code that relates to handling change of media devices to separate module. Fixed couple of bugs.

master
tsareg 9 jaren geleden
bovenliggende
commit
897a6bfbe6
2 gewijzigde bestanden met toevoegingen van 366 en 281 verwijderingen
  1. 133
    281
      conference.js
  2. 233
    0
      modules/devices/mediaDeviceHelper.js

+ 133
- 281
conference.js Bestand weergeven

@@ -12,6 +12,8 @@ import Recorder from './modules/recorder/Recorder';
12 12
 import CQEvents from './service/connectionquality/CQEvents';
13 13
 import UIEvents from './service/UI/UIEvents';
14 14
 
15
+import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
16
+
15 17
 const ConnectionEvents = JitsiMeetJS.events.connection;
16 18
 const ConnectionErrors = JitsiMeetJS.errors.connection;
17 19
 
@@ -22,7 +24,6 @@ const TrackEvents = JitsiMeetJS.events.track;
22 24
 const TrackErrors = JitsiMeetJS.errors.track;
23 25
 
24 26
 let room, connection, localAudio, localVideo, roomLocker;
25
-let currentAudioInputDevices, currentVideoInputDevices;
26 27
 
27 28
 import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";
28 29
 
@@ -211,17 +212,6 @@ function createLocalTracks (devices, cameraDeviceId, micDeviceId) {
211 212
     });
212 213
 }
213 214
 
214
-/**
215
- * Stores lists of current 'audioinput' and 'videoinput' devices
216
- * @param {MediaDeviceInfo[]} devices
217
- */
218
-function setCurrentMediaDevices(devices) {
219
-    currentAudioInputDevices = devices.filter(
220
-        d => d.kind === 'audioinput');
221
-    currentVideoInputDevices = devices.filter(
222
-        d => d.kind === 'videoinput');
223
-}
224
-
225 215
 /**
226 216
  * Changes the email for the local user
227 217
  * @param email {string} the new email
@@ -489,44 +479,8 @@ export default {
489 479
                 APP.UI.disableCameraButton();
490 480
             }
491 481
 
492
-            // update list of available devices
493
-            if (JitsiMeetJS.mediaDevices.isDeviceListAvailable() &&
494
-                JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
495
-                JitsiMeetJS.mediaDevices.enumerateDevices(function(devices) {
496
-                    // Ugly way to synchronize real device IDs with local
497
-                    // storage and settings menu. This is a workaround until
498
-                    // getConstraints() method will be implemented in browsers.
499
-                    if (localAudio) {
500
-                        localAudio._setRealDeviceIdFromDeviceList(devices);
501
-                        APP.settings.setMicDeviceId(localAudio.getDeviceId());
502
-                    }
482
+            this._initDeviceList();
503 483
 
504
-                    if (localVideo) {
505
-                        localVideo._setRealDeviceIdFromDeviceList(devices);
506
-                        APP.settings.setCameraDeviceId(
507
-                            localVideo.getDeviceId());
508
-                    }
509
-
510
-                    setCurrentMediaDevices(devices);
511
-
512
-                    APP.UI.onAvailableDevicesChanged(devices);
513
-                });
514
-
515
-                JitsiMeetJS.mediaDevices.addEventListener(
516
-                    JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED,
517
-                    (devices) => {
518
-                        // Just defer callback until other event callbacks are
519
-                        // processed.
520
-                        window.setTimeout(() => {
521
-                            checkLocalDevicesAfterDeviceListChanged(devices)
522
-                                .then(() => {
523
-                                    setCurrentMediaDevices(devices);
524
-
525
-                                    APP.UI.onAvailableDevicesChanged(devices);
526
-                                });
527
-                        }, 0);
528
-                    });
529
-            }
530 484
             if (config.iAmRecorder)
531 485
                 this.recorder = new Recorder();
532 486
 
@@ -535,220 +489,6 @@ export default {
535 489
             return new Promise((resolve, reject) => {
536 490
                 (new ConferenceConnector(resolve, reject)).connect();
537 491
             });
538
-
539
-            function checkAudioOutputDeviceAfterDeviceListChanged(newDevices) {
540
-                if (!JitsiMeetJS.mediaDevices
541
-                        .isDeviceChangeAvailable('output')) {
542
-                    return;
543
-                }
544
-
545
-                var selectedAudioOutputDeviceId =
546
-                        APP.settings.getAudioOutputDeviceId(),
547
-                    availableAudioOutputDevices = newDevices.filter(d => {
548
-                        return d.kind === 'audiooutput';
549
-                    });
550
-
551
-                if (selectedAudioOutputDeviceId !== 'default' &&
552
-                    !availableAudioOutputDevices.find(d =>
553
-                    d.deviceId === selectedAudioOutputDeviceId)) {
554
-                    APP.settings.setAudioOutputDeviceId('default');
555
-                }
556
-            }
557
-
558
-            function checkLocalDevicesAfterDeviceListChanged(newDevices) {
559
-                // Event handler can be fire before direct enumerateDevices()
560
-                // call, so handle this situation here.
561
-                if (!currentAudioInputDevices && !currentVideoInputDevices) {
562
-                    setCurrentMediaDevices(newDevices);
563
-                }
564
-
565
-                checkAudioOutputDeviceAfterDeviceListChanged(newDevices);
566
-
567
-                let availableAudioInputDevices = newDevices.filter(
568
-                        d => d.kind === 'audioinput'),
569
-                    availableVideoInputDevices = newDevices.filter(
570
-                        d => d.kind === 'videoinput'),
571
-                    selectedAudioInputDeviceId = APP.settings.getMicDeviceId(),
572
-                    selectedVideoInputDeviceId =
573
-                        APP.settings.getCameraDeviceId(),
574
-                    selectedAudioInputDevice = availableAudioInputDevices.find(
575
-                        d => d.deviceId === selectedAudioInputDeviceId),
576
-                    selectedVideoInputDevice = availableVideoInputDevices.find(
577
-                        d => d.deviceId === selectedVideoInputDeviceId),
578
-                    tracksToCreate = [],
579
-                    micIdToUse = null,
580
-                    cameraIdToUse = null;
581
-
582
-                // Here we handle case when no device was initially plugged, but
583
-                // then it's connected OR new device was connected when previous
584
-                // track has ended.
585
-                if (!localAudio || localAudio.disposed || localAudio.isEnded()){
586
-                    if (availableAudioInputDevices.length
587
-                        && availableAudioInputDevices[0].label !== '') {
588
-                        tracksToCreate.push('audio');
589
-                        micIdToUse = availableAudioInputDevices[0].deviceId;
590
-                    } else {
591
-                        APP.UI.disableMicrophoneButton();
592
-                    }
593
-                }
594
-
595
-                if ((!localVideo || localVideo.disposed || localVideo.isEnded())
596
-                    && !self.isSharingScreen){
597
-                    if (availableVideoInputDevices.length
598
-                        && availableVideoInputDevices[0].label !== '') {
599
-                        tracksToCreate.push('video');
600
-                        cameraIdToUse = availableVideoInputDevices[0].deviceId;
601
-                    } else {
602
-                        APP.UI.disableCameraButton();
603
-                    }
604
-                }
605
-
606
-                if (localAudio && !localAudio.disposed && !localAudio.isEnded()
607
-                    && selectedAudioInputDevice
608
-                    && selectedAudioInputDeviceId !== localAudio.getDeviceId()
609
-                    && tracksToCreate.indexOf('audio') === -1) {
610
-                    tracksToCreate.push('audio');
611
-                    micIdToUse = selectedAudioInputDeviceId;
612
-                }
613
-
614
-                if (localVideo && !localVideo.disposed && !localVideo.isEnded()
615
-                    && selectedVideoInputDevice
616
-                    && selectedVideoInputDeviceId !== localVideo.getDeviceId()
617
-                    && tracksToCreate.indexOf('video') === -1
618
-                    && !self.isSharingScreen) {
619
-                    tracksToCreate.push('video');
620
-                    cameraIdToUse = selectedVideoInputDeviceId;
621
-                }
622
-
623
-                if (tracksToCreate.length) {
624
-                    return createNewTracks(
625
-                        tracksToCreate, cameraIdToUse, micIdToUse);
626
-                } else {
627
-                    return Promise.resolve();
628
-                }
629
-
630
-                function createNewTracks(type, cameraDeviceId, micDeviceId) {
631
-                    let audioTrackCreationError;
632
-                    let videoTrackCreationError;
633
-                    let audioRequested = type.indexOf('audio') !== -1;
634
-                    let videoRequested = type.indexOf('video') !== -1;
635
-                    let promise;
636
-
637
-                    if (audioRequested && micDeviceId !== null) {
638
-                        if (videoRequested && cameraDeviceId !== null) {
639
-                            promise = createLocalTracks(
640
-                                type, cameraDeviceId, micDeviceId)
641
-                                .catch(() => {
642
-                                    return Promise.all([
643
-                                        createAudioTrack(false),
644
-                                        createVideoTrack(false)]);
645
-                                })
646
-                                .then((audioTracks, videoTracks) => {
647
-                                    if (audioTrackCreationError) {
648
-                                        if (videoTrackCreationError) {
649
-                                            APP.UI.showDeviceErrorDialog(
650
-                                                audioTrackCreationError,
651
-                                                videoTrackCreationError);
652
-                                        } else {
653
-                                            APP.UI.showDeviceErrorDialog(
654
-                                                audioTrackCreationError,
655
-                                                null);
656
-                                        }
657
-                                    } else if (videoTrackCreationError) {
658
-                                        APP.UI.showDeviceErrorDialog(
659
-                                            null,
660
-                                            videoTrackCreationError);
661
-                                    }
662
-
663
-                                    return (audioTracks || [])
664
-                                        .concat(videoTracks || []);
665
-                                });
666
-                        } else {
667
-                            promise = createAudioTrack();
668
-                        }
669
-                    } else if (videoRequested && cameraDeviceId !== null) {
670
-                        promise = createVideoTrack();
671
-                    } else {
672
-                        promise = Promise.resolve([]);
673
-                    }
674
-
675
-                    return promise
676
-                        .then(onTracksCreated);
677
-
678
-                    function createAudioTrack(showError) {
679
-                        return createLocalTracks(['audio'], null, micDeviceId)
680
-                            .catch(err => {
681
-                                audioTrackCreationError = err;
682
-
683
-                                if (showError) {
684
-                                    APP.UI.showDeviceErrorDialog(err, null);
685
-                                }
686
-
687
-                                return [];
688
-                            });
689
-                    }
690
-
691
-                    function createVideoTrack(showError) {
692
-                        return createLocalTracks(
693
-                                ['video'], cameraDeviceId, null)
694
-                            .catch(err => {
695
-                                videoTrackCreationError = err;
696
-
697
-                                if (showError) {
698
-                                    APP.UI.showDeviceErrorDialog(null, err);
699
-                                }
700
-
701
-                                return [];
702
-                            });
703
-                    }
704
-                }
705
-
706
-                function onTracksCreated(tracks) {
707
-                    return Promise.all((tracks || []).map(track => {
708
-                        if (track.isAudioTrack()) {
709
-                            let audioWasMuted = self.audioMuted;
710
-
711
-                            return self.useAudioStream(track).then(() => {
712
-                                console.log('switched local audio');
713
-
714
-                                // If we plugged-in new device (and switched to
715
-                                // it), but video was muted before, or we
716
-                                // unplugged current device and selected new
717
-                                // one, then mute new video track.
718
-                                if (audioWasMuted ||
719
-                                    currentAudioInputDevices.length >
720
-                                    availableAudioInputDevices.length) {
721
-                                    muteLocalAudio(true);
722
-                                }
723
-                            });
724
-                        } else if (track.isVideoTrack()) {
725
-                            let videoWasMuted = self.videoMuted;
726
-
727
-                            return self.useVideoStream(track).then(() => {
728
-                                console.log('switched local video');
729
-
730
-                                // TODO: maybe make video large if we
731
-                                // are not in conference yet
732
-
733
-                                // If we plugged-in new device (and switched to
734
-                                // it), but video was muted before, or we
735
-                                // unplugged current device and selected new
736
-                                // one, then mute new video track.
737
-                                if (videoWasMuted ||
738
-                                    (currentVideoInputDevices.length >
739
-                                    availableVideoInputDevices.length)) {
740
-                                    muteLocalVideo(true);
741
-                                }
742
-                            });
743
-                        } else {
744
-                            console.error("Ignored not an audio nor a "
745
-                                + "video track: ", track);
746
-
747
-                            return Promise.resolve();
748
-                        }
749
-                    }));
750
-                }
751
-            }
752 492
         });
753 493
     },
754 494
     /**
@@ -956,16 +696,7 @@ export default {
956 696
         room = connection.initJitsiConference(APP.conference.roomName,
957 697
             this._getConferenceOptions());
958 698
         this.localId = room.myUserId();
959
-        localTracks.forEach((track) => {
960
-            if (track.isAudioTrack()) {
961
-                this.useAudioStream(track);
962
-            } else if (track.isVideoTrack()) {
963
-                this.useVideoStream(track);
964
-            } else {
965
-                console.error(
966
-                    "Ignored not an audio nor a video track: ", track);
967
-            }
968
-        });
699
+        this._setLocalAudioVideoStreams(localTracks);
969 700
         roomLocker = createRoomLocker(room);
970 701
         this._room = room; // FIXME do not use this
971 702
 
@@ -986,6 +717,26 @@ export default {
986 717
         this._setupListeners();
987 718
     },
988 719
 
720
+    /**
721
+     * Sets local video and audio streams.
722
+     * @param {JitsiLocalTrack[]} tracks=[]
723
+     * @returns {Promise[]}
724
+     * @private
725
+     */
726
+    _setLocalAudioVideoStreams(tracks = []) {
727
+        return tracks.map(track => {
728
+            if (track.isAudioTrack()) {
729
+                return this.useAudioStream(track);
730
+            } else if (track.isVideoTrack()) {
731
+                return this.useVideoStream(track);
732
+            } else {
733
+                console.error(
734
+                    "Ignored not an audio nor a video track: ", track);
735
+                return Promise.resolve();
736
+            }
737
+        });
738
+    },
739
+
989 740
     _getConferenceOptions() {
990 741
         let options = config;
991 742
         if(config.enableRecording && !config.recordingType) {
@@ -1480,7 +1231,7 @@ export default {
1480 1231
         APP.UI.addListener(
1481 1232
             UIEvents.VIDEO_DEVICE_CHANGED,
1482 1233
             (cameraDeviceId) => {
1483
-                createLocalTracks(['video'])
1234
+                createLocalTracks(['video'], cameraDeviceId, null)
1484 1235
                     .then(([stream]) => {
1485 1236
                         this.useVideoStream(stream);
1486 1237
                         console.log('switched local video device');
@@ -1496,7 +1247,7 @@ export default {
1496 1247
         APP.UI.addListener(
1497 1248
             UIEvents.AUDIO_DEVICE_CHANGED,
1498 1249
             (micDeviceId) => {
1499
-                createLocalTracks(['audio'])
1250
+                createLocalTracks(['audio'], null, micDeviceId)
1500 1251
                     .then(([stream]) => {
1501 1252
                         this.useAudioStream(stream);
1502 1253
                         console.log('switched local audio device');
@@ -1574,11 +1325,112 @@ export default {
1574 1325
             });
1575 1326
     },
1576 1327
     /**
1577
-     * Adss any room listener.
1578
-     * @param eventName one of the ConferenceEvents
1579
-     * @param callBack the function to be called when the event occurs
1580
-     */
1581
-     addConferenceListener(eventName, callBack) {
1328
+    * Adds any room listener.
1329
+    * @param eventName one of the ConferenceEvents
1330
+    * @param callBack the function to be called when the event occurs
1331
+    */
1332
+    addConferenceListener(eventName, callBack) {
1582 1333
         room.on(eventName, callBack);
1334
+    },
1335
+    /**
1336
+     * Inits list of current devices and event listener for device change.
1337
+     * @private
1338
+     */
1339
+    _initDeviceList() {
1340
+        if (JitsiMeetJS.mediaDevices.isDeviceListAvailable() &&
1341
+            JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
1342
+            JitsiMeetJS.mediaDevices.enumerateDevices(devices => {
1343
+                // Ugly way to synchronize real device IDs with local
1344
+                // storage and settings menu. This is a workaround until
1345
+                // getConstraints() method will be implemented in browsers.
1346
+                if (localAudio) {
1347
+                    localAudio._setRealDeviceIdFromDeviceList(devices);
1348
+                    APP.settings.setMicDeviceId(localAudio.getDeviceId());
1349
+                }
1350
+
1351
+                if (localVideo) {
1352
+                    localVideo._setRealDeviceIdFromDeviceList(devices);
1353
+                    APP.settings.setCameraDeviceId(localVideo.getDeviceId());
1354
+                }
1355
+
1356
+                mediaDeviceHelper.setCurrentMediaDevices(devices);
1357
+
1358
+                APP.UI.onAvailableDevicesChanged(devices);
1359
+            });
1360
+
1361
+            JitsiMeetJS.mediaDevices.addEventListener(
1362
+                JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED,
1363
+                (devices) =>
1364
+                    window.setTimeout(
1365
+                        () => this._onDeviceListChanged(devices), 0));
1366
+        }
1367
+    },
1368
+    /**
1369
+     * Event listener for JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED to
1370
+     * handle change of available media devices.
1371
+     * @private
1372
+     * @param {MediaDeviceInfo[]} devices
1373
+     * @returns {Promise}
1374
+     */
1375
+    _onDeviceListChanged(devices) {
1376
+        let currentDevices = mediaDeviceHelper.getCurrentMediaDevices();
1377
+
1378
+        // Event handler can be fired before direct
1379
+        // enumerateDevices() call, so handle this situation here.
1380
+        if (!currentDevices.audioinput &&
1381
+            !currentDevices.videoinput &&
1382
+            !currentDevices.audiooutput) {
1383
+            mediaDeviceHelper.setCurrentMediaDevices(devices);
1384
+            currentDevices = mediaDeviceHelper.getCurrentMediaDevices();
1385
+        }
1386
+
1387
+        let newDevices =
1388
+            mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
1389
+                devices, this.isSharingScreen, localVideo, localAudio);
1390
+        let promises = [];
1391
+        let audioWasMuted = this.audioMuted;
1392
+        let videoWasMuted = this.videoMuted;
1393
+        let availableAudioInputDevices =
1394
+            mediaDeviceHelper.getDevicesFromListByKind(devices, 'audioinput');
1395
+        let availableVideoInputDevices =
1396
+            mediaDeviceHelper.getDevicesFromListByKind(devices, 'videoinput');
1397
+
1398
+        if (typeof newDevices.audiooutput !== 'undefined') {
1399
+            // Just ignore any errors in catch block.
1400
+            promises.push(APP.settings
1401
+                .setAudioOutputDeviceId(newDevices.audiooutput)
1402
+                .catch());
1403
+        }
1404
+
1405
+        promises.push(
1406
+            mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
1407
+                    createLocalTracks,
1408
+                    newDevices.videoinput,
1409
+                    newDevices.audioinput)
1410
+                .then(tracks =>
1411
+                    Promise.all(this._setLocalAudioVideoStreams(tracks)))
1412
+                .then(() => {
1413
+                    // If audio was muted before, or we unplugged current device
1414
+                    // and selected new one, then mute new audio track.
1415
+                    if (audioWasMuted ||
1416
+                        currentDevices.audioinput.length >
1417
+                        availableAudioInputDevices.length) {
1418
+                        muteLocalAudio(true);
1419
+                    }
1420
+
1421
+                    // If video was muted before, or we unplugged current device
1422
+                    // and selected new one, then mute new video track.
1423
+                    if (videoWasMuted ||
1424
+                        currentDevices.videoinput.length >
1425
+                        availableVideoInputDevices.length) {
1426
+                        muteLocalVideo(true);
1427
+                    }
1428
+                }));
1429
+
1430
+        return Promise.all(promises)
1431
+            .then(() => {
1432
+                mediaDeviceHelper.setCurrentMediaDevices(devices);
1433
+                APP.UI.onAvailableDevicesChanged(devices);
1434
+            });
1583 1435
     }
1584
-};
1436
+};

+ 233
- 0
modules/devices/mediaDeviceHelper.js Bestand weergeven

@@ -0,0 +1,233 @@
1
+/* global $, APP, JitsiMeetJS, config, interfaceConfig */
2
+
3
+let currentAudioInputDevices,
4
+    currentVideoInputDevices,
5
+    currentAudioOutputDevices;
6
+
7
+/**
8
+ * Determines if currently selected audio output device should be changed after
9
+ * list of available devices has been changed.
10
+ * @param {MediaDeviceInfo[]} newDevices
11
+ * @returns {string|undefined} - ID of new audio output device to use, undefined
12
+ *      if audio output device should not be changed.
13
+ */
14
+function getNewAudioOutputDevice(newDevices) {
15
+    if (!JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output')) {
16
+        return;
17
+    }
18
+
19
+    let selectedAudioOutputDeviceId = APP.settings.getAudioOutputDeviceId();
20
+    let availableAudioOutputDevices = newDevices.filter(
21
+        d => d.kind === 'audiooutput');
22
+
23
+    // Switch to 'default' audio output device if we don't have the selected one
24
+    // available anymore.
25
+    if (selectedAudioOutputDeviceId !== 'default' &&
26
+        !availableAudioOutputDevices.find(d =>
27
+            d.deviceId === selectedAudioOutputDeviceId)) {
28
+        return 'default';
29
+    }
30
+}
31
+
32
+/**
33
+ * Determines if currently selected audio input device should be changed after
34
+ * list of available devices has been changed.
35
+ * @param {MediaDeviceInfo[]} newDevices
36
+ * @param {JitsiLocalTrack} localAudio
37
+ * @returns {string|undefined} - ID of new microphone device to use, undefined
38
+ *      if audio input device should not be changed.
39
+ */
40
+function getNewAudioInputDevice(newDevices, localAudio) {
41
+    let availableAudioInputDevices = newDevices.filter(
42
+        d => d.kind === 'audioinput');
43
+    let selectedAudioInputDeviceId = APP.settings.getMicDeviceId();
44
+    let selectedAudioInputDevice = availableAudioInputDevices.find(
45
+        d => d.deviceId === selectedAudioInputDeviceId);
46
+
47
+    // Here we handle case when no device was initially plugged, but
48
+    // then it's connected OR new device was connected when previous
49
+    // track has ended.
50
+    if (!localAudio || localAudio.disposed || localAudio.isEnded()) {
51
+        // If we have new audio device and permission to use it was granted
52
+        // (label is not an empty string), then we will try to use the first
53
+        // available device.
54
+        if (availableAudioInputDevices.length &&
55
+            availableAudioInputDevices[0].label !== '') {
56
+            return availableAudioInputDevices[0].deviceId;
57
+        }
58
+        // Otherwise we assume that we don't have any audio input devices
59
+        // to use and that's why disable microphone button on UI.
60
+        else {
61
+            APP.UI.disableMicrophoneButton();
62
+        }
63
+    } else {
64
+        // And here we handle case when we already have some device working,
65
+        // but we plug-in a "preferred" (previously selected in settings, stored
66
+        // in local storage) device.
67
+        if (selectedAudioInputDevice &&
68
+            selectedAudioInputDeviceId !== localAudio.getDeviceId()) {
69
+            return selectedAudioInputDeviceId;
70
+        }
71
+    }
72
+}
73
+
74
+/**
75
+ * Determines if currently selected video input device should be changed after
76
+ * list of available devices has been changed.
77
+ * @param {MediaDeviceInfo[]} newDevices
78
+ * @param {JitsiLocalTrack} localVideo
79
+ * @returns {string|undefined} - ID of new camera device to use, undefined
80
+ *      if video input device should not be changed.
81
+ */
82
+function getNewVideoInputDevice(newDevices, localVideo) {
83
+    let availableVideoInputDevices = newDevices.filter(
84
+        d => d.kind === 'videoinput');
85
+    let selectedVideoInputDeviceId = APP.settings.getCameraDeviceId();
86
+    let selectedVideoInputDevice = availableVideoInputDevices.find(
87
+        d => d.deviceId === selectedVideoInputDeviceId);
88
+
89
+    // Here we handle case when no video input device was initially plugged,
90
+    // but then device is connected OR new device was connected when
91
+    // previous track has ended.
92
+    if (!localVideo || localVideo.disposed || localVideo.isEnded()) {
93
+        // If we have new video device and permission to use it was granted
94
+        // (label is not an empty string), then we will try to use the first
95
+        // available device.
96
+        if (availableVideoInputDevices.length &&
97
+            availableVideoInputDevices[0].label !== '') {
98
+            return availableVideoInputDevices[0].deviceId;
99
+        }
100
+        // Otherwise we assume that we don't have any video input devices
101
+        // to use and that's why disable microphone button on UI.
102
+        else {
103
+            APP.UI.disableCameraButton();
104
+        }
105
+    } else {
106
+        // And here we handle case when we already have some device working,
107
+        // but we plug-in a "preferred" (previously selected in settings, stored
108
+        // in local storage) device.
109
+        if (selectedVideoInputDevice &&
110
+            selectedVideoInputDeviceId !== localVideo.getDeviceId()) {
111
+            return selectedVideoInputDeviceId;
112
+        }
113
+    }
114
+}
115
+
116
+export default {
117
+    /**
118
+     * Returns list of devices of single kind.
119
+     * @param {MediaDeviceInfo[]} devices
120
+     * @param {'audioinput'|'audiooutput'|'videoinput'} kind
121
+     * @returns {MediaDeviceInfo[]}
122
+     */
123
+    getDevicesFromListByKind(devices, kind) {
124
+        return devices.filter(d => d.kind === kind);
125
+    },
126
+    /**
127
+     * Stores lists of current 'audioinput', 'videoinput' and 'audiooutput'
128
+     * devices.
129
+     * @param {MediaDeviceInfo[]} devices
130
+     */
131
+    setCurrentMediaDevices(devices) {
132
+        currentAudioInputDevices =
133
+            this.getDevicesFromListByKind(devices, 'audioinput');
134
+        currentVideoInputDevices =
135
+            this.getDevicesFromListByKind(devices, 'videoinput');
136
+        currentAudioOutputDevices =
137
+            this.getDevicesFromListByKind(devices, 'audiooutput');
138
+    },
139
+    /**
140
+     * Returns lists of current 'audioinput', 'videoinput' and 'audiooutput'
141
+     * devices.
142
+     * @returns {{
143
+     *  audioinput: (MediaDeviceInfo[]|undefined),
144
+     *  videoinput: (MediaDeviceInfo[]|undefined),
145
+     *  audiooutput: (MediaDeviceInfo[]|undefined),
146
+     *  }}
147
+     */
148
+    getCurrentMediaDevices() {
149
+        return {
150
+            audioinput: currentAudioInputDevices,
151
+            videoinput: currentVideoInputDevices,
152
+            audiooutput: currentAudioOutputDevices
153
+        };
154
+    },
155
+    /**
156
+     * Determines if currently selected media devices should be changed after
157
+     * list of available devices has been changed.
158
+     * @param {MediaDeviceInfo[]} newDevices
159
+     * @param {boolean} isSharingScreen
160
+     * @param {JitsiLocalTrack} localVideo
161
+     * @param {JitsiLocalTrack} localAudio
162
+     * @returns {{
163
+     *  audioinput: (string|undefined),
164
+     *  videoinput: (string|undefined),
165
+     *  audiooutput: (string|undefined)
166
+     *  }}
167
+     */
168
+    getNewMediaDevicesAfterDeviceListChanged(
169
+        newDevices, isSharingScreen, localVideo, localAudio) {
170
+        return {
171
+            audioinput: getNewAudioInputDevice(newDevices, localAudio),
172
+            videoinput: !isSharingScreen &&
173
+                getNewVideoInputDevice(newDevices, localVideo),
174
+            audiooutput: getNewAudioOutputDevice(newDevices)
175
+        };
176
+    },
177
+    /**
178
+     * Tries to create new local tracks for new devices obtained after device
179
+     * list changed. Shows error dialog in case of failures.
180
+     * @param {function} createLocalTracks
181
+     * @param {string} (cameraDeviceId)
182
+     * @param {string} (micDeviceId)
183
+     * @returns {Promise.<JitsiLocalTrack[]>}
184
+     */
185
+    createLocalTracksAfterDeviceListChanged(
186
+        createLocalTracks, cameraDeviceId, micDeviceId) {
187
+        let audioTrackError;
188
+        let videoTrackError;
189
+        let audioRequested = !!micDeviceId;
190
+        let videoRequested = !!cameraDeviceId;
191
+
192
+        if (audioRequested && videoRequested) {
193
+            // First we try to create both audio and video tracks together.
194
+            return createLocalTracks(
195
+                    ['audio', 'video'], cameraDeviceId, micDeviceId)
196
+                    // If we fail to do this, try to create them separately.
197
+                    .catch(() => Promise.all(
198
+                        [createAudioTrack(false), createVideoTrack(false)]))
199
+                    .then((audioTracks, videoTracks) => {
200
+                        if (audioTrackError || videoTrackError) {
201
+                            APP.UI.showDeviceErrorDialog(
202
+                                audioTrackError, videoTrackError);
203
+                        }
204
+
205
+                        return (audioTracks || []).concat(videoTracks || []);
206
+                    });
207
+        } else if (videoRequested && !audioRequested) {
208
+            return createVideoTrack();
209
+        } else if (audioRequested && !videoRequested) {
210
+            return createAudioTrack();
211
+        } else {
212
+            return Promise.resolve([]);
213
+        }
214
+
215
+        function createAudioTrack(showError) {
216
+            return createLocalTracks(['audio'], null, micDeviceId)
217
+                .catch(err => {
218
+                    audioTrackError = err;
219
+                    showError && APP.UI.showDeviceErrorDialog(err, null);
220
+                    return [];
221
+                });
222
+        }
223
+
224
+        function createVideoTrack(showError) {
225
+            return createLocalTracks(['video'], cameraDeviceId, null)
226
+                .catch(err => {
227
+                    videoTrackError = err;
228
+                    showError && APP.UI.showDeviceErrorDialog(null, err);
229
+                    return [];
230
+                });
231
+        }
232
+    }
233
+};

Laden…
Annuleren
Opslaan