livekit-client 1.12.3 → 1.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) 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 +198 -107
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +515 -192
  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/api/SignalClient.d.ts +2 -5
  10. package/dist/src/api/SignalClient.d.ts.map +1 -1
  11. package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -1
  12. package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
  13. package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
  14. package/dist/src/e2ee/E2eeManager.d.ts +9 -3
  15. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  16. package/dist/src/e2ee/KeyProvider.d.ts +10 -7
  17. package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
  18. package/dist/src/e2ee/constants.d.ts +2 -0
  19. package/dist/src/e2ee/constants.d.ts.map +1 -1
  20. package/dist/src/e2ee/events.d.ts +34 -0
  21. package/dist/src/e2ee/events.d.ts.map +1 -0
  22. package/dist/src/e2ee/index.d.ts +1 -0
  23. package/dist/src/e2ee/index.d.ts.map +1 -1
  24. package/dist/src/e2ee/types.d.ts +23 -33
  25. package/dist/src/e2ee/types.d.ts.map +1 -1
  26. package/dist/src/e2ee/utils.d.ts +1 -0
  27. package/dist/src/e2ee/utils.d.ts.map +1 -1
  28. package/dist/src/e2ee/worker/FrameCryptor.d.ts +18 -13
  29. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  30. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +6 -8
  31. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  32. package/dist/src/e2ee/worker/SifGuard.d.ts +11 -0
  33. package/dist/src/e2ee/worker/SifGuard.d.ts.map +1 -0
  34. package/dist/src/options.d.ts +5 -0
  35. package/dist/src/options.d.ts.map +1 -1
  36. package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
  37. package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -1
  38. package/dist/src/room/DeviceManager.d.ts +1 -0
  39. package/dist/src/room/DeviceManager.d.ts.map +1 -1
  40. package/dist/src/room/PCTransport.d.ts.map +1 -1
  41. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  42. package/dist/src/room/Room.d.ts +1 -1
  43. package/dist/src/room/Room.d.ts.map +1 -1
  44. package/dist/src/room/defaults.d.ts.map +1 -1
  45. package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
  46. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  47. package/dist/src/room/participant/Participant.d.ts +5 -0
  48. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  49. package/dist/src/room/participant/RemoteParticipant.d.ts +0 -5
  50. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  51. package/dist/src/room/timers.d.ts +2 -2
  52. package/dist/src/room/timers.d.ts.map +1 -1
  53. package/dist/src/room/track/LocalAudioTrack.d.ts +9 -1
  54. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  55. package/dist/src/room/track/LocalTrack.d.ts +3 -3
  56. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  57. package/dist/src/room/track/LocalVideoTrack.d.ts +6 -0
  58. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  59. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  60. package/dist/src/room/track/processor/types.d.ts +14 -2
  61. package/dist/src/room/track/processor/types.d.ts.map +1 -1
  62. package/dist/src/room/types.d.ts +1 -1
  63. package/dist/src/room/types.d.ts.map +1 -1
  64. package/dist/ts4.2/src/api/SignalClient.d.ts +2 -5
  65. package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +9 -3
  66. package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +10 -7
  67. package/dist/ts4.2/src/e2ee/constants.d.ts +2 -0
  68. package/dist/ts4.2/src/e2ee/events.d.ts +34 -0
  69. package/dist/ts4.2/src/e2ee/index.d.ts +1 -0
  70. package/dist/ts4.2/src/e2ee/types.d.ts +23 -33
  71. package/dist/ts4.2/src/e2ee/utils.d.ts +1 -0
  72. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +18 -13
  73. package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +6 -8
  74. package/dist/ts4.2/src/e2ee/worker/SifGuard.d.ts +11 -0
  75. package/dist/ts4.2/src/options.d.ts +5 -0
  76. package/dist/ts4.2/src/room/DeviceManager.d.ts +1 -0
  77. package/dist/ts4.2/src/room/Room.d.ts +1 -1
  78. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -0
  79. package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -0
  80. package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +0 -5
  81. package/dist/ts4.2/src/room/timers.d.ts +2 -2
  82. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +9 -1
  83. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -3
  84. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +6 -0
  85. package/dist/ts4.2/src/room/track/processor/types.d.ts +14 -2
  86. package/dist/ts4.2/src/room/types.d.ts +1 -1
  87. package/package.json +15 -16
  88. package/src/api/SignalClient.ts +13 -9
  89. package/src/connectionHelper/checks/turn.ts +1 -0
  90. package/src/connectionHelper/checks/webrtc.ts +9 -7
  91. package/src/connectionHelper/checks/websocket.ts +1 -0
  92. package/src/e2ee/E2eeManager.ts +129 -76
  93. package/src/e2ee/KeyProvider.ts +31 -16
  94. package/src/e2ee/constants.ts +3 -0
  95. package/src/e2ee/events.ts +48 -0
  96. package/src/e2ee/index.ts +1 -0
  97. package/src/e2ee/types.ts +27 -41
  98. package/src/e2ee/utils.ts +9 -0
  99. package/src/e2ee/worker/FrameCryptor.ts +90 -47
  100. package/src/e2ee/worker/ParticipantKeyHandler.ts +25 -26
  101. package/src/e2ee/worker/SifGuard.ts +47 -0
  102. package/src/e2ee/worker/e2ee.worker.ts +75 -68
  103. package/src/options.ts +6 -0
  104. package/src/proto/livekit_models_pb.ts +14 -0
  105. package/src/proto/livekit_rtc_pb.ts +14 -0
  106. package/src/room/DeviceManager.ts +7 -2
  107. package/src/room/PCTransport.ts +12 -2
  108. package/src/room/RTCEngine.ts +3 -2
  109. package/src/room/Room.ts +47 -22
  110. package/src/room/defaults.ts +1 -0
  111. package/src/room/participant/LocalParticipant.ts +18 -2
  112. package/src/room/participant/Participant.ts +16 -0
  113. package/src/room/participant/RemoteParticipant.ts +0 -12
  114. package/src/room/track/LocalAudioTrack.ts +45 -0
  115. package/src/room/track/LocalTrack.ts +22 -14
  116. package/src/room/track/LocalVideoTrack.ts +39 -0
  117. package/src/room/track/RemoteAudioTrack.ts +9 -1
  118. package/src/room/track/RemoteTrackPublication.ts +2 -2
  119. package/src/room/track/facingMode.ts +1 -1
  120. package/src/room/track/processor/types.ts +18 -2
  121. package/src/room/types.ts +5 -1
