livekit-client 2.0.1 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
  }