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.
- 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 +198 -107
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +515 -192
- 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 +2 -5
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +9 -3
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/KeyProvider.d.ts +10 -7
- package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts +2 -0
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/events.d.ts +34 -0
- package/dist/src/e2ee/events.d.ts.map +1 -0
- package/dist/src/e2ee/index.d.ts +1 -0
- package/dist/src/e2ee/index.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +23 -33
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +1 -0
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +18 -13
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +6 -8
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/e2ee/worker/SifGuard.d.ts +11 -0
- package/dist/src/e2ee/worker/SifGuard.d.ts.map +1 -0
- package/dist/src/options.d.ts +5 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -1
- package/dist/src/room/DeviceManager.d.ts +1 -0
- package/dist/src/room/DeviceManager.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +1 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +5 -0
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +0 -5
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/timers.d.ts +2 -2
- package/dist/src/room/timers.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +9 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +3 -3
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +6 -0
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/processor/types.d.ts +14 -2
- package/dist/src/room/track/processor/types.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +1 -1
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +2 -5
- package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +9 -3
- package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +10 -7
- package/dist/ts4.2/src/e2ee/constants.d.ts +2 -0
- package/dist/ts4.2/src/e2ee/events.d.ts +34 -0
- package/dist/ts4.2/src/e2ee/index.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/types.d.ts +23 -33
- package/dist/ts4.2/src/e2ee/utils.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +18 -13
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +6 -8
- package/dist/ts4.2/src/e2ee/worker/SifGuard.d.ts +11 -0
- package/dist/ts4.2/src/options.d.ts +5 -0
- package/dist/ts4.2/src/room/DeviceManager.d.ts +1 -0
- package/dist/ts4.2/src/room/Room.d.ts +1 -1
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -0
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +0 -5
- package/dist/ts4.2/src/room/timers.d.ts +2 -2
- package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +9 -1
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -3
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +6 -0
- package/dist/ts4.2/src/room/track/processor/types.d.ts +14 -2
- package/dist/ts4.2/src/room/types.d.ts +1 -1
- package/package.json +15 -16
- package/src/api/SignalClient.ts +13 -9
- package/src/connectionHelper/checks/turn.ts +1 -0
- package/src/connectionHelper/checks/webrtc.ts +9 -7
- package/src/connectionHelper/checks/websocket.ts +1 -0
- package/src/e2ee/E2eeManager.ts +129 -76
- package/src/e2ee/KeyProvider.ts +31 -16
- package/src/e2ee/constants.ts +3 -0
- package/src/e2ee/events.ts +48 -0
- package/src/e2ee/index.ts +1 -0
- package/src/e2ee/types.ts +27 -41
- package/src/e2ee/utils.ts +9 -0
- package/src/e2ee/worker/FrameCryptor.ts +90 -47
- package/src/e2ee/worker/ParticipantKeyHandler.ts +25 -26
- package/src/e2ee/worker/SifGuard.ts +47 -0
- package/src/e2ee/worker/e2ee.worker.ts +75 -68
- package/src/options.ts +6 -0
- package/src/proto/livekit_models_pb.ts +14 -0
- package/src/proto/livekit_rtc_pb.ts +14 -0
- package/src/room/DeviceManager.ts +7 -2
- package/src/room/PCTransport.ts +12 -2
- package/src/room/RTCEngine.ts +3 -2
- package/src/room/Room.ts +47 -22
- package/src/room/defaults.ts +1 -0
- package/src/room/participant/LocalParticipant.ts +18 -2
- package/src/room/participant/Participant.ts +16 -0
- package/src/room/participant/RemoteParticipant.ts +0 -12
- package/src/room/track/LocalAudioTrack.ts +45 -0
- package/src/room/track/LocalTrack.ts +22 -14
- package/src/room/track/LocalVideoTrack.ts +39 -0
- package/src/room/track/RemoteAudioTrack.ts +9 -1
- package/src/room/track/RemoteTrackPublication.ts +2 -2
- package/src/room/track/facingMode.ts +1 -1
- package/src/room/track/processor/types.ts +18 -2
- 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
|
58
|
-
resumeUpstream
|
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
|
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
|
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:
|
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.
|
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.
|
65
|
-
"@babel/preset-env": "7.22.
|
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.
|
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.
|
81
|
-
"@typescript-eslint/parser": "5.
|
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.
|
84
|
-
"eslint-config-airbnb-typescript": "17.
|
85
|
-
"eslint-config-prettier": "8.
|
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.
|
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.
|
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.
|
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.
|
100
|
-
"vite": "4.
|
101
|
-
"vitest": "^0.
|
98
|
+
"typescript": "5.1.6",
|
99
|
+
"vite": "4.4.7",
|
100
|
+
"vitest": "^0.33.0"
|
102
101
|
}
|
103
102
|
}
|
package/src/api/SignalClient.ts
CHANGED
@@ -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')) {
|
@@ -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.
|
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
|
-
|
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
|
-
|
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) {
|
package/src/e2ee/E2eeManager.ts
CHANGED
@@ -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
|
-
|
89
|
-
log.
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
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
|
-
|
107
|
-
this.emit(EncryptionEvent.
|
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 (
|
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
|
123
|
+
this.room!.localParticipant,
|
115
124
|
);
|
116
125
|
this.encryptionEnabled = data.enabled;
|
117
|
-
} else if (data.
|
118
|
-
const participant = this.room?.getParticipantByIdentity(data.
|
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(
|
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.
|
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
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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(
|
189
|
-
.on(
|
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(
|
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
|
-
|
223
|
+
participantIdentity: participantIdentity,
|
202
224
|
keyIndex,
|
203
225
|
},
|
204
226
|
};
|
205
227
|
this.worker.postMessage(msg);
|
206
228
|
}
|
207
229
|
|
208
|
-
private postKey({ key,
|
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
|
-
|
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
|
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
|
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,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 (
|
345
|
-
|
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
|
-
|
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.
|
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
|
-
|
419
|
+
participantIdentity: this.room.localParticipant.identity,
|
367
420
|
},
|
368
421
|
};
|
369
422
|
this.worker.postMessage(msg, [senderStreams.readable, senderStreams.writable]);
|