livekit-client 2.0.1 → 2.0.3

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 (67) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +53 -18
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +94 -57
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.umd.js +1 -1
  8. package/dist/livekit-client.umd.js.map +1 -1
  9. package/dist/src/e2ee/KeyProvider.d.ts +1 -1
  10. package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
  11. package/dist/src/e2ee/worker/FrameCryptor.d.ts +1 -0
  12. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  13. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +2 -2
  14. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  15. package/dist/src/room/Room.d.ts +1 -0
  16. package/dist/src/room/Room.d.ts.map +1 -1
  17. package/dist/src/room/events.d.ts +5 -1
  18. package/dist/src/room/events.d.ts.map +1 -1
  19. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  20. package/dist/src/room/track/LocalAudioTrack.d.ts +8 -7
  21. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  22. package/dist/src/room/track/LocalTrack.d.ts +11 -9
  23. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  24. package/dist/src/room/track/LocalTrackPublication.d.ts +2 -2
  25. package/dist/src/room/track/LocalVideoTrack.d.ts +3 -3
  26. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  27. package/dist/src/room/track/RemoteAudioTrack.d.ts +2 -1
  28. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  29. package/dist/src/room/track/RemoteTrack.d.ts +2 -2
  30. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
  31. package/dist/src/room/track/RemoteVideoTrack.d.ts +2 -1
  32. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  33. package/dist/src/room/track/Track.d.ts +5 -3
  34. package/dist/src/room/track/Track.d.ts.map +1 -1
  35. package/dist/src/room/track/processor/types.d.ts +4 -0
  36. package/dist/src/room/track/processor/types.d.ts.map +1 -1
  37. package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +1 -1
  38. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +1 -0
  39. package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +2 -2
  40. package/dist/ts4.2/src/room/Room.d.ts +1 -0
  41. package/dist/ts4.2/src/room/events.d.ts +5 -1
  42. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +8 -7
  43. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +11 -9
  44. package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +2 -2
  45. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +3 -3
  46. package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +2 -1
  47. package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +2 -2
  48. package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +2 -1
  49. package/dist/ts4.2/src/room/track/Track.d.ts +5 -3
  50. package/dist/ts4.2/src/room/track/processor/types.d.ts +4 -0
  51. package/package.json +1 -1
  52. package/src/api/SignalClient.ts +1 -1
  53. package/src/e2ee/KeyProvider.ts +6 -1
  54. package/src/e2ee/worker/FrameCryptor.ts +26 -0
  55. package/src/e2ee/worker/ParticipantKeyHandler.ts +9 -5
  56. package/src/e2ee/worker/e2ee.worker.ts +17 -14
  57. package/src/room/Room.ts +21 -4
  58. package/src/room/events.ts +4 -0
  59. package/src/room/participant/LocalParticipant.ts +0 -1
  60. package/src/room/track/LocalAudioTrack.ts +9 -11
  61. package/src/room/track/LocalTrack.ts +78 -56
  62. package/src/room/track/LocalVideoTrack.ts +3 -3
  63. package/src/room/track/RemoteAudioTrack.ts +1 -1
  64. package/src/room/track/RemoteTrack.ts +4 -2
  65. package/src/room/track/RemoteVideoTrack.ts +1 -1
  66. package/src/room/track/Track.ts +7 -3
  67. package/src/room/track/processor/types.ts +4 -0
@@ -3,7 +3,7 @@ import { Mutex } from '../utils';
3
3
  import { Track } from './Track';
4
4
  import type { VideoCodec } from './options';
5
5
  import type { TrackProcessor } from './processor/types';
