livekit-client 2.0.0 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. package/README.md +8 -0
  2. package/dist/livekit-client.e2ee.worker.js +1 -1
  3. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  4. package/dist/livekit-client.e2ee.worker.mjs +4 -4
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +122 -39
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.umd.js +1 -1
  9. package/dist/livekit-client.umd.js.map +1 -1
  10. package/dist/src/proto/livekit_models_pb.d.ts +9 -1
  11. package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
  12. package/dist/src/proto/livekit_rtc_pb.d.ts +38 -0
  13. package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -1
  14. package/dist/src/room/PCTransport.d.ts +1 -2
  15. package/dist/src/room/PCTransport.d.ts.map +1 -1
  16. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  17. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  18. package/dist/src/room/Room.d.ts.map +1 -1
  19. package/dist/src/room/events.d.ts +8 -1
  20. package/dist/src/room/events.d.ts.map +1 -1
  21. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  22. package/dist/src/room/track/LocalAudioTrack.d.ts +8 -7
  23. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  24. package/dist/src/room/track/LocalTrack.d.ts +10 -9
  25. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  26. package/dist/src/room/track/LocalTrackPublication.d.ts +2 -2
  27. package/dist/src/room/track/LocalVideoTrack.d.ts +3 -3
  28. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  29. package/dist/src/room/track/RemoteAudioTrack.d.ts +2 -1
  30. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  31. package/dist/src/room/track/RemoteTrack.d.ts +2 -2
  32. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
  33. package/dist/src/room/track/RemoteVideoTrack.d.ts +2 -1
  34. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  35. package/dist/src/room/track/Track.d.ts +3 -3
  36. package/dist/src/room/track/Track.d.ts.map +1 -1
  37. package/dist/src/room/track/processor/types.d.ts +4 -0
  38. package/dist/src/room/track/processor/types.d.ts.map +1 -1
  39. package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +9 -1
  40. package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +38 -0
  41. package/dist/ts4.2/src/room/PCTransport.d.ts +1 -2
  42. package/dist/ts4.2/src/room/events.d.ts +8 -1
  43. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +8 -7
  44. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +10 -9
  45. package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +2 -2
  46. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +3 -3
  47. package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +2 -1
  48. package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +2 -2
  49. package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +2 -1
  50. package/dist/ts4.2/src/room/track/Track.d.ts +3 -3
  51. package/dist/ts4.2/src/room/track/processor/types.d.ts +4 -0
  52. package/package.json +2 -2
  53. package/src/api/SignalClient.ts +1 -1
  54. package/src/e2ee/worker/e2ee.worker.ts +8 -4
  55. package/src/proto/livekit_models_pb.ts +12 -0
  56. package/src/proto/livekit_rtc_pb.ts +53 -0
  57. package/src/room/PCTransport.ts +3 -13
  58. package/src/room/PCTransportManager.ts +1 -2
  59. package/src/room/RTCEngine.ts +5 -0
  60. package/src/room/Room.ts +3 -0
  61. package/src/room/events.ts +8 -1
  62. package/src/room/participant/LocalParticipant.ts +0 -1
  63. package/src/room/track/LocalAudioTrack.ts +8 -11
  64. package/src/room/track/LocalTrack.ts +36 -23
  65. package/src/room/track/LocalVideoTrack.ts +3 -3
  66. package/src/room/track/RemoteAudioTrack.ts +1 -1
  67. package/src/room/track/RemoteTrack.ts +4 -2
  68. package/src/room/track/RemoteVideoTrack.ts +1 -1
  69. package/src/room/track/Track.ts +5 -3
  70. package/src/room/track/processor/types.ts +4 -0
@@ -84,7 +84,7 @@ onmessage = (ev) => {
84
84
  }
85
85
  break;
86
86
  case 'removeTransform':
87
- unsetCryptorParticipant(data.trackId);
87
+ unsetCryptorParticipant(data.trackId, data.participantIdentity);
88
88
  break;
89
89
  case 'updateCodec':
90
90
  getTrackCryptor(data.participantIdentity, data.trackId).setVideoCodec(data.codec);
@@ -125,7 +125,9 @@ async function handleRatchetRequest(data: RatchetRequestMessage['data']) {
125
125
  }
126
126
 
