|
@@ -4,8 +4,12 @@ import CameraFacingMode from '../../service/RTC/CameraFacingMode';
|
4
|
4
|
import { getLogger } from 'jitsi-meet-logger';
|
5
|
5
|
import JitsiTrack from './JitsiTrack';
|
6
|
6
|
import JitsiTrackError from '../../JitsiTrackError';
|
7
|
|
-import * as JitsiTrackErrors from '../../JitsiTrackErrors';
|
8
|
|
-import * as JitsiTrackEvents from '../../JitsiTrackEvents';
|
|
7
|
+import { TRACK_NO_STREAM_FOUND } from '../../JitsiTrackErrors';
|
|
8
|
+import {
|
|
9
|
+ LOCAL_TRACK_STOPPED,
|
|
10
|
+ NO_DATA_FROM_SOURCE,
|
|
11
|
+ TRACK_MUTE_CHANGED
|
|
12
|
+} from '../../JitsiTrackEvents';
|
9
|
13
|
import * as MediaType from '../../service/RTC/MediaType';
|
10
|
14
|
import RTCBrowserType from './RTCBrowserType';
|
11
|
15
|
import RTCEvents from '../../service/RTC/RTCEvents';
|
|
@@ -37,27 +41,23 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
37
|
41
|
* source. NOTE: defined for desktop sharing tracks only.
|
38
|
42
|
* @constructor
|
39
|
43
|
*/
|
40
|
|
- constructor(trackInfo) {
|
41
|
|
- const {
|
42
|
|
- rtcId,
|
43
|
|
- stream,
|
44
|
|
- track,
|
45
|
|
- mediaType,
|
46
|
|
- videoType,
|
47
|
|
- resolution,
|
48
|
|
- deviceId,
|
49
|
|
- facingMode,
|
50
|
|
- sourceId,
|
51
|
|
- sourceType
|
52
|
|
- } = trackInfo;
|
53
|
|
-
|
|
44
|
+ constructor({
|
|
45
|
+ deviceId,
|
|
46
|
+ facingMode,
|
|
47
|
+ mediaType,
|
|
48
|
+ resolution,
|
|
49
|
+ rtcId,
|
|
50
|
+ sourceId,
|
|
51
|
+ sourceType,
|
|
52
|
+ stream,
|
|
53
|
+ track,
|
|
54
|
+ videoType
|
|
55
|
+ }) {
|
54
|
56
|
super(
|
55
|
|
- null /* RTC */,
|
|
57
|
+ /* conference */ null,
|
56
|
58
|
stream,
|
57
|
59
|
track,
|
58
|
|
- () => {
|
59
|
|
- this.emit(JitsiTrackEvents.LOCAL_TRACK_STOPPED);
|
60
|
|
- } /* inactiveHandler */,
|
|
60
|
+ /* streamInactiveHandler */ () => this.emit(LOCAL_TRACK_STOPPED),
|
61
|
61
|
mediaType,
|
62
|
62
|
videoType);
|
63
|
63
|
|
|
@@ -66,29 +66,25 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
66
|
66
|
* @type {number}
|
67
|
67
|
*/
|
68
|
68
|
this.rtcId = rtcId;
|
69
|
|
- this.resolution = resolution;
|
70
|
69
|
this.sourceId = sourceId;
|
71
|
70
|
this.sourceType = sourceType;
|
72
|
71
|
|
73
|
|
- // FIXME: currently firefox is ignoring our constraints about
|
|
72
|
+ // FIXME Currently, Firefox is ignoring our constraints about
|
74
|
73
|
// resolutions so we do not store it, to avoid wrong reporting of local
|
75
|
|
- // track resolution
|
76
|
|
- if (RTCBrowserType.isFirefox()) {
|
77
|
|
- this.resolution = null;
|
78
|
|
- }
|
|
74
|
+ // track resolution.
|
|
75
|
+ this.resolution = RTCBrowserType.isFirefox() ? null : resolution;
|
79
|
76
|
|
80
|
77
|
this.deviceId = deviceId;
|
81
|
|
- this.storedMSID = this.getMSID();
|
82
|
78
|
|
83
|
79
|
/**
|
84
|
80
|
* The <tt>Promise</tt> which represents the progress of a previously
|
85
|
|
- * queued/scheduled {@link _setMute} (from the point of view of
|
86
|
|
- * {@link _queueSetMute}).
|
|
81
|
+ * queued/scheduled {@link _setMuted} (from the point of view of
|
|
82
|
+ * {@link _queueSetMuted}).
|
87
|
83
|
*
|
88
|
84
|
* @private
|
89
|
85
|
* @type {Promise}
|
90
|
86
|
*/
|
91
|
|
- this._prevSetMute = Promise.resolve();
|
|
87
|
+ this._prevSetMuted = Promise.resolve();
|
92
|
88
|
|
93
|
89
|
/**
|
94
|
90
|
* The facing mode of the camera from which this JitsiLocalTrack
|
|
@@ -123,19 +119,6 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
123
|
119
|
// soon as it's called.
|
124
|
120
|
this._realDeviceId = this.deviceId === '' ? undefined : this.deviceId;
|
125
|
121
|
|
126
|
|
- /**
|
127
|
|
- * Set to <tt>true</tt> when there's ongoing "mute/unmute" operation in
|
128
|
|
- * progress. Used by {@link LocalSdpMunger}.
|
129
|
|
- * @type {boolean}
|
130
|
|
- */
|
131
|
|
- this.inMuteOrUnmuteProgress = true;
|
132
|
|
-
|
133
|
|
- /**
|
134
|
|
- * Indicates that we have called RTCUtils.stopMediaStream for the
|
135
|
|
- * MediaStream related to this JitsiTrack object.
|
136
|
|
- */
|
137
|
|
- this.stopStreamInProgress = false;
|
138
|
|
-
|
139
|
122
|
/**
|
140
|
123
|
* On mute event we are waiting for 3s to check if the stream is going
|
141
|
124
|
* to be still muted before firing the event for camera issue detected
|
|
@@ -234,11 +217,10 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
234
|
217
|
}
|
235
|
218
|
|
236
|
219
|
/**
|
237
|
|
- * Fires JitsiTrackEvents.NO_DATA_FROM_SOURCE and logs it to analytics and
|
238
|
|
- * callstats.
|
|
220
|
+ * Fires NO_DATA_FROM_SOURCE event and logs it to analytics and callstats.
|
239
|
221
|
*/
|
240
|
222
|
_fireNoDataFromSourceEvent() {
|
241
|
|
- this.emit(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
|
|
223
|
+ this.emit(NO_DATA_FROM_SOURCE);
|
242
|
224
|
const eventName = `${this.getType()}.no_data_from_source`;
|
243
|
225
|
|
244
|
226
|
Statistics.analytics.sendEvent(eventName);
|
|
@@ -273,13 +255,15 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
273
|
255
|
/**
|
274
|
256
|
* Sets the stream property of JitsiLocalTrack object and sets all stored
|
275
|
257
|
* handlers to it.
|
|
258
|
+ *
|
276
|
259
|
* @param {MediaStream} stream the new stream.
|
|
260
|
+ * @protected
|
277
|
261
|
*/
|
278
|
262
|
_setStream(stream) {
|
279
|
263
|
super._setStream(stream);
|
280
|
264
|
|
281
|
|
- // Store the MSID for video mute/unmute purposes
|
282
|
265
|
if (stream) {
|
|
266
|
+ // Store the MSID for video mute/unmute purposes.
|
283
|
267
|
this.storedMSID = this.getMSID();
|
284
|
268
|
logger.debug(`Setting new MSID: ${this.storedMSID} on ${this}`);
|
285
|
269
|
} else {
|
|
@@ -288,81 +272,88 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
288
|
272
|
}
|
289
|
273
|
|
290
|
274
|
/**
|
291
|
|
- * Mutes the track. Will reject the Promise if there is mute/unmute
|
292
|
|
- * operation in progress.
|
|
275
|
+ * Asynchronously mutes this track.
|
|
276
|
+ *
|
293
|
277
|
* @returns {Promise}
|
294
|
278
|
*/
|
295
|
279
|
mute() {
|
296
|
|
- return this._queueSetMute(true);
|
|
280
|
+ return this._queueSetMuted(true);
|
297
|
281
|
}
|
298
|
282
|
|
299
|
283
|
/**
|
300
|
|
- * Unmutes the track. Will reject the Promise if there is mute/unmute
|
301
|
|
- * operation in progress.
|
|
284
|
+ * Asynchronously unmutes this track.
|
|
285
|
+ *
|
302
|
286
|
* @returns {Promise}
|
303
|
287
|
*/
|
304
|
288
|
unmute() {
|
305
|
|
- return this._queueSetMute(false);
|
|
289
|
+ return this._queueSetMuted(false);
|
306
|
290
|
}
|
307
|
291
|
|
308
|
292
|
/**
|
309
|
|
- * Initializes a new Promise to execute {@link _setMute}. May be called
|
310
|
|
- * multiple times in a row and the invocations of {@link _setMute} and,
|
311
|
|
- * consequently, {@link mute} and/or {@link unmute} will be resolved in a
|
|
293
|
+ * Initializes a new Promise to execute {@link #_setMuted}. May be called
|
|
294
|
+ * multiple times in a row and the invocations of {@link #_setMuted} and,
|
|
295
|
+ * consequently, {@link #mute} and/or {@link #unmute} will be resolved in a
|
312
|
296
|
* serialized fashion.
|
313
|
297
|
*
|
314
|
|
- * @param {boolean} mute - Whether to mute or unmute this track.
|
|
298
|
+ * @param {boolean} muted - The value to invoke <tt>_setMuted</tt> with.
|
315
|
299
|
* @returns {Promise}
|
316
|
300
|
*/
|
317
|
|
- _queueSetMute(mute) {
|
318
|
|
- const setMute = this._setMute.bind(this, mute);
|
|
301
|
+ _queueSetMuted(muted) {
|
|
302
|
+ const setMuted = this._setMuted.bind(this, muted);
|
319
|
303
|
|
320
|
|
- this._prevSetMute = this._prevSetMute.then(setMute, setMute);
|
|
304
|
+ this._prevSetMuted = this._prevSetMuted.then(setMuted, setMuted);
|
321
|
305
|
|
322
|
|
- return this._prevSetMute;
|
|
306
|
+ return this._prevSetMuted;
|
323
|
307
|
}
|
324
|
308
|
|
325
|
309
|
/**
|
326
|
|
- * Mutes / unmutes the track.
|
|
310
|
+ * Mutes / unmutes this track.
|
327
|
311
|
*
|
328
|
|
- * @param {boolean} mute - If true the track will be muted. Otherwise the
|
329
|
|
- * track will be unmuted.
|
|
312
|
+ * @param {boolean} muted - If <tt>true</tt>, this track will be muted;
|
|
313
|
+ * otherwise, this track will be unmuted.
|
330
|
314
|
* @private
|
331
|
315
|
* @returns {Promise}
|
332
|
316
|
*/
|
333
|
|
- _setMute(mute) {
|
334
|
|
- if (this.isMuted() === mute) {
|
|
317
|
+ _setMuted(muted) {
|
|
318
|
+ if (this.isMuted() === muted) {
|
335
|
319
|
return Promise.resolve();
|
336
|
320
|
}
|
337
|
321
|
|
338
|
322
|
let promise = Promise.resolve();
|
339
|
323
|
|
340
|
|
- this.inMuteOrUnmuteProgress = true;
|
|
324
|
+ /**
|
|
325
|
+ * Set to <tt>true</tt> when there's ongoing "mute/unmute" operation in
|
|
326
|
+ * progress. Used by {@link LocalSdpMunger}.
|
|
327
|
+ *
|
|
328
|
+ * @public
|
|
329
|
+ * @type {boolean}
|
|
330
|
+ */
|
|
331
|
+ this.setMutedInProgress = true;
|
341
|
332
|
|
342
|
333
|
// A function that will print info about muted status transition
|
343
|
|
- const logMuteInfo = () => logger.info(`Mute ${this}: ${mute}`);
|
|
334
|
+ const logMuteInfo = () => logger.info(`Mute ${this}: ${muted}`);
|
344
|
335
|
|
345
|
336
|
if (this.isAudioTrack()
|
346
|
|
- || this.videoType === VideoType.DESKTOP
|
347
|
|
- || !RTCBrowserType.doesVideoMuteByStreamRemove()) {
|
|
337
|
+ || this.videoType === VideoType.DESKTOP
|
|
338
|
+ || !RTCBrowserType.doesVideoMuteByStreamRemove()) {
|
348
|
339
|
logMuteInfo();
|
349
|
340
|
if (this.track) {
|
350
|
|
- this.track.enabled = !mute;
|
|
341
|
+ this.track.enabled = !muted;
|
351
|
342
|
}
|
352
|
|
- } else if (mute) {
|
|
343
|
+ } else if (muted) {
|
353
|
344
|
promise = new Promise((resolve, reject) => {
|
354
|
345
|
logMuteInfo();
|
355
|
|
- this._removeStreamFromConferenceAsMute(() => {
|
356
|
|
- // FIXME: Maybe here we should set the SRC for the
|
357
|
|
- // containers to something
|
358
|
|
- // We don't want any events to be fired on this stream
|
359
|
|
- this._unregisterHandlers();
|
360
|
|
- this._stopMediaStream();
|
361
|
|
- this._setStream(null);
|
362
|
|
- resolve();
|
363
|
|
- }, err => {
|
364
|
|
- reject(err);
|
365
|
|
- });
|
|
346
|
+ this._removeStreamFromConferenceAsMute(
|
|
347
|
+ () => {
|
|
348
|
+ // FIXME: Maybe here we should set the SRC for the
|
|
349
|
+ // containers to something
|
|
350
|
+ // We don't want any events to be fired on this stream
|
|
351
|
+ this._unregisterHandlers();
|
|
352
|
+ this._stopStream();
|
|
353
|
+ this._setStream(null);
|
|
354
|
+ resolve();
|
|
355
|
+ },
|
|
356
|
+ reject);
|
366
|
357
|
});
|
367
|
358
|
} else {
|
368
|
359
|
logMuteInfo();
|
|
@@ -397,8 +388,7 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
397
|
388
|
this.videoType = streamInfo.videoType;
|
398
|
389
|
}
|
399
|
390
|
} else {
|
400
|
|
- throw new JitsiTrackError(
|
401
|
|
- JitsiTrackErrors.TRACK_NO_STREAM_FOUND);
|
|
391
|
+ throw new JitsiTrackError(TRACK_NO_STREAM_FOUND);
|
402
|
392
|
}
|
403
|
393
|
|
404
|
394
|
this.containers = this.containers.map(
|
|
@@ -409,17 +399,17 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
409
|
399
|
}
|
410
|
400
|
|
411
|
401
|
return promise
|
412
|
|
- .then(() => this._sendMuteStatus(mute))
|
413
|
|
- .then(() => {
|
414
|
|
- this.inMuteOrUnmuteProgress = false;
|
415
|
|
- }, error => {
|
416
|
|
- this.inMuteOrUnmuteProgress = false;
|
417
|
|
-
|
418
|
|
- throw error;
|
419
|
|
- })
|
420
|
|
- .then(() => {
|
421
|
|
- this.emit(JitsiTrackEvents.TRACK_MUTE_CHANGED, this);
|
422
|
|
- });
|
|
402
|
+ .then(() => this._sendMuteStatus(muted))
|
|
403
|
+ .then(
|
|
404
|
+ /* onFulfilled */ () => {
|
|
405
|
+ this.setMutedInProgress = false;
|
|
406
|
+ },
|
|
407
|
+ /* onRejected */ error => {
|
|
408
|
+ this.setMutedInProgress = false;
|
|
409
|
+
|
|
410
|
+ throw error;
|
|
411
|
+ })
|
|
412
|
+ .then(() => this.emit(TRACK_MUTE_CHANGED, this));
|
423
|
413
|
}
|
424
|
414
|
|
425
|
415
|
/**
|
|
@@ -503,7 +493,7 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
503
|
493
|
}
|
504
|
494
|
|
505
|
495
|
if (this.stream) {
|
506
|
|
- this._stopMediaStream();
|
|
496
|
+ this._stopStream();
|
507
|
497
|
this.detach();
|
508
|
498
|
}
|
509
|
499
|
|
|
@@ -648,10 +638,23 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
648
|
638
|
/**
|
649
|
639
|
* Stops the associated MediaStream.
|
650
|
640
|
*/
|
651
|
|
- _stopMediaStream() {
|
652
|
|
- this.stopStreamInProgress = true;
|
653
|
|
- RTCUtils.stopMediaStream(this.stream);
|
654
|
|
- this.stopStreamInProgress = false;
|
|
641
|
+ _stopStream() {
|
|
642
|
+
|
|
643
|
+ /**
|
|
644
|
+ * Indicates that we are executing {@link #_stopStream} i.e.
|
|
645
|
+ * {@link RTCUtils#stopMediaStream} for the <tt>MediaStream</tt>
|
|
646
|
+ * associated with this <tt>JitsiTrack</tt> instance.
|
|
647
|
+ *
|
|
648
|
+ * @private
|
|
649
|
+ * @type {boolean}
|
|
650
|
+ */
|
|
651
|
+ this._stopStreamInProgress = true;
|
|
652
|
+
|
|
653
|
+ try {
|
|
654
|
+ RTCUtils.stopMediaStream(this.stream);
|
|
655
|
+ } finally {
|
|
656
|
+ this._stopStreamInProgress = false;
|
|
657
|
+ }
|
655
|
658
|
}
|
656
|
659
|
|
657
|
660
|
/**
|
|
@@ -685,8 +688,9 @@ export default class JitsiLocalTrack extends JitsiTrack {
|
685
|
688
|
* @returns {boolean} true if an issue is detected and false otherwise
|
686
|
689
|
*/
|
687
|
690
|
_checkForCameraIssues() {
|
688
|
|
- if (!this.isVideoTrack() || this.stopStreamInProgress
|
689
|
|
- || this.videoType === VideoType.DESKTOP) {
|
|
691
|
+ if (!this.isVideoTrack()
|
|
692
|
+ || this._stopStreamInProgress
|
|
693
|
+ || this.videoType === VideoType.DESKTOP) {
|
690
|
694
|
return false;
|
691
|
695
|
}
|
692
|
696
|
|