|
|
@@ -34,29 +34,25 @@ class DeviceSelectionDialog extends Component {
|
|
34
|
34
|
* All known audio and video devices split by type. This prop comes from
|
|
35
|
35
|
* the app state.
|
|
36
|
36
|
*/
|
|
37
|
|
- _devices: React.PropTypes.object,
|
|
|
37
|
+ _availableDevices: React.PropTypes.object,
|
|
38
|
38
|
|
|
39
|
39
|
/**
|
|
40
|
|
- * Device id for the current audio output device.
|
|
|
40
|
+ * Device id for the current audio input device. This device will be set
|
|
|
41
|
+ * as the default audio input device to preview.
|
|
41
|
42
|
*/
|
|
42
|
|
- currentAudioOutputId: React.PropTypes.string,
|
|
|
43
|
+ currentAudioInputId: React.PropTypes.string,
|
|
43
|
44
|
|
|
44
|
45
|
/**
|
|
45
|
|
- * JitsiLocalTrack for the current local audio.
|
|
46
|
|
- *
|
|
47
|
|
- * JitsiLocalTracks for the current audio and video, if any, should be
|
|
48
|
|
- * passed in for re-use in the previews. This is needed for Internet
|
|
49
|
|
- * Explorer, which cannot get multiple tracks from the same device, even
|
|
50
|
|
- * across tabs.
|
|
|
46
|
+ * Device id for the current audio output device. This device will be
|
|
|
47
|
+ * set as the default audio output device to preview.
|
|
51
|
48
|
*/
|
|
52
|
|
- currentAudioTrack: React.PropTypes.object,
|
|
|
49
|
+ currentAudioOutputId: React.PropTypes.string,
|
|
53
|
50
|
|
|
54
|
51
|
/**
|
|
55
|
|
- * JitsiLocalTrack for the current local video.
|
|
56
|
|
- *
|
|
57
|
|
- * Needed for reuse. See comment for propTypes.currentAudioTrack.
|
|
|
52
|
+ * Device id for the current video input device. This device will be set
|
|
|
53
|
+ * as the default video input device to preview.
|
|
58
|
54
|
*/
|
|
59
|
|
- currentVideoTrack: React.PropTypes.object,
|
|
|
55
|
+ currentVideoInputId: React.PropTypes.string,
|
|
60
|
56
|
|
|
61
|
57
|
/**
|
|
62
|
58
|
* Whether or not the audio selector can be interacted with. If true,
|
|
|
@@ -78,12 +74,12 @@ class DeviceSelectionDialog extends Component {
|
|
78
|
74
|
dispatch: React.PropTypes.func,
|
|
79
|
75
|
|
|
80
|
76
|
/**
|
|
81
|
|
- * Whether or not new audio input source can be selected.
|
|
|
77
|
+ * Whether or not a new audio input source can be selected.
|
|
82
|
78
|
*/
|
|
83
|
79
|
hasAudioPermission: React.PropTypes.bool,
|
|
84
|
80
|
|
|
85
|
81
|
/**
|
|
86
|
|
- * Whether or not new video input sources can be selected.
|
|
|
82
|
+ * Whether or not a new video input sources can be selected.
|
|
87
|
83
|
*/
|
|
88
|
84
|
hasVideoPermission: React.PropTypes.bool,
|
|
89
|
85
|
|
|
|
@@ -117,15 +113,40 @@ class DeviceSelectionDialog extends Component {
|
|
117
|
113
|
constructor(props) {
|
|
118
|
114
|
super(props);
|
|
119
|
115
|
|
|
|
116
|
+ const { _availableDevices } = this.props;
|
|
|
117
|
+
|
|
120
|
118
|
this.state = {
|
|
121
|
|
- // JitsiLocalTracks to use for live previewing.
|
|
|
119
|
+ // JitsiLocalTrack to use for live previewing of audio input.
|
|
122
|
120
|
previewAudioTrack: null,
|
|
|
121
|
+
|
|
|
122
|
+ // JitsiLocalTrack to use for live previewing of video input.
|
|
123
|
123
|
previewVideoTrack: null,
|
|
124
|
124
|
|
|
125
|
|
- // Device ids to keep track of new selections.
|
|
126
|
|
- videInput: null,
|
|
127
|
|
- audioInput: null,
|
|
128
|
|
- audioOutput: null
|
|
|
125
|
+ // An message describing a problem with obtaining a video preview.
|
|
|
126
|
+ previewVideoTrackError: null,
|
|
|
127
|
+
|
|
|
128
|
+ // The audio input device id to show as selected by default.
|
|
|
129
|
+ selectedAudioInputId: this.props.currentAudioInputId || '',
|
|
|
130
|
+
|
|
|
131
|
+ // The audio output device id to show as selected by default.
|
|
|
132
|
+ selectedAudioOutputId: this.props.currentAudioOutputId || '',
|
|
|
133
|
+
|
|
|
134
|
+ // The video input device id to show as selected by default.
|
|
|
135
|
+ // FIXME: On temasys, without a device selected and put into local
|
|
|
136
|
+ // storage as the default device to use, the current video device id
|
|
|
137
|
+ // is a blank string. This is because the library gets a local video
|
|
|
138
|
+ // track and then maps the track's device id by matching the track's
|
|
|
139
|
+ // label to the MediaDeviceInfos returned from enumerateDevices. In
|
|
|
140
|
+ // WebRTC, the track label is expected to return the camera device
|
|
|
141
|
+ // label. However, temasys video track labels refer to track id, not
|
|
|
142
|
+ // device label, so the library cannot match the track to a device.
|
|
|
143
|
+ // The workaround of defaulting to the first videoInput available
|
|
|
144
|
+ // is re-used from the previous device settings implementation.
|
|
|
145
|
+ selectedVideoInputId: this.props.currentVideoInputId
|
|
|
146
|
+ || (_availableDevices.videoInput
|
|
|
147
|
+ && _availableDevices.videoInput[0]
|
|
|
148
|
+ && _availableDevices.videoInput[0].deviceId)
|
|
|
149
|
+ || ''
|
|
129
|
150
|
};
|
|
130
|
151
|
|
|
131
|
152
|
// Preventing closing while cleaning up previews is important for
|
|
|
@@ -134,16 +155,29 @@ class DeviceSelectionDialog extends Component {
|
|
134
|
155
|
// closure until cleanup is complete ensures no errors in the process.
|
|
135
|
156
|
this._isClosing = false;
|
|
136
|
157
|
|
|
|
158
|
+ // Bind event handlers so they are only bound once for every instance.
|
|
137
|
159
|
this._closeModal = this._closeModal.bind(this);
|
|
138
|
|
- this._getAndSetAudioOutput = this._getAndSetAudioOutput.bind(this);
|
|
139
|
|
- this._getAndSetAudioTrack = this._getAndSetAudioTrack.bind(this);
|
|
140
|
|
- this._getAndSetVideoTrack = this._getAndSetVideoTrack.bind(this);
|
|
141
|
160
|
this._onCancel = this._onCancel.bind(this);
|
|
142
|
161
|
this._onSubmit = this._onSubmit.bind(this);
|
|
|
162
|
+ this._updateAudioOutput = this._updateAudioOutput.bind(this);
|
|
|
163
|
+ this._updateAudioInput = this._updateAudioInput.bind(this);
|
|
|
164
|
+ this._updateVideoInput = this._updateVideoInput.bind(this);
|
|
143
|
165
|
}
|
|
144
|
166
|
|
|
145
|
167
|
/**
|
|
146
|
|
- * Clean up any preview tracks that might not have been cleaned up already.
|
|
|
168
|
+ * Sets default device choices so a choice is pre-selected in the dropdowns
|
|
|
169
|
+ * and live previews are created.
|
|
|
170
|
+ *
|
|
|
171
|
+ * @inheritdoc
|
|
|
172
|
+ */
|
|
|
173
|
+ componentDidMount() {
|
|
|
174
|
+ this._updateAudioOutput(this.state.selectedAudioOutputId);
|
|
|
175
|
+ this._updateAudioInput(this.state.selectedAudioInputId);
|
|
|
176
|
+ this._updateVideoInput(this.state.selectedVideoInputId);
|
|
|
177
|
+ }
|
|
|
178
|
+
|
|
|
179
|
+ /**
|
|
|
180
|
+ * Disposes preview tracks that might not already be disposed.
|
|
147
|
181
|
*
|
|
148
|
182
|
* @inheritdoc
|
|
149
|
183
|
*/
|
|
|
@@ -173,8 +207,8 @@ class DeviceSelectionDialog extends Component {
|
|
173
|
207
|
<div className = 'device-selection-column column-video'>
|
|
174
|
208
|
<div className = 'device-selection-video-container'>
|
|
175
|
209
|
<VideoInputPreview
|
|
176
|
|
- track = { this.state.previewVideoTrack
|
|
177
|
|
- || this.props.currentVideoTrack } />
|
|
|
210
|
+ error = { this.state.previewVideoTrackError }
|
|
|
211
|
+ track = { this.state.previewVideoTrack } />
|
|
178
|
212
|
</div>
|
|
179
|
213
|
{ this._renderAudioInputPreview() }
|
|
180
|
214
|
</div>
|
|
|
@@ -197,17 +231,10 @@ class DeviceSelectionDialog extends Component {
|
|
197
|
231
|
* promise can be for video cleanup and another for audio cleanup.
|
|
198
|
232
|
*/
|
|
199
|
233
|
_attemptPreviewTrackCleanup() {
|
|
200
|
|
- const cleanupPromises = [];
|
|
201
|
|
-
|
|
202
|
|
- if (!this._isPreviewingCurrentVideoTrack()) {
|
|
203
|
|
- cleanupPromises.push(this._disposeVideoPreview());
|
|
204
|
|
- }
|
|
205
|
|
-
|
|
206
|
|
- if (!this._isPreviewingCurrentAudioTrack()) {
|
|
207
|
|
- cleanupPromises.push(this._disposeAudioPreview());
|
|
208
|
|
- }
|
|
209
|
|
-
|
|
210
|
|
- return cleanupPromises;
|
|
|
234
|
+ return Promise.all([
|
|
|
235
|
+ this._disposeVideoPreview(),
|
|
|
236
|
+ this._disposeAudioPreview()
|
|
|
237
|
+ ]);
|
|
211
|
238
|
}
|
|
212
|
239
|
|
|
213
|
240
|
/**
|
|
|
@@ -243,147 +270,7 @@ class DeviceSelectionDialog extends Component {
|
|
243
|
270
|
}
|
|
244
|
271
|
|
|
245
|
272
|
/**
|
|
246
|
|
- * Callback invoked when a new audio output device has been selected.
|
|
247
|
|
- * Updates the internal state of the user's selection.
|
|
248
|
|
- *
|
|
249
|
|
- * @param {string} deviceId - The id of the chosen audio output device.
|
|
250
|
|
- * @private
|
|
251
|
|
- * @returns {void}
|
|
252
|
|
- */
|
|
253
|
|
- _getAndSetAudioOutput(deviceId) {
|
|
254
|
|
- this.setState({
|
|
255
|
|
- audioOutput: deviceId
|
|
256
|
|
- });
|
|
257
|
|
- }
|
|
258
|
|
-
|
|
259
|
|
- /**
|
|
260
|
|
- * Callback invoked when a new audio input device has been selected.
|
|
261
|
|
- * Updates the internal state of the user's selection as well as the audio
|
|
262
|
|
- * track that should display in the preview. Will reuse the current local
|
|
263
|
|
- * audio track if it has been selected.
|
|
264
|
|
- *
|
|
265
|
|
- * @param {string} deviceId - The id of the chosen audio input device.
|
|
266
|
|
- * @private
|
|
267
|
|
- * @returns {void}
|
|
268
|
|
- */
|
|
269
|
|
- _getAndSetAudioTrack(deviceId) {
|
|
270
|
|
- this.setState({
|
|
271
|
|
- audioInput: deviceId
|
|
272
|
|
- }, () => {
|
|
273
|
|
- const cleanupPromise = this._isPreviewingCurrentAudioTrack()
|
|
274
|
|
- ? Promise.resolve() : this._disposeAudioPreview();
|
|
275
|
|
-
|
|
276
|
|
- if (this._isCurrentAudioTrack(deviceId)) {
|
|
277
|
|
- cleanupPromise
|
|
278
|
|
- .then(() => {
|
|
279
|
|
- this.setState({
|
|
280
|
|
- previewAudioTrack: this.props.currentAudioTrack
|
|
281
|
|
- });
|
|
282
|
|
- });
|
|
283
|
|
- } else {
|
|
284
|
|
- cleanupPromise
|
|
285
|
|
- .then(() => createLocalTrack('audio', deviceId))
|
|
286
|
|
- .then(jitsiLocalTrack => {
|
|
287
|
|
- this.setState({
|
|
288
|
|
- previewAudioTrack: jitsiLocalTrack
|
|
289
|
|
- });
|
|
290
|
|
- });
|
|
291
|
|
- }
|
|
292
|
|
- });
|
|
293
|
|
- }
|
|
294
|
|
-
|
|
295
|
|
- /**
|
|
296
|
|
- * Callback invoked when a new video input device has been selected. Updates
|
|
297
|
|
- * the internal state of the user's selection as well as the video track
|
|
298
|
|
- * that should display in the preview. Will reuse the current local video
|
|
299
|
|
- * track if it has been selected.
|
|
300
|
|
- *
|
|
301
|
|
- * @param {string} deviceId - The id of the chosen video input device.
|
|
302
|
|
- * @private
|
|
303
|
|
- * @returns {void}
|
|
304
|
|
- */
|
|
305
|
|
- _getAndSetVideoTrack(deviceId) {
|
|
306
|
|
- this.setState({
|
|
307
|
|
- videoInput: deviceId
|
|
308
|
|
- }, () => {
|
|
309
|
|
- const cleanupPromise = this._isPreviewingCurrentVideoTrack()
|
|
310
|
|
- ? Promise.resolve() : this._disposeVideoPreview();
|
|
311
|
|
-
|
|
312
|
|
- if (this._isCurrentVideoTrack(deviceId)) {
|
|
313
|
|
- cleanupPromise
|
|
314
|
|
- .then(() => {
|
|
315
|
|
- this.setState({
|
|
316
|
|
- previewVideoTrack: this.props.currentVideoTrack
|
|
317
|
|
- });
|
|
318
|
|
- });
|
|
319
|
|
- } else {
|
|
320
|
|
- cleanupPromise
|
|
321
|
|
- .then(() => createLocalTrack('video', deviceId))
|
|
322
|
|
- .then(jitsiLocalTrack => {
|
|
323
|
|
- this.setState({
|
|
324
|
|
- previewVideoTrack: jitsiLocalTrack
|
|
325
|
|
- });
|
|
326
|
|
- });
|
|
327
|
|
- }
|
|
328
|
|
- });
|
|
329
|
|
- }
|
|
330
|
|
-
|
|
331
|
|
- /**
|
|
332
|
|
- * Utility function for determining if the current local audio track has the
|
|
333
|
|
- * passed in device id.
|
|
334
|
|
- *
|
|
335
|
|
- * @param {string} deviceId - The device id to match against.
|
|
336
|
|
- * @private
|
|
337
|
|
- * @returns {boolean} True if the device id is being used by the local audio
|
|
338
|
|
- * track.
|
|
339
|
|
- */
|
|
340
|
|
- _isCurrentAudioTrack(deviceId) {
|
|
341
|
|
- return this.props.currentAudioTrack
|
|
342
|
|
- && this.props.currentAudioTrack.getDeviceId() === deviceId;
|
|
343
|
|
- }
|
|
344
|
|
-
|
|
345
|
|
- /**
|
|
346
|
|
- * Utility function for determining if the current local video track has the
|
|
347
|
|
- * passed in device id.
|
|
348
|
|
- *
|
|
349
|
|
- * @param {string} deviceId - The device id to match against.
|
|
350
|
|
- * @private
|
|
351
|
|
- * @returns {boolean} True if the device id is being used by the local
|
|
352
|
|
- * video track.
|
|
353
|
|
- */
|
|
354
|
|
- _isCurrentVideoTrack(deviceId) {
|
|
355
|
|
- return this.props.currentVideoTrack
|
|
356
|
|
- && this.props.currentVideoTrack.getDeviceId() === deviceId;
|
|
357
|
|
- }
|
|
358
|
|
-
|
|
359
|
|
- /**
|
|
360
|
|
- * Utility function for detecting if the current audio preview track is not
|
|
361
|
|
- * the currently used audio track.
|
|
362
|
|
- *
|
|
363
|
|
- * @private
|
|
364
|
|
- * @returns {boolean} True if the current audio track is being used for
|
|
365
|
|
- * the preview.
|
|
366
|
|
- */
|
|
367
|
|
- _isPreviewingCurrentAudioTrack() {
|
|
368
|
|
- return !this.state.previewAudioTrack
|
|
369
|
|
- || this.state.previewAudioTrack === this.props.currentAudioTrack;
|
|
370
|
|
- }
|
|
371
|
|
-
|
|
372
|
|
- /**
|
|
373
|
|
- * Utility function for detecting if the current video preview track is not
|
|
374
|
|
- * the currently used video track.
|
|
375
|
|
- *
|
|
376
|
|
- * @private
|
|
377
|
|
- * @returns {boolean} True if the current video track is being used as the
|
|
378
|
|
- * preview.
|
|
379
|
|
- */
|
|
380
|
|
- _isPreviewingCurrentVideoTrack() {
|
|
381
|
|
- return !this.state.previewVideoTrack
|
|
382
|
|
- || this.state.previewVideoTrack === this.props.currentVideoTrack;
|
|
383
|
|
- }
|
|
384
|
|
-
|
|
385
|
|
- /**
|
|
386
|
|
- * Cleans existing preview tracks and signal to closeDeviceSelectionDialog.
|
|
|
273
|
+ * Disposes preview tracks and signals to close DeviceSelectionDialog.
|
|
387
|
274
|
*
|
|
388
|
275
|
* @private
|
|
389
|
276
|
* @returns {boolean} Returns false to prevent closure until cleanup is
|
|
|
@@ -406,7 +293,7 @@ class DeviceSelectionDialog extends Component {
|
|
406
|
293
|
}
|
|
407
|
294
|
|
|
408
|
295
|
/**
|
|
409
|
|
- * Identify changes to the preferred input/output devices and perform
|
|
|
296
|
+ * Identifies changes to the preferred input/output devices and perform
|
|
410
|
297
|
* necessary cleanup and requests to use those devices. Closes the modal
|
|
411
|
298
|
* after cleanup and device change requests complete.
|
|
412
|
299
|
*
|
|
|
@@ -421,32 +308,26 @@ class DeviceSelectionDialog extends Component {
|
|
421
|
308
|
|
|
422
|
309
|
this._isClosing = true;
|
|
423
|
310
|
|
|
424
|
|
- const deviceChangePromises = [];
|
|
425
|
|
-
|
|
426
|
|
- if (this.state.videoInput && !this._isPreviewingCurrentVideoTrack()) {
|
|
427
|
|
- const changeVideoPromise = this._disposeVideoPreview()
|
|
428
|
|
- .then(() => {
|
|
429
|
|
- this.props.dispatch(setVideoInputDevice(
|
|
430
|
|
- this.state.videoInput));
|
|
431
|
|
- });
|
|
432
|
|
-
|
|
433
|
|
- deviceChangePromises.push(changeVideoPromise);
|
|
434
|
|
- }
|
|
435
|
|
-
|
|
436
|
|
- if (this.state.audioInput && !this._isPreviewingCurrentAudioTrack()) {
|
|
437
|
|
- const changeAudioPromise = this._disposeAudioPreview()
|
|
438
|
|
- .then(() => {
|
|
439
|
|
- this.props.dispatch(setAudioInputDevice(
|
|
440
|
|
- this.state.audioInput));
|
|
441
|
|
- });
|
|
442
|
|
-
|
|
443
|
|
- deviceChangePromises.push(changeAudioPromise);
|
|
444
|
|
- }
|
|
445
|
|
-
|
|
446
|
|
- if (this.state.audioOutput
|
|
447
|
|
- && this.state.audioOutput !== this.props.currentAudioOutputId) {
|
|
448
|
|
- this.props.dispatch(setAudioOutputDevice(this.state.audioOutput));
|
|
449
|
|
- }
|
|
|
311
|
+ const deviceChangePromises = this._attemptPreviewTrackCleanup()
|
|
|
312
|
+ .then(() => {
|
|
|
313
|
+ if (this.state.selectedVideoInputId
|
|
|
314
|
+ !== this.props.currentVideoInputId) {
|
|
|
315
|
+ this.props.dispatch(
|
|
|
316
|
+ setVideoInputDevice(this.state.selectedVideoInputId));
|
|
|
317
|
+ }
|
|
|
318
|
+
|
|
|
319
|
+ if (this.state.selectedAudioInputId
|
|
|
320
|
+ !== this.props.currentAudioInputId) {
|
|
|
321
|
+ this.props.dispatch(
|
|
|
322
|
+ setAudioInputDevice(this.state.selectedAudioInputId));
|
|
|
323
|
+ }
|
|
|
324
|
+
|
|
|
325
|
+ if (this.state.selectedAudioOutputId
|
|
|
326
|
+ !== this.props.currentAudioOutputId) {
|
|
|
327
|
+ this.props.dispatch(
|
|
|
328
|
+ setAudioOutputDevice(this.state.selectedAudioOutputId));
|
|
|
329
|
+ }
|
|
|
330
|
+ });
|
|
450
|
331
|
|
|
451
|
332
|
Promise.all(deviceChangePromises)
|
|
452
|
333
|
.then(this._closeModal)
|
|
|
@@ -470,8 +351,7 @@ class DeviceSelectionDialog extends Component {
|
|
470
|
351
|
|
|
471
|
352
|
return (
|
|
472
|
353
|
<AudioInputPreview
|
|
473
|
|
- track = { this.state.previewAudioTrack
|
|
474
|
|
- || this.props.currentAudioTrack } />
|
|
|
354
|
+ track = { this.state.previewAudioTrack } />
|
|
475
|
355
|
);
|
|
476
|
356
|
}
|
|
477
|
357
|
|
|
|
@@ -489,8 +369,7 @@ class DeviceSelectionDialog extends Component {
|
|
489
|
369
|
|
|
490
|
370
|
return (
|
|
491
|
371
|
<AudioOutputPreview
|
|
492
|
|
- deviceId = { this.state.audioOutput
|
|
493
|
|
- || this.props.currentAudioOutputId } />
|
|
|
372
|
+ deviceId = { this.state.selectedAudioOutputId } />
|
|
494
|
373
|
);
|
|
495
|
374
|
}
|
|
496
|
375
|
|
|
|
@@ -515,70 +394,120 @@ class DeviceSelectionDialog extends Component {
|
|
515
|
394
|
* @returns {Array<ReactElement>} DeviceSelector instances.
|
|
516
|
395
|
*/
|
|
517
|
396
|
_renderSelectors() {
|
|
518
|
|
- const availableDevices = this.props._devices;
|
|
519
|
|
- const currentAudioId = this.state.audioInput
|
|
520
|
|
- || (this.props.currentAudioTrack
|
|
521
|
|
- && this.props.currentAudioTrack.getDeviceId());
|
|
522
|
|
- const currentAudioOutId = this.state.audioOutput
|
|
523
|
|
- || this.props.currentAudioOutputId;
|
|
524
|
|
-
|
|
525
|
|
- // FIXME: On temasys, without a device selected and put into local
|
|
526
|
|
- // storage as the default device to use, the current video device id is
|
|
527
|
|
- // a blank string. This is because the library gets a local video track
|
|
528
|
|
- // and then maps the track's device id by matching the track's label to
|
|
529
|
|
- // the MediaDeviceInfos returned from enumerateDevices. In WebRTC, the
|
|
530
|
|
- // track label is expected to return the camera device label. However,
|
|
531
|
|
- // temasys video track labels refer to track id, not device label, so
|
|
532
|
|
- // the library cannot match the track to a device. The workaround of
|
|
533
|
|
- // defaulting to the first videoInput available has been re-used from
|
|
534
|
|
- // the previous device settings implementation.
|
|
535
|
|
- const currentVideoId = this.state.videoInput
|
|
536
|
|
- || (this.props.currentVideoTrack
|
|
537
|
|
- && this.props.currentVideoTrack.getDeviceId())
|
|
538
|
|
- || (availableDevices.videoInput[0]
|
|
539
|
|
- && availableDevices.videoInput[0].deviceId)
|
|
540
|
|
- || ''; // DeviceSelector expects a string for prop selectedDeviceId.
|
|
541
|
|
-
|
|
|
397
|
+ const { _availableDevices } = this.props;
|
|
542
|
398
|
const configurations = [
|
|
543
|
399
|
{
|
|
544
|
|
- devices: availableDevices.videoInput,
|
|
|
400
|
+ devices: _availableDevices.videoInput,
|
|
545
|
401
|
hasPermission: this.props.hasVideoPermission,
|
|
546
|
402
|
icon: 'icon-camera',
|
|
547
|
403
|
isDisabled: this.props.disableDeviceChange,
|
|
548
|
404
|
key: 'videoInput',
|
|
549
|
405
|
label: 'settings.selectCamera',
|
|
550
|
|
- onSelect: this._getAndSetVideoTrack,
|
|
551
|
|
- selectedDeviceId: currentVideoId
|
|
|
406
|
+ onSelect: this._updateVideoInput,
|
|
|
407
|
+ selectedDeviceId: this.state.selectedVideoInputId
|
|
552
|
408
|
},
|
|
553
|
409
|
{
|
|
554
|
|
- devices: availableDevices.audioInput,
|
|
|
410
|
+ devices: _availableDevices.audioInput,
|
|
555
|
411
|
hasPermission: this.props.hasAudioPermission,
|
|
556
|
412
|
icon: 'icon-microphone',
|
|
557
|
413
|
isDisabled: this.props.disableAudioInputChange
|
|
558
|
414
|
|| this.props.disableDeviceChange,
|
|
559
|
415
|
key: 'audioInput',
|
|
560
|
416
|
label: 'settings.selectMic',
|
|
561
|
|
- onSelect: this._getAndSetAudioTrack,
|
|
562
|
|
- selectedDeviceId: currentAudioId
|
|
|
417
|
+ onSelect: this._updateAudioInput,
|
|
|
418
|
+ selectedDeviceId: this.state.selectedAudioInputId
|
|
563
|
419
|
}
|
|
564
|
420
|
];
|
|
565
|
421
|
|
|
566
|
422
|
if (!this.props.hideAudioOutputSelect) {
|
|
567
|
423
|
configurations.push({
|
|
568
|
|
- devices: availableDevices.audioOutput,
|
|
|
424
|
+ devices: _availableDevices.audioOutput,
|
|
569
|
425
|
hasPermission: this.props.hasAudioPermission
|
|
570
|
426
|
|| this.props.hasVideoPermission,
|
|
571
|
427
|
icon: 'icon-volume',
|
|
572
|
428
|
isDisabled: this.props.disableDeviceChange,
|
|
573
|
429
|
key: 'audioOutput',
|
|
574
|
430
|
label: 'settings.selectAudioOutput',
|
|
575
|
|
- onSelect: this._getAndSetAudioOutput,
|
|
576
|
|
- selectedDeviceId: currentAudioOutId
|
|
|
431
|
+ onSelect: this._updateAudioOutput,
|
|
|
432
|
+ selectedDeviceId: this.state.selectedAudioOutputId
|
|
577
|
433
|
});
|
|
578
|
434
|
}
|
|
579
|
435
|
|
|
580
|
436
|
return configurations.map(this._renderSelector);
|
|
581
|
437
|
}
|
|
|
438
|
+
|
|
|
439
|
+ /**
|
|
|
440
|
+ * Callback invoked when a new audio input device has been selected. Updates
|
|
|
441
|
+ * the internal state of the user's selection as well as the audio track
|
|
|
442
|
+ * that should display in the preview.
|
|
|
443
|
+ *
|
|
|
444
|
+ * @param {string} deviceId - The id of the chosen audio input device.
|
|
|
445
|
+ * @private
|
|
|
446
|
+ * @returns {void}
|
|
|
447
|
+ */
|
|
|
448
|
+ _updateAudioInput(deviceId) {
|
|
|
449
|
+ this.setState({
|
|
|
450
|
+ selectedAudioInputId: deviceId
|
|
|
451
|
+ }, () => {
|
|
|
452
|
+ this._disposeAudioPreview()
|
|
|
453
|
+ .then(() => createLocalTrack('audio', deviceId))
|
|
|
454
|
+ .then(jitsiLocalTrack => {
|
|
|
455
|
+ this.setState({
|
|
|
456
|
+ previewAudioTrack: jitsiLocalTrack
|
|
|
457
|
+ });
|
|
|
458
|
+ })
|
|
|
459
|
+ .catch(() => {
|
|
|
460
|
+ this.setState({
|
|
|
461
|
+ previewAudioTrack: null
|
|
|
462
|
+ });
|
|
|
463
|
+ });
|
|
|
464
|
+ });
|
|
|
465
|
+ }
|
|
|
466
|
+
|
|
|
467
|
+ /**
|
|
|
468
|
+ * Callback invoked when a new audio output device has been selected.
|
|
|
469
|
+ * Updates the internal state of the user's selection.
|
|
|
470
|
+ *
|
|
|
471
|
+ * @param {string} deviceId - The id of the chosen audio output device.
|
|
|
472
|
+ * @private
|
|
|
473
|
+ * @returns {void}
|
|
|
474
|
+ */
|
|
|
475
|
+ _updateAudioOutput(deviceId) {
|
|
|
476
|
+ this.setState({
|
|
|
477
|
+ selectedAudioOutputId: deviceId
|
|
|
478
|
+ });
|
|
|
479
|
+ }
|
|
|
480
|
+
|
|
|
481
|
+ /**
|
|
|
482
|
+ * Callback invoked when a new video input device has been selected. Updates
|
|
|
483
|
+ * the internal state of the user's selection as well as the video track
|
|
|
484
|
+ * that should display in the preview.
|
|
|
485
|
+ *
|
|
|
486
|
+ * @param {string} deviceId - The id of the chosen video input device.
|
|
|
487
|
+ * @private
|
|
|
488
|
+ * @returns {void}
|
|
|
489
|
+ */
|
|
|
490
|
+ _updateVideoInput(deviceId) {
|
|
|
491
|
+ this.setState({
|
|
|
492
|
+ selectedVideoInputId: deviceId
|
|
|
493
|
+ }, () => {
|
|
|
494
|
+ this._disposeVideoPreview()
|
|
|
495
|
+ .then(() => createLocalTrack('video', deviceId))
|
|
|
496
|
+ .then(jitsiLocalTrack => {
|
|
|
497
|
+ this.setState({
|
|
|
498
|
+ previewVideoTrack: jitsiLocalTrack,
|
|
|
499
|
+ previewVideoTrackError: null
|
|
|
500
|
+ });
|
|
|
501
|
+ })
|
|
|
502
|
+ .catch(() => {
|
|
|
503
|
+ this.setState({
|
|
|
504
|
+ previewVideoTrack: null,
|
|
|
505
|
+ previewVideoTrackError:
|
|
|
506
|
+ this.props.t('deviceSelection.previewUnavailable')
|
|
|
507
|
+ });
|
|
|
508
|
+ });
|
|
|
509
|
+ });
|
|
|
510
|
+ }
|
|
582
|
511
|
}
|
|
583
|
512
|
|
|
584
|
513
|
/**
|
|
|
@@ -588,12 +517,12 @@ class DeviceSelectionDialog extends Component {
|
|
588
|
517
|
* @param {Object} state - The Redux state.
|
|
589
|
518
|
* @private
|
|
590
|
519
|
* @returns {{
|
|
591
|
|
- * _devices: Object
|
|
|
520
|
+ * _availableDevices: Object
|
|
592
|
521
|
* }}
|
|
593
|
522
|
*/
|
|
594
|
523
|
function _mapStateToProps(state) {
|
|
595
|
524
|
return {
|
|
596
|
|
- _devices: state['features/base/devices']
|
|
|
525
|
+ _availableDevices: state['features/base/devices']
|
|
597
|
526
|
};
|
|
598
|
527
|
}
|
|
599
|
528
|
|