livekit-client 1.1.7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +1 -0
  2. package/dist/livekit-client.esm.mjs +667 -176
  3. package/dist/livekit-client.esm.mjs.map +1 -1
  4. package/dist/livekit-client.umd.js +1 -1
  5. package/dist/livekit-client.umd.js.map +1 -1
  6. package/dist/src/api/SignalClient.d.ts +4 -1
  7. package/dist/src/api/SignalClient.d.ts.map +1 -1
  8. package/dist/src/index.d.ts +4 -3
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/options.d.ts +1 -0
  11. package/dist/src/options.d.ts.map +1 -1
  12. package/dist/src/proto/livekit_models.d.ts +234 -0
  13. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  14. package/dist/src/proto/livekit_rtc.d.ts +944 -6
  15. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  16. package/dist/src/room/RTCEngine.d.ts +3 -2
  17. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  18. package/dist/src/room/Room.d.ts +3 -3
  19. package/dist/src/room/Room.d.ts.map +1 -1
  20. package/dist/src/room/events.d.ts +8 -1
  21. package/dist/src/room/events.d.ts.map +1 -1
  22. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  23. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  24. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  25. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  26. package/dist/src/room/track/LocalTrack.d.ts +2 -0
  27. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  28. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  29. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
  30. package/dist/src/room/track/RemoteTrackPublication.d.ts +4 -1
  31. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  32. package/dist/src/room/track/Track.d.ts +7 -0
  33. package/dist/src/room/track/Track.d.ts.map +1 -1
  34. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  35. package/package.json +3 -1
  36. package/src/api/SignalClient.ts +21 -6
  37. package/src/index.ts +6 -2
  38. package/src/options.ts +1 -0
  39. package/src/proto/livekit_models.ts +179 -4
  40. package/src/proto/livekit_rtc.ts +14 -1
  41. package/src/room/RTCEngine.ts +45 -18
  42. package/src/room/Room.ts +46 -25
  43. package/src/room/events.ts +7 -0
  44. package/src/room/participant/LocalParticipant.ts +45 -7
  45. package/src/room/participant/Participant.ts +2 -0
  46. package/src/room/participant/RemoteParticipant.ts +16 -10
  47. package/src/room/track/LocalAudioTrack.ts +16 -12
  48. package/src/room/track/LocalTrack.ts +41 -25
  49. package/src/room/track/LocalVideoTrack.ts +15 -11
  50. package/src/room/track/RemoteTrack.ts +1 -0
  51. package/src/room/track/RemoteTrackPublication.ts +37 -11
  52. package/src/room/track/Track.ts +12 -0
  53. package/src/room/track/TrackPublication.ts +2 -0
  54. package/dist/src/api/RequestQueue.d.ts +0 -13
  55. package/dist/src/api/RequestQueue.d.ts.map +0 -1
  56. package/src/api/RequestQueue.ts +0 -53