127
127
  function getTrackCryptor(participantIdentity: string, trackId: string) {
128
- let cryptor = participantCryptors.find((c) => c.getTrackId() === trackId);
128
+ let cryptor = participantCryptors.find(
129
+ (c) => c.getParticipantIdentity() === participantIdentity && c.getTrackId() === trackId,
130
+ );
129
131
  if (!cryptor) {
130
132
  workerLogger.info('creating new cryptor for', { participantIdentity });
131
133
  if (!keyProviderOptions) {
@@ -172,8 +174,10 @@ function getSharedKeyHandler() {
172
174
  return sharedKeyHandler;
173
175
  }
174
176
 
175
- function unsetCryptorParticipant(trackId: string) {
176
- participantCryptors.find((c) => c.getTrackId() === trackId)?.unsetParticipant();
177
+ function unsetCryptorParticipant(trackId: string, participantIdentity: string) {
178
+ participantCryptors
179
+ .find((c) => c.getParticipantIdentity() === participantIdentity && c.getTrackId() === trackId)
180
+ ?.unsetParticipant();
177
181
  }
178
182
 
179
183
  function setEncryptionEnabled(enable: boolean, participantIdentity: string) {
@@ -301,6 +301,16 @@ export enum DisconnectReason {
301
301
  * @generated from enum value: JOIN_FAILURE = 7;
302
302
  */
303
303
  JOIN_FAILURE = 7,
304
+
305
+ /**
306
+ * @generated from enum value: MIGRATION = 8;
307
+ */
308
+ MIGRATION = 8,
309
+
310
+ /**
311
+ * @generated from enum value: SIGNAL_CLOSE = 9;
312
+ */
313
+ SIGNAL_CLOSE = 9,
304
314
  }
305
315
  // Retrieve enum metadata with: proto3.getEnumType(DisconnectReason)
306
316
  proto3.util.setEnumType(DisconnectReason, "livekit.DisconnectReason", [
@@ -312,6 +322,8 @@ proto3.util.setEnumType(DisconnectReason, "livekit.DisconnectReason", [
312
322
  { no: 5, name: "ROOM_DELETED" },
313
323
  { no: 6, name: "STATE_MISMATCH" },
314
324
  { no: 7, name: "JOIN_FAILURE" },
325
+ { no: 8, name: "MIGRATION" },
326
+ { no: 9, name: "SIGNAL_CLOSE" },
315
327
  ]);
316
328
 
317
329
  /**
@@ -1208,6 +1208,7 @@ export class LeaveRequest extends Message<LeaveRequest> {
1208
1208
  /**
1209
1209
  * sent when server initiates the disconnect due to server-restart
1210
1210
  * indicates clients should attempt full-reconnect sequence
1211
+ * NOTE: `can_reconnect` obsoleted by `action` starting in protocol version 13
1211
1212
  *
1212
1213
  * @generated from field: bool can_reconnect = 1;
1213
1214
  */
@@ -1218,6 +1219,16 @@ export class LeaveRequest extends Message<LeaveRequest> {
1218
1219
  */
1219
1220
  reason = DisconnectReason.UNKNOWN_REASON;
1220
1221
 
1222
+ /**
1223
+ * @generated from field: livekit.LeaveRequest.Action action = 3;
1224
+ */
1225
+ action = LeaveRequest_Action.DISCONNECT;
1226
+
1227
+ /**
1228
+ * @generated from field: livekit.RegionSettings regions = 4;
1229
+ */
1230
+ regions?: RegionSettings;
1231
+
1221
1232
  constructor(data?: PartialMessage<LeaveRequest>) {
1222
1233
  super();
1223
1234
  proto3.util.initPartial(data, this);
@@ -1228,6 +1239,8 @@ export class LeaveRequest extends Message<LeaveRequest> {
1228
1239
  static readonly fields: FieldList = proto3.util.newFieldList(() => [
1229
1240
  { no: 1, name: "can_reconnect", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
1230
1241
  { no: 2, name: "reason", kind: "enum", T: proto3.getEnumType(DisconnectReason) },
1242
+ { no: 3, name: "action", kind: "enum", T: proto3.getEnumType(LeaveRequest_Action) },
1243
+ { no: 4, name: "regions", kind: "message", T: RegionSettings },
1231
1244
  ]);
1232
1245
 
1233
1246
  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): LeaveRequest {
@@ -1247,6 +1260,40 @@ export class LeaveRequest extends Message<LeaveRequest> {
1247
1260
  }
1248
1261
  }
1249
1262
 
1263
+ /**
1264
+ * indicates action clients should take on receiving this message
1265
+ *
1266
+ * @generated from enum livekit.LeaveRequest.Action
1267
+ */
1268
+ export enum LeaveRequest_Action {
1269
+ /**
1270
+ * should disconnect
1271
+ *
1272
+ * @generated from enum value: DISCONNECT = 0;
1273
+ */
1274
+ DISCONNECT = 0,
1275
+
1276
+ /**
1277
+ * should attempt a resume with `reconnect=1` in join URL
1278
+ *
1279
+ * @generated from enum value: RESUME = 1;
1280
+ */
1281
+ RESUME = 1,
1282
+
1283
+ /**
1284
+ * should attempt a reconnect, i. e. no `reconnect=1`
1285
+ *
1286
+ * @generated from enum value: RECONNECT = 2;
1287
+ */
1288
+ RECONNECT = 2,
1289
+ }
1290
+ // Retrieve enum metadata with: proto3.getEnumType(LeaveRequest_Action)
1291
+ proto3.util.setEnumType(LeaveRequest_Action, "livekit.LeaveRequest.Action", [
1292
+ { no: 0, name: "DISCONNECT" },
1293
+ { no: 1, name: "RESUME" },
1294
+ { no: 2, name: "RECONNECT" },
1295
+ ]);
1296
+
1250
1297
  /**
1251
1298
  * message to indicate published video track dimensions are changing
1252
1299
  *
@@ -1947,6 +1994,11 @@ export class SyncState extends Message<SyncState> {
1947
1994
  */
1948
1995
  offer?: SessionDescription;
1949
1996
 
1997
+ /**
1998
+ * @generated from field: repeated string track_sids_disabled = 6;
1999
+ */
2000
+ trackSidsDisabled: string[] = [];
2001
+
1950
2002
  constructor(data?: PartialMessage<SyncState>) {
1951
2003
  super();
1952
2004
  proto3.util.initPartial(data, this);
@@ -1960,6 +2012,7 @@ export class SyncState extends Message<SyncState> {
1960
2012
  { no: 3, name: "publish_tracks", kind: "message", T: TrackPublishedResponse, repeated: true },
1961
2013
  { no: 4, name: "data_channels", kind: "message", T: DataChannelInfo, repeated: true },
1962
2014
  { no: 5, name: "offer", kind: "message", T: SessionDescription },
2015
+ { no: 6, name: "track_sids_disabled", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true },
1963
2016
  ]);
1964
2017
 
1965
2018
  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): SyncState {
@@ -5,7 +5,7 @@ import { debounce } from 'ts-debounce';
5
5
  import log, { LoggerNames, getLogger } from '../logger';
6
6
  import { NegotiationError, UnexpectedConnectionState } from './errors';
7
7
  import type { LoggerOptions } from './types';
8
- import { ddExtensionURI, isChromiumBased, isSVCCodec } from './utils';
8
+ import { ddExtensionURI, isSVCCodec } from './utils';
9
9
 
10
10
  /** @internal */
11
11
  interface TrackBitrateInfo {
@@ -42,8 +42,6 @@ export default class PCTransport extends EventEmitter {
42
42
 
43
43
  private config?: RTCConfiguration;
44
44
 
45
- private mediaConstraints: Record<string, unknown>;
46
-
47
45
  private log = log;
48
46
 
49
47
  private loggerOptions: LoggerOptions;
@@ -76,24 +74,16 @@ export default class PCTransport extends EventEmitter {
76
74
 
77
75
  onTrack?: (ev: RTCTrackEvent) => void;
78
76
 
79
- constructor(
80
- config?: RTCConfiguration,
81
- mediaConstraints: Record<string, unknown> = {},
82
- loggerOptions: LoggerOptions = {},
83
- ) {
77
+ constructor(config?: RTCConfiguration, loggerOptions: LoggerOptions = {}) {
84
78
  super();
85
79
  this.log = getLogger(loggerOptions.loggerName ?? LoggerNames.PCTransport);
86
80
  this.loggerOptions = loggerOptions;
87
81
  this.config = config;
88
- this.mediaConstraints = mediaConstraints;
89
82
  this._pc = this.createPC();
90
83
  }
91
84
 
92
85
  private createPC() {
93
- const pc = isChromiumBased()
94
- ? // @ts-expect-error chrome allows additional media constraints to be passed into the RTCPeerConnection constructor
95
- new RTCPeerConnection(this.config, this.mediaConstraints)
96
- : new RTCPeerConnection(this.config);
86
+ const pc = new RTCPeerConnection(this.config);
97
87
 
98
88
  pc.onicecandidate = (ev) => {
99
89
  if (!ev.candidate) return;
@@ -71,8 +71,7 @@ export class PCTransportManager {
71
71
 
72
72
  this.isPublisherConnectionRequired = !subscriberPrimary;
73
73
  this.isSubscriberConnectionRequired = subscriberPrimary;
74
- const googConstraints = { optional: [{ googDscp: true }] };
75
- this.publisher = new PCTransport(rtcConfig, googConstraints, loggerOptions);
74
+ this.publisher = new PCTransport(rtcConfig, loggerOptions);
76
75
  this.subscriber = new PCTransport(rtcConfig, loggerOptions);
77
76
 
78
77
  this.publisher.onConnectionStateChange = this.updateState;
@@ -1271,11 +1271,15 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1271
1271
  */
1272
1272
  const autoSubscribe = this.signalOpts?.autoSubscribe ?? true;
1273
1273
  const trackSids = new Array<string>();
1274
+ const trackSidsDisabled = new Array<string>();
1274
1275
 
1275
1276
  remoteTracks.forEach((track) => {
1276
1277
  if (track.isDesired !== autoSubscribe) {
1277
1278
  trackSids.push(track.trackSid);
1278
1279
  }
1280
+ if (!track.isEnabled) {
1281
+ trackSidsDisabled.push(track.trackSid);
1282
+ }
1279
1283
  });
1280
1284
 
1281
1285
  this.client.sendSyncState(
@@ -1299,6 +1303,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1299
1303
  }),
1300
1304
  publishTracks: getTrackPublicationInfo(localTracks),
1301
1305
  dataChannels: this.dataChannelsInfo(),
1306
+ trackSidsDisabled,
1302
1307
  }),
1303
1308
  );
1304
1309
  }
package/src/room/Room.ts CHANGED
@@ -1776,7 +1776,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1776
1776
  };
1777
1777
 
1778
1778
  private onLocalTrackPublished = async (pub: LocalTrackPublication) => {
1779
+ pub.track?.getProcessor()?.onPublish?.(this);
1780
+
1779
1781
  this.emit(RoomEvent.LocalTrackPublished, pub, this.localParticipant);
1782
+
1780
1783
  if (pub.track instanceof LocalAudioTrack) {
1781
1784
  const trackIsSilent = await pub.track.checkForSilence();
1782
1785
  if (trackIsSilent) {
@@ -27,7 +27,14 @@ export enum RoomEvent {
27
27
 
28
28
  /**
29
29
  * When disconnected from room. This fires when room.disconnect() is called or
30
- * when an unrecoverable connection issue had occured
30
+ * when an unrecoverable connection issue had occured.
31
+ *
32
+ * DisconnectReason can be used to determine why the participant was disconnected. Notable reasons are
33
+ * - DUPLICATE_IDENTITY: another client with the same identity has joined the room
34
+ * - PARTICIPANT_REMOVED: participant was removed by RemoveParticipant API
35
+ * - ROOM_DELETED: the room has ended via DeleteRoom API
36
+ *
37
+ * args: ([[DisconnectReason]])
31
38
  */
32
39
  Disconnected = 'disconnected',
33
40
 
@@ -888,7 +888,6 @@ export default class LocalParticipant extends Participant {
888
888
  }
889
889
 
890
890
  this.addTrackPublication(publication);
891
-
892
891
  // send event for publication
893
892
  this.emit(ParticipantEvent.LocalTrackPublished, publication);
894
893
  return publication;
@@ -6,17 +6,17 @@ import { isWeb, unwrapConstraint } from '../utils';
6
6
  import LocalTrack from './LocalTrack';
7
7
  import { Track } from './Track';
8
8
  import type { AudioCaptureOptions } from './options';
9
- import type { TrackProcessor } from './processor/types';
9
+ import type { AudioProcessorOptions, TrackProcessor } from './processor/types';
10
10
  import { constraintsForOptions, detectSilence } from './utils';
11
11
 
12
- export default class LocalAudioTrack extends LocalTrack {
12
+ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
13
13
  /** @internal */
14
14
  stopOnMute: boolean = false;
15
15
 
16
- private audioContext?: AudioContext;
17
-
18
16
  private prevStats?: AudioSenderStats;
19
17
 
18
+ protected processor?: TrackProcessor<Track.Kind.Audio, AudioProcessorOptions> | undefined;
19
+
20
20
  /**
21
21
  *
22
22
  * @param mediaTrack
@@ -48,7 +48,7 @@ export default class LocalAudioTrack extends LocalTrack {
48
48
  );
49
49
  }
50
50
 
51
- async mute(): Promise<LocalAudioTrack> {
51
+ async mute(): Promise<typeof this> {
52
52
  const unlock = await this.muteLock.lock();
53
53
  try {
54
54
  // disabled special handling as it will cause BT headsets to switch communication modes
@@ -64,7 +64,7 @@ export default class LocalAudioTrack extends LocalTrack {
64
64
  }
65
65
  }
66
66
 
67
- async unmute(): Promise<LocalAudioTrack> {
67
+ async unmute(): Promise<typeof this> {
68
68
  const unlock = await this.muteLock.lock();
69
69
  try {
70
70
  const deviceHasChanged =
@@ -99,7 +99,7 @@ export default class LocalAudioTrack extends LocalTrack {
99
99
  await this.restart(constraints);
100
100
  }
101
101
 
102
- protected async restart(constraints?: MediaTrackConstraints): Promise<LocalTrack> {
102
+ protected async restart(constraints?: MediaTrackConstraints): Promise<typeof this> {
103
103
  const track = await super.restart(constraints);
104
104
  this.checkForSilence();
105
105
  return track;
@@ -139,7 +139,7 @@ export default class LocalAudioTrack extends LocalTrack {
139
139
  this.prevStats = stats;
140
140
  };
141
141
 
142
- async setProcessor(processor: TrackProcessor<this['kind']>) {
142
+ async setProcessor(processor: TrackProcessor<Track.Kind.Audio, AudioProcessorOptions>) {
143
143
  const unlock = await this.processorLock.lock();
144
144
  try {
145
145
  if (!this.audioContext) {
@@ -150,9 +150,6 @@ export default class LocalAudioTrack extends LocalTrack {
150
150
  if (this.processor) {
151
151
  await this.stopProcessor();
152
152
  }
153
- if (this.kind === 'unknown') {
154
- throw TypeError('cannot set processor on track of unknown kind');
155
- }
156
153
 
157
154
  const processorOptions = {
158
155
  kind: this.kind,
@@ -11,7 +11,9 @@ import type { TrackProcessor } from './processor/types';
11
11
 
12
12
  const defaultDimensionsTimeout = 1000;
13
13
 
14
- export default abstract class LocalTrack extends Track {
14
+ export default abstract class LocalTrack<
15
+ TrackKind extends Track.Kind = Track.Kind,
16
+ > extends Track<TrackKind> {
15
17
  /** @internal */
16
18
  sender?: RTCRtpSender;
17
19
 
@@ -34,10 +36,12 @@ export default abstract class LocalTrack extends Track {
34
36
 
35
37
  protected processorElement?: HTMLMediaElement;
36
38
 
37
- protected processor?: TrackProcessor<this['kind']>;
39
+ protected processor?: TrackProcessor<TrackKind, any>;
38
40
 
39
41
  protected processorLock: Mutex;
40
42
 
43
+ protected audioContext?: AudioContext;
44
+
41
45
  /**
42
46
  *
43
47
  * @param mediaTrack
@@ -47,7 +51,7 @@ export default abstract class LocalTrack extends Track {
47
51
  */
48
52
  protected constructor(
49
53
  mediaTrack: MediaStreamTrack,
50
- kind: Track.Kind,
54
+ kind: TrackKind,
51
55
  constraints?: MediaTrackConstraints,
52
56
  userProvidedTrack = false,
53
57
  loggerOptions?: LoggerOptions,
@@ -128,21 +132,28 @@ export default abstract class LocalTrack extends Track {
128
132
  this._constraints = newTrack.getConstraints();
129
133
  }
130
134
  let processedTrack: MediaStreamTrack | undefined;
131
- if (this.processor && newTrack && this.processorElement) {
132
- this.log.debug('restarting processor', this.logContext);
133
- if (this.kind === 'unknown') {
134
- throw TypeError('cannot set processor on track of unknown kind');
135
- }
135
+ if (this.processor && newTrack) {
136
+ const unlock = await this.processorLock.lock();
137
+ try {
138
+ this.log.debug('restarting processor', this.logContext);
139
+ if (this.kind === 'unknown') {
140
+ throw TypeError('cannot set processor on track of unknown kind');
141
+ }
136
142
 
137
- attachToElement(newTrack, this.processorElement);
138
- // ensure the processorElement itself stays muted
139
- this.processorElement.muted = true;
140
- await this.processor.restart({
141
- track: newTrack,
142
- kind: this.kind,
143
- element: this.processorElement,
144
- });
145
- processedTrack = this.processor.processedTrack;
143
+ if (this.processorElement) {
144
+ attachToElement(newTrack, this.processorElement);
145
+ // ensure the processorElement itself stays muted
146
+ this.processorElement.muted = true;
147
+ }
148
+ await this.processor.restart({
149
+ track: newTrack,
150
+ kind: this.kind,
151
+ element: this.processorElement,
152
+ });
153
+ processedTrack = this.processor.processedTrack;
154
+ } finally {
155
+ unlock();
156
+ }
146
157
  }
147
158
  if (this.sender) {
148
159
  await this.sender.replaceTrack(processedTrack ?? newTrack);
@@ -200,17 +211,17 @@ export default abstract class LocalTrack extends Track {
200
211
  return DeviceManager.getInstance().normalizeDeviceId(kind, deviceId, groupId);
201
212
  }
202
213
 
203
- async mute(): Promise<LocalTrack> {
214
+ async mute() {
204
215
  this.setTrackMuted(true);
205
216
  return this;
206
217
  }
207
218
 
208
- async unmute(): Promise<LocalTrack> {
219
+ async unmute() {
209
220
  this.setTrackMuted(false);
210
221
  return this;
211
222
  }
212
223
 
213
- async replaceTrack(track: MediaStreamTrack, userProvidedTrack = true): Promise<LocalTrack> {
224
+ async replaceTrack(track: MediaStreamTrack, userProvidedTrack = true) {
214
225
  if (!this.sender) {
215
226
  throw new TrackInvalidError('unable to replace an unpublished track');
216
227
  }
@@ -227,7 +238,7 @@ export default abstract class LocalTrack extends Track {
227
238
  return this;
228
239
  }
229
240
 
230
- protected async restart(constraints?: MediaTrackConstraints): Promise<LocalTrack> {
241
+ protected async restart(constraints?: MediaTrackConstraints) {
231
242
  if (!constraints) {
232
243
  constraints = this._constraints;
233
244
  }
@@ -408,7 +419,7 @@ export default abstract class LocalTrack extends Track {
408
419
  * @param showProcessedStreamLocally
409
420
  * @returns
410
421
  */
411
- async setProcessor(processor: TrackProcessor<this['kind']>, showProcessedStreamLocally = true) {
422
+ async setProcessor(processor: TrackProcessor<TrackKind>, showProcessedStreamLocally = true) {
412
423
  const unlock = await this.processorLock.lock();
413
424
  try {
414
425
  this.log.debug('setting up processor', this.logContext);
@@ -418,7 +429,8 @@ export default abstract class LocalTrack extends Track {
418
429
  if (this.kind === 'unknown') {
419
430
  throw TypeError('cannot set processor on track of unknown kind');
420
431
  }
421
- this.processorElement = this.processorElement ?? document.createElement(this.kind);
432
+ this.processorElement =
433
+ this.processorElement ?? (document.createElement(this.kind) as HTMLMediaElement);
422
434
 
423
435
  attachToElement(this._mediaStreamTrack, this.processorElement);
424
436
  this.processorElement.muted = true;
@@ -433,6 +445,7 @@ export default abstract class LocalTrack extends Track {
433
445
  kind: this.kind,
434
446
  track: this._mediaStreamTrack,
435
447
  element: this.processorElement,
448
+ audioContext: this.audioContext,
436
449
  };
437
450
 
438
451
  await processor.init(processorOptions);
@@ -30,7 +30,7 @@ export class SimulcastTrackInfo {
30
30
 
31
31
  const refreshSubscribedCodecAfterNewCodec = 5000;
32
32
 
33
- export default class LocalVideoTrack extends LocalTrack {
33
+ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
34
34
  /* @internal */
35
35
  signalClient?: SignalClient;
36
36
 
@@ -115,7 +115,7 @@ export default class LocalVideoTrack extends LocalTrack {
115
115
  }
116
116
  }
117
117
 
118
- async mute(): Promise<LocalVideoTrack> {
118
+ async mute(): Promise<typeof this> {
119
119
  const unlock = await this.muteLock.lock();
120
120
  try {
121
121
  if (this.source === Track.Source.Camera && !this.isUserProvided) {
@@ -130,7 +130,7 @@ export default class LocalVideoTrack extends LocalTrack {
130
130
  }
131
131
  }
132
132
 
133
- async unmute(): Promise<LocalVideoTrack> {
133
+ async unmute(): Promise<typeof this> {
134
134
  const unlock = await this.muteLock.lock();
135
135
  try {
136
136
  if (this.source === Track.Source.Camera && !this.isUserProvided) {
@@ -7,7 +7,7 @@ import RemoteTrack from './RemoteTrack';
7
7
  import { Track } from './Track';
8
8
  import type { AudioOutputOptions } from './options';
9
9
 
10
- export default class RemoteAudioTrack extends RemoteTrack {
10
+ export default class RemoteAudioTrack extends RemoteTrack<Track.Kind.Audio> {
11
11
  private prevStats?: AudioReceiverStats;
12
12
 
13
13
  private elementVolume: number | undefined;
@@ -3,14 +3,16 @@ import { monitorFrequency } from '../stats';
3
3
  import type { LoggerOptions } from '../types';
4
4
  import { Track } from './Track';
5
5
 
6
- export default abstract class RemoteTrack extends Track {
6
+ export default abstract class RemoteTrack<
7
+ TrackKind extends Track.Kind = Track.Kind,
8
+ > extends Track<TrackKind> {
7
9
  /** @internal */
8
10
  receiver?: RTCRtpReceiver;
9
11
 
10
12
  constructor(
11
13
  mediaTrack: MediaStreamTrack,
12
14
  sid: string,
13
- kind: Track.Kind,
15
+ kind: TrackKind,
14
16
  receiver?: RTCRtpReceiver,
15
17
  loggerOptions?: LoggerOptions,
16
18
  ) {
@@ -12,7 +12,7 @@ import type { AdaptiveStreamSettings } from './types';
12
12
 
13
13
  const REACTION_DELAY = 100;
14
14
 
15
- export default class RemoteVideoTrack extends RemoteTrack {
15
+ export default class RemoteVideoTrack extends RemoteTrack<Track.Kind.Video> {
16
16
  private prevStats?: VideoReceiverStats;
17
17
 
18
18
  private elementInfos: ElementInfo[] = [];
@@ -24,8 +24,10 @@ export enum VideoQuality {
24
24
  MEDIUM = ProtoQuality.MEDIUM,
25
25
  HIGH = ProtoQuality.HIGH,
26
26
  }
27
- export abstract class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEventCallbacks>) {
28
- kind: Track.Kind;
27
+ export abstract class Track<
28
+ TrackKind extends Track.Kind = Track.Kind,
29
+ > extends (EventEmitter as new () => TypedEventEmitter<TrackEventCallbacks>) {
30
+ readonly kind: TrackKind;
29
31
 
30
32
  attachedElements: HTMLMediaElement[] = [];
31
33
 
@@ -67,7 +69,7 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
67
69
 
68
70
  protected constructor(
69
71
  mediaTrack: MediaStreamTrack,
70
- kind: Track.Kind,
72
+ kind: TrackKind,
71
73
  loggerOptions: LoggerOptions = {},
72
74
  ) {
73
75
  super();
@@ -1,3 +1,4 @@
1
+ import type Room from '../../Room';
1
2
  import type { Track } from '../Track';
2
3
 
3
4
  /**
@@ -7,6 +8,7 @@ export type ProcessorOptions<T extends Track.Kind> = {
7
8
  kind: T;
8
9
  track: MediaStreamTrack;
9
10
  element?: HTMLMediaElement;
11
+ audioContext?: AudioContext;
10
12
  };
11
13
 
12
14
  /**
@@ -33,4 +35,6 @@ export interface TrackProcessor<
33
35
  restart: (opts: U) => Promise<void>;
34
36
  destroy: () => Promise<void>;
35
37
  processedTrack?: MediaStreamTrack;
38
+ onPublish?: (room: Room) => Promise<void>;
39
+ onUnpublish?: () => Promise<void>;
36
40
  }