@@ -54,8 +54,8 @@ export default abstract class LocalTrack extends Track {
54
54
  * the server.
55
55
  * this API is unsupported on Safari < 12 due to a bug
56
56
  **/
57
- pauseUpstream: () => Promise<void>;
58
- resumeUpstream: () => Promise<void>;
57
+ pauseUpstream(): Promise<void>;
58
+ resumeUpstream(): Promise<void>;
59
59
  /**
60
60
  * Sets a processor on this track.
61
61
  * See https://github.com/livekit/track-processors-js for example usage
@@ -67,7 +67,7 @@ export default abstract class LocalTrack extends Track {
67
67
  * @returns
68
68
  */
69
69
  setProcessor(processor: TrackProcessor<typeof this.kind>, showProcessedStreamLocally?: boolean): Promise<void>;
70
- getProcessor(): TrackProcessor<Track.Kind> | undefined;
70
+ getProcessor(): TrackProcessor<Track.Kind, import("./processor/types").ProcessorOptions<Track.Kind>> | undefined;
71
71
  /**
72
72
  * Stops the track processor
73
73
  * See https://github.com/livekit/track-processors-js for example usage
@@ -3,7 +3,9 @@ import { VideoLayer, VideoQuality } from '../../proto/livekit_models_pb';
3
3
  import { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc_pb';
4
4
  import type { VideoSenderStats } from '../stats';
5
5
  import LocalTrack from './LocalTrack';
6
+ import { Track } from './Track';
6
7
  import type { VideoCaptureOptions, VideoCodec } from './options';
8
+ import type { TrackProcessor } from './processor/types';
7
9
  export declare class SimulcastTrackInfo {
8
10
  codec: VideoCodec;
9
11
  mediaStreamTrack: MediaStreamTrack;
@@ -28,12 +30,16 @@ export default class LocalVideoTrack extends LocalTrack {
28
30
  get isSimulcast(): boolean;
29
31
  startMonitor(signalClient: SignalClient): void;
30
32
  stop(): void;
33
+ pauseUpstream(): Promise<void>;
34
+ resumeUpstream(): Promise<void>;
31
35
  mute(): Promise<LocalVideoTrack>;
32
36
  unmute(): Promise<LocalVideoTrack>;
37
+ protected setTrackMuted(muted: boolean): void;
33
38
  getSenderStats(): Promise<VideoSenderStats[]>;
34
39
  setPublishingQuality(maxQuality: VideoQuality): void;
35
40
  setDeviceId(deviceId: ConstrainDOMString): Promise<boolean>;
36
41
  restartTrack(options?: VideoCaptureOptions): Promise<void>;
42
+ setProcessor(processor: TrackProcessor<Track.Kind>, showProcessedStreamLocally?: boolean): Promise<void>;
37
43
  addSimulcastTrack(codec: VideoCodec, encodings?: RTCRtpEncodingParameters[]): SimulcastTrackInfo;
38
44
  setSimulcastTrackSender(codec: VideoCodec, sender: RTCRtpSender): void;
39
45
  /**
@@ -10,9 +10,21 @@ export type ProcessorOptions<T extends Track.Kind> = {
10
10
  /**
11
11
  * @experimental
12
12
  */
13
- export interface TrackProcessor<T extends Track.Kind> {
13
+ export interface AudioProcessorOptions extends ProcessorOptions<Track.Kind.Audio> {
14
+ audioContext: AudioContext;
15
+ }
16
+ /**
17
+ * @experimental
18
+ */
19
+ export interface VideoProcessorOptions extends ProcessorOptions<Track.Kind.Video> {
20
+ }
21
+ /**
22
+ * @experimental
23
+ */
24
+ export interface TrackProcessor<T extends Track.Kind, U extends ProcessorOptions<T> = ProcessorOptions<T>> {
14
25
  name: string;
15
- init: (opts: ProcessorOptions<T>) => void;
26
+ init: (opts: U) => Promise<void>;
27
+ restart: (opts: U) => Promise<void>;
16
28
  destroy: () => Promise<void>;
17
29
  processedTrack?: MediaStreamTrack;
18
30
  }
@@ -22,5 +22,5 @@ export type LiveKitReactNativeInfo = {
22
22
  platform: 'ios' | 'android' | 'windows' | 'macos' | 'web' | 'native';
23
23
  devicePixelRatio: number;
24
24
  };
25
- export type SimulationScenario = 'signal-reconnect' | 'speaker' | 'node-failure' | 'server-leave' | 'migration' | 'resume-reconnect' | 'force-tcp' | 'force-tls' | 'full-reconnect';
25
+ export type SimulationScenario = 'signal-reconnect' | 'speaker' | 'node-failure' | 'server-leave' | 'migration' | 'resume-reconnect' | 'force-tcp' | 'force-tls' | 'full-reconnect' | 'subscriber-bandwidth';
26
26
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livekit-client",
3
- "version": "1.12.3",
3
+ "version": "1.13.1",
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",
@@ -61,10 +61,10 @@
61
61
  "webrtc-adapter": "^8.1.1"
62
62
  },
63
63
  "devDependencies": {
64
- "@babel/core": "7.22.1",
65
- "@babel/preset-env": "7.22.4",
64
+ "@babel/core": "7.22.9",
65
+ "@babel/preset-env": "7.22.9",
66
66
  "@bufbuild/protoc-gen-es": "^1.3.0",
67
- "@changesets/cli": "2.26.1",
67
+ "@changesets/cli": "2.26.2",
68
68
  "@livekit/changesets-changelog-github": "^0.0.4",
69
69
  "@rollup/plugin-babel": "6.0.3",
70
70
  "@rollup/plugin-commonjs": "24.1.0",
@@ -77,27 +77,26 @@
77
77
  "@types/events": "^3.0.0",
78
78
  "@types/sdp-transform": "2.4.6",
79
79
  "@types/ua-parser-js": "0.7.36",
80
- "@typescript-eslint/eslint-plugin": "5.59.8",
81
- "@typescript-eslint/parser": "5.59.8",
80
+ "@typescript-eslint/eslint-plugin": "5.62.0",
81
+ "@typescript-eslint/parser": "5.62.0",
82
82
  "downlevel-dts": "^0.11.0",
83
- "eslint": "8.42.0",
84
- "eslint-config-airbnb-typescript": "17.0.0",
85
- "eslint-config-prettier": "8.8.0",
83
+ "eslint": "8.46.0",
84
+ "eslint-config-airbnb-typescript": "17.1.0",
85
+ "eslint-config-prettier": "8.9.0",
86
86
  "eslint-plugin-ecmascript-compat": "^3.0.0",
87
- "eslint-plugin-import": "2.27.5",
87
+ "eslint-plugin-import": "2.28.0",
88
88
  "gh-pages": "5.0.0",
89
89
  "jsdom": "^22.1.0",
90
90
  "prettier": "^2.8.8",
91
- "rollup": "3.23.1",
91
+ "rollup": "3.27.0",
92
92
  "rollup-plugin-delete": "^2.0.0",
93
93
  "rollup-plugin-re": "1.0.7",
94
- "rollup-plugin-typescript2": "0.34.1",
94
+ "rollup-plugin-typescript2": "0.35.0",
95
95
  "size-limit": "^8.2.4",
96
- "ts-proto": "1.148.2",
97
96
  "typedoc": "0.24.8",
98
97
  "typedoc-plugin-no-inherit": "1.4.0",
99
- "typescript": "5.1.3",
100
- "vite": "4.3.9",
101
- "vitest": "^0.32.0"
98
+ "typescript": "5.1.6",
99
+ "vite": "4.4.7",
100
+ "vitest": "^0.33.0"
102
101
  }
103
102
  }
