livekit-client 1.15.10 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. package/README.md +21 -17
  2. package/dist/livekit-client.esm.mjs +1603 -1493
  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 +1 -3
  7. package/dist/src/api/SignalClient.d.ts.map +1 -1
  8. package/dist/src/index.d.ts +3 -3
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/options.d.ts +3 -9
  11. package/dist/src/options.d.ts.map +1 -1
  12. package/dist/src/proto/livekit_models_pb.d.ts +47 -0
  13. package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
  14. package/dist/src/room/RTCEngine.d.ts +1 -0
  15. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  16. package/dist/src/room/Room.d.ts +14 -16
  17. package/dist/src/room/Room.d.ts.map +1 -1
  18. package/dist/src/room/defaults.d.ts.map +1 -1
  19. package/dist/src/room/events.d.ts +0 -4
  20. package/dist/src/room/events.d.ts.map +1 -1
  21. package/dist/src/room/participant/LocalParticipant.d.ts +8 -25
  22. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  23. package/dist/src/room/participant/Participant.d.ts +6 -10
  24. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  25. package/dist/src/room/participant/RemoteParticipant.d.ts +9 -6
  26. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  27. package/dist/src/room/timers.d.ts +4 -5
  28. package/dist/src/room/timers.d.ts.map +1 -1
  29. package/dist/src/room/track/LocalVideoTrack.d.ts +3 -3
  30. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  31. package/dist/src/room/track/RemoteTrackPublication.d.ts +2 -2
  32. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  33. package/dist/src/room/track/Track.d.ts +5 -0
  34. package/dist/src/room/track/Track.d.ts.map +1 -1
  35. package/dist/src/room/track/options.d.ts +0 -5
  36. package/dist/src/room/track/options.d.ts.map +1 -1
  37. package/dist/src/room/types.d.ts +11 -3
  38. package/dist/src/room/types.d.ts.map +1 -1
  39. package/dist/src/version.d.ts +1 -1
  40. package/dist/ts4.2/src/api/SignalClient.d.ts +1 -3
  41. package/dist/ts4.2/src/index.d.ts +3 -3
  42. package/dist/ts4.2/src/options.d.ts +3 -9
  43. package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +47 -0
  44. package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -0
  45. package/dist/ts4.2/src/room/Room.d.ts +14 -16
  46. package/dist/ts4.2/src/room/events.d.ts +0 -4
  47. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +8 -25
  48. package/dist/ts4.2/src/room/participant/Participant.d.ts +6 -10
  49. package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +9 -6
  50. package/dist/ts4.2/src/room/timers.d.ts +4 -5
  51. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +3 -3
  52. package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +2 -2
  53. package/dist/ts4.2/src/room/track/Track.d.ts +5 -0
  54. package/dist/ts4.2/src/room/track/options.d.ts +0 -5
  55. package/dist/ts4.2/src/room/types.d.ts +11 -3
  56. package/dist/ts4.2/src/version.d.ts +1 -1
  57. package/package.json +8 -7
  58. package/src/api/SignalClient.ts +10 -10
  59. package/src/e2ee/E2eeManager.ts +2 -2
  60. package/src/index.ts +2 -4
  61. package/src/options.ts +3 -10
  62. package/src/proto/livekit_models_pb.ts +66 -0
  63. package/src/room/RTCEngine.ts +6 -1
  64. package/src/room/Room.ts +169 -129
  65. package/src/room/defaults.ts +1 -5
  66. package/src/room/events.ts +0 -5
  67. package/src/room/participant/LocalParticipant.ts +36 -77
  68. package/src/room/participant/Participant.ts +23 -24
  69. package/src/room/participant/RemoteParticipant.ts +27 -24
  70. package/src/room/track/LocalVideoTrack.test.ts +1 -1
  71. package/src/room/track/LocalVideoTrack.ts +11 -7
  72. package/src/room/track/RemoteTrackPublication.ts +2 -7
  73. package/src/room/track/Track.ts +10 -1
  74. package/src/room/track/options.ts +0 -6
  75. package/src/room/types.ts +11 -4
  76. package/src/version.ts +1 -1
@@ -10,10 +10,6 @@ import { AudioPresets, ScreenSharePresets, VideoPresets } from './track/options'
10
10
  export const defaultVideoCodec = 'vp8';
11
11
 
