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.
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +253 -118
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +2442 -323
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +31 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/api/WebSocketStream.d.ts +29 -0
- package/dist/src/api/WebSocketStream.d.ts.map +1 -0
- package/dist/src/api/utils.d.ts +2 -0
- package/dist/src/api/utils.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +16 -2
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +35 -1
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +2 -0
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/DataCryptor.d.ts +15 -0
- package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -0
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +3 -2
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/e2ee/worker/sifPayload.d.ts +6 -6
- package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -1
- package/dist/src/index.d.ts +5 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +1 -0
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/options.d.ts +10 -2
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +1 -0
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.d.ts +6 -4
- package/dist/src/room/PCTransportManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +6 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +3 -2
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -2
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +2 -1
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +2 -2
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/token-source/TokenSource.d.ts +70 -0
- package/dist/src/room/token-source/TokenSource.d.ts.map +1 -0
- package/dist/src/room/token-source/types.d.ts +68 -0
- package/dist/src/room/token-source/types.d.ts.map +1 -0
- package/dist/src/room/token-source/utils.d.ts +5 -0
- package/dist/src/room/token-source/utils.d.ts.map +1 -0
- package/dist/src/room/track/LocalTrack.d.ts +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +7 -3
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +1 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +8 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/utils/camelToSnakeCase.d.ts +8 -0
- package/dist/src/utils/camelToSnakeCase.d.ts.map +1 -0
- package/dist/ts4.2/{src/api → api}/SignalClient.d.ts +31 -2
- package/dist/ts4.2/api/WebSocketStream.d.ts +29 -0
- package/dist/ts4.2/{src/api → api}/utils.d.ts +2 -0
- package/dist/ts4.2/{src/e2ee → e2ee}/E2eeManager.d.ts +16 -2
- package/dist/ts4.2/{src/e2ee → e2ee}/types.d.ts +35 -1
- package/dist/ts4.2/{src/e2ee → e2ee}/utils.d.ts +3 -0
- package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +15 -0
- package/dist/ts4.2/{src/e2ee → e2ee}/worker/ParticipantKeyHandler.d.ts +3 -2
- package/dist/ts4.2/{src/e2ee → e2ee}/worker/sifPayload.d.ts +6 -6
- package/dist/ts4.2/{src/index.d.ts → index.d.ts} +5 -3
- package/dist/ts4.2/{src/logger.d.ts → logger.d.ts} +1 -0
- package/dist/ts4.2/{src/options.d.ts → options.d.ts} +10 -2
- package/dist/ts4.2/{src/room → room}/PCTransport.d.ts +1 -0
- package/dist/ts4.2/{src/room → room}/PCTransportManager.d.ts +6 -4
- package/dist/ts4.2/{src/room → room}/RTCEngine.d.ts +6 -3
- package/dist/ts4.2/{src/room → room}/Room.d.ts +3 -2
- package/dist/ts4.2/{src/room → room}/data-stream/incoming/IncomingDataStreamManager.d.ts +2 -1
- package/dist/ts4.2/{src/room → room}/errors.d.ts +2 -1
- package/dist/ts4.2/{src/room → room}/participant/Participant.d.ts +2 -2
- package/dist/ts4.2/room/token-source/TokenSource.d.ts +71 -0
- package/dist/ts4.2/room/token-source/types.d.ts +68 -0
- package/dist/ts4.2/room/token-source/utils.d.ts +5 -0
- package/dist/ts4.2/{src/room → room}/track/LocalTrack.d.ts +1 -1
- package/dist/ts4.2/{src/room → room}/track/options.d.ts +10 -3
- package/dist/ts4.2/{src/room → room}/types.d.ts +1 -0
- package/dist/ts4.2/{src/room → room}/utils.d.ts +8 -1
- package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -0
- package/package.json +11 -10
- package/src/api/SignalClient.test.ts +688 -0
- package/src/api/SignalClient.ts +308 -161
- package/src/api/WebSocketStream.test.ts +625 -0
- package/src/api/WebSocketStream.ts +118 -0
- package/src/api/utils.ts +10 -0
- package/src/connectionHelper/checks/publishVideo.ts +5 -0
- package/src/connectionHelper/checks/turn.ts +1 -0
- package/src/connectionHelper/checks/webrtc.ts +1 -1
- package/src/connectionHelper/checks/websocket.ts +1 -0
- package/src/e2ee/E2eeManager.ts +94 -2
- package/src/e2ee/types.ts +44 -1
- package/src/e2ee/utils.ts +16 -0
- package/src/e2ee/worker/DataCryptor.test.ts +271 -0
- package/src/e2ee/worker/DataCryptor.ts +147 -0
- package/src/e2ee/worker/ParticipantKeyHandler.ts +4 -3
- package/src/e2ee/worker/e2ee.worker.ts +47 -0
- package/src/e2ee/worker/sifPayload.ts +10 -6
- package/src/index.ts +16 -1
- package/src/logger.ts +1 -0
- package/src/options.ts +15 -2
- package/src/room/PCTransport.ts +7 -3
- package/src/room/PCTransportManager.ts +39 -35
- package/src/room/RTCEngine.ts +109 -22
- package/src/room/Room.ts +43 -18
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +64 -17
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -0
- package/src/room/defaults.ts +1 -0
- package/src/room/errors.ts +3 -0
- package/src/room/participant/LocalParticipant.ts +8 -6
- package/src/room/participant/Participant.ts +6 -1
- package/src/room/token-source/TokenSource.ts +285 -0
- package/src/room/token-source/types.ts +84 -0
- package/src/room/token-source/utils.test.ts +63 -0
- package/src/room/token-source/utils.ts +40 -0
- package/src/room/track/LocalAudioTrack.ts +1 -1
- package/src/room/track/LocalTrack.ts +1 -1
- package/src/room/track/options.ts +12 -4
- package/src/room/track/utils.ts +10 -2
- package/src/room/types.ts +1 -0
- package/src/room/utils.ts +37 -4
- package/src/utils/camelToSnakeCase.ts +16 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/ConnectionCheck.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/Checker.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/cloudRegion.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/connectionProtocol.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishAudio.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/publishVideo.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/reconnect.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/turn.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/webrtc.d.ts +0 -0
- /package/dist/ts4.2/{src/connectionHelper → connectionHelper}/checks/websocket.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/KeyProvider.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/constants.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/errors.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/events.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/index.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/worker/FrameCryptor.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/worker/e2ee.worker.d.ts +0 -0
- /package/dist/ts4.2/{src/e2ee → e2ee}/worker/naluUtils.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/DefaultReconnectPolicy.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/DeviceManager.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/ReconnectPolicy.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/RegionUrlProvider.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/attribute-typings.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/data-stream/incoming/StreamReader.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/OutgoingDataStreamManager.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/data-stream/outgoing/StreamWriter.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/defaults.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/events.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/participant/LocalParticipant.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/participant/ParticipantTrackPermission.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/participant/RemoteParticipant.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/participant/publishUtils.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/rpc.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/stats.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/timers.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/LocalAudioTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/LocalTrackPublication.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/LocalVideoTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteAudioTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteTrackPublication.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/RemoteVideoTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/Track.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/TrackPublication.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/create.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/facingMode.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/processor/types.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/record.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/types.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/track/utils.d.ts +0 -0
- /package/dist/ts4.2/{src/test → test}/MockMediaStreamTrack.d.ts +0 -0
- /package/dist/ts4.2/{src/test → test}/mocks.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/AsyncQueue.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/browserParser.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/cloneDeep.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/dataPacketBuffer.d.ts +0 -0
- /package/dist/ts4.2/{src/utils → utils}/ttlmap.d.ts +0 -0
- /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
|
-
|
|
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 = [
|
|
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 {
|
|
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
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
|
-
|
|
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
|
package/src/room/PCTransport.ts
CHANGED
|
@@ -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
|
-
//
|
|
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
|
|
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
|
|
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 =
|
|
76
|
-
this.isSubscriberConnectionRequired =
|
|
72
|
+
this.isPublisherConnectionRequired = mode !== 'subscriber-primary';
|
|
73
|
+
this.isSubscriberConnectionRequired = mode === 'subscriber-primary';
|
|
77
74
|
this.publisher = new PCTransport(rtcConfig, loggerOptions);
|
|
78
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
318
|
+
this.subscriber?.getConnectionState(),
|
|
315
319
|
);
|
|
316
320
|
}
|
|
317
321
|
};
|