@@ -43,21 +43,13 @@ import { Mutex, getClientInfo, isReactNative, sleep, toWebsocketUrl } from '../r
43
43
  import { AsyncQueue } from '../utils/AsyncQueue';
44
44
 
45
45
  // internal options
46
- interface ConnectOpts {
47
- autoSubscribe: boolean;
46
+ interface ConnectOpts extends SignalOptions {
48
47
  /** internal */
49
48
  reconnect?: boolean;
50
-
51
49
  /** internal */
52
50
  reconnectReason?: number;
53
-
54
51
  /** internal */
55
52
  sid?: string;
56
-
57
- /** @deprecated */
58
- publishOnly?: string;
59
-
60
- adaptiveStream?: boolean;
61
53
  }
62
54
 
63
55
  // public options
@@ -68,6 +60,7 @@ export interface SignalOptions {
68
60
  adaptiveStream?: boolean;
69
61
  maxRetries: number;
70
62
  e2eeEnabled: boolean;
63
+ websocketTimeout: number;
71
64
  }
72
65
 
73
66
  type SignalMessage = SignalRequest['message'];
@@ -224,9 +217,15 @@ export class SignalClient {
224
217
  return new Promise<JoinResponse | ReconnectResponse | void>(async (resolve, reject) => {
225
218
  const abortHandler = async () => {
226
219
  this.close();
220
+ clearTimeout(wsTimeout);
227
221
  reject(new ConnectionError('room connection has been cancelled (signal)'));
228
222
  };
229
223
 
224
+ const wsTimeout = setTimeout(() => {
225
+ this.close();
226
+ reject(new ConnectionError('room connection has timed out (signal)'));
227
+ }, opts.websocketTimeout);
228
+
230
229
  if (abortSignal?.aborted) {
231
230
  abortHandler();
232
231
  }
@@ -238,8 +237,13 @@ export class SignalClient {
238
237
  this.ws = new WebSocket(url + params);
239
238
  this.ws.binaryType = 'arraybuffer';
240
239
 
240
+ this.ws.onopen = () => {
241
+ clearTimeout(wsTimeout);
242
+ };
243
+
241
244
  this.ws.onerror = async (ev: Event) => {
242
245
  if (!this.isConnected) {
246
+ clearTimeout(wsTimeout);
243
247
  try {
244
248
  const resp = await fetch(`http${url.substring(2)}/validate${params}`);
245
249
  if (resp.status.toFixed(0).startsWith('4')) {
@@ -12,6 +12,7 @@ export class TURNCheck extends Checker {
12
12
  autoSubscribe: true,
13
13
  maxRetries: 0,
14
14
  e2eeEnabled: false,
15
+ websocketTimeout: 15_000,
15
16
  });
16
17
 
17
18
  let hasTLS = false;
@@ -1,3 +1,4 @@
1
+ import log from '../../logger';
1
2
  import { RoomEvent } from '../../room/events';
2
3
  import { Checker } from './Checker';
3
4
 
@@ -14,19 +15,20 @@ export class WebRTCCheck extends Checker {
14
15
 
15
16
  const candidates: RTCIceCandidate[] = [];
16
17
  this.room.engine.client.onTrickle = (sd, target) => {
17
- console.log('got candidate', sd);
18
18
  if (sd.candidate) {
19
19
  const candidate = new RTCIceCandidate(sd);
20
20
  candidates.push(candidate);
21
21
  let str = `${candidate.protocol} ${candidate.address}:${candidate.port} ${candidate.type}`;
22
- if (candidate.protocol === 'tcp' && candidate.tcpType === 'passive') {
23
- hasTcp = true;
24
- str += ' (active)';
25
- } else if (candidate.protocol === 'udp' && candidate.address) {
22
+ if (candidate.address) {
26
23
  if (isIPPrivate(candidate.address)) {
27
24
  str += ' (private)';
28
25
  } else {
29
- hasIpv4Udp = true;
26
+ if (candidate.protocol === 'tcp' && candidate.tcpType === 'passive') {
27
+ hasTcp = true;
28
+ str += ' (passive)';
29
+ } else if (candidate.protocol === 'udp') {
30
+ hasIpv4Udp = true;
31
+ }
30
32
  }
31
33
  }
32
34
  this.appendMessage(str);
@@ -48,7 +50,7 @@ export class WebRTCCheck extends Checker {
48
50
  });
49
51
  try {
50
52
  await this.connect();
51
- console.log('now the room is connected');
53
+ log.info('now the room is connected');
52
54
  } catch (err) {
53
55
  this.appendWarning('ports need to be open on firewall in order to connect.');
54
56
  throw err;
@@ -17,6 +17,7 @@ export class WebSocketCheck extends Checker {
17
17
  autoSubscribe: true,
18
18
  maxRetries: 0,
19
19
  e2eeEnabled: false,
20
+ websocketTimeout: 15_000,
20
21
  });
21
22
  this.appendMessage(`Connected to server, version ${joinRes.serverVersion}.`);
22
23
  if (joinRes.serverInfo?.edition === ServerInfo_Edition.Cloud && joinRes.serverInfo?.region) {
@@ -13,8 +13,8 @@ import type { Track } from '../room/track/Track';
13
13
  import type { VideoCodec } from '../room/track/options';
14
14
  import type { BaseKeyProvider } from './KeyProvider';
15
15
  import { E2EE_FLAG } from './constants';
16
+ import { type E2EEManagerCallbacks, EncryptionEvent, KeyProviderEvent } from './events';
16
17
  import type {
17
- E2EEManagerCallbacks,
18
18
  E2EEOptions,
19
19
  E2EEWorkerMessage,
20
20
  EnableMessage,
@@ -25,9 +25,9 @@ import type {
25
25
  RatchetRequestMessage,
26
26
  RemoveTransformMessage,
27
27
  SetKeyMessage,
28
+ SifTrailerMessage,
28
29
  UpdateCodecMessage,
29
30
  } from './types';
30
- import { EncryptionEvent } from './types';
31
31
  import { isE2EESupported, isScriptTransformSupported, mimeTypeToVideoCodecString } from './utils';
32
32
 
33
33
  /**
@@ -42,10 +42,6 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
42
42
 
43
43
  private keyProvider: BaseKeyProvider;
44
44
 
45
- get isEnabled() {
46
- return this.encryptionEnabled;
47
- }
48
-
49
45
  constructor(options: E2EEOptions) {
50
46
  super();
51
47
  this.keyProvider = options.keyProvider;
@@ -85,17 +81,19 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
85
81
  /**
86
82
  * @internal
87
83
  */
88
- async setParticipantCryptorEnabled(enabled: boolean, participantId?: string) {
89
- log.info(`set e2ee to ${enabled}`);
84
+ setParticipantCryptorEnabled(enabled: boolean, participantIdentity: string) {
85
+ log.debug(`set e2ee to ${enabled} for participant ${participantIdentity}`);
86
+ this.postEnable(enabled, participantIdentity);
87
+ }
90
88
 
91
- if (this.worker) {
92
- const enableMsg: EnableMessage = {
93
- kind: 'enable',
94
- data: { enabled, participantId },
95
- };
96
- this.worker.postMessage(enableMsg);
89
+ /**
90
+ * @internal
91
+ */
92
+ setSifTrailer(trailer: Uint8Array) {
93
+ if (!trailer || trailer.length === 0) {
94
+ log.warn("ignoring server sent trailer as it's empty");
97
95
  } else {
98
- throw new ReferenceError('failed to enable e2ee, worker is not ready');
96
+ this.postSifTrailer(trailer);
99
97
  }
100
98
  }
101
99
 
@@ -103,19 +101,35 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
103
101
  const { kind, data } = ev.data;
104
102
  switch (kind) {
105
103
  case 'error':
106
- console.error('error in worker', { data });
107
- this.emit(EncryptionEvent.Error, data.error);
104
+ log.error(data.error.message);
105
+ this.emit(EncryptionEvent.EncryptionError, data.error);
108
106
  break;
107
+ case 'initAck':
108
+ if (data.enabled) {
109
+ this.keyProvider.getKeys().forEach((keyInfo) => {
110
+ this.postKey(keyInfo);
111
+ });
112
+ }
113
+ break;
114
+
109
115
  case 'enable':
110
- if (this.encryptionEnabled !== data.enabled && !data.participantId) {
116
+ if (
117
+ this.encryptionEnabled !== data.enabled &&
118
+ data.participantIdentity === this.room?.localParticipant.identity
119
+ ) {
111
120
  this.emit(
112
121
  EncryptionEvent.ParticipantEncryptionStatusChanged,
113
122
  data.enabled,
114
- this.room?.localParticipant,
123
+ this.room!.localParticipant,
115
124
  );
116
125
  this.encryptionEnabled = data.enabled;
117
- } else if (data.participantId) {
118
- const participant = this.room?.getParticipantByIdentity(data.participantId);
126
+ } else if (data.participantIdentity) {
127
+ const participant = this.room?.getParticipantByIdentity(data.participantIdentity);
128
+ if (!participant) {
129
+ throw TypeError(
130
+ `couldn't set encryption status, participant not found${data.participantIdentity}`,
131
+ );
132
+ }
119
133
  this.emit(EncryptionEvent.ParticipantEncryptionStatusChanged, data.enabled, participant);
120
134
  }
121
135
  if (this.encryptionEnabled) {
@@ -125,7 +139,7 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
125
139
  }
126
140
  break;
127
141
  case 'ratchetKey':
128
- this.keyProvider.emit('keyRatcheted', data.material, data.keyIndex);
142
+ this.keyProvider.emit(KeyProviderEvent.KeyRatcheted, data.material, data.keyIndex);
129
143
  break;
130
144
  default:
131
145
  break;
@@ -134,7 +148,7 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
134
148
 
135
149
  private onWorkerError = (ev: ErrorEvent) => {
136
150
  log.error('e2ee worker encountered an error:', { error: ev.error });
137
- this.emit(EncryptionEvent.Error, ev.error);
151
+ this.emit(EncryptionEvent.EncryptionError, ev.error);
138
152
  };
139
153
 
140
154
  public setupEngine(engine: RTCEngine) {
@@ -150,69 +164,78 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
150
164
  participant.identity,
151
165
  ),
152
166
  );
153
- room.on(RoomEvent.ConnectionStateChanged, (state) => {
154
- if (state === ConnectionState.Connected) {
155
- room.participants.forEach((participant) => {
156
- participant.tracks.forEach((pub) => {
157
- this.setParticipantCryptorEnabled(
158
- pub.trackInfo!.encryption !== Encryption_Type.NONE,
159
- participant.identity,
160
- );
167
+ room
168
+ .on(RoomEvent.ConnectionStateChanged, (state) => {
169
+ if (state === ConnectionState.Connected) {
170
+ room.participants.forEach((participant) => {
171
+ participant.tracks.forEach((pub) => {
172
+ this.setParticipantCryptorEnabled(
173
+ pub.trackInfo!.encryption !== Encryption_Type.NONE,
174
+ participant.identity,
175
+ );
176
+ });
161
177
  });
178
+ }
179
+ })
180
+ .on(RoomEvent.TrackUnsubscribed, (track, _, participant) => {
181
+ const msg: RemoveTransformMessage = {
182
+ kind: 'removeTransform',
183
+ data: {
184
+ participantIdentity: participant.identity,
185
+ trackId: track.mediaStreamID,
186
+ },
187
+ };
188
+ this.worker?.postMessage(msg);
189
+ })
190
+ .on(RoomEvent.TrackSubscribed, (track, pub, participant) => {
191
+ this.setupE2EEReceiver(track, participant.identity, pub.trackInfo);
192
+ })
193
+ .on(RoomEvent.SignalConnected, () => {
194
+ if (!this.room) {
195
+ throw new TypeError(`expected room to be present on signal connect`);
196
+ }
197
+ this.setParticipantCryptorEnabled(
198
+ this.room.localParticipant.isE2EEEnabled,
199
+ this.room.localParticipant.identity,
200
+ );
201
+ keyProvider.getKeys().forEach((keyInfo) => {
202
+ this.postKey(keyInfo);
162
203
  });
163
- }
164
- });
165
-
166
- room.on(RoomEvent.TrackUnsubscribed, (track, _, participant) => {
167
- const msg: RemoveTransformMessage = {
168
- kind: 'removeTransform',
169
- data: {
170
- participantId: participant.identity,
171
- trackId: track.mediaStreamID,
172
- },
173
- };
174
- this.worker?.postMessage(msg);
175
- });
176
- room.on(RoomEvent.TrackSubscribed, (track, pub, participant) => {
177
- this.setupE2EEReceiver(track, participant.identity, pub.trackInfo);
178
- });
204
+ });
179
205
  room.localParticipant.on(ParticipantEvent.LocalTrackPublished, async (publication) => {
180
- this.setupE2EESender(
181
- publication.track!,
182
- publication.track!.sender!,
183
- room.localParticipant.identity,
184
- );
206
+ this.setupE2EESender(publication.track!, publication.track!.sender!);
185
207
  });
186
208
 
187
209
  keyProvider
188
- .on('setKey', (keyInfo) => this.postKey(keyInfo))
189
- .on('ratchetRequest', (participantId, keyIndex) =>
210
+ .on(KeyProviderEvent.SetKey, (keyInfo) => this.postKey(keyInfo))
211
+ .on(KeyProviderEvent.RatchetRequest, (participantId, keyIndex) =>
190
212
  this.postRatchetRequest(participantId, keyIndex),
191
213
  );
192
214
  }
193
215
 
194
- private postRatchetRequest(participantId?: string, keyIndex?: number) {
216
+ private postRatchetRequest(participantIdentity?: string, keyIndex?: number) {
195
217
  if (!this.worker) {
196
218
  throw Error('could not ratchet key, worker is missing');
197
219
  }
198
220
  const msg: RatchetRequestMessage = {
199
221
  kind: 'ratchetRequest',
200
222
  data: {
201
- participantId,
223
+ participantIdentity: participantIdentity,
202
224
  keyIndex,
203
225
  },
204
226
  };
205
227
  this.worker.postMessage(msg);
206
228
  }
207
229
 
208
- private postKey({ key, participantId, keyIndex }: KeyInfo) {
230
+ private postKey({ key, participantIdentity, keyIndex }: KeyInfo) {
209
231
  if (!this.worker) {
210
232
  throw Error('could not set key, worker is missing');
211
233
  }
212
234
  const msg: SetKeyMessage = {
213
235
  kind: 'setKey',
214
236
  data: {
215
- participantId,
237
+ participantIdentity: participantIdentity,
238
+ isPublisher: participantIdentity === this.room?.localParticipant.identity,
216
239
  key,
217
240
  keyIndex,
218
241
  },
@@ -220,14 +243,46 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
220
243
  this.worker.postMessage(msg);
221
244
  }
222
245
 
246
+ private postEnable(enabled: boolean, participantIdentity: string) {
247
+ if (this.worker) {
248
+ const enableMsg: EnableMessage = {
249
+ kind: 'enable',
250
+ data: {
251
+ enabled,
252
+ participantIdentity,
253
+ },
254
+ };
255
+ this.worker.postMessage(enableMsg);
256
+ } else {
257
+ throw new ReferenceError('failed to enable e2ee, worker is not ready');
258
+ }
259
+ }
260
+
223
261
  private postRTPMap(map: Map<number, VideoCodec>) {
224
262
  if (!this.worker) {
225
- throw Error('could not post rtp map, worker is missing');
263
+ throw TypeError('could not post rtp map, worker is missing');
264
+ }
265
+ if (!this.room?.localParticipant.identity) {
266
+ throw TypeError('could not post rtp map, local participant identity is missing');
226
267
  }
227
268
  const msg: RTPVideoMapMessage = {
228
269
  kind: 'setRTPMap',
229
270
  data: {
230
271
  map,
272
+ participantIdentity: this.room.localParticipant.identity,
273
+ },
274
+ };
275
+ this.worker.postMessage(msg);
276
+ }
277
+
278
+ private postSifTrailer(trailer: Uint8Array) {
279
+ if (!this.worker) {
280
+ throw Error('could not post SIF trailer, worker is missing');
281
+ }
282
+ const msg: SifTrailerMessage = {
283
+ kind: 'setSifTrailer',
284
+ data: {
285
+ trailer,
231
286
  },
232
287
  };
233
288
  this.worker.postMessage(msg);
@@ -248,12 +303,12 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
248
303
  );
249
304
  }
250
305
 
251
- private setupE2EESender(track: Track, sender: RTCRtpSender, localId: string) {
306
+ private setupE2EESender(track: Track, sender: RTCRtpSender) {
252
307
  if (!(track instanceof LocalTrack) || !sender) {
253
308
  if (!sender) log.warn('early return because sender is not ready');
254
309
  return;
255
310
  }
256
- this.handleSender(sender, track.mediaStreamID, localId, undefined);
311
+ this.handleSender(sender, track.mediaStreamID, undefined);
257
312
  }
258
313
 
259
314
  /**
@@ -264,7 +319,7 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
264
319
  private async handleReceiver(
265
320
  receiver: RTCRtpReceiver,
266
321
  trackId: string,
267
- participantId: string,
322
+ participantIdentity: string,
268
323
  codec?: VideoCodec,
269
324
  ) {
270
325
  if (!this.worker) {
@@ -274,7 +329,7 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
274
329
  if (isScriptTransformSupported()) {
275
330
  const options = {
276
331
  kind: 'decode',
277
- participantId,
332
+ participantIdentity,
278
333
  trackId,
279
334
  codec,
280
335
  };
@@ -288,7 +343,7 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
288
343
  data: {
289
344
  trackId,
290
345
  codec,
291
- participantId,
346
+ participantIdentity: participantIdentity,
292
347
  },
293
348
  };
294
349
  this.worker.postMessage(msg);
@@ -316,7 +371,7 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
316
371
  writableStream: writable,
317
372
  trackId: trackId,
318
373
  codec,
319
- participantId,
374
+ participantIdentity: participantIdentity,
320
375
  },
321
376
  };
322
377
  this.worker.postMessage(msg, [readable, writable]);
@@ -331,29 +386,27 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
331
386
  * a frame encoder.
332
387
  *
333
388
  */
334
- private handleSender(
335
- sender: RTCRtpSender,
336
- trackId: string,
337
- participantId: string,
338
- codec?: VideoCodec,
339
- ) {
389
+ private handleSender(sender: RTCRtpSender, trackId: string, codec?: VideoCodec) {
340
390
  if (E2EE_FLAG in sender || !this.worker) {
341
391
  return;
342
392
  }
343
393
 
344
- if (isScriptTransformSupported()) {
345
- log.warn('initialize script transform');
394
+ if (!this.room?.localParticipant.identity || this.room.localParticipant.identity === '') {
395
+ throw TypeError('local identity needs to be known in order to set up encrypted sender');
396
+ }
346
397
 
398
+ if (isScriptTransformSupported()) {
399
+ log.info('initialize script transform');
347
400
  const options = {
348
401
  kind: 'encode',
349
- participantId,
402
+ participantIdentity: this.room.localParticipant.identity,
350
403
  trackId,
351
404
  codec,
352
405
  };
353
406
  // @ts-ignore
354
407
  sender.transform = new RTCRtpScriptTransform(this.worker, options);
355
408
  } else {
356
- log.warn('initialize encoded streams');
409
+ log.info('initialize encoded streams');
357
410
  // @ts-ignore
358
411
  const senderStreams = sender.createEncodedStreams();
359
412
  const msg: EncodeMessage = {
@@ -363,7 +416,7 @@ export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2
363
416
  writableStream: senderStreams.writable,
364
417
  codec,
365
418
  trackId,
366
- participantId,
419
+ participantIdentity: this.room.localParticipant.identity,
367
420
  },
368
421
  };
369
422
  this.worker.postMessage(msg, [senderStreams.readable, senderStreams.writable]);