livekit-client 2.15.7 → 2.15.9

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 (196) 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 +253 -118
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +2442 -323
  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 +31 -2
  10. package/dist/src/api/SignalClient.d.ts.map +1 -1
  11. package/dist/src/api/WebSocketStream.d.ts +29 -0
  12. package/dist/src/api/WebSocketStream.d.ts.map +1 -0
  13. package/dist/src/api/utils.d.ts +2 -0
  14. package/dist/src/api/utils.d.ts.map +1 -1
  15. package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -1
  16. package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -1
  17. package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
  18. package/dist/src/e2ee/E2eeManager.d.ts +16 -2
  19. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  20. package/dist/src/e2ee/types.d.ts +35 -1
  21. package/dist/src/e2ee/types.d.ts.map +1 -1
  22. package/dist/src/e2ee/utils.d.ts +2 -0
  23. package/dist/src/e2ee/utils.d.ts.map +1 -1
  24. package/dist/src/e2ee/worker/DataCryptor.d.ts +15 -0
  25. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -0
  26. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +3 -2
  27. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  28. package/dist/src/e2ee/worker/sifPayload.d.ts +6 -6
  29. package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -1
  30. package/dist/src/index.d.ts +5 -3
  31. package/dist/src/index.d.ts.map +1 -1
  32. package/dist/src/logger.d.ts +1 -0
  33. package/dist/src/logger.d.ts.map +1 -1
  34. package/dist/src/options.d.ts +10 -2
  35. package/dist/src/options.d.ts.map +1 -1
  36. package/dist/src/room/PCTransport.d.ts +1 -0
  37. package/dist/src/room/PCTransport.d.ts.map +1 -1
  38. package/dist/src/room/PCTransportManager.d.ts +6 -4
  39. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  40. package/dist/src/room/RTCEngine.d.ts +6 -3
  41. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  42. package/dist/src/room/Room.d.ts +3 -2
  43. package/dist/src/room/Room.d.ts.map +1 -1
  44. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -2
  45. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  46. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
  47. package/dist/src/room/defaults.d.ts.map +1 -1
  48. package/dist/src/room/errors.d.ts +2 -1
  49. package/dist/src/room/errors.d.ts.map +1 -1
  50. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  51. package/dist/src/room/participant/Participant.d.ts +2 -2
  52. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  53. package/dist/src/room/token-source/TokenSource.d.ts +70 -0
  54. package/dist/src/room/token-source/TokenSource.d.ts.map +1 -0
  55. package/dist/src/room/token-source/types.d.ts +68 -0
  56. package/dist/src/room/token-source/types.d.ts.map +1 -0
  57. package/dist/src/room/token-source/utils.d.ts +5 -0
  58. package/dist/src/room/token-source/utils.d.ts.map +1 -0
  59. package/dist/src/room/track/LocalTrack.d.ts +1 -1
  60. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  61. package/dist/src/room/track/options.d.ts +7 -3
  62. package/dist/src/room/track/options.d.ts.map +1 -1
  63. package/dist/src/room/track/utils.d.ts.map +1 -1
  64. package/dist/src/room/types.d.ts +1 -0
  65. package/dist/src/room/types.d.ts.map +1 -1
  66. package/dist/src/room/utils.d.ts +8 -1
  67. package/dist/src/room/utils.d.ts.map +1 -1
  68. package/dist/src/utils/camelToSnakeCase.d.ts +8 -0
  69. package/dist/src/utils/camelToSnakeCase.d.ts.map +1 -0
  70. package/dist/ts4.2/{src/api → api}/SignalClient.d.ts +31 -2
  71. package/dist/ts4.2/api/WebSocketStream.d.ts +29 -0
  72. package/dist/ts4.2/{src/api → api}/utils.d.ts +2 -0
  73. package/dist/ts4.2/{src/e2ee → e2ee}/E2eeManager.d.ts +16 -2
  74. package/dist/ts4.2/{src/e2ee → e2ee}/types.d.ts +35 -1
  75. package/dist/ts4.2/{src/e2ee → e2ee}/utils.d.ts +3 -0
  76. package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +15 -0
  77. package/dist/ts4.2/{src/e2ee → e2ee}/worker/ParticipantKeyHandler.d.ts +3 -2
  78. package/dist/ts4.2/{src/e2ee → e2ee}/worker/sifPayload.d.ts +6 -6
  79. package/dist/ts4.2/{src/index.d.ts → index.d.ts} +5 -3
  80. package/dist/ts4.2/{src/logger.d.ts → logger.d.ts} +1 -0
  81. package/dist/ts4.2/{src/options.d.ts → options.d.ts} +10 -2
  82. package/dist/ts4.2/{src/room → room}/PCTransport.d.ts +1 -0
  83. package/dist/ts4.2/{src/room → room}/PCTransportManager.d.ts +6 -4
  84. package/dist/ts4.2/{src/room → room}/RTCEngine.d.ts +6 -3
  85. package/dist/ts4.2/{src/room → room}/Room.d.ts +3 -2
  86. package/dist/ts4.2/{src/room → room}/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -1
  87. package/dist/ts4.2/{src/room → room}/errors.d.ts +2 -1
  88. package/dist/ts4.2/{src/room → room}/participant/Participant.d.ts +2 -2
  89. package/dist/ts4.2/room/token-source/TokenSource.d.ts +71 -0
  90. package/dist/ts4.2/room/token-source/types.d.ts +68 -0
  91. package/dist/ts4.2/room/token-source/utils.d.ts +5 -0
  92. package/dist/ts4.2/{src/room → room}/track/LocalTrack.d.ts +1 -1
  93. package/dist/ts4.2/{src/room → room}/track/options.d.ts +10 -3
  94. package/dist/ts4.2/{src/room → room}/types.d.ts +1 -0
  95. package/dist/ts4.2/{src/room → room}/utils.d.ts +8 -1
  96. package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -0
  97. package/package.json +11 -10
  98. package/src/api/SignalClient.test.ts +688 -0
  99. package/src/api/SignalClient.ts +308 -161
  100. package/src/api/WebSocketStream.test.ts +625 -0
  101. package/src/api/WebSocketStream.ts +118 -0
  102. package/src/api/utils.ts +10 -0
  103. package/src/connectionHelper/checks/publishVideo.ts +5 -0
  104. package/src/connectionHelper/checks/turn.ts +1 -0
  105. package/src/connectionHelper/checks/webrtc.ts +1 -1
  106. package/src/connectionHelper/checks/websocket.ts +1 -0
  107. package/src/e2ee/E2eeManager.ts +94 -2
  108. package/src/e2ee/types.ts +44 -1
  109. package/src/e2ee/utils.ts +16 -0
  110. package/src/e2ee/worker/DataCryptor.test.ts +271 -0
  111. package/src/e2ee/worker/DataCryptor.ts +147 -0
  112. package/src/e2ee/worker/ParticipantKeyHandler.ts +4 -3
  113. package/src/e2ee/worker/e2ee.worker.ts +47 -0
  114. package/src/e2ee/worker/sifPayload.ts +10 -6
  115. package/src/index.ts +16 -1
  116. package/src/logger.ts +1 -0
  117. package/src/options.ts +15 -2
  118. package/src/room/PCTransport.ts +7 -3
  119. package/src/room/PCTransportManager.ts +39 -35
  120. package/src/room/RTCEngine.ts +109 -22
  121. package/src/room/Room.ts +43 -18
  122. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +64 -17
  123. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -0
  124. package/src/room/defaults.ts +1 -0
  125. package/src/room/errors.ts +3 -0
  126. package/src/room/participant/LocalParticipant.ts +8 -6
  127. package/src/room/participant/Participant.ts +6 -1
  128. package/src/room/token-source/TokenSource.ts +285 -0
  129. package/src/room/token-source/types.ts +84 -0
  130. package/src/room/token-source/utils.test.ts +63 -0
  131. package/src/room/token-source/utils.ts +40 -0
  132. package/src/room/track/LocalAudioTrack.ts +1 -1
  133. package/src/room/track/LocalTrack.ts +1 -1
  134. package/src/room/track/options.ts +12 -4
  135. package/src/room/track/utils.ts +10 -2
  136. package/src/room/types.ts +1 -0
  137. package/src/room/utils.ts +37 -4
  138. package/src/utils/camelToSnakeCase.ts +16 -0
  139. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/ConnectionCheck.d.ts +0 -0
  140. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/Checker.d.ts +0 -0
  141. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/cloudRegion.d.ts +0 -0
  142. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/connectionProtocol.d.ts +0 -0
  143. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishAudio.d.ts +0 -0
  144. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishVideo.d.ts +0 -0
  145. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/reconnect.d.ts +0 -0
  146. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/turn.d.ts +0 -0
  147. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/webrtc.d.ts +0 -0
  148. /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/websocket.d.ts +0 -0
  149. /package/dist/ts4.2/{src/e2ee → e2ee}/KeyProvider.d.ts +0 -0
  150. /package/dist/ts4.2/{src/e2ee → e2ee}/constants.d.ts +0 -0
  151. /package/dist/ts4.2/{src/e2ee → e2ee}/errors.d.ts +0 -0
  152. /package/dist/ts4.2/{src/e2ee → e2ee}/events.d.ts +0 -0
  153. /package/dist/ts4.2/{src/e2ee → e2ee}/index.d.ts +0 -0
  154. /package/dist/ts4.2/{src/e2ee → e2ee}/worker/FrameCryptor.d.ts +0 -0
  155. /package/dist/ts4.2/{src/e2ee → e2ee}/worker/e2ee.worker.d.ts +0 -0
  156. /package/dist/ts4.2/{src/e2ee → e2ee}/worker/naluUtils.d.ts +0 -0
  157. /package/dist/ts4.2/{src/room → room}/DefaultReconnectPolicy.d.ts +0 -0
  158. /package/dist/ts4.2/{src/room → room}/DeviceManager.d.ts +0 -0
  159. /package/dist/ts4.2/{src/room → room}/ReconnectPolicy.d.ts +0 -0
  160. /package/dist/ts4.2/{src/room → room}/RegionUrlProvider.d.ts +0 -0
  161. /package/dist/ts4.2/{src/room → room}/attribute-typings.d.ts +0 -0
  162. /package/dist/ts4.2/{src/room → room}/data-stream/incoming/StreamReader.d.ts +0 -0
  163. /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/OutgoingDataStreamManager.d.ts +0 -0
  164. /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/StreamWriter.d.ts +0 -0
  165. /package/dist/ts4.2/{src/room → room}/defaults.d.ts +0 -0
  166. /package/dist/ts4.2/{src/room → room}/events.d.ts +0 -0
  167. /package/dist/ts4.2/{src/room → room}/participant/LocalParticipant.d.ts +0 -0
  168. /package/dist/ts4.2/{src/room → room}/participant/ParticipantTrackPermission.d.ts +0 -0
  169. /package/dist/ts4.2/{src/room → room}/participant/RemoteParticipant.d.ts +0 -0
  170. /package/dist/ts4.2/{src/room → room}/participant/publishUtils.d.ts +0 -0
  171. /package/dist/ts4.2/{src/room → room}/rpc.d.ts +0 -0
  172. /package/dist/ts4.2/{src/room → room}/stats.d.ts +0 -0
  173. /package/dist/ts4.2/{src/room → room}/timers.d.ts +0 -0
  174. /package/dist/ts4.2/{src/room → room}/track/LocalAudioTrack.d.ts +0 -0
  175. /package/dist/ts4.2/{src/room → room}/track/LocalTrackPublication.d.ts +0 -0
  176. /package/dist/ts4.2/{src/room → room}/track/LocalVideoTrack.d.ts +0 -0
  177. /package/dist/ts4.2/{src/room → room}/track/RemoteAudioTrack.d.ts +0 -0
  178. /package/dist/ts4.2/{src/room → room}/track/RemoteTrack.d.ts +0 -0
  179. /package/dist/ts4.2/{src/room → room}/track/RemoteTrackPublication.d.ts +0 -0
  180. /package/dist/ts4.2/{src/room → room}/track/RemoteVideoTrack.d.ts +0 -0
  181. /package/dist/ts4.2/{src/room → room}/track/Track.d.ts +0 -0
  182. /package/dist/ts4.2/{src/room → room}/track/TrackPublication.d.ts +0 -0
  183. /package/dist/ts4.2/{src/room → room}/track/create.d.ts +0 -0
  184. /package/dist/ts4.2/{src/room → room}/track/facingMode.d.ts +0 -0
  185. /package/dist/ts4.2/{src/room → room}/track/processor/types.d.ts +0 -0
  186. /package/dist/ts4.2/{src/room → room}/track/record.d.ts +0 -0
  187. /package/dist/ts4.2/{src/room → room}/track/types.d.ts +0 -0
  188. /package/dist/ts4.2/{src/room → room}/track/utils.d.ts +0 -0
  189. /package/dist/ts4.2/{src/test → test}/MockMediaStreamTrack.d.ts +0 -0
  190. /package/dist/ts4.2/{src/test → test}/mocks.d.ts +0 -0
  191. /package/dist/ts4.2/{src/utils → utils}/AsyncQueue.d.ts +0 -0
  192. /package/dist/ts4.2/{src/utils → utils}/browserParser.d.ts +0 -0
  193. /package/dist/ts4.2/{src/utils → utils}/cloneDeep.d.ts +0 -0
  194. /package/dist/ts4.2/{src/utils → utils}/dataPacketBuffer.d.ts +0 -0
  195. /package/dist/ts4.2/{src/utils → utils}/ttlmap.d.ts +0 -0
  196. /package/dist/ts4.2/{src/version.d.ts → version.d.ts} +0 -0