6
- export default abstract class LocalTrack extends Track {
6
+ export default abstract class LocalTrack<TrackKind extends Track.Kind = Track.Kind> extends Track<TrackKind> {
7
7
  /** @internal */
8
8
  sender?: RTCRtpSender;
9
9
  /** @internal */
@@ -15,8 +15,10 @@ export default abstract class LocalTrack extends Track {
15
15
  protected muteLock: Mutex;
16
16
  protected pauseUpstreamLock: Mutex;
17
17
  protected processorElement?: HTMLMediaElement;
18
- protected processor?: TrackProcessor<this['kind']>;
18
+ protected processor?: TrackProcessor<TrackKind, any>;
19
19
  protected processorLock: Mutex;
20
+ protected audioContext?: AudioContext;
21
+ private restartLock;
20
22
  /**
21
23
  *
22
24
  * @param mediaTrack
@@ -24,7 +26,7 @@ export default abstract class LocalTrack extends Track {
24
26
  * @param constraints MediaTrackConstraints that are being used when restarting or reacquiring tracks
25
27
  * @param userProvidedTrack Signals to the SDK whether or not the mediaTrack should be managed (i.e. released and reacquired) internally by the SDK
26
28
  */
27
- protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, loggerOptions?: LoggerOptions);
29
+ protected constructor(mediaTrack: MediaStreamTrack, kind: TrackKind, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, loggerOptions?: LoggerOptions);
28
30
  get id(): string;
29
31
  get dimensions(): Track.Dimensions | undefined;
30
32
  private _isUpstreamPaused;
@@ -37,10 +39,10 @@ export default abstract class LocalTrack extends Track {
37
39
  * @returns DeviceID of the device that is currently being used for this track
38
40
  */
39
41
  getDeviceId(): Promise<string | undefined>;
40
- mute(): Promise<LocalTrack>;
41
- unmute(): Promise<LocalTrack>;
42
- replaceTrack(track: MediaStreamTrack, userProvidedTrack?: boolean): Promise<LocalTrack>;
43
- protected restart(constraints?: MediaTrackConstraints): Promise<LocalTrack>;
42
+ mute(): Promise<this>;
43
+ unmute(): Promise<this>;
44
+ replaceTrack(track: MediaStreamTrack, userProvidedTrack?: boolean): Promise<this>;
45
+ protected restart(constraints?: MediaTrackConstraints): Promise<this>;
44
46
  protected setTrackMuted(muted: boolean): void;
45
47
  protected get needsReAcquisition(): boolean;
46
48
  protected handleAppVisibilityChanged(): Promise<void>;
@@ -74,8 +76,8 @@ export default abstract class LocalTrack extends Track {
74
76
  * @param showProcessedStreamLocally
75
77
  * @returns
76
78
  */
77
- setProcessor(processor: TrackProcessor<this['kind']>, showProcessedStreamLocally?: boolean): Promise<void>;
78
- getProcessor(): TrackProcessor<this["kind"], import("./processor/types").ProcessorOptions<this["kind"]>> | undefined;
79
+ setProcessor(processor: TrackProcessor<TrackKind>, showProcessedStreamLocally?: boolean): Promise<void>;
80
+ getProcessor(): TrackProcessor<TrackKind, any> | undefined;
79
81
  /**
80
82
  * Stops the track processor
81
83
  * See https://github.com/livekit/track-processors-js for example usage
@@ -18,11 +18,11 @@ export default class LocalTrackPublication extends TrackPublication {
18
18
  /**
19
19
  * Mute the track associated with this publication
20
20
  */
21
- mute(): Promise<LocalTrack | undefined>;
21
+ mute(): Promise<LocalTrack<Track.Kind> | undefined>;
22
22
  /**
23
23
  * Unmute track associated with this publication
24
24
  */
25
- unmute(): Promise<LocalTrack | undefined>;
25
+ unmute(): Promise<LocalTrack<Track.Kind> | undefined>;
26
26
  /**
27
27
  * Pauses the media stream track associated with this publication from being sent to the server
28
28
  * and signals "muted" event to other participants
@@ -14,7 +14,7 @@ export declare class SimulcastTrackInfo {
14
14
  encodings?: RTCRtpEncodingParameters[];
15
15
  constructor(codec: VideoCodec, mediaStreamTrack: MediaStreamTrack);
16
16
  }
17
- export default class LocalVideoTrack extends LocalTrack {
17
+ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
18
18
  signalClient?: SignalClient;
19
19
  private prevStats?;
20
20
  private encodings?;
@@ -33,8 +33,8 @@ export default class LocalVideoTrack extends LocalTrack {
33
33
  stop(): void;
34
34
  pauseUpstream(): Promise<void>;
35
35
  resumeUpstream(): Promise<void>;
36
- mute(): Promise<LocalVideoTrack>;
37
- unmute(): Promise<LocalVideoTrack>;
36
+ mute(): Promise<typeof this>;
37
+ unmute(): Promise<typeof this>;
38
38
  protected setTrackMuted(muted: boolean): void;
39
39
  getSenderStats(): Promise<VideoSenderStats[]>;
40
40
  setPublishingQuality(maxQuality: VideoQuality): void;
@@ -1,8 +1,9 @@
1
1
  import type { AudioReceiverStats } from '../stats';
2
2
  import type { LoggerOptions } from '../types';
3
3
  import RemoteTrack from './RemoteTrack';
4
+ import { Track } from './Track';
4
5
  import type { AudioOutputOptions } from './options';
5
- export default class RemoteAudioTrack extends RemoteTrack {
6
+ export default class RemoteAudioTrack extends RemoteTrack<Track.Kind.Audio> {
6
7
  private prevStats?;
7
8
  private elementVolume;
8
9
  private audioContext?;
@@ -1,9 +1,9 @@
1
1
  import type { LoggerOptions } from '../types';
2
2
  import { Track } from './Track';
3
- export default abstract class RemoteTrack extends Track {
3
+ export default abstract class RemoteTrack<TrackKind extends Track.Kind = Track.Kind> extends Track<TrackKind> {
4
4
  /** @internal */
5
5
  receiver?: RTCRtpReceiver;
6
- constructor(mediaTrack: MediaStreamTrack, sid: string, kind: Track.Kind, receiver?: RTCRtpReceiver, loggerOptions?: LoggerOptions);
6
+ constructor(mediaTrack: MediaStreamTrack, sid: string, kind: TrackKind, receiver?: RTCRtpReceiver, loggerOptions?: LoggerOptions);
7
7
  /** @internal */
8
8
  setMuted(muted: boolean): void;
9
9
  /** @internal */
@@ -1,7 +1,8 @@
1
1
  import type { LoggerOptions } from '../types';
2
2
  import RemoteTrack from './RemoteTrack';
3
+ import { Track } from './Track';
3
4
  import type { AdaptiveStreamSettings } from './types';
4
- export default class RemoteVideoTrack extends RemoteTrack {
5
+ export default class RemoteVideoTrack extends RemoteTrack<Track.Kind.Video> {
5
6
  private prevStats?;
6
7
  private elementInfos;
7
8
  private adaptiveStreamSettings?;
@@ -4,14 +4,15 @@ import { StructuredLogger } from '../../logger';
4
4
  import { TrackSource, TrackType } from '../../proto/livekit_models_pb';
5
5
  import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc_pb';
6
6
  import type { LoggerOptions } from '../types';
7
+ import type { TrackProcessor } from './processor/types';
7
8
  export declare enum VideoQuality {
8
9
  LOW = 0,
9
10
  MEDIUM = 1,
10
11
  HIGH = 2
11
12
  }
12
13
  declare const Track_base: new () => TypedEventEmitter<TrackEventCallbacks>;
13
- export declare abstract class Track extends Track_base {
14
- kind: Track.Kind;
14
+ export declare abstract class Track<TrackKind extends Track.Kind = Track.Kind> extends Track_base {
15
+ readonly kind: TrackKind;
15
16
  attachedElements: HTMLMediaElement[];
16
17
  isMuted: boolean;
17
18
  source: Track.Source;
@@ -36,7 +37,7 @@ export declare abstract class Track extends Track_base {
36
37
  protected _currentBitrate: number;
37
38
  protected monitorInterval?: ReturnType<typeof setInterval>;
38
39
  protected log: StructuredLogger;
39
- protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, loggerOptions?: LoggerOptions);
40
+ protected constructor(mediaTrack: MediaStreamTrack, kind: TrackKind, loggerOptions?: LoggerOptions);
40
41
  protected get logContext(): {
41
42
  [x: string]: unknown;
42
43
  };
@@ -135,6 +136,7 @@ export type TrackEventCallbacks = {
135
136
  elementDetached: (element: HTMLMediaElement) => void;
136
137
  upstreamPaused: (track: any) => void;
137
138
  upstreamResumed: (track: any) => void;
139
+ trackProcessorUpdate: (processor?: TrackProcessor<Track.Kind, any>) => void;
138
140
  };
139
141
  export {};
140
142
  //# sourceMappingURL=Track.d.ts.map
@@ -1,3 +1,4 @@
1
+ import type Room from '../../Room';
1
2
  import type { Track } from '../Track';
2
3
  /**
3
4
  * @experimental
@@ -6,6 +7,7 @@ export type ProcessorOptions<T extends Track.Kind> = {
6
7
  kind: T;
7
8
  track: MediaStreamTrack;
8
9
  element?: HTMLMediaElement;
10
+ audioContext?: AudioContext;
9
11
  };
10
12
  /**
11
13
  * @experimental
@@ -27,5 +29,7 @@ export interface TrackProcessor<T extends Track.Kind, U extends ProcessorOptions
27
29
  restart: (opts: U) => Promise<void>;
28
30
  destroy: () => Promise<void>;
29
31
  processedTrack?: MediaStreamTrack;
32
+ onPublish?: (room: Room) => Promise<void>;
33
+ onUnpublish?: () => Promise<void>;
30
34
  }
31
35
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livekit-client",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "JavaScript/TypeScript client SDK for LiveKit",
5
5
  "main": "./dist/livekit-client.umd.js",
6
6
  "unpkg": "./dist/livekit-client.umd.js",
@@ -317,7 +317,7 @@ export class SignalClient {
317
317
  let resp: SignalResponse;
318
318
  if (typeof ev.data === 'string') {
319
319
  const json = JSON.parse(ev.data);
320
- resp = SignalResponse.fromJson(json);
320
+ resp = SignalResponse.fromJson(json, { ignoreUnknownFields: true });
321
321
  } else if (ev.data instanceof ArrayBuffer) {
322
322
  resp = SignalResponse.fromBinary(new Uint8Array(ev.data));
323
323
  } else {
@@ -12,7 +12,7 @@ import { createKeyMaterialFromBuffer, createKeyMaterialFromString } from './util
12
12
  export class BaseKeyProvider extends (EventEmitter as new () => TypedEventEmitter<KeyProviderCallbacks>) {
13
13
  private keyInfoMap: Map<string, KeyInfo>;
14
14
 
15
- private options: KeyProviderOptions;
15
+ private readonly options: KeyProviderOptions;
16
16
 
17
17
  constructor(options: Partial<KeyProviderOptions> = {}) {
18
18
  super();
@@ -29,6 +29,11 @@ export class BaseKeyProvider extends (EventEmitter as new () => TypedEventEmitte
29
29
  */
30
30
  protected onSetEncryptionKey(key: CryptoKey, participantIdentity?: string, keyIndex?: number) {
31
31
  const keyInfo: KeyInfo = { key, participantIdentity, keyIndex };
32
+ if (!this.options.sharedKey && !participantIdentity) {
33
+ throw new Error(
34
+ 'participant identity needs to be passed for encryption key if sharedKey option is false',
35
+ );
36
+ }
32
37
  this.keyInfoMap.set(`${participantIdentity ?? 'shared'}-${keyIndex ?? 0}`, keyInfo);
33
38
  this.emit(KeyProviderEvent.SetKey, keyInfo);
34
39
  }
@@ -83,6 +83,14 @@ export class FrameCryptor extends BaseFrameCryptor {
83
83
  this.sifGuard = new SifGuard();
84
84
  }
85
85
 
86
+ private get logContext() {
87
+ return {
88
+ identity: this.participantIdentity,
89
+ trackId: this.trackId,
90
+ fallbackCodec: this.videoCodec,
91
+ };
92
+ }
93
+
86
94
  /**
87
95
  * Assign a different participant to the cryptor.
88
96
  * useful for transceiver re-use
@@ -96,6 +104,7 @@ export class FrameCryptor extends BaseFrameCryptor {
96
104
  }
97
105
 
98
106
  unsetParticipant() {
107
+ workerLogger.debug('unsetting participant', this.logContext);
99
108
  this.participantIdentity = undefined;
100
109
  }
101
110
 
@@ -143,6 +152,13 @@ export class FrameCryptor extends BaseFrameCryptor {
143
152
  this.videoCodec = codec;
144
153
  }
145
154
 
155
+ workerLogger.debug('Setting up frame cryptor transform', {
156
+ operation,
157
+ passedTrackId: trackId,
158
+ codec,
159
+ ...this.logContext,
160
+ });
161
+
146
162
  const transformFn = operation === 'encode' ? this.encodeFunction : this.decodeFunction;
147
163
  const transformStream = new TransformStream({
148
164
  transform: transformFn.bind(this),
@@ -159,6 +175,7 @@ export class FrameCryptor extends BaseFrameCryptor {
159
175
  }
160
176
 
161
177
  setSifTrailer(trailer: Uint8Array) {
178
+ workerLogger.debug('setting SIF trailer', { ...this.logContext, trailer });
162
179
  this.sifTrailer = trailer;
163
180
  }
164
181
 
@@ -212,6 +229,8 @@ export class FrameCryptor extends BaseFrameCryptor {
212
229
  encodedFrame.timestamp,
213
230
  );
214
231
  let frameInfo = this.getUnencryptedBytes(encodedFrame);
232
+ workerLogger.debug('frameInfo for encoded frame', { ...frameInfo, ...this.logContext });
233
+
215
234
  // Thіs is not encrypted and contains the VP8 payload descriptor or the Opus TOC byte.
216
235
  const frameHeader = new Uint8Array(encodedFrame.data, 0, frameInfo.unencryptedBytes);
217
236
 
@@ -262,6 +281,7 @@ export class FrameCryptor extends BaseFrameCryptor {
262
281
  workerLogger.error(e);
263
282
  }
264
283
  } else {
284
+ workerLogger.debug('failed to decrypt, emitting error', this.logContext);
265
285
  this.emit(
266
286
  CryptorEvent.Error,
267
287
  new CryptorError(`encryption key missing for encoding`, CryptorErrorReason.MissingKey),
@@ -284,11 +304,13 @@ export class FrameCryptor extends BaseFrameCryptor {
284
304
  // skip for decryption for empty dtx frames
285
305
  encodedFrame.data.byteLength === 0
286
306
  ) {
307
+ workerLogger.debug('skipping empty frame', this.logContext);
287
308
  this.sifGuard.recordUserFrame();
288
309
  return controller.enqueue(encodedFrame);
289
310
  }
290
311
 
291
312
  if (isFrameServerInjected(encodedFrame.data, this.sifTrailer)) {
313
+ workerLogger.debug('enqueue SIF', this.logContext);
292
314
  this.sifGuard.recordSif();
293
315
 
294
316
  if (this.sifGuard.isSifAllowed()) {
@@ -312,6 +334,7 @@ export class FrameCryptor extends BaseFrameCryptor {
312
334
  const decodedFrame = await this.decryptFrame(encodedFrame, keyIndex);
313
335
  this.keys.decryptionSuccess();
314
336
  if (decodedFrame) {
337
+ workerLogger.debug('enqueue decrypted frame', this.logContext);
315
338
  return controller.enqueue(decodedFrame);
316
339
  }
317
340
  } catch (error) {
@@ -352,6 +375,8 @@ export class FrameCryptor extends BaseFrameCryptor {
352
375
  throw new TypeError(`no encryption key found for decryption of ${this.participantIdentity}`);
353
376
  }
354
377
  let frameInfo = this.getUnencryptedBytes(encodedFrame);
378
+ workerLogger.debug('frameInfo for decoded frame', { ...frameInfo, ...this.logContext });
379
+
355
380
  // Construct frame trailer. Similar to the frame header described in
356
381
  // https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2
357
382
  // but we put it at the end.
@@ -566,6 +591,7 @@ export class FrameCryptor extends BaseFrameCryptor {
566
591
  // @ts-expect-error payloadType is not yet part of the typescript definition and currently not supported in Safari
567
592
  const payloadType = frame.getMetadata().payloadType;
568
593
  const codec = payloadType ? this.rtpMap.get(payloadType) : undefined;
594
+ workerLogger.debug('reading codec from frame', { codec, ...this.logContext });
569
595
  return codec;
570
596
  }
571
597
  }
@@ -133,15 +133,19 @@ export class ParticipantKeyHandler extends (EventEmitter as new () => TypedEvent
133
133
 
134
134
  /**
135
135
  * takes in a key material with `deriveBits` and `deriveKey` set as key usages
136
- * and derives encryption keys from the material and sets it on the key ring buffer
136
+ * and derives encryption keys from the material and sets it on the key ring buffers
137
137
  * together with the material
138
138
  * also updates the currentKeyIndex
139
139
  */
140
- async setKeyFromMaterial(material: CryptoKey, keyIndex = 0, emitRatchetEvent = false) {
141
- const newIndex = keyIndex >= 0 ? keyIndex % this.cryptoKeyRing.length : -1;
142
- workerLogger.debug(`setting new key with index ${newIndex}`);
140
+ async setKeyFromMaterial(material: CryptoKey, keyIndex: number, emitRatchetEvent = false) {
143
141
  const keySet = await deriveKeys(material, this.keyProviderOptions.ratchetSalt);
144
- this.setKeySet(keySet, newIndex >= 0 ? newIndex : this.currentKeyIndex, emitRatchetEvent);
142
+ const newIndex = keyIndex >= 0 ? keyIndex % this.cryptoKeyRing.length : this.currentKeyIndex;
143
+ workerLogger.debug(`setting new key with index ${keyIndex}`, {
144
+ usage: material.usages,
145
+ algorithm: material.algorithm,
146
+ ratchetSalt: this.keyProviderOptions.ratchetSalt,
147
+ });
148
+ this.setKeySet(keySet, newIndex, emitRatchetEvent);
145
149
  if (newIndex >= 0) this.currentKeyIndex = newIndex;
146
150
  }
147
151
 
@@ -21,8 +21,6 @@ let isEncryptionEnabled: boolean = false;
21
21
 
22
22
  let useSharedKey: boolean = false;
23
23
 
24
- let sharedKey: CryptoKey | undefined;
25
-
26
24
  let sifTrailer: Uint8Array | undefined;
27
25
 
28
26
  let keyProviderOptions: KeyProviderOptions = KEY_PROVIDER_DEFAULTS;
@@ -72,10 +70,9 @@ onmessage = (ev) => {
72
70
  break;
73
71
  case 'setKey':
74
72
  if (useSharedKey) {
75
- workerLogger.warn('set shared key');
76
73
  setSharedKey(data.key, data.keyIndex);
77
74
  } else if (data.participantIdentity) {
78
- workerLogger.warn(
75
+ workerLogger.info(
79
76
  `set participant sender key ${data.participantIdentity} index ${data.keyIndex}`,
80
77
  );
81
78
  getParticipantKeyHandler(data.participantIdentity).setKey(data.key, data.keyIndex);
@@ -84,7 +81,7 @@ onmessage = (ev) => {
84
81
  }
85
82
  break;
86
83
  case 'removeTransform':
87
- unsetCryptorParticipant(data.trackId);
84
+ unsetCryptorParticipant(data.trackId, data.participantIdentity);
88
85
  break;
89
86
  case 'updateCodec':
90
87
  getTrackCryptor(data.participantIdentity, data.trackId).setVideoCodec(data.codec);
@@ -144,8 +141,7 @@ function getTrackCryptor(participantIdentity: string, trackId: string) {
144
141
  // assign new participant id to track cryptor and pass in correct key handler
145
142
  cryptor.setParticipant(participantIdentity, getParticipantKeyHandler(participantIdentity));
146
143
  }
147
- if (sharedKey) {
148
- }
144
+
149
145
  return cryptor;
150
146
  }
151
147
 
@@ -156,9 +152,6 @@ function getParticipantKeyHandler(participantIdentity: string) {
156
152
  let keys = participantKeys.get(participantIdentity);
157
153
  if (!keys) {
158
154
  keys = new ParticipantKeyHandler(participantIdentity, keyProviderOptions);
159
- if (sharedKey) {
160
- keys.setKey(sharedKey);
161
- }
162
155
  keys.on(KeyHandlerEvent.KeyRatcheted, emitRatchetedKeys);
163
156
  participantKeys.set(participantIdentity, keys);
164
157
  }
@@ -167,22 +160,32 @@ function getParticipantKeyHandler(participantIdentity: string) {
167
160
 
168
161
  function getSharedKeyHandler() {
169
162
  if (!sharedKeyHandler) {
163
+ workerLogger.debug('creating new shared key handler');
170
164
  sharedKeyHandler = new ParticipantKeyHandler('shared-key', keyProviderOptions);
171
165
  }
172
166
  return sharedKeyHandler;
173
167
  }
174
168
 
175
- function unsetCryptorParticipant(trackId: string) {
176
- participantCryptors.find((c) => c.getTrackId() === trackId)?.unsetParticipant();
169
+ function unsetCryptorParticipant(trackId: string, participantIdentity: string) {
170
+ const cryptor = participantCryptors.find(
171
+ (c) => c.getParticipantIdentity() === participantIdentity && c.getTrackId() === trackId,
172
+ );
173
+ if (!cryptor) {
174
+ workerLogger.warn('Could not unset participant on cryptor', { trackId, participantIdentity });
175
+ } else {
176
+ cryptor.unsetParticipant();
177
+ }
177
178
  }
178
179
 
179
180
  function setEncryptionEnabled(enable: boolean, participantIdentity: string) {
181
+ workerLogger.debug(`setting encryption enabled for all tracks of ${participantIdentity}`, {
182
+ enable,
183
+ });
180
184
  encryptionEnabledMap.set(participantIdentity, enable);
181
185
  }
182
186
 
183
187
  function setSharedKey(key: CryptoKey, index?: number) {
184
- workerLogger.debug('setting shared key');
185
- sharedKey = key;
188
+ workerLogger.info('set shared key', { index });
186
189
  getSharedKeyHandler().setKey(key, index);
187
190
  }
188
191
 
package/src/room/Room.ts CHANGED
@@ -60,6 +60,7 @@ import type RemoteTrack from './track/RemoteTrack';
60
60
  import RemoteTrackPublication from './track/RemoteTrackPublication';
61
61
  import { Track } from './track/Track';
62
62
  import type { TrackPublication } from './track/TrackPublication';
63
+ import type { TrackProcessor } from './track/processor/types';
63
64
  import type { AdaptiveStreamSettings } from './track/types';
64
65
  import { getNewAudioContext, sourceToKind } from './track/utils';
65
66
  import type { SimulationOptions, SimulationScenario } from './types';
@@ -573,16 +574,23 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
573
574
  this.localParticipant.sid = pi.sid;
574
575
  this.localParticipant.identity = pi.identity;
575
576
 
577
+ if (this.options.e2ee && this.e2eeManager) {
578
+ try {
579
+ this.e2eeManager.setSifTrailer(joinResponse.sifTrailer);
580
+ } catch (e: any) {
581
+ this.log.error(e instanceof Error ? e.message : 'Could not set SifTrailer', {
582
+ ...this.logContext,
583
+ error: e,
584
+ });
585
+ }
586
+ }
587
+
576
588
  // populate remote participants, these should not trigger new events
577
589
  this.handleParticipantUpdates([pi, ...joinResponse.otherParticipants]);
578
590
 
579
591
  if (joinResponse.room) {
580
592
  this.handleRoomUpdate(joinResponse.room);
581
593
  }
582
-
583
- if (this.options.e2ee && this.e2eeManager) {
584
- this.e2eeManager.setSifTrailer(joinResponse.sifTrailer);
585
- }
586
594
  };
587
595
 
588
596
  private attemptConnection = async (
@@ -1775,8 +1783,16 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1775
1783
  this.emit(RoomEvent.TrackUnmuted, pub, this.localParticipant);
1776
1784
  };
1777
1785
 
1786
+ private onTrackProcessorUpdate = (processor?: TrackProcessor<Track.Kind, any>) => {
1787
+ processor?.onPublish?.(this);
1788
+ };
1789
+
1778
1790
  private onLocalTrackPublished = async (pub: LocalTrackPublication) => {
1791
+ pub.track?.on(TrackEvent.TrackProcessorUpdate, this.onTrackProcessorUpdate);
1792
+ pub.track?.getProcessor()?.onPublish?.(this);
1793
+
1779
1794
  this.emit(RoomEvent.LocalTrackPublished, pub, this.localParticipant);
1795
+
1780
1796
  if (pub.track instanceof LocalAudioTrack) {
1781
1797
  const trackIsSilent = await pub.track.checkForSilence();
1782
1798
  if (trackIsSilent) {
@@ -1796,6 +1812,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1796
1812
  };
1797
1813
 
1798
1814
  private onLocalTrackUnpublished = (pub: LocalTrackPublication) => {
1815
+ pub.track?.off(TrackEvent.TrackProcessorUpdate, this.onTrackProcessorUpdate);
1799
1816
  this.emit(RoomEvent.LocalTrackUnpublished, pub, this.localParticipant);
1800
1817
  };
1801
1818
 
@@ -552,4 +552,8 @@ export enum TrackEvent {
552
552
  * Fires on RemoteTrackPublication
553
553
  */
554
554
  SubscriptionFailed = 'subscriptionFailed',
555
+ /**
556
+ * @internal
557
+ */
558
+ TrackProcessorUpdate = 'trackProcessorUpdate',
555
559
  }
@@ -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,
@@ -166,6 +163,7 @@ export default class LocalAudioTrack extends LocalTrack {
166
163
  if (this.processor.processedTrack) {
167
164
  await this.sender?.replaceTrack(this.processor.processedTrack);
168
165
  }
166
+ this.emit(TrackEvent.TrackProcessorUpdate, this.processor);
169
167
  } finally {
170
168
  unlock();
171
169
  }