@@ -402,6 +402,8 @@ export enum TrackEvent {
402
402
  Muted = 'muted',
403
403
  Unmuted = 'unmuted',
404
404
  Ended = 'ended',
405
+ Subscribed = 'subscribed',
406
+ Unsubscribed = 'unsubscribed',
405
407
  /** @internal */
406
408
  UpdateSettings = 'updateSettings',
407
409
  /** @internal */
@@ -433,4 +435,9 @@ export enum TrackEvent {
433
435
  * Only fires on LocalTracks
434
436
  */
435
437
  UpstreamResumed = 'upstreamResumed',
438
+ /**
439
+ * @internal
440
+ * Fires on RemoteTrackPublication
441
+ */
442
+ SubscriptionPermissionChanged = 'subscriptionPermissionChanged',
436
443
  }
@@ -32,7 +32,7 @@ import {
32
32
  } from '../track/options';
33
33
  import { Track } from '../track/Track';
34
34
  import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
35
- import { isFireFox } from '../utils';
35
+ import { isFireFox, isWeb } from '../utils';
36
36
  import Participant from './Participant';
37
37
  import { ParticipantTrackPermission, trackPermissionToProto } from './ParticipantTrackPermission';
38
38
  import { computeVideoEncodings, mediaTrackToLocalTrack } from './publishUtils';
@@ -894,11 +894,49 @@ export default class LocalParticipant extends Participant {
894
894
  this.unpublishTrack(track.track!);
895
895
  };
896
896
 
897
- private handleTrackEnded = (track: LocalTrack) => {
898
- log.debug('unpublishing local track due to TrackEnded', {
899
- track: track.sid,
900
- });
901
- this.unpublishTrack(track);
897
+ private handleTrackEnded = async (track: LocalTrack) => {
898
+ if (
899
+ track.source === Track.Source.ScreenShare ||
900
+ track.source === Track.Source.ScreenShareAudio
901
+ ) {
902
+ log.debug('unpublishing local track due to TrackEnded', {
903
+ track: track.sid,
904
+ });
905
+ this.unpublishTrack(track);
906
+ } else if (track.isUserProvided) {
907
+ await track.pauseUpstream();
908
+ } else if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
909
+ try {
910
+ if (isWeb()) {
911
+ try {
912
+ const currentPermissions = await navigator?.permissions.query({
913
+ // the permission query for camera and microphone currently not supported in Safari and Firefox
914
+ // @ts-ignore
915
+ name: track.source === Track.Source.Camera ? 'camera' : 'microphone',
916
+ });
917
+ if (currentPermissions && currentPermissions.state === 'denied') {
918
+ log.warn(`user has revoked access to ${track.source}`);
919
+
920
+ // detect granted change after permissions were denied to try and resume then
921
+ currentPermissions.onchange = () => {
922
+ if (currentPermissions.state !== 'denied') {
923
+ track.restartTrack();
924
+ currentPermissions.onchange = null;
925
+ }
926
+ };
927
+ throw new Error('GetUserMedia Permission denied');
928
+ }
929
+ } catch (e: any) {
930
+ // permissions query fails for firefox, we continue and try to restart the track
931
+ }
932
+ }
933
+ log.debug('track ended, attempting to use a different device');
934
+ await track.restartTrack();
935
+ } catch (e) {
936
+ log.warn(`could not restart track, pausing upstream instead`);
937
+ await track.pauseUpstream();
938
+ }
939
+ }
902
940
  };
903
941
 
904
942
  private getPublicationForTrack(
@@ -975,7 +1013,7 @@ export default class LocalParticipant extends Participant {
975
1013
  this.tracks.forEach((track: LocalTrackPublication) => {
976
1014
  if (track.track !== undefined) {
977
1015
  infos.push({
978
- cid: track.track.mediaStreamTrack.id,
1016
+ cid: track.track.mediaStreamID,
979
1017
  track: track.trackInfo,
980
1018
  });
981
1019
  }
@@ -12,6 +12,7 @@ import RemoteTrackPublication from '../track/RemoteTrackPublication';
12
12
  import { Track } from '../track/Track';
13
13
  import { TrackPublication } from '../track/TrackPublication';
14
14
  import { RemoteTrack } from '../track/types';
15
+ import log from '../../logger';
15
16
 
16
17
  export enum ConnectionQuality {
17
18
  Excellent = 'excellent',
@@ -179,6 +180,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
179
180
  }
180
181
  // set this last so setMetadata can detect changes
181
182
  this.participantInfo = info;
183
+ log.trace('update participant info', { info });
182
184
  }
183
185
 
184
186
  /** @internal */
@@ -7,6 +7,7 @@ import RemoteAudioTrack from '../track/RemoteAudioTrack';
7
7
  import RemoteTrackPublication from '../track/RemoteTrackPublication';
8
8
  import RemoteVideoTrack from '../track/RemoteVideoTrack';
9
9
  import { Track } from '../track/Track';
10
+ import { TrackPublication } from '../track/TrackPublication';
10
11
  import { AdaptiveStreamSettings, RemoteTrack } from '../track/types';
11
12
  import Participant, { ParticipantEventCallbacks } from './Participant';
12
13
 
@@ -49,8 +50,17 @@ export default class RemoteParticipant extends Participant {
49
50
  });
50
51
  this.signalClient.sendUpdateSubscription(sub);
51
52
  });
52
- publication.on(TrackEvent.Ended, (track: RemoteTrack) => {
53
- this.emit(ParticipantEvent.TrackUnsubscribed, track, publication);
53
+ publication.on(
54
+ TrackEvent.SubscriptionPermissionChanged,
55
+ (status: TrackPublication.SubscriptionStatus) => {
56
+ this.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, publication, status);
57
+ },
58
+ );
59
+ publication.on(TrackEvent.Subscribed, (track: RemoteTrack) => {
60
+ this.emit(ParticipantEvent.TrackSubscribed, track, publication);
61
+ });
62
+ publication.on(TrackEvent.Unsubscribed, (previousTrack: RemoteTrack) => {
63
+ this.emit(ParticipantEvent.TrackUnsubscribed, previousTrack, publication);
54
64
  });
55
65
  }
56
66
 
@@ -156,8 +166,6 @@ export default class RemoteParticipant extends Participant {
156
166
  track.start();
157
167
 
158
168
  publication.setTrack(track);
159
- // subscription means participant has permissions to subscribe
160
- publication._allowed = true;
161
169
  // set participant volume on new microphone tracks
162
170
  if (
163
171
  this.volume !== undefined &&
@@ -166,7 +174,6 @@ export default class RemoteParticipant extends Participant {
166
174
  ) {
167
175
  track.setVolume(this.volume);
168
176
  }
169
- this.emit(ParticipantEvent.TrackSubscribed, track, publication);
170
177
 
171
178
  return publication;
172
179
  }
@@ -218,6 +225,10 @@ export default class RemoteParticipant extends Participant {
218
225
  // detect removed tracks
219
226
  this.tracks.forEach((publication) => {
220
227
  if (!validTracks.has(publication.trackSid)) {
228
+ log.trace('detected removed track on remote participant, unpublishing', {
229
+ publication,
230
+ participantSid: this.sid,
231
+ });
221
232
  this.unpublishTrack(publication.trackSid, true);
222
233
  }
223
234
  });
@@ -247,13 +258,8 @@ export default class RemoteParticipant extends Participant {
247
258
  // also send unsubscribe, if track is actively subscribed
248
259
  const { track } = publication;
249
260
  if (track) {
250
- const { isSubscribed } = publication;
251
261
  track.stop();
252
262
  publication.setTrack(undefined);
253
- // always send unsubscribed, since apps may rely on this
254
- if (isSubscribed) {
255
- this.emit(ParticipantEvent.TrackUnsubscribed, track, publication);
256
- }
257
263
  }
258
264
  if (sendUnpublish) {
259
265
  this.emit(ParticipantEvent.TrackUnpublished, publication);
@@ -35,22 +35,26 @@ export default class LocalAudioTrack extends LocalTrack {
35
35
  }
36
36
 
37
37
  async mute(): Promise<LocalAudioTrack> {
38
- // disabled special handling as it will cause BT headsets to switch communication modes
39
- if (this.source === Track.Source.Microphone && this.stopOnMute) {
40
- log.debug('stopping mic track');
41
- // also stop the track, so that microphone indicator is turned off
42
- this._mediaStreamTrack.stop();
43
- }
44
- await super.mute();
38
+ await this.muteQueue.run(async () => {
39
+ // disabled special handling as it will cause BT headsets to switch communication modes
40
+ if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
41
+ log.debug('stopping mic track');
42
+ // also stop the track, so that microphone indicator is turned off
43
+ this._mediaStreamTrack.stop();
44
+ }
45
+ await super.mute();
46
+ });
45
47
  return this;
46
48
  }
47
49
 
48
50
  async unmute(): Promise<LocalAudioTrack> {
49
- if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
50
- log.debug('reacquiring mic track');
51
- await this.restartTrack();
52
- }
53
- await super.unmute();
51
+ await this.muteQueue.run(async () => {
52
+ if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
53
+ log.debug('reacquiring mic track');
54
+ await this.restartTrack();
55
+ }
56
+ await super.unmute();
57
+ });
54
58
  return this;
55
59
  }
56
60
 
@@ -1,9 +1,10 @@
1
+ import Queue from 'async-await-queue';
1
2
  import log from '../../logger';
2
3
  import DeviceManager from '../DeviceManager';
3
4
  import { TrackInvalidError } from '../errors';
4
5
  import { TrackEvent } from '../events';
5
- import { VideoCodec } from './options';
6
6
  import { getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isMobile } from '../utils';
7
+ import { VideoCodec } from './options';
7
8
  import { attachToElement, detachTrack, Track } from './Track';
8
9
 
9
10
  export default class LocalTrack extends Track {
@@ -21,6 +22,8 @@ export default class LocalTrack extends Track {
21
22
 
22
23
  protected providedByUser: boolean;
23
24
 
25
+ protected muteQueue: Queue;
26
+
24
27
  protected constructor(
25
28
  mediaTrack: MediaStreamTrack,
26
29
  kind: Track.Kind,
@@ -33,6 +36,7 @@ export default class LocalTrack extends Track {
33
36
  this.reacquireTrack = false;
34
37
  this.wasMuted = false;
35
38
  this.providedByUser = userProvidedTrack;
39
+ this.muteQueue = new Queue();
36
40
  }
37
41
 
38
42
  get id(): string {
@@ -101,7 +105,9 @@ export default class LocalTrack extends Track {
101
105
  // on Safari, the old audio track must be stopped before attempting to acquire
102
106
  // the new track, otherwise the new track will stop with
103
107
  // 'A MediaStreamTrack ended due to a capture failure`
104
- this._mediaStreamTrack.stop();
108
+ if (!this.providedByUser) {
109
+ this._mediaStreamTrack.stop();
110
+ }
105
111
 
106
112
  track.addEventListener('ended', this.handleEnded);
107
113
  log.debug('replace MediaStreamTrack');
@@ -111,6 +117,8 @@ export default class LocalTrack extends Track {
111
117
  }
112
118
  this._mediaStreamTrack = track;
113
119
 
120
+ await this.resumeUpstream();
121
+
114
122
  this.attachedElements.forEach((el) => {
115
123
  attachToElement(track, el);
116
124
  });
@@ -160,6 +168,8 @@ export default class LocalTrack extends Track {
160
168
 
161
169
  this._mediaStreamTrack = newTrack;
162
170
 
171
+ await this.resumeUpstream();
172
+
163
173
  this.attachedElements.forEach((el) => {
164
174
  attachToElement(newTrack, el);
165
175
  });
@@ -170,6 +180,7 @@ export default class LocalTrack extends Track {
170
180
  }
171
181
 
172
182
  protected setTrackMuted(muted: boolean) {
183
+ log.debug(`setting ${this.kind} track ${muted ? 'muted' : 'unmuted'}`);
173
184
  if (this.isMuted === muted) {
174
185
  return;
175
186
  }
@@ -215,31 +226,36 @@ export default class LocalTrack extends Track {
215
226
  };
216
227
 
217
228
  async pauseUpstream() {
218
- if (this._isUpstreamPaused === true) {
219
- return;
220
- }
221
- if (!this.sender) {
222
- log.warn('unable to pause upstream for an unpublished track');
223
- return;
224
- }
225
- this._isUpstreamPaused = true;
226
- this.emit(TrackEvent.UpstreamPaused, this);
227
- const emptyTrack =
228
- this.kind === Track.Kind.Audio ? getEmptyAudioStreamTrack() : getEmptyVideoStreamTrack();
229
- await this.sender.replaceTrack(emptyTrack);
229
+ this.muteQueue.run(async () => {
230
+ if (this._isUpstreamPaused === true) {
231
+ return;
232
+ }
233
+ if (!this.sender) {
234
+ log.warn('unable to pause upstream for an unpublished track');
235
+ return;
236
+ }
237
+
238
+ this._isUpstreamPaused = true;
239
+ this.emit(TrackEvent.UpstreamPaused, this);
240
+ const emptyTrack =
241
+ this.kind === Track.Kind.Audio ? getEmptyAudioStreamTrack() : getEmptyVideoStreamTrack();
242
+ await this.sender.replaceTrack(emptyTrack);
243
+ });
230
244
  }
231
245
 
232
246
  async resumeUpstream() {
233
- if (this._isUpstreamPaused === false) {
234
- return;
235
- }
236
- if (!this.sender) {
237
- log.warn('unable to resume upstream for an unpublished track');
238
- return;
239
- }
240
- this._isUpstreamPaused = false;
241
- this.emit(TrackEvent.UpstreamResumed, this);
242
-
243
- await this.sender.replaceTrack(this._mediaStreamTrack);
247
+ this.muteQueue.run(async () => {
248
+ if (this._isUpstreamPaused === false) {
249
+ return;
250
+ }
251
+ if (!this.sender) {
252
+ log.warn('unable to resume upstream for an unpublished track');
253
+ return;
254
+ }
255
+ this._isUpstreamPaused = false;
256
+ this.emit(TrackEvent.UpstreamResumed, this);
257
+
258
+ await this.sender.replaceTrack(this._mediaStreamTrack);
259
+ });
244
260
  }
245
261
  }
@@ -86,21 +86,25 @@ export default class LocalVideoTrack extends LocalTrack {
86
86
  }
87
87
 
88
88
  async mute(): Promise<LocalVideoTrack> {
89
- if (this.source === Track.Source.Camera) {
90
- log.debug('stopping camera track');
91
- // also stop the track, so that camera indicator is turned off
92
- this._mediaStreamTrack.stop();
93
- }
94
- await super.mute();
89
+ await this.muteQueue.run(async () => {
90
+ if (this.source === Track.Source.Camera && !this.isUserProvided) {
91
+ log.debug('stopping camera track');
92
+ // also stop the track, so that camera indicator is turned off
93
+ this._mediaStreamTrack.stop();
94
+ }
95
+ await super.mute();
96
+ });
95
97
  return this;
96
98
  }
97
99
 
98
100
  async unmute(): Promise<LocalVideoTrack> {
99
- if (this.source === Track.Source.Camera && !this.isUserProvided) {
100
- log.debug('reacquiring camera track');
101
- await this.restartTrack();
102
- }
103
- await super.unmute();
101
+ await this.muteQueue.run(async () => {
102
+ if (this.source === Track.Source.Camera && !this.isUserProvided) {
103
+ log.debug('reacquiring camera track');
104
+ await this.restartTrack();
105
+ }
106
+ await super.unmute();
107
+ });
104
108
  return this;
105
109
  }
106
110
 
@@ -21,6 +21,7 @@ export default abstract class RemoteTrack extends Track {
21
21
  setMuted(muted: boolean) {
22
22
  if (this.isMuted !== muted) {
23
23
  this.isMuted = muted;
24
+ this._mediaStreamTrack.enabled = !muted;
24
25
  this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this);
25
26
  }
26
27
  }
@@ -11,7 +11,7 @@ export default class RemoteTrackPublication extends TrackPublication {
11
11
  track?: RemoteTrack;
12
12
 
13
13
  /** @internal */
14
- _allowed = true;
14
+ protected allowed = true;
15
15
 
16
16
  // keeps track of client's desire to subscribe to a track
17
17
  protected subscribed?: boolean;
@@ -28,6 +28,9 @@ export default class RemoteTrackPublication extends TrackPublication {
28
28
  */
29
29
  setSubscribed(subscribed: boolean) {
30
30
  this.subscribed = subscribed;
31
+ // reset allowed status when desired subscription state changes
32
+ // server will notify client via signal message if it's not allowed
33
+ this.allowed = true;
31
34
 
32
35
  const sub: UpdateSubscription = {
33
36
  trackSids: [this.trackSid],
@@ -46,11 +49,11 @@ export default class RemoteTrackPublication extends TrackPublication {
46
49
 
47
50
  get subscriptionStatus(): TrackPublication.SubscriptionStatus {
48
51
  if (this.subscribed === false || !super.isSubscribed) {
52
+ if (!this.allowed) {
53
+ return TrackPublication.SubscriptionStatus.NotAllowed;
54
+ }
49
55
  return TrackPublication.SubscriptionStatus.Unsubscribed;
50
56
  }
51
- if (!this._allowed) {
52
- return TrackPublication.SubscriptionStatus.NotAllowed;
53
- }
54
57
  return TrackPublication.SubscriptionStatus.Subscribed;
55
58
  }
56
59
 
@@ -61,9 +64,6 @@ export default class RemoteTrackPublication extends TrackPublication {
61
64
  if (this.subscribed === false) {
62
65
  return false;
63
66
  }
64
- if (!this._allowed) {
65
- return false;
66
- }
67
67
  return super.isSubscribed;
68
68
  }
69
69
 
@@ -127,11 +127,13 @@ export default class RemoteTrackPublication extends TrackPublication {
127
127
 
128
128
  /** @internal */
129
129
  setTrack(track?: Track) {
130
- if (this.track) {
130
+ const prevStatus = this.subscriptionStatus;
131
+ const prevTrack = this.track;
132
+ if (prevTrack) {
131
133
  // unregister listener
132
- this.track.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
133
- this.track.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
134
- this.track.off(TrackEvent.Ended, this.handleEnded);
134
+ prevTrack.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
135
+ prevTrack.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
136
+ prevTrack.off(TrackEvent.Ended, this.handleEnded);
135
137
  }
136
138
  super.setTrack(track);
137
139
  if (track) {
@@ -140,6 +142,22 @@ export default class RemoteTrackPublication extends TrackPublication {
140
142
  track.on(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
141
143
  track.on(TrackEvent.Ended, this.handleEnded);
142
144
  }
145
+ this.emitSubscriptionUpdateIfChanged(prevStatus);
146
+ if (!!track !== !!prevTrack) {
147
+ // when undefined status changes, there's a subscription changed event
148
+ if (track) {
149
+ this.emit(TrackEvent.Subscribed, track);
150
+ } else {
151
+ this.emit(TrackEvent.Unsubscribed, prevTrack);
152
+ }
153
+ }
154
+ }
155
+
156
+ /** @internal */
157
+ setAllowed(allowed: boolean) {
158
+ const prevStatus = this.subscriptionStatus;
159
+ this.allowed = allowed;
160
+ this.emitSubscriptionUpdateIfChanged(prevStatus);
143
161
  }
144
162
 
145
163
  /** @internal */
@@ -149,6 +167,14 @@ export default class RemoteTrackPublication extends TrackPublication {
149
167
  this.track?.setMuted(info.muted);
150
168
  }
151
169
 
170
+ private emitSubscriptionUpdateIfChanged(previousStatus: TrackPublication.SubscriptionStatus) {
171
+ const currentStatus = this.subscriptionStatus;
172
+ if (previousStatus === currentStatus) {
173
+ return;
174
+ }
175
+ this.emit(TrackEvent.SubscriptionPermissionChanged, currentStatus, previousStatus);
176
+ }
177
+
152
178
  private isManualOperationAllowed(): boolean {
153
179
  if (this.isAdaptiveStream) {
154
180
  log.warn('adaptive stream is enabled, cannot change track settings', {
@@ -37,6 +37,8 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
37
37
 
38
38
  protected _mediaStreamTrack: MediaStreamTrack;
39
39
 
40
+ protected _mediaStreamID: string;
41
+
40
42
  protected isInBackground: boolean;
41
43
 
42
44
  private backgroundTimeout: ReturnType<typeof setTimeout> | undefined;
@@ -47,6 +49,7 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
47
49
  super();
48
50
  this.kind = kind;
49
51
  this._mediaStreamTrack = mediaTrack;
52
+ this._mediaStreamID = mediaTrack.id;
50
53
  this.source = Track.Source.Unknown;
51
54
  if (isWeb()) {
52
55
  this.isInBackground = document.visibilityState === 'hidden';
@@ -65,6 +68,15 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
65
68
  return this._mediaStreamTrack;
66
69
  }
67
70
 
71
+ /**
72
+ * @internal
73
+ * used for keep mediaStream's first id, since it's id might change
74
+ * if we disable/enable a track
75
+ */
76
+ get mediaStreamID(): string {
77
+ return this._mediaStreamID;
78
+ }
79
+
68
80
  /**
69
81
  * creates a new HTMLAudioElement or HTMLVideoElement, attaches to it, and returns it
70
82
  */
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
+ import log from '../../logger';
2
3
  import { TrackInfo } from '../../proto/livekit_models';
3
4
  import { TrackEvent } from '../events';
4
5
  import LocalAudioTrack from './LocalAudioTrack';
@@ -108,6 +109,7 @@ export class TrackPublication extends EventEmitter {
108
109
  this.simulcasted = info.simulcast;
109
110
  }
110
111
  this.trackInfo = info;
112
+ log.trace('update publication info', { info });
111
113
  }
112
114
  }
113
115
 
@@ -1,13 +0,0 @@
1
- export default class Queue {
2
- private queue;
3
- private running;
4
- constructor();
5
- enqueue(cb: () => void): void;
6
- dequeue(): void;
7
- run(): Promise<void>;
8
- pause(): void;
9
- reset(): void;
10
- isRunning(): boolean;
11
- isEmpty(): boolean;
12
- }
13
- //# sourceMappingURL=RequestQueue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RequestQueue.d.ts","sourceRoot":"","sources":["../../../src/api/RequestQueue.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,OAAO,CAAC,KAAK,CAAoB;IAEjC,OAAO,CAAC,OAAO,CAAU;;IAOzB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI;IAKtB,OAAO;IAMD,GAAG;IAWT,KAAK;IAKL,KAAK;IAML,SAAS;IAIT,OAAO;CAGR"}
@@ -1,53 +0,0 @@
1
- import log from '../logger';
2
-
3
- export default class Queue {
4
- private queue: Array<() => void>;
5
-
6
- private running: boolean;
7
-
8
- constructor() {
9
- this.queue = [];
10
- this.running = false;
11
- }
12
-
13
- enqueue(cb: () => void) {
14
- log.trace('enqueuing request to fire later');
15
- this.queue.push(cb);
16
- }
17
-
18
- dequeue() {
19
- const evt = this.queue.shift();
20
- if (evt) evt();
21
- log.trace('firing request from queue');
22
- }
23
-
24
- async run() {
25
- if (this.running) return;
26
- log.trace('start queue');
27
- this.running = true;
28
- while (this.running && this.queue.length > 0) {
29
- this.dequeue();
30
- }
31
- this.running = false;
32
- log.trace('queue finished');
33
- }
34
-
35
- pause() {
36
- log.trace('pausing queue');
37
- this.running = false;
38
- }
39
-
40
- reset() {
41
- log.trace('resetting queue');
42
- this.running = false;
43
- this.queue = [];
44
- }
45
-
46
- isRunning() {
47
- return this.running;
48
- }
49
-
50
- isEmpty() {
51
- return this.queue.length === 0;
52
- }
53
- }