@@ -0,0 +1,147 @@
1
+ import { workerLogger } from '../../logger';
2
+ import { ENCRYPTION_ALGORITHM } from '../constants';
3
+ import { CryptorError, CryptorErrorReason } from '../errors';
4
+ import type { DecodeRatchetOptions, KeySet, RatchetResult } from '../types';
5
+ import { deriveKeys } from '../utils';
6
+ import type { ParticipantKeyHandler } from './ParticipantKeyHandler';
7
+
8
+ export class DataCryptor {
9
+ private static sendCount = 0;
10
+
11
+ private static makeIV(timestamp: number) {
12
+ const iv = new ArrayBuffer(12);
13
+ const ivView = new DataView(iv);
14
+ const randomBytes = crypto.getRandomValues(new Uint32Array(1));
15
+ ivView.setUint32(0, randomBytes[0]);
16
+ ivView.setUint32(4, timestamp);
17
+ ivView.setUint32(8, timestamp - (DataCryptor.sendCount % 0xffff));
18
+ DataCryptor.sendCount++;
19
+
20
+ return iv;
21
+ }
22
+
23
+ static async encrypt(
24
+ data: Uint8Array,
25
+ keys: ParticipantKeyHandler,
26
+ ): Promise<{
27
+ payload: Uint8Array;
28
+ iv: Uint8Array;
29
+ keyIndex: number;
30
+ }> {
31
+ const iv = DataCryptor.makeIV(performance.now());
32
+ const keySet = await keys.getKeySet();
33
+ if (!keySet) {
34
+ throw new Error('No key set found');
35
+ }
36
+
37
+ const cipherText = await crypto.subtle.encrypt(
38
+ {
39
+ name: ENCRYPTION_ALGORITHM,
40
+ iv,
41
+ },
42
+ keySet.encryptionKey,
43
+ new Uint8Array(data),
44
+ );
45
+
46
+ return {
47
+ payload: new Uint8Array(cipherText),
48
+ iv: new Uint8Array(iv),
49
+ keyIndex: keys.getCurrentKeyIndex(),
50
+ };
51
+ }
52
+
53
+ static async decrypt(
54
+ data: Uint8Array,
55
+ iv: Uint8Array,
56
+ keys: ParticipantKeyHandler,
57
+ keyIndex: number = 0,
58
+ initialMaterial?: KeySet,
59
+ ratchetOpts: DecodeRatchetOptions = { ratchetCount: 0 },
60
+ ): Promise<{
61
+ payload: Uint8Array;
62
+ }> {
63
+ const keySet = await keys.getKeySet(keyIndex);
64
+ if (!keySet) {
65
+ throw new Error('No key set found');
66
+ }
67
+
68
+ try {
69
+ const plainText = await crypto.subtle.decrypt(
70
+ {
71
+ name: ENCRYPTION_ALGORITHM,
72
+ iv,
73
+ },
74
+ keySet.encryptionKey,
75
+ new Uint8Array(data),
76
+ );
77
+ return {
78
+ payload: new Uint8Array(plainText),
79
+ };
80
+ } catch (error: any) {
81
+ if (keys.keyProviderOptions.ratchetWindowSize > 0) {
82
+ if (ratchetOpts.ratchetCount < keys.keyProviderOptions.ratchetWindowSize) {
83
+ workerLogger.debug(
84
+ `DataCryptor: ratcheting key attempt ${ratchetOpts.ratchetCount} of ${
85
+ keys.keyProviderOptions.ratchetWindowSize
86
+ }, for data packet`,
87
+ );
88
+
89
+ let ratchetedKeySet: KeySet | undefined;
90
+ let ratchetResult: RatchetResult | undefined;
91
+ if ((initialMaterial ?? keySet) === keys.getKeySet(keyIndex)) {
92
+ // only ratchet if the currently set key is still the same as the one used to decrypt this frame
93
+ // if not, it might be that a different frame has already ratcheted and we try with that one first
94
+ ratchetResult = await keys.ratchetKey(keyIndex, false);
95
+
96
+ ratchetedKeySet = await deriveKeys(
97
+ ratchetResult.cryptoKey,
98
+ keys.keyProviderOptions.ratchetSalt,
99
+ );
100
+ }
101
+
102
+ const decryptedData = await DataCryptor.decrypt(
103
+ data,
104
+ iv,
105
+ keys,
106
+ keyIndex,
107
+ initialMaterial,
108
+ {
109
+ ratchetCount: ratchetOpts.ratchetCount + 1,
110
+ encryptionKey: ratchetedKeySet?.encryptionKey,
111
+ },
112
+ );
113
+
114
+ if (decryptedData && ratchetedKeySet) {
115
+ // before updating the keys, make sure that the keySet used for this frame is still the same as the currently set key
116
+ // if it's not, a new key might have been set already, which we don't want to override
117
+ if ((initialMaterial ?? keySet) === keys.getKeySet(keyIndex)) {
118
+ keys.setKeySet(ratchetedKeySet, keyIndex, ratchetResult);
119
+ // decryption was successful, set the new key index to reflect the ratcheted key set
120
+ keys.setCurrentKeyIndex(keyIndex);
121
+ }
122
+ }
123
+ return decryptedData;
124
+ } else {
125
+ /**
126
+ * Because we only set a new key once decryption has been successful,
127
+ * we can be sure that we don't need to reset the key to the initial material at this point
128
+ * as the key has not been updated on the keyHandler instance
129
+ */
130
+
131
+ workerLogger.warn('DataCryptor: maximum ratchet attempts exceeded');
132
+ throw new CryptorError(
133
+ `DataCryptor: valid key missing for participant ${keys.participantIdentity}`,
134
+ CryptorErrorReason.InvalidKey,
135
+ keys.participantIdentity,
136
+ );
137
+ }
138
+ } else {
139
+ throw new CryptorError(
140
+ `DataCryptor: Decryption failed: ${error.message}`,
141
+ CryptorErrorReason.InvalidKey,
142
+ keys.participantIdentity,
143
+ );
144
+ }
145
+ }
146
+ }
147
+ }
@@ -23,11 +23,12 @@ export class ParticipantKeyHandler extends (EventEmitter as new () => TypedEvent
23
23
 
24
24
  private decryptionFailureCounts: Array<number>;
25
25
 
26
- private keyProviderOptions: KeyProviderOptions;
27
-
28
26
  private ratchetPromiseMap: Map<number, Promise<RatchetResult>>;
29
27
 
30
- private participantIdentity: string;
28
+ readonly participantIdentity: string;
29
+
30
+ /** @internal */
31
+ readonly keyProviderOptions: KeyProviderOptions;
31
32
 
32
33
  /**
33
34
  * true if the current key has not been marked as invalid
@@ -5,7 +5,9 @@ import { KEY_PROVIDER_DEFAULTS } from '../constants';
5
5
  import { CryptorErrorReason } from '../errors';
6
6
  import { CryptorEvent, KeyHandlerEvent } from '../events';
7
7
  import type {
8
+ DecryptDataResponseMessage,
8
9
  E2EEWorkerMessage,
10
+ EncryptDataResponseMessage,
9
11
  ErrorMessage,
10
12
  InitAck,
11
13
  KeyProviderOptions,
@@ -14,6 +16,7 @@ import type {
14
16
  RatchetResult,
15
17
  ScriptTransformOptions,
16
18
  } from '../types';
19
+ import { DataCryptor } from './DataCryptor';
17
20
  import { FrameCryptor, encryptionEnabledMap } from './FrameCryptor';
18
21
  import { ParticipantKeyHandler } from './ParticipantKeyHandler';
19
22
 
@@ -81,6 +84,50 @@ onmessage = (ev) => {
81
84
  data.codec,
82
85
  );
83
86
  break;
87
+
88
+ case 'encryptDataRequest':
89
+ const {
90
+ payload: encryptedPayload,
91
+ iv,
92
+ keyIndex,
93
+ } = await DataCryptor.encrypt(
94
+ data.payload,
95
+ getParticipantKeyHandler(data.participantIdentity),
96
+ );
97
+ console.log('encrypted payload', {
98
+ original: data.payload,
99
+ encrypted: encryptedPayload,
100
+ iv,
101
+ });
102
+ postMessage({
103
+ kind: 'encryptDataResponse',
104
+ data: {
105
+ payload: encryptedPayload,
106
+ iv,
107
+ keyIndex,
108
+ uuid: data.uuid,
109
+ },
110
+ } satisfies EncryptDataResponseMessage);
111
+ break;
112
+
113
+ case 'decryptDataRequest':
114
+ const { payload: decryptedPayload } = await DataCryptor.decrypt(
115
+ data.payload,
116
+ data.iv,
117
+ getParticipantKeyHandler(data.participantIdentity),
118
+ data.keyIndex,
119
+ );
120
+ console.log('decrypted payload', {
121
+ original: data.payload,
122
+ decrypted: decryptedPayload,
123
+ iv: data.iv,
124
+ });
125
+ postMessage({
126
+ kind: 'decryptDataResponse',
127
+ data: { payload: decryptedPayload, uuid: data.uuid },
128
+ } satisfies DecryptDataResponseMessage);
129
+ break;
130
+
84
131
  case 'setKey':
85
132
  if (useSharedKey) {
86
133
  await setSharedKey(data.key, data.keyIndex);
@@ -2,25 +2,29 @@ import type { VideoCodec } from '../..';
2
2
 
3
3
  // Payload definitions taken from https://github.com/livekit/livekit/blob/master/pkg/sfu/downtrack.go#L104
4
4
 
5
- export const VP8KeyFrame8x8 = new Uint8Array([
5
+ export const VP8KeyFrame8x8: Uint8Array = new Uint8Array([
6
6
  0x10, 0x02, 0x00, 0x9d, 0x01, 0x2a, 0x08, 0x00, 0x08, 0x00, 0x00, 0x47, 0x08, 0x85, 0x85, 0x88,
7
7
  0x85, 0x84, 0x88, 0x02, 0x02, 0x00, 0x0c, 0x0d, 0x60, 0x00, 0xfe, 0xff, 0xab, 0x50, 0x80,
8
8
  ]);
9
9
 
10
- export const H264KeyFrame2x2SPS = new Uint8Array([
10
+ export const H264KeyFrame2x2SPS: Uint8Array = new Uint8Array([
11
11
  0x67, 0x42, 0xc0, 0x1f, 0x0f, 0xd9, 0x1f, 0x88, 0x88, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00,
12
12
  0x00, 0x03, 0x00, 0xc8, 0x3c, 0x60, 0xc9, 0x20,
13
13
  ]);
14
14
 
15
- export const H264KeyFrame2x2PPS = new Uint8Array([0x68, 0x87, 0xcb, 0x83, 0xcb, 0x20]);
15
+ export const H264KeyFrame2x2PPS: Uint8Array = new Uint8Array([0x68, 0x87, 0xcb, 0x83, 0xcb, 0x20]);
16
16
 
17
- export const H264KeyFrame2x2IDR = new Uint8Array([
17
+ export const H264KeyFrame2x2IDR: Uint8Array = new Uint8Array([
18
18
  0x65, 0x88, 0x84, 0x0a, 0xf2, 0x62, 0x80, 0x00, 0xa7, 0xbe,
19
19
  ]);
20
20
 
21
- export const H264KeyFrame2x2 = [H264KeyFrame2x2SPS, H264KeyFrame2x2PPS, H264KeyFrame2x2IDR];
21
+ export const H264KeyFrame2x2: Uint8Array[] = [
22
+ H264KeyFrame2x2SPS,
23
+ H264KeyFrame2x2PPS,
24
+ H264KeyFrame2x2IDR,
25
+ ];
22
26
 
23
- export const OpusSilenceFrame = new Uint8Array([
27
+ export const OpusSilenceFrame: Uint8Array = new Uint8Array([
24
28
  0xf8, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25
29
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26
30
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
package/src/index.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { Mutex } from '@livekit/mutex';
2
- import { DataPacket_Kind, DisconnectReason, SubscriptionError, TrackType } from '@livekit/protocol';
2
+ import {
3
+ DataPacket_Kind,
4
+ DisconnectReason,
5
+ Encryption_Type,
6
+ SubscriptionError,
7
+ TrackType,
8
+ } from '@livekit/protocol';
3
9
  import { LogLevel, LoggerNames, getLogger, setLogExtension, setLogLevel } from './logger';
4
10
  import DefaultReconnectPolicy from './room/DefaultReconnectPolicy';
5
11
  import type { ReconnectContext, ReconnectPolicy } from './room/ReconnectPolicy';
@@ -33,15 +39,18 @@ import {
33
39
  createAudioAnalyser,
34
40
  getEmptyAudioStreamTrack,
35
41
  getEmptyVideoStreamTrack,
42
+ isAudioCodec,
36
43
  isAudioTrack,
37
44
  isBrowserSupported,
38
45
  isLocalParticipant,
39
46
  isLocalTrack,
40
47
  isRemoteParticipant,
41
48
  isRemoteTrack,
49
+ isVideoCodec,
42
50
  isVideoTrack,
43
51
  supportsAV1,
44
52
  supportsAdaptiveStream,
53
+ supportsAudioOutputSelection,
45
54
  supportsDynacast,
46
55
  supportsVP9,
47
56
  } from './room/utils';
@@ -58,6 +67,8 @@ export * from './room/errors';
58
67
  export * from './room/events';
59
68
  export * from './room/track/Track';
60
69
  export * from './room/track/create';
70
+ export * from './room/token-source/TokenSource';
71
+ export * from './room/token-source/types';
61
72
  export { facingModeFromDeviceLabel, facingModeFromLocalTrack } from './room/track/facingMode';
62
73
  export * from './room/track/options';
63
74
  export * from './room/track/processor/types';
@@ -79,6 +90,7 @@ export {
79
90
  ConnectionState,
80
91
  CriticalTimers,
81
92
  DataPacket_Kind,
93
+ Encryption_Type,
82
94
  DefaultReconnectPolicy,
83
95
  DisconnectReason,
84
96
  LocalAudioTrack,
@@ -110,12 +122,15 @@ export {
110
122
  setLogLevel,
111
123
  supportsAV1,
112
124
  supportsAdaptiveStream,
125
+ supportsAudioOutputSelection,
113
126
  supportsDynacast,
114
127
  supportsVP9,
115
128
  Mutex,
129
+ isAudioCodec,
116
130
  isAudioTrack,
117
131
  isLocalTrack,
118
132
  isRemoteTrack,
133
+ isVideoCodec,
119
134
  isVideoTrack,
120
135
  isLocalParticipant,
121
136
  isRemoteParticipant,
package/src/logger.ts CHANGED
@@ -12,6 +12,7 @@ export enum LogLevel {
12
12
  export enum LoggerNames {
13
13
  Default = 'livekit',
14
14
  Room = 'livekit-room',
15
+ TokenSource = 'livekit-token-source',
15
16
  Participant = 'livekit-participant',
16
17
  Track = 'livekit-track',
17
18
  Publication = 'livekit-track-publication',
package/src/options.ts CHANGED
@@ -87,18 +87,31 @@ export interface InternalRoomOptions {
87
87
 
88
88
  webAudioMix: boolean | WebAudioSettings;
89
89
 
90
+ // /**
91
+ // * @deprecated Use `encryption` field instead.
92
+ // */
93
+ e2ee?: E2EEOptions;
94
+
90
95
  /**
91
96
  * @experimental
97
+ * Options for enabling end-to-end encryption.
92
98
  */
93
- e2ee?: E2EEOptions;
99
+ encryption?: E2EEOptions;
94
100
 
95
101
  loggerName?: string;
102
+
103
+ /**
104
+ * @experimental
105
+ * only supported on LiveKit Cloud
106
+ * and LiveKit OSS >= 1.9.2
107
+ */
108
+ singlePeerConnection: boolean;
96
109
  }