12
12
  export const publishDefaults: TrackPublishDefaults = {
13
- /**
14
- * @deprecated
15
- */
16
- audioBitrate: AudioPresets.music.maxBitrate,
17
13
  audioPreset: AudioPresets.music,
18
14
  dtx: true,
19
15
  red: true,
@@ -41,7 +37,7 @@ export const roomOptionDefaults: InternalRoomOptions = {
41
37
  stopLocalTrackOnUnpublish: true,
42
38
  reconnectPolicy: new DefaultReconnectPolicy(),
43
39
  disconnectOnPageLeave: true,
44
- expWebAudioMix: false,
40
+ webAudioMix: true,
45
41
  } as const;
46
42
 
47
43
  export const roomConnectOptionDefaults: InternalRoomConnectOptions = {
@@ -38,11 +38,6 @@ export enum RoomEvent {
38
38
  */
39
39
  ConnectionStateChanged = 'connectionStateChanged',
40
40
 
41
- /**
42
- * @deprecated StateChanged has been renamed to ConnectionStateChanged
43
- */
44
- StateChanged = 'connectionStateChanged',
45
-
46
41
  /**
47
42
  * When input or output devices on the machine have changed.
48
43
  */
@@ -53,7 +53,6 @@ import {
53
53
  import Participant from './Participant';
54
54
  import type { ParticipantTrackPermission } from './ParticipantTrackPermission';
55
55
  import { trackPermissionToProto } from './ParticipantTrackPermission';
56
- import RemoteParticipant from './RemoteParticipant';
57
56
  import {
58
57
  computeTrackBackupEncodings,
59
58
  computeVideoEncodings,
@@ -61,12 +60,12 @@ import {
61
60
  } from './publishUtils';
62
61
 
63
62
  export default class LocalParticipant extends Participant {
64
- audioTracks: Map<string, LocalTrackPublication>;
63
+ audioTrackPublications: Map<string, LocalTrackPublication>;
65
64
 
66
- videoTracks: Map<string, LocalTrackPublication>;
65
+ videoTrackPublications: Map<string, LocalTrackPublication>;
67
66
 
68
67
  /** map of track sid => all published tracks */
69
- tracks: Map<string, LocalTrackPublication>;
68
+ trackPublications: Map<string, LocalTrackPublication>;
70
69
 
71
70
  /** @internal */
72
71
  engine: RTCEngine;
@@ -99,9 +98,9 @@ export default class LocalParticipant extends Participant {
99
98
  loggerName: options.loggerName,
100
99
  loggerContextCb: () => this.engine.logContext,
101
100
  });
102
- this.audioTracks = new Map();
103
- this.videoTracks = new Map();
104
- this.tracks = new Map();
101
+ this.audioTrackPublications = new Map();
102
+ this.videoTrackPublications = new Map();
103
+ this.trackPublications = new Map();
105
104
  this.engine = engine;
106
105
  this.roomOptions = options;
107
106
  this.setupEngine(engine);
@@ -120,15 +119,15 @@ export default class LocalParticipant extends Participant {
120
119
  return this.encryptionType !== Encryption_Type.NONE;
121
120
  }
122
121
 
123
- getTrack(source: Track.Source): LocalTrackPublication | undefined {
124
- const track = super.getTrack(source);
122
+ getTrackPublication(source: Track.Source): LocalTrackPublication | undefined {
123
+ const track = super.getTrackPublication(source);
125
124
  if (track) {
126
125
  return track as LocalTrackPublication;
127
126
  }
128
127
  }
129
128
 
130
- getTrackByName(name: string): LocalTrackPublication | undefined {
131
- const track = super.getTrackByName(name);
129
+ getTrackPublicationByName(name: string): LocalTrackPublication | undefined {
130
+ const track = super.getTrackPublicationByName(name);
132
131
  if (track) {
133
132
  return track as LocalTrackPublication;
134
133
  }
@@ -140,7 +139,7 @@ export default class LocalParticipant extends Participant {
140
139
  setupEngine(engine: RTCEngine) {
141
140
  this.engine = engine;
142
141
  this.engine.on(EngineEvent.RemoteMute, (trackSid: string, muted: boolean) => {
143
- const pub = this.tracks.get(trackSid);
142
+ const pub = this.trackPublications.get(trackSid);
144
143
  if (!pub || !pub.track) {
145
144
  return;
146
145
  }
@@ -290,7 +289,7 @@ export default class LocalParticipant extends Participant {
290
289
  publishOptions?: TrackPublishOptions,
291
290
  ) {
292
291
  this.log.debug('setTrackEnabled', { ...this.logContext, source, enabled });
293
- let track = this.getTrack(source);
292
+ let track = this.getTrackPublication(source);
294
293
  if (enabled) {
295
294
  if (track) {
296
295
  await track.unmute();
@@ -351,7 +350,7 @@ export default class LocalParticipant extends Participant {
351
350
  // screenshare cannot be muted, unpublish instead
352
351
  if (source === Track.Source.ScreenShare) {
353
352
  track = await this.unpublishTrack(track.track);
354
- const screenAudioTrack = this.getTrack(Track.Source.ScreenShareAudio);
353
+ const screenAudioTrack = this.getTrackPublication(Track.Source.ScreenShareAudio);
355
354
  if (screenAudioTrack && screenAudioTrack.track) {
356
355
  this.unpublishTrack(screenAudioTrack.track);
357
356
  }
@@ -573,7 +572,7 @@ export default class LocalParticipant extends Participant {
573
572
 
574
573
  // is it already published? if so skip
575
574
  let existingPublication: LocalTrackPublication | undefined;
576
- this.tracks.forEach((publication) => {
575
+ this.trackPublications.forEach((publication) => {
577
576
  if (!publication.track) {
578
577
  return;
579
578
  }
@@ -651,7 +650,7 @@ export default class LocalParticipant extends Participant {
651
650
  }
652
651
 
653
652
  private async publish(track: LocalTrack, opts: TrackPublishOptions, isStereo: boolean) {
654
- const existingTrackOfSource = Array.from(this.tracks.values()).find(
653
+ const existingTrackOfSource = Array.from(this.trackPublications.values()).find(
655
654
  (publishedTrack) => track instanceof LocalTrack && publishedTrack.source === track.source,
656
655
  );
657
656
  if (existingTrackOfSource && track.source !== Track.Source.Unknown) {
@@ -788,7 +787,7 @@ export default class LocalParticipant extends Participant {
788
787
  } else if (track.kind === Track.Kind.Audio) {
789
788
  encodings = [
790
789
  {
791
- maxBitrate: opts.audioPreset?.maxBitrate ?? opts.audioBitrate,
790
+ maxBitrate: opts.audioPreset?.maxBitrate,
792
791
  priority: opts.audioPreset?.priority ?? 'high',
793
792
  networkPriority: opts.audioPreset?.priority ?? 'high',
794
793
  },
@@ -914,7 +913,7 @@ export default class LocalParticipant extends Participant {
914
913
 
915
914
  // is it not published? if so skip
916
915
  let existingPublication: LocalTrackPublication | undefined;
917
- this.tracks.forEach((publication) => {
916
+ this.trackPublications.forEach((publication) => {
918
917
  if (!publication.track) {
919
918
  return;
920
919
  }
@@ -947,6 +946,9 @@ export default class LocalParticipant extends Participant {
947
946
  return;
948
947
  }
949
948
  const simulcastTrack = track.addSimulcastTrack(videoCodec, encodings);
949
+ if (!simulcastTrack) {
950
+ return;
951
+ }
950
952
  const req = new AddTrackRequest({
951
953
  cid: simulcastTrack.mediaStreamTrack.id,
952
954
  type: Track.kindToProto(track.kind),
@@ -1061,13 +1063,13 @@ export default class LocalParticipant extends Participant {
1061
1063
  }
1062
1064
 
1063
1065
  // remove from our maps
1064
- this.tracks.delete(publication.trackSid);
1066
+ this.trackPublications.delete(publication.trackSid);
1065
1067
  switch (publication.kind) {
1066
1068
  case Track.Kind.Audio:
1067
- this.audioTracks.delete(publication.trackSid);
1069
+ this.audioTrackPublications.delete(publication.trackSid);
1068
1070
  break;
1069
1071
  case Track.Kind.Video:
1070
- this.videoTracks.delete(publication.trackSid);
1072
+ this.videoTrackPublications.delete(publication.trackSid);
1071
1073
  break;
1072
1074
  default:
1073
1075
  break;
@@ -1093,7 +1095,7 @@ export default class LocalParticipant extends Participant {
1093
1095
 
1094
1096
  async republishAllTracks(options?: TrackPublishOptions, restartTracks: boolean = true) {
1095
1097
  const localPubs: LocalTrackPublication[] = [];
1096
- this.tracks.forEach((pub) => {
1098
+ this.trackPublications.forEach((pub) => {
1097
1099
  if (pub.track) {
1098
1100
  if (options) {
1099
1101
  pub.options = { ...pub.options, ...options };
@@ -1132,64 +1134,21 @@ export default class LocalParticipant extends Participant {
1132
1134
  * participant in the room if the destination field in publishOptions is empty
1133
1135
  *
1134
1136
  * @param data Uint8Array of the payload. To send string data, use TextEncoder.encode
1135
- * @param kind whether to send this as reliable or lossy.
1136
- * For data that you need delivery guarantee (such as chat messages), use Reliable.
1137
- * For data that should arrive as quickly as possible, but you are ok with dropped
1138
- * packets, use Lossy.
1139
- * @param publishOptions optionally specify a `topic` and `destination`
1137
+ * @param options optionally specify a `reliable`, `topic` and `destination`
1140
1138
  */
1141
- async publishData(
1142
- data: Uint8Array,
1143
- kind: DataPacket_Kind,
1144
- publishOptions?: DataPublishOptions,
1145
- ): Promise<void>;
1146
- /**
1147
- * Publish a new data payload to the room. Data will be forwarded to each
1148
- * participant in the room if the destination argument is empty
1149
- *
1150
- * @param data Uint8Array of the payload. To send string data, use TextEncoder.encode
1151
- * @param kind whether to send this as reliable or lossy.
1152
- * For data that you need delivery guarantee (such as chat messages), use Reliable.
1153
- * For data that should arrive as quickly as possible, but you are ok with dropped
1154
- * packets, use Lossy.
1155
- * @param destination the participants who will receive the message
1156
- */
1157
- async publishData(
1158
- data: Uint8Array,
1159
- kind: DataPacket_Kind,
1160
- destination?: RemoteParticipant[] | string[],
1161
- ): Promise<void>;
1162
-
1163
- async publishData(
1164
- data: Uint8Array,
1165
- kind: DataPacket_Kind,
1166
- publishOptions: DataPublishOptions | RemoteParticipant[] | string[] = {},
1167
- ) {
1168
- const destination = Array.isArray(publishOptions)
1169
- ? publishOptions
1170
- : publishOptions?.destination;
1171
- const destinationSids: string[] = [];
1172
-
1173
- const topic = !Array.isArray(publishOptions) ? publishOptions.topic : undefined;
1174
-
1175
- if (destination !== undefined) {
1176
- destination.forEach((val: any) => {
1177
- if (val instanceof RemoteParticipant) {
1178
- destinationSids.push(val.sid);
1179
- } else {
1180
- destinationSids.push(val);
1181
- }
1182
- });
1183
- }
1139
+ async publishData(data: Uint8Array, options: DataPublishOptions = {}): Promise<void> {
1140
+ const kind = options.reliable ? DataPacket_Kind.RELIABLE : DataPacket_Kind.LOSSY;
1141
+ const destinationIdentities = options.destinationIdentities;
1142
+ const topic = options.topic;
1184
1143
 
1185
1144
  const packet = new DataPacket({
1186
- kind,
1145
+ kind: kind,
1187
1146
  value: {
1188
1147
  case: 'user',
1189
1148
  value: new UserPacket({
1190
- participantSid: this.sid,
1149
+ participantIdentity: this.identity,
1191
1150
  payload: data,
1192
- destinationSids: destinationSids,
1151
+ destinationIdentities,
1193
1152
  topic,
1194
1153
  }),
1195
1154
  },
@@ -1241,7 +1200,7 @@ export default class LocalParticipant extends Participant {
1241
1200
  // if server's track mute status doesn't match actual, we'll have to update
1242
1201
  // the server's copy
1243
1202
  info.tracks.forEach((ti) => {
1244
- const pub = this.tracks.get(ti.sid);
1203
+ const pub = this.trackPublications.get(ti.sid);
1245
1204
 
1246
1205
  if (pub) {
1247
1206
  const mutedOnServer = pub.isMuted || (pub.track?.isUpstreamPaused ?? false);
@@ -1313,7 +1272,7 @@ export default class LocalParticipant extends Participant {
1313
1272
  if (!this.roomOptions?.dynacast) {
1314
1273
  return;
1315
1274
  }
1316
- const pub = this.videoTracks.get(update.trackSid);
1275
+ const pub = this.videoTrackPublications.get(update.trackSid);
1317
1276
  if (!pub) {
1318
1277
  this.log.warn('received subscribed quality update for unknown track', {
1319
1278
  ...this.logContext,
@@ -1341,7 +1300,7 @@ export default class LocalParticipant extends Participant {
1341
1300
  };
1342
1301
 
1343
1302
  private handleLocalTrackUnpublished = (unpublished: TrackUnpublishedResponse) => {
1344
- const track = this.tracks.get(unpublished.trackSid);
1303
+ const track = this.trackPublications.get(unpublished.trackSid);
1345
1304
  if (!track) {
1346
1305
  this.log.warn('received unpublished event for unknown track', {
1347
1306
  ...this.logContext,
@@ -1415,7 +1374,7 @@ export default class LocalParticipant extends Participant {
1415
1374
  track: LocalTrack | MediaStreamTrack,
1416
1375
  ): LocalTrackPublication | undefined {
1417
1376
  let publication: LocalTrackPublication | undefined;
1418
- this.tracks.forEach((pub) => {
1377
+ this.trackPublications.forEach((pub) => {
1419
1378
  const localTrack = pub.track;
1420
1379
  if (!localTrack) {
1421
1380
  return;
@@ -48,12 +48,12 @@ function qualityFromProto(q: ProtoQuality): ConnectionQuality {
48
48
  export default class Participant extends (EventEmitter as new () => TypedEmitter<ParticipantEventCallbacks>) {
49
49
  protected participantInfo?: ParticipantInfo;
50
50
 
51
- audioTracks: Map<string, TrackPublication>;
51
+ audioTrackPublications: Map<string, TrackPublication>;
52
52
 
53
- videoTracks: Map<string, TrackPublication>;
53
+ videoTrackPublications: Map<string, TrackPublication>;
54
54
 
55
55
  /** map of track sid => all published tracks */
56
- tracks: Map<string, TrackPublication>;
56
+ trackPublications: Map<string, TrackPublication>;
57
57
 
58
58
  /** audio level between 0-1.0, 1 being loudest, 0 being softest */
59
59
  audioLevel: number = 0;
@@ -94,7 +94,10 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
94
94
  }
95
95
 
96
96
  get isEncrypted() {
97
- return this.tracks.size > 0 && Array.from(this.tracks.values()).every((tr) => tr.isEncrypted);
97
+ return (
98
+ this.trackPublications.size > 0 &&
99
+ Array.from(this.trackPublications.values()).every((tr) => tr.isEncrypted)
100
+ );
98
101
  }
99
102
 
100
103
  get isAgent() {
@@ -119,23 +122,21 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
119
122
  this.identity = identity;
120
123
  this.name = name;
121
124
  this.metadata = metadata;
122
- this.audioTracks = new Map();
123
- this.videoTracks = new Map();
124
- this.tracks = new Map();
125
+ this.audioTrackPublications = new Map();
126
+ this.videoTrackPublications = new Map();
127
+ this.trackPublications = new Map();
125
128
  }
126
129
 
127
- getTracks(): TrackPublication[] {
128
- return Array.from(this.tracks.values());
130
+ getTrackPublications(): TrackPublication[] {
131
+ return Array.from(this.trackPublications.values());
129
132
  }
130
133
 
131
134
  /**
132
135
  * Finds the first track that matches the source filter, for example, getting
133
136
  * the user's camera track with getTrackBySource(Track.Source.Camera).
134
- * @param source
135
- * @returns
136
137
  */
137
- getTrack(source: Track.Source): TrackPublication | undefined {
138
- for (const [, pub] of this.tracks) {
138
+ getTrackPublication(source: Track.Source): TrackPublication | undefined {
139
+ for (const [, pub] of this.trackPublications) {
139
140
  if (pub.source === source) {
140
141
  return pub;
141
142
  }
@@ -144,11 +145,9 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
144
145
 
145
146
  /**
146
147
  * Finds the first track that matches the track's name.
147
- * @param name
148
- * @returns
149
148
  */
150
- getTrackByName(name: string): TrackPublication | undefined {
151
- for (const [, pub] of this.tracks) {
149
+ getTrackPublicationByName(name: string): TrackPublication | undefined {
150
+ for (const [, pub] of this.trackPublications) {
152
151
  if (pub.trackName === name) {
153
152
  return pub;
154
153
  }
@@ -160,17 +159,17 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
160
159
  }
161
160
 
162
161
  get isCameraEnabled(): boolean {
163
- const track = this.getTrack(Track.Source.Camera);
162
+ const track = this.getTrackPublication(Track.Source.Camera);
164
163
  return !(track?.isMuted ?? true);
165
164
  }
166
165
 
167
166
  get isMicrophoneEnabled(): boolean {
168
- const track = this.getTrack(Track.Source.Microphone);
167
+ const track = this.getTrackPublication(Track.Source.Microphone);
169
168
  return !(track?.isMuted ?? true);
170
169
  }
171
170
 
172
171
  get isScreenShareEnabled(): boolean {
173
- const track = this.getTrack(Track.Source.ScreenShare);
172
+ const track = this.getTrackPublication(Track.Source.ScreenShare);
174
173
  return !!track;
175
174
  }
176
175
 
@@ -283,7 +282,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
283
282
  */
284
283
  setAudioContext(ctx: AudioContext | undefined) {
285
284
  this.audioContext = ctx;
286
- this.audioTracks.forEach(
285
+ this.audioTrackPublications.forEach(
287
286
  (track) =>
288
287
  (track.track instanceof RemoteAudioTrack || track.track instanceof LocalAudioTrack) &&
289
288
  track.track.setAudioContext(ctx),
@@ -305,13 +304,13 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
305
304
  pub.track.sid = publication.trackSid;
306
305
  }
307
306
 
308
- this.tracks.set(publication.trackSid, publication);
307
+ this.trackPublications.set(publication.trackSid, publication);
309
308
  switch (publication.kind) {
310
309
  case Track.Kind.Audio:
311
- this.audioTracks.set(publication.trackSid, publication);
310
+ this.audioTrackPublications.set(publication.trackSid, publication);
312
311
  break;
313
312
  case Track.Kind.Video:
314
- this.videoTracks.set(publication.trackSid, publication);
313
+ this.videoTrackPublications.set(publication.trackSid, publication);
315
314
  break;
316
315
  default:
317
316
  break;
@@ -16,11 +16,11 @@ import Participant from './Participant';
16
16
  import type { ParticipantEventCallbacks } from './Participant';
17
17
 
18
18
  export default class RemoteParticipant extends Participant {
19
- audioTracks: Map<string, RemoteTrackPublication>;
19
+ audioTrackPublications: Map<string, RemoteTrackPublication>;
20
20
 
21
- videoTracks: Map<string, RemoteTrackPublication>;
21
+ videoTrackPublications: Map<string, RemoteTrackPublication>;
22
22
 
23
- tracks: Map<string, RemoteTrackPublication>;
23
+ trackPublications: Map<string, RemoteTrackPublication>;
24
24
 
25
25
  signalClient: SignalClient;
26
26
 
@@ -44,9 +44,9 @@ export default class RemoteParticipant extends Participant {
44
44
  ) {
45
45
  super(sid, identity || '', name, metadata, loggerOptions);
46
46
  this.signalClient = signalClient;
47
- this.tracks = new Map();
48
- this.audioTracks = new Map();
49
- this.videoTracks = new Map();
47
+ this.trackPublications = new Map();
48
+ this.audioTrackPublications = new Map();
49
+ this.videoTrackPublications = new Map();
50
50
  this.volumeMap = new Map();
51
51
  }
52
52
 
@@ -90,15 +90,15 @@ export default class RemoteParticipant extends Participant {
90
90
  });
91
91
  }
92
92
 
93
- getTrack(source: Track.Source): RemoteTrackPublication | undefined {
94
- const track = super.getTrack(source);
93
+ getTrackPublication(source: Track.Source): RemoteTrackPublication | undefined {
94
+ const track = super.getTrackPublication(source);
95
95
  if (track) {
96
96
  return track as RemoteTrackPublication;
97
97
  }
98
98
  }
99
99
 
100
- getTrackByName(name: string): RemoteTrackPublication | undefined {
101
- const track = super.getTrackByName(name);
100
+ getTrackPublicationByName(name: string): RemoteTrackPublication | undefined {
101
+ const track = super.getTrackPublicationByName(name);
102
102
  if (track) {
103
103
  return track as RemoteTrackPublication;
104
104
  }
@@ -115,7 +115,7 @@ export default class RemoteParticipant extends Participant {
115
115
  source: Track.Source.Microphone | Track.Source.ScreenShareAudio = Track.Source.Microphone,
116
116
  ) {
117
117
  this.volumeMap.set(source, volume);
118
- const audioPublication = this.getTrack(source);
118
+ const audioPublication = this.getTrackPublication(source);
119
119
  if (audioPublication && audioPublication.track) {
120
120
  (audioPublication.track as RemoteAudioTrack).setVolume(volume);
121
121
  }
@@ -127,7 +127,7 @@ export default class RemoteParticipant extends Participant {
127
127
  getVolume(
128
128
  source: Track.Source.Microphone | Track.Source.ScreenShareAudio = Track.Source.Microphone,
129
129
  ) {
130
- const audioPublication = this.getTrack(source);
130
+ const audioPublication = this.getTrackPublication(source);
131
131
  if (audioPublication && audioPublication.track) {
132
132
  return (audioPublication.track as RemoteAudioTrack).getVolume();
133
133
  }
@@ -145,14 +145,14 @@ export default class RemoteParticipant extends Participant {
145
145
  ) {
146
146
  // find the track publication
147
147
  // it's possible for the media track to arrive before participant info
148
- let publication = this.getTrackPublication(sid);
148
+ let publication = this.getTrackPublicationBySid(sid);
149
149
 
150
150
  // it's also possible that the browser didn't honor our original track id
151
151
  // FireFox would use its own local uuid instead of server track id
152
152
  if (!publication) {
153
153
  if (!sid.startsWith('TR')) {
154
154
  // find the first track that matches type
155
- this.tracks.forEach((p) => {
155
+ this.trackPublications.forEach((p) => {
156
156
  if (!publication && mediaTrack.kind === p.kind.toString()) {
157
157
  publication = p;
158
158
  }
@@ -224,8 +224,11 @@ export default class RemoteParticipant extends Participant {
224
224
  return !!this.participantInfo;
225
225
  }
226
226
 
227
- getTrackPublication(sid: Track.SID): RemoteTrackPublication | undefined {
228
- return this.tracks.get(sid);
227
+ /**
228
+ * @internal
229
+ */
230
+ getTrackPublicationBySid(sid: Track.SID): RemoteTrackPublication | undefined {
231
+ return this.trackPublications.get(sid);
229
232
  }
230
233
 
231
234
  /** @internal */
@@ -243,7 +246,7 @@ export default class RemoteParticipant extends Participant {
243
246
  const newTracks = new Map<string, RemoteTrackPublication>();
244
247
 
245
248
  info.tracks.forEach((ti) => {
246
- let publication = this.getTrackPublication(ti.sid);
249
+ let publication = this.getTrackPublicationBySid(ti.sid);
247
250
  if (!publication) {
248
251
  // new publication
249
252
  const kind = Track.kindFromProto(ti.type);
@@ -258,7 +261,7 @@ export default class RemoteParticipant extends Participant {
258
261
  );
259
262
  publication.updateInfo(ti);
260
263
  newTracks.set(ti.sid, publication);
261
- const existingTrackOfSource = Array.from(this.tracks.values()).find(
264
+ const existingTrackOfSource = Array.from(this.trackPublications.values()).find(
262
265
  (publishedTrack) => publishedTrack.source === publication?.source,
263
266
  );
264
267
  if (existingTrackOfSource && publication.source !== Track.Source.Unknown) {
@@ -279,7 +282,7 @@ export default class RemoteParticipant extends Participant {
279
282
  });
280
283
 
281
284
  // detect removed tracks
282
- this.tracks.forEach((publication) => {
285
+ this.trackPublications.forEach((publication) => {
283
286
  if (!validTracks.has(publication.trackSid)) {
284
287
  this.log.trace('detected removed track on remote participant, unpublishing', {
285
288
  ...this.logContext,
@@ -298,7 +301,7 @@ export default class RemoteParticipant extends Participant {
298
301
 
299
302
  /** @internal */
300
303
  unpublishTrack(sid: Track.SID, sendUnpublish?: boolean) {
301
- const publication = <RemoteTrackPublication>this.tracks.get(sid);
304
+ const publication = <RemoteTrackPublication>this.trackPublications.get(sid);
302
305
  if (!publication) {
303
306
  return;
304
307
  }
@@ -311,15 +314,15 @@ export default class RemoteParticipant extends Participant {
311
314
  }
312
315
 
313
316
  // remove track from maps only after unsubscribed has been fired
314
- this.tracks.delete(sid);
317
+ this.trackPublications.delete(sid);
315
318
 
316
319
  // remove from the right type map
317
320
  switch (publication.kind) {
318
321
  case Track.Kind.Audio:
319
- this.audioTracks.delete(sid);
322
+ this.audioTrackPublications.delete(sid);
320
323
  break;
321
324
  case Track.Kind.Video:
322
- this.videoTracks.delete(sid);
325
+ this.videoTrackPublications.delete(sid);
323
326
  break;
324
327
  default:
325
328
  break;
@@ -336,7 +339,7 @@ export default class RemoteParticipant extends Participant {
336
339
  async setAudioOutput(output: AudioOutputOptions) {
337
340
  this.audioOutput = output;
338
341
  const promises: Promise<void>[] = [];
339
- this.audioTracks.forEach((pub) => {
342
+ this.audioTrackPublications.forEach((pub) => {
340
343
  if (pub.track instanceof RemoteAudioTrack) {
341
344
  promises.push(pub.track.setSinkId(output.deviceId ?? 'default'));
342
345
  }
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { VideoQuality } from '../../proto/livekit_models_pb';
3
2
  import { videoLayersFromEncodings } from './LocalVideoTrack';
3
+ import { VideoQuality } from './Track';
4
4
 
5
5
  describe('videoLayersFromEncodings', () => {
6
6
  it('returns single layer for no encoding', () => {
@@ -1,6 +1,6 @@
1
1
  import type { SignalClient } from '../../api/SignalClient';
2
2
  import type { StructuredLogger } from '../../logger';
3
- import { VideoLayer, VideoQuality } from '../../proto/livekit_models_pb';
3
+ import { VideoQuality as ProtoVideoQuality, VideoLayer } from '../../proto/livekit_models_pb';
4
4
  import { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc_pb';
5
5
  import { ScalabilityMode } from '../participant/publishUtils';
6
6
  import type { VideoSenderStats } from '../stats';
@@ -8,7 +8,7 @@ import { computeBitrate, monitorFrequency } from '../stats';
8
8
  import type { LoggerOptions } from '../types';
9
9
  import { Mutex, isFireFox, isMobile, isWeb, unwrapConstraint } from '../utils';
10
10
  import LocalTrack from './LocalTrack';
11
- import { Track } from './Track';
11
+ import { Track, VideoQuality } from './Track';
12
12
  import type { VideoCaptureOptions, VideoCodec } from './options';
13
13
  import type { TrackProcessor } from './processor/types';
14
14
  import { constraintsForOptions } from './utils';
@@ -254,9 +254,13 @@ export default class LocalVideoTrack extends LocalTrack {
254
254
  }
255
255
  }
256
256
 
257
- addSimulcastTrack(codec: VideoCodec, encodings?: RTCRtpEncodingParameters[]): SimulcastTrackInfo {
257
+ addSimulcastTrack(
258
+ codec: VideoCodec,
259
+ encodings?: RTCRtpEncodingParameters[],
260
+ ): SimulcastTrackInfo | undefined {
258
261
  if (this.simulcastCodecs.has(codec)) {
259
- throw new Error(`${codec} already added`);
262
+ this.log.error(`${codec} already added, skipping adding simulcast codec`, this.logContext);
263
+ return;
260
264
  }
261
265
  const simulcastCodecInfo: SimulcastTrackInfo = {
262
266
  codec,
@@ -427,14 +431,14 @@ async function setPublishingLayersForSender(
427
431
  const encoding = encodings[0];
428
432
  /* @ts-ignore */
429
433
  // const mode = new ScalabilityMode(encoding.scalabilityMode);
430
- let maxQuality = VideoQuality.OFF;
434
+ let maxQuality = ProtoVideoQuality.OFF;
431
435
  qualities.forEach((q) => {
432
- if (q.enabled && (maxQuality === VideoQuality.OFF || q.quality > maxQuality)) {
436
+ if (q.enabled && (maxQuality === ProtoVideoQuality.OFF || q.quality > maxQuality)) {
433
437
  maxQuality = q.quality;
434
438
  }
435
439
  });
436
440
 
437
- if (maxQuality === VideoQuality.OFF) {
441
+ if (maxQuality === ProtoVideoQuality.OFF) {
438
442
  if (encoding.active) {
439
443
  encoding.active = false;
440
444
  hasChanged = true;
@@ -1,15 +1,10 @@
1
- import {
2
- ParticipantTracks,
3
- SubscriptionError,
4
- TrackInfo,
5
- VideoQuality,
6
- } from '../../proto/livekit_models_pb';
1
+ import { ParticipantTracks, SubscriptionError, TrackInfo } from '../../proto/livekit_models_pb';
7
2
  import { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc_pb';
8
3
  import { TrackEvent } from '../events';
9
4
  import type { LoggerOptions } from '../types';
10
5
  import type RemoteTrack from './RemoteTrack';
11
6
  import RemoteVideoTrack from './RemoteVideoTrack';
12
- import { Track } from './Track';
7
+ import { Track, VideoQuality } from './Track';
13
8
  import { TrackPublication } from './TrackPublication';
14
9
 
15
10
  export default class RemoteTrackPublication extends TrackPublication {
@@ -2,7 +2,11 @@ import { EventEmitter } from 'events';
2
2
  import type TypedEventEmitter from 'typed-emitter';
3
3
  import type { SignalClient } from '../../api/SignalClient';
4
4
  import log, { LoggerNames, StructuredLogger, getLogger } from '../../logger';
5
- import { TrackSource, TrackType } from '../../proto/livekit_models_pb';
5
+ import {
6
+ VideoQuality as ProtoQuality,
7
+ TrackSource,
8
+ TrackType,
9
+ } from '../../proto/livekit_models_pb';
6
10
  import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc_pb';
7
11
  import { TrackEvent } from '../events';
8
12
  import type { LoggerOptions } from '../types';
@@ -15,6 +19,11 @@ const BACKGROUND_REACTION_DELAY = 5000;
15
19
  // Safari tracks which audio elements have been "blessed" by the user.
16
20
  const recycledElements: Array<HTMLAudioElement> = [];
17
21
 
22
+ export enum VideoQuality {
23
+ LOW = ProtoQuality.LOW,
24
+ MEDIUM = ProtoQuality.MEDIUM,
25
+ HIGH = ProtoQuality.HIGH,
26
+ }
18
27
  export abstract class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEventCallbacks>) {
19
28
  kind: Track.Kind;
20
29