97
110
 
98
111
  /**
99
112
  * Options for when creating a new room
100
113
  */
101
- export interface RoomOptions extends Partial<InternalRoomOptions> {}
114
+ export interface RoomOptions extends Partial<Omit<InternalRoomOptions, 'encryption'>> {}
102
115
 
103
116
  /**
104
117
  * @internal
@@ -167,7 +167,7 @@ export default class PCTransport extends EventEmitter {
167
167
  sdpParsed.media.forEach((media) => {
168
168
  const mid = getMidString(media.mid!);
169
169
  if (media.type === 'audio') {
170
- // mung sdp for opus bitrate settings
170
+ // munge sdp for opus bitrate settings
171
171
  this.trackBitrates.some((trackbr): boolean => {
172
172
  if (!trackbr.transceiver || mid != trackbr.transceiver.mid) {
173
173
  return false;
@@ -297,7 +297,7 @@ export default class PCTransport extends EventEmitter {
297
297
  sdpParsed.media.forEach((media) => {
298
298
  ensureIPAddrMatchVersion(media);
299
299
  if (media.type === 'audio') {
300
- ensureAudioNackAndStereo(media, [], []);
300
+ ensureAudioNackAndStereo(media, ['all'], []);
301
301
  } else if (media.type === 'video') {
302
302
  this.trackBitrates.some((trackbr): boolean => {
303
303
  if (!media.msid || !trackbr.cid || !media.msid.includes(trackbr.cid)) {
@@ -380,6 +380,10 @@ export default class PCTransport extends EventEmitter {
380
380
  return this.pc.addTransceiver(mediaStreamTrack, transceiverInit);
381
381
  }
382
382
 
383
+ addTransceiverOfKind(kind: 'audio' | 'video', transceiverInit: RTCRtpTransceiverInit) {
384
+ return this.pc.addTransceiver(kind, transceiverInit);
385
+ }
386
+
383
387
  addTrack(track: MediaStreamTrack) {
384
388
  if (!this._pc) {
385
389
  throw new UnexpectedConnectionState('PC closed, cannot add track');
@@ -623,7 +627,7 @@ function ensureAudioNackAndStereo(
623
627
  });
624
628
  }
625
629
 
626
- if (stereoMids.includes(mid)) {
630
+ if (stereoMids.includes(mid) || (stereoMids.length === 1 && stereoMids[0] === 'all')) {
627
631
  media.fmtp.some((fmtp): boolean => {
628
632
  if (fmtp.payload === opusPayload) {
629
633
  if (!fmtp.config.includes('stereo=1')) {
@@ -17,10 +17,11 @@ export enum PCTransportState {
17
17
  CLOSED,
18
18
  }
19
19
 
20
+ type PCMode = 'subscriber-primary' | 'publisher-primary' | 'publisher-only';
20
21
  export class PCTransportManager {
21
22
  public publisher: PCTransport;
22
23
 
23
- public subscriber: PCTransport;
24
+ public subscriber?: PCTransport;
24
25
 
25
26
  public peerConnectionTimeout: number = roomConnectOptionDefaults.peerConnectionTimeout;
26
27
 
@@ -39,7 +40,7 @@ export class PCTransportManager {
39
40
  public onStateChange?: (
40
41
  state: PCTransportState,
41
42
  pubState: RTCPeerConnectionState,
42
- subState: RTCPeerConnectionState,
43
+ subState?: RTCPeerConnectionState,
43
44
  ) => void;
44
45
 
45
46
  public onIceCandidate?: (ev: RTCIceCandidate, target: SignalTarget) => void;
@@ -64,38 +65,40 @@ export class PCTransportManager {
64
65
 
65
66
  private loggerOptions: LoggerOptions;
66
67
 
67
- constructor(
68
- rtcConfig: RTCConfiguration,
69
- subscriberPrimary: boolean,
70
- loggerOptions: LoggerOptions,
71
- ) {
68
+ constructor(rtcConfig: RTCConfiguration, mode: PCMode, loggerOptions: LoggerOptions) {
72
69
  this.log = getLogger(loggerOptions.loggerName ?? LoggerNames.PCManager);
73
70
  this.loggerOptions = loggerOptions;
74
71
 
75
- this.isPublisherConnectionRequired = !subscriberPrimary;
76
- this.isSubscriberConnectionRequired = subscriberPrimary;
72
+ this.isPublisherConnectionRequired = mode !== 'subscriber-primary';
73
+ this.isSubscriberConnectionRequired = mode === 'subscriber-primary';
77
74
  this.publisher = new PCTransport(rtcConfig, loggerOptions);
78
- this.subscriber = new PCTransport(rtcConfig, loggerOptions);
75
+ if (mode !== 'publisher-only') {
76
+ this.subscriber = new PCTransport(rtcConfig, loggerOptions);
77
+ this.subscriber.onConnectionStateChange = this.updateState;
78
+ this.subscriber.onIceConnectionStateChange = this.updateState;
79
+ this.subscriber.onSignalingStatechange = this.updateState;
80
+ this.subscriber.onIceCandidate = (candidate) => {
81
+ this.onIceCandidate?.(candidate, SignalTarget.SUBSCRIBER);
82
+ };
83
+ // in subscriber primary mode, server side opens sub data channels.
84
+ this.subscriber.onDataChannel = (ev) => {
85
+ this.onDataChannel?.(ev);
86
+ };
87
+ this.subscriber.onTrack = (ev) => {
88
+ this.onTrack?.(ev);
89
+ };
90
+ }
79
91
 
80
92
  this.publisher.onConnectionStateChange = this.updateState;
81
- this.subscriber.onConnectionStateChange = this.updateState;
82
93
  this.publisher.onIceConnectionStateChange = this.updateState;
83
- this.subscriber.onIceConnectionStateChange = this.updateState;
84
94
  this.publisher.onSignalingStatechange = this.updateState;
85
- this.subscriber.onSignalingStatechange = this.updateState;
86
95
  this.publisher.onIceCandidate = (candidate) => {
87
96
  this.onIceCandidate?.(candidate, SignalTarget.PUBLISHER);
88
97
  };
89
- this.subscriber.onIceCandidate = (candidate) => {
90
- this.onIceCandidate?.(candidate, SignalTarget.SUBSCRIBER);
91
- };
92
- // in subscriber primary mode, server side opens sub data channels.
93
- this.subscriber.onDataChannel = (ev) => {
94
- this.onDataChannel?.(ev);
95
- };
96
- this.subscriber.onTrack = (ev) => {
98
+ this.publisher.onTrack = (ev) => {
97
99
  this.onTrack?.(ev);
98
100
  };
101
+
99
102
  this.publisher.onOffer = (offer, offerId) => {
100
103
  this.onPublisherOffer?.(offer, offerId);
101
104
  };
@@ -117,11 +120,6 @@ export class PCTransportManager {
117
120
  this.updateState();
118
121
  }
119
122
 
120
- requireSubscriber(require = true) {
121
- this.isSubscriberConnectionRequired = require;
122
- this.updateState();
123
- }
124
-
125
123
  createAndSendPublisherOffer(options?: RTCOfferOptions) {
126
124
  return this.publisher.createAndSendOffer(options);
127
125
  }
@@ -148,12 +146,14 @@ export class PCTransportManager {
148
146
  }
149
147
  }
150
148
  }
151
- await Promise.all([this.publisher.close(), this.subscriber.close()]);
149
+ await Promise.all([this.publisher.close(), this.subscriber?.close()]);
152
150
  this.updateState();
153
151
  }
154
152
 
155
153
  async triggerIceRestart() {
156
- this.subscriber.restartingIce = true;
154
+ if (this.subscriber) {
155
+ this.subscriber.restartingIce = true;
156
+ }
157
157
  // only restart publisher if it's needed
158
158
  if (this.needsPublisher) {
159
159
  await this.createAndSendPublisherOffer({ iceRestart: true });
@@ -164,7 +164,7 @@ export class PCTransportManager {
164
164
  if (target === SignalTarget.PUBLISHER) {
165
165
  await this.publisher.addIceCandidate(candidate);
166
166
  } else {
167
- await this.subscriber.addIceCandidate(candidate);
167
+ await this.subscriber?.addIceCandidate(candidate);
168
168
  }
169
169
  }
170
170
 
@@ -173,17 +173,17 @@ export class PCTransportManager {
173
173
  ...this.logContext,
174
174
  RTCSdpType: sd.type,
175
175
  sdp: sd.sdp,
176
- signalingState: this.subscriber.getSignallingState().toString(),
176
+ signalingState: this.subscriber?.getSignallingState().toString(),
177
177
  });
178
178
  const unlock = await this.remoteOfferLock.lock();
179
179
  try {
180
- const success = await this.subscriber.setRemoteDescription(sd, offerId);
180
+ const success = await this.subscriber?.setRemoteDescription(sd, offerId);
181
181
  if (!success) {
182
182
  return undefined;
183
183
  }
184
184
 
185
185
  // answer the offer
186
- const answer = await this.subscriber.createAndSetAnswer();
186
+ const answer = await this.subscriber?.createAndSetAnswer();
187
187
  return answer;
188
188
  } finally {
189
189
  unlock();
@@ -192,7 +192,7 @@ export class PCTransportManager {
192
192
 
193
193
  updateConfiguration(config: RTCConfiguration, iceRestart?: boolean) {
194
194
  this.publisher.setConfiguration(config);
195
- this.subscriber.setConfiguration(config);
195
+ this.subscriber?.setConfiguration(config);
196
196
  if (iceRestart) {
197
197
  this.triggerIceRestart();
198
198
  }
@@ -252,6 +252,10 @@ export class PCTransportManager {
252
252
  return this.publisher.addTransceiver(track, transceiverInit);
253
253
  }
254
254
 
255
+ addPublisherTransceiverOfKind(kind: 'audio' | 'video', transceiverInit: RTCRtpTransceiverInit) {
256
+ return this.publisher.addTransceiverOfKind(kind, transceiverInit);
257
+ }
258
+
255
259
  addPublisherTrack(track: MediaStreamTrack) {
256
260
  return this.publisher.addTrack(track);
257
261
  }
@@ -277,7 +281,7 @@ export class PCTransportManager {
277
281
  if (this.isPublisherConnectionRequired) {
278
282
  transports.push(this.publisher);
279
283
  }
280
- if (this.isSubscriberConnectionRequired) {
284
+ if (this.isSubscriberConnectionRequired && this.subscriber) {
281
285
  transports.push(this.subscriber);
282
286
  }
283
287
  return transports;
@@ -311,7 +315,7 @@ export class PCTransportManager {
311
315
  this.onStateChange?.(
312
316
  this.state,
313
317
  this.publisher.getConnectionState(),
314
- this.subscriber.getConnectionState(),
318
+ this.subscriber?.getConnectionState(),
315
319
  );
316
320
  }
317
321
  };