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
package/src/room/RTCEngine.ts
CHANGED
|
@@ -9,9 +9,13 @@ import {
|
|
|
9
9
|
DataPacket,
|
|
10
10
|
DataPacket_Kind,
|
|
11
11
|
DisconnectReason,
|
|
12
|
+
EncryptedPacket,
|
|
13
|
+
EncryptedPacketPayload,
|
|
14
|
+
Encryption_Type,
|
|
12
15
|
type JoinResponse,
|
|
13
16
|
type LeaveRequest,
|
|
14
17
|
LeaveRequest_Action,
|
|
18
|
+
MediaSectionsRequirement,
|
|
15
19
|
ParticipantInfo,
|
|
16
20
|
ReconnectReason,
|
|
17
21
|
type ReconnectResponse,
|
|
@@ -43,6 +47,8 @@ import {
|
|
|
43
47
|
SignalConnectionState,
|
|
44
48
|
toProtoSessionDescription,
|
|
45
49
|
} from '../api/SignalClient';
|
|
50
|
+
import type { BaseE2EEManager } from '../e2ee/E2eeManager';
|
|
51
|
+
import { asEncryptablePacket } from '../e2ee/utils';
|
|
46
52
|
import log, { LoggerNames, getLogger } from '../logger';
|
|
47
53
|
import type { InternalRoomOptions } from '../options';
|
|
48
54
|
import { DataPacketBuffer } from '../utils/dataPacketBuffer';
|
|
@@ -116,6 +122,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
116
122
|
*/
|
|
117
123
|
latestRemoteOfferId: number = 0;
|
|
118
124
|
|
|
125
|
+
/** @internal */
|
|
126
|
+
e2eeManager: BaseE2EEManager | undefined;
|
|
127
|
+
|
|
119
128
|
get isClosed() {
|
|
120
129
|
return this._isClosed;
|
|
121
130
|
}
|
|
@@ -203,7 +212,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
203
212
|
this.client = new SignalClient(undefined, this.loggerOptions);
|
|
204
213
|
this.client.signalLatency = this.options.expSignalLatency;
|
|
205
214
|
this.reconnectPolicy = this.options.reconnectPolicy;
|
|
206
|
-
this.registerOnLineListener();
|
|
207
215
|
this.closingLock = new Mutex();
|
|
208
216
|
this.dataProcessLock = new Mutex();
|
|
209
217
|
this.dcBufferStatus = new Map([
|
|
@@ -260,9 +268,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
260
268
|
|
|
261
269
|
// create offer
|
|
262
270
|
if (!this.subscriberPrimary || joinResponse.fastPublish) {
|
|
263
|
-
this.negotiate()
|
|
271
|
+
this.negotiate().catch((err) => {
|
|
272
|
+
log.error(err, this.logContext);
|
|
273
|
+
});
|
|
264
274
|
}
|
|
265
275
|
|
|
276
|
+
this.registerOnLineListener();
|
|
266
277
|
this.clientConfiguration = joinResponse.clientConfiguration;
|
|
267
278
|
this.emit(EngineEvent.SignalConnected, joinResponse);
|
|
268
279
|
return joinResponse;
|
|
@@ -415,7 +426,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
415
426
|
|
|
416
427
|
this.pcManager = new PCTransportManager(
|
|
417
428
|
rtcConfig,
|
|
418
|
-
|
|
429
|
+
this.options.singlePeerConnection
|
|
430
|
+
? 'publisher-only'
|
|
431
|
+
: joinResponse.subscriberPrimary
|
|
432
|
+
? 'subscriber-primary'
|
|
433
|
+
: 'publisher-primary',
|
|
419
434
|
this.loggerOptions,
|
|
420
435
|
);
|
|
421
436
|
|
|
@@ -445,7 +460,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
445
460
|
}
|
|
446
461
|
} else if (connectionState === PCTransportState.FAILED) {
|
|
447
462
|
// on Safari, PeerConnection will switch to 'disconnected' during renegotiation
|
|
448
|
-
if (this.pcState === PCState.Connected) {
|
|
463
|
+
if (this.pcState === PCState.Connected || this.pcState === PCState.Reconnecting) {
|
|
449
464
|
this.pcState = PCState.Disconnected;
|
|
450
465
|
|
|
451
466
|
this.handleDisconnect(
|
|
@@ -471,6 +486,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
471
486
|
}
|
|
472
487
|
};
|
|
473
488
|
this.pcManager.onTrack = (ev: RTCTrackEvent) => {
|
|
489
|
+
// this fires after the underlying transceiver is stopped and potentially
|
|
490
|
+
// peer connection closed, so do not bubble up if there are no streams
|
|
491
|
+
if (ev.streams.length === 0) return;
|
|
474
492
|
this.emit(EngineEvent.MediaTrackAdded, ev.track, ev.streams[0], ev.receiver);
|
|
475
493
|
};
|
|
476
494
|
|
|
@@ -556,6 +574,18 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
556
574
|
this.emit(EngineEvent.RoomMoved, res);
|
|
557
575
|
};
|
|
558
576
|
|
|
577
|
+
this.client.onMediaSectionsRequirement = (requirement: MediaSectionsRequirement) => {
|
|
578
|
+
const transceiverInit: RTCRtpTransceiverInit = { direction: 'recvonly' };
|
|
579
|
+
for (let i: number = 0; i < requirement.numAudios; i++) {
|
|
580
|
+
this.pcManager?.addPublisherTransceiverOfKind('audio', transceiverInit);
|
|
581
|
+
}
|
|
582
|
+
for (let i: number = 0; i < requirement.numVideos; i++) {
|
|
583
|
+
this.pcManager?.addPublisherTransceiverOfKind('video', transceiverInit);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
this.negotiate();
|
|
587
|
+
};
|
|
588
|
+
|
|
559
589
|
this.client.onClose = () => {
|
|
560
590
|
this.handleDisconnect('signal', ReconnectReason.RR_SIGNAL_DISCONNECTED);
|
|
561
591
|
};
|
|
@@ -710,12 +740,34 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
710
740
|
if (dp.value?.case === 'speaker') {
|
|
711
741
|
// dispatch speaker updates
|
|
712
742
|
this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.value.speakers);
|
|
743
|
+
} else if (dp.value?.case === 'encryptedPacket') {
|
|
744
|
+
if (!this.e2eeManager) {
|
|
745
|
+
this.log.error('Received encrypted packet but E2EE not set up', this.logContext);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const decryptedData = await this.e2eeManager?.handleEncryptedData(
|
|
749
|
+
dp.value.value.encryptedValue,
|
|
750
|
+
dp.value.value.iv,
|
|
751
|
+
dp.participantIdentity,
|
|
752
|
+
dp.value.value.keyIndex,
|
|
753
|
+
);
|
|
754
|
+
const decryptedPacket = EncryptedPacketPayload.fromBinary(decryptedData.payload);
|
|
755
|
+
const newDp = new DataPacket({
|
|
756
|
+
value: decryptedPacket.value,
|
|
757
|
+
participantIdentity: dp.participantIdentity,
|
|
758
|
+
participantSid: dp.participantSid,
|
|
759
|
+
});
|
|
760
|
+
if (newDp.value?.case === 'user') {
|
|
761
|
+
// compatibility
|
|
762
|
+
applyUserDataCompat(newDp, newDp.value.value);
|
|
763
|
+
}
|
|
764
|
+
this.emit(EngineEvent.DataPacketReceived, newDp, dp.value.value.encryptionType);
|
|
713
765
|
} else {
|
|
714
766
|
if (dp.value?.case === 'user') {
|
|
715
767
|
// compatibility
|
|
716
768
|
applyUserDataCompat(dp, dp.value.value);
|
|
717
769
|
}
|
|
718
|
-
this.emit(EngineEvent.DataPacketReceived, dp);
|
|
770
|
+
this.emit(EngineEvent.DataPacketReceived, dp, Encryption_Type.NONE);
|
|
719
771
|
}
|
|
720
772
|
} finally {
|
|
721
773
|
unlock();
|
|
@@ -1191,11 +1243,28 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1191
1243
|
// make sure we do have a data connection
|
|
1192
1244
|
await this.ensurePublisherConnected(kind);
|
|
1193
1245
|
|
|
1246
|
+
if (this.e2eeManager && this.e2eeManager.isDataChannelEncryptionEnabled) {
|
|
1247
|
+
const encryptablePacket = asEncryptablePacket(packet);
|
|
1248
|
+
if (encryptablePacket) {
|
|
1249
|
+
const encryptedData = await this.e2eeManager.encryptData(encryptablePacket.toBinary());
|
|
1250
|
+
packet.value = {
|
|
1251
|
+
case: 'encryptedPacket',
|
|
1252
|
+
value: new EncryptedPacket({
|
|
1253
|
+
encryptedValue: encryptedData.payload,
|
|
1254
|
+
iv: encryptedData.iv,
|
|
1255
|
+
keyIndex: encryptedData.keyIndex,
|
|
1256
|
+
}),
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1194
1261
|
if (kind === DataPacket_Kind.RELIABLE) {
|
|
1195
1262
|
packet.sequence = this.reliableDataSequence;
|
|
1196
1263
|
this.reliableDataSequence += 1;
|
|
1197
1264
|
}
|
|
1265
|
+
|
|
1198
1266
|
const msg = packet.toBinary();
|
|
1267
|
+
|
|
1199
1268
|
const dc = this.dataChannelForKind(kind);
|
|
1200
1269
|
if (dc) {
|
|
1201
1270
|
if (kind === DataPacket_Kind.RELIABLE) {
|
|
@@ -1293,7 +1362,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1293
1362
|
}
|
|
1294
1363
|
if (needNegotiation) {
|
|
1295
1364
|
// start negotiation
|
|
1296
|
-
this.negotiate()
|
|
1365
|
+
this.negotiate().catch((err) => {
|
|
1366
|
+
log.error(err, this.logContext);
|
|
1367
|
+
});
|
|
1297
1368
|
}
|
|
1298
1369
|
|
|
1299
1370
|
const targetChannel = this.dataChannelForKind(kind, subscriber);
|
|
@@ -1429,8 +1500,10 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1429
1500
|
this.log.warn('sync state cannot be sent without peer connection setup', this.logContext);
|
|
1430
1501
|
return;
|
|
1431
1502
|
}
|
|
1432
|
-
const
|
|
1433
|
-
const
|
|
1503
|
+
const previousPublisherOffer = this.pcManager.publisher.getLocalDescription();
|
|
1504
|
+
const previousPublisherAnswer = this.pcManager.publisher.getRemoteDescription();
|
|
1505
|
+
const previousSubscriberOffer = this.pcManager.subscriber?.getRemoteDescription();
|
|
1506
|
+
const previousSubscriberAnswer = this.pcManager.subscriber?.getLocalDescription();
|
|
1434
1507
|
|
|
1435
1508
|
/* 1. autosubscribe on, so subscribed tracks = all tracks - unsub tracks,
|
|
1436
1509
|
in this case, we send unsub tracks, so server add all tracks to this
|
|
@@ -1452,18 +1525,32 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1452
1525
|
|
|
1453
1526
|
this.client.sendSyncState(
|
|
1454
1527
|
new SyncState({
|
|
1455
|
-
answer:
|
|
1456
|
-
?
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1528
|
+
answer: this.options.singlePeerConnection
|
|
1529
|
+
? previousPublisherAnswer
|
|
1530
|
+
? toProtoSessionDescription({
|
|
1531
|
+
sdp: previousPublisherAnswer.sdp,
|
|
1532
|
+
type: previousPublisherAnswer.type,
|
|
1533
|
+
})
|
|
1534
|
+
: undefined
|
|
1535
|
+
: previousSubscriberAnswer
|
|
1536
|
+
? toProtoSessionDescription({
|
|
1537
|
+
sdp: previousSubscriberAnswer.sdp,
|
|
1538
|
+
type: previousSubscriberAnswer.type,
|
|
1539
|
+
})
|
|
1540
|
+
: undefined,
|
|
1541
|
+
offer: this.options.singlePeerConnection
|
|
1542
|
+
? previousPublisherOffer
|
|
1543
|
+
? toProtoSessionDescription({
|
|
1544
|
+
sdp: previousPublisherOffer.sdp,
|
|
1545
|
+
type: previousPublisherOffer.type,
|
|
1546
|
+
})
|
|
1547
|
+
: undefined
|
|
1548
|
+
: previousSubscriberOffer
|
|
1549
|
+
? toProtoSessionDescription({
|
|
1550
|
+
sdp: previousSubscriberOffer.sdp,
|
|
1551
|
+
type: previousSubscriberOffer.type,
|
|
1552
|
+
})
|
|
1553
|
+
: undefined,
|
|
1467
1554
|
subscription: new UpdateSubscription({
|
|
1468
1555
|
trackSids,
|
|
1469
1556
|
subscribe: !autoSubscribe,
|
|
@@ -1558,9 +1645,9 @@ export type EngineEventCallbacks = {
|
|
|
1558
1645
|
receiver: RTCRtpReceiver,
|
|
1559
1646
|
) => void;
|
|
1560
1647
|
activeSpeakersUpdate: (speakers: Array<SpeakerInfo>) => void;
|
|
1561
|
-
dataPacketReceived: (packet: DataPacket) => void;
|
|
1648
|
+
dataPacketReceived: (packet: DataPacket, encryptionType: Encryption_Type) => void;
|
|
1562
1649
|
transcriptionReceived: (transcription: Transcription) => void;
|
|
1563
|
-
transportsCreated: (publisher: PCTransport, subscriber
|
|
1650
|
+
transportsCreated: (publisher: PCTransport, subscriber?: PCTransport) => void;
|
|
1564
1651
|
/** @internal */
|
|
1565
1652
|
trackSenderAdded: (track: Track, sender: RTCRtpSender) => void;
|
|
1566
1653
|
rtpVideoMapUpdate: (rtpMap: Map<number, VideoCodec>) => void;
|
package/src/room/Room.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type DataPacket,
|
|
6
6
|
DataPacket_Kind,
|
|
7
7
|
DisconnectReason,
|
|
8
|
+
Encryption_Type,
|
|
8
9
|
JoinResponse,
|
|
9
10
|
LeaveRequest,
|
|
10
11
|
LeaveRequest_Action,
|
|
@@ -198,6 +199,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
198
199
|
|
|
199
200
|
private rpcHandlers: Map<string, (data: RpcInvocationData) => Promise<string>> = new Map();
|
|
200
201
|
|
|
202
|
+
get hasE2EESetup(): boolean {
|
|
203
|
+
return this.e2eeManager !== undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
201
206
|
/**
|
|
202
207
|
* Creates a new Room, the primary construct for a LiveKit session.
|
|
203
208
|
* @param options
|
|
@@ -241,6 +246,12 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
241
246
|
this.outgoingDataStreamManager,
|
|
242
247
|
);
|
|
243
248
|
|
|
249
|
+
if (this.options.e2ee || this.options.encryption) {
|
|
250
|
+
this.setupE2EE();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
this.engine.e2eeManager = this.e2eeManager;
|
|
254
|
+
|
|
244
255
|
if (this.options.videoCaptureDefaults.deviceId) {
|
|
245
256
|
this.localParticipant.activeDeviceMap.set(
|
|
246
257
|
'videoinput',
|
|
@@ -260,10 +271,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
260
271
|
).catch((e) => this.log.warn(`Could not set audio output: ${e.message}`, this.logContext));
|
|
261
272
|
}
|
|
262
273
|
|
|
263
|
-
if (this.options.e2ee) {
|
|
264
|
-
this.setupE2EE();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
274
|
if (isWeb()) {
|
|
268
275
|
const abortController = new AbortController();
|
|
269
276
|
|
|
@@ -355,11 +362,17 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
355
362
|
}
|
|
356
363
|
|
|
357
364
|
private setupE2EE() {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
365
|
+
// when encryption is enabled via `options.encryption`, we enable data channel encryption
|
|
366
|
+
|
|
367
|
+
const dcEncryptionEnabled = !!this.options.encryption;
|
|
368
|
+
const e2eeOptions = this.options.encryption || this.options.e2ee;
|
|
369
|
+
|
|
370
|
+
if (e2eeOptions) {
|
|
371
|
+
if ('e2eeManager' in e2eeOptions) {
|
|
372
|
+
this.e2eeManager = e2eeOptions.e2eeManager;
|
|
373
|
+
this.e2eeManager.isDataChannelEncryptionEnabled = dcEncryptionEnabled;
|
|
361
374
|
} else {
|
|
362
|
-
this.e2eeManager = new E2EEManager(
|
|
375
|
+
this.e2eeManager = new E2EEManager(e2eeOptions, dcEncryptionEnabled);
|
|
363
376
|
}
|
|
364
377
|
this.e2eeManager.on(
|
|
365
378
|
EncryptionEvent.ParticipantEncryptionStatusChanged,
|
|
@@ -443,6 +456,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
443
456
|
}
|
|
444
457
|
|
|
445
458
|
this.engine = new RTCEngine(this.options);
|
|
459
|
+
this.engine.e2eeManager = this.e2eeManager;
|
|
446
460
|
|
|
447
461
|
this.engine
|
|
448
462
|
.on(EngineEvent.ParticipantUpdate, this.handleParticipantUpdates)
|
|
@@ -748,6 +762,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
748
762
|
maxRetries: connectOptions.maxRetries,
|
|
749
763
|
e2eeEnabled: !!this.e2eeManager,
|
|
750
764
|
websocketTimeout: connectOptions.websocketTimeout,
|
|
765
|
+
singlePeerConnection: roomOptions.singlePeerConnection,
|
|
751
766
|
},
|
|
752
767
|
abortController.signal,
|
|
753
768
|
);
|
|
@@ -789,7 +804,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
789
804
|
this.localParticipant.identity = pi.identity;
|
|
790
805
|
this.localParticipant.setEnabledPublishCodecs(joinResponse.enabledPublishCodecs);
|
|
791
806
|
|
|
792
|
-
if (this.
|
|
807
|
+
if (this.e2eeManager) {
|
|
793
808
|
try {
|
|
794
809
|
this.e2eeManager.setSifTrailer(joinResponse.sifTrailer);
|
|
795
810
|
} catch (e: any) {
|
|
@@ -926,8 +941,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
926
941
|
this.isResuming
|
|
927
942
|
) {
|
|
928
943
|
// try aborting pending connection attempt
|
|
929
|
-
|
|
930
|
-
this.
|
|
944
|
+
const msg = 'Abort connection attempt due to user initiated disconnect';
|
|
945
|
+
this.log.warn(msg, this.logContext);
|
|
946
|
+
this.abortController?.abort(msg);
|
|
931
947
|
// in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
|
|
932
948
|
this.connectFuture?.reject?.(
|
|
933
949
|
new ConnectionError('Client initiated disconnect', ConnectionErrorReason.Cancelled),
|
|
@@ -1711,11 +1727,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1711
1727
|
pub.setSubscriptionError(update.err);
|
|
1712
1728
|
};
|
|
1713
1729
|
|
|
1714
|
-
private handleDataPacket = (packet: DataPacket) => {
|
|
1730
|
+
private handleDataPacket = (packet: DataPacket, encryptionType: Encryption_Type) => {
|
|
1715
1731
|
// find the participant
|
|
1716
1732
|
const participant = this.remoteParticipants.get(packet.participantIdentity);
|
|
1717
1733
|
if (packet.value.case === 'user') {
|
|
1718
|
-
this.handleUserPacket(participant, packet.value.value, packet.kind);
|
|
1734
|
+
this.handleUserPacket(participant, packet.value.value, packet.kind, encryptionType);
|
|
1719
1735
|
} else if (packet.value.case === 'transcription') {
|
|
1720
1736
|
this.handleTranscription(participant, packet.value.value);
|
|
1721
1737
|
} else if (packet.value.case === 'sipDtmf') {
|
|
@@ -1729,7 +1745,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1729
1745
|
packet.value.case === 'streamChunk' ||
|
|
1730
1746
|
packet.value.case === 'streamTrailer'
|
|
1731
1747
|
) {
|
|
1732
|
-
this.handleDataStream(packet);
|
|
1748
|
+
this.handleDataStream(packet, encryptionType);
|
|
1733
1749
|
} else if (packet.value.case === 'rpcRequest') {
|
|
1734
1750
|
const rpc = packet.value.value;
|
|
1735
1751
|
this.handleIncomingRpcRequest(
|
|
@@ -1747,11 +1763,19 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1747
1763
|
participant: RemoteParticipant | undefined,
|
|
1748
1764
|
userPacket: UserPacket,
|
|
1749
1765
|
kind: DataPacket_Kind,
|
|
1766
|
+
encryptionType: Encryption_Type,
|
|
1750
1767
|
) => {
|
|
1751
|
-
this.emit(
|
|
1768
|
+
this.emit(
|
|
1769
|
+
RoomEvent.DataReceived,
|
|
1770
|
+
userPacket.payload,
|
|
1771
|
+
participant,
|
|
1772
|
+
kind,
|
|
1773
|
+
userPacket.topic,
|
|
1774
|
+
encryptionType,
|
|
1775
|
+
);
|
|
1752
1776
|
|
|
1753
1777
|
// also emit on the participant
|
|
1754
|
-
participant?.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
|
|
1778
|
+
participant?.emit(ParticipantEvent.DataReceived, userPacket.payload, kind, encryptionType);
|
|
1755
1779
|
};
|
|
1756
1780
|
|
|
1757
1781
|
private handleSipDtmf = (participant: RemoteParticipant | undefined, dtmf: SipDTMF) => {
|
|
@@ -1791,8 +1815,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1791
1815
|
this.emit(RoomEvent.MetricsReceived, metrics, participant);
|
|
1792
1816
|
};
|
|
1793
1817
|
|
|
1794
|
-
private handleDataStream = (packet: DataPacket) => {
|
|
1795
|
-
this.incomingDataStreamManager.handleDataStreamPacket(packet);
|
|
1818
|
+
private handleDataStream = (packet: DataPacket, encryptionType: Encryption_Type) => {
|
|
1819
|
+
this.incomingDataStreamManager.handleDataStreamPacket(packet, encryptionType);
|
|
1796
1820
|
};
|
|
1797
1821
|
|
|
1798
1822
|
private async handleIncomingRpcRequest(
|
|
@@ -2596,6 +2620,7 @@ export type RoomEventCallbacks = {
|
|
|
2596
2620
|
participant?: RemoteParticipant,
|
|
2597
2621
|
kind?: DataPacket_Kind,
|
|
2598
2622
|
topic?: string,
|
|
2623
|
+
encryptionType?: Encryption_Type,
|
|
2599
2624
|
) => void;
|
|
2600
2625
|
sipDTMFReceived: (dtmf: SipDTMF, participant?: RemoteParticipant) => void;
|
|
2601
2626
|
transcriptionReceived: (
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
DataStream_Chunk,
|
|
4
4
|
DataStream_Header,
|
|
5
5
|
DataStream_Trailer,
|
|
6
|
+
Encryption_Type,
|
|
6
7
|
} from '@livekit/protocol';
|
|
7
8
|
import log from '../../../logger';
|
|
8
9
|
import { DataStreamError, DataStreamErrorReason } from '../../errors';
|
|
@@ -89,20 +90,28 @@ export default class IncomingDataStreamManager {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
async handleDataStreamPacket(packet: DataPacket) {
|
|
93
|
+
async handleDataStreamPacket(packet: DataPacket, encryptionType: Encryption_Type) {
|
|
93
94
|
switch (packet.value.case) {
|
|
94
95
|
case 'streamHeader':
|
|
95
|
-
return this.handleStreamHeader(
|
|
96
|
+
return this.handleStreamHeader(
|
|
97
|
+
packet.value.value,
|
|
98
|
+
packet.participantIdentity,
|
|
99
|
+
encryptionType,
|
|
100
|
+
);
|
|
96
101
|
case 'streamChunk':
|
|
97
|
-
return this.handleStreamChunk(packet.value.value);
|
|
102
|
+
return this.handleStreamChunk(packet.value.value, encryptionType);
|
|
98
103
|
case 'streamTrailer':
|
|
99
|
-
return this.handleStreamTrailer(packet.value.value);
|
|
104
|
+
return this.handleStreamTrailer(packet.value.value, encryptionType);
|
|
100
105
|
default:
|
|
101
106
|
throw new Error(`DataPacket of value "${packet.value.case}" is not data stream related!`);
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
|
|
105
|
-
private async handleStreamHeader(
|
|
110
|
+
private async handleStreamHeader(
|
|
111
|
+
streamHeader: DataStream_Header,
|
|
112
|
+
participantIdentity: string,
|
|
113
|
+
encryptionType: Encryption_Type,
|
|
114
|
+
) {
|
|
106
115
|
if (streamHeader.contentHeader.case === 'byteHeader') {
|
|
107
116
|
const streamHandlerCallback = this.byteStreamHandlers.get(streamHeader.topic);
|
|
108
117
|
if (!streamHandlerCallback) {
|
|
@@ -115,6 +124,9 @@ export default class IncomingDataStreamManager {
|
|
|
115
124
|
|
|
116
125
|
let streamController: ReadableStreamDefaultController<DataStream_Chunk>;
|
|
117
126
|
const outOfBandFailureRejectingFuture = new Future<never>();
|
|
127
|
+
outOfBandFailureRejectingFuture.promise.catch((err) => {
|
|
128
|
+
this.log.error(err);
|
|
129
|
+
});
|
|
118
130
|
|
|
119
131
|
const info: ByteStreamInfo = {
|
|
120
132
|
id: streamHeader.streamId,
|
|
@@ -124,6 +136,7 @@ export default class IncomingDataStreamManager {
|
|
|
124
136
|
topic: streamHeader.topic,
|
|
125
137
|
timestamp: bigIntToNumber(streamHeader.timestamp),
|
|
126
138
|
attributes: streamHeader.attributes,
|
|
139
|
+
encryptionType,
|
|
127
140
|
};
|
|
128
141
|
const stream = new ReadableStream({
|
|
129
142
|
start: (controller) => {
|
|
@@ -168,6 +181,10 @@ export default class IncomingDataStreamManager {
|
|
|
168
181
|
|
|
169
182
|
let streamController: ReadableStreamDefaultController<DataStream_Chunk>;
|
|
170
183
|
const outOfBandFailureRejectingFuture = new Future<never>();
|
|
184
|
+
outOfBandFailureRejectingFuture.promise.catch((err) => {
|
|
185
|
+
this.log.error(err);
|
|
186
|
+
});
|
|
187
|
+
|
|
171
188
|
const info: TextStreamInfo = {
|
|
172
189
|
id: streamHeader.streamId,
|
|
173
190
|
mimeType: streamHeader.mimeType,
|
|
@@ -175,6 +192,7 @@ export default class IncomingDataStreamManager {
|
|
|
175
192
|
topic: streamHeader.topic,
|
|
176
193
|
timestamp: Number(streamHeader.timestamp),
|
|
177
194
|
attributes: streamHeader.attributes,
|
|
195
|
+
encryptionType,
|
|
178
196
|
};
|
|
179
197
|
|
|
180
198
|
const stream = new ReadableStream<DataStream_Chunk>({
|
|
@@ -209,39 +227,68 @@ export default class IncomingDataStreamManager {
|
|
|
209
227
|
}
|
|
210
228
|
}
|
|
211
229
|
|
|
212
|
-
private handleStreamChunk(chunk: DataStream_Chunk) {
|
|
230
|
+
private handleStreamChunk(chunk: DataStream_Chunk, encryptionType: Encryption_Type) {
|
|
213
231
|
const fileBuffer = this.byteStreamControllers.get(chunk.streamId);
|
|
214
232
|
if (fileBuffer) {
|
|
215
|
-
if (
|
|
233
|
+
if (fileBuffer.info.encryptionType !== encryptionType) {
|
|
234
|
+
fileBuffer.controller.error(
|
|
235
|
+
new DataStreamError(
|
|
236
|
+
`Encryption type mismatch for stream ${chunk.streamId}. Expected ${encryptionType}, got ${fileBuffer.info.encryptionType}`,
|
|
237
|
+
DataStreamErrorReason.EncryptionTypeMismatch,
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
this.byteStreamControllers.delete(chunk.streamId);
|
|
241
|
+
} else if (chunk.content.length > 0) {
|
|
216
242
|
fileBuffer.controller.enqueue(chunk);
|
|
217
243
|
}
|
|
218
244
|
}
|
|
219
245
|
const textBuffer = this.textStreamControllers.get(chunk.streamId);
|
|
220
246
|
if (textBuffer) {
|
|
221
|
-
if (
|
|
247
|
+
if (textBuffer.info.encryptionType !== encryptionType) {
|
|
248
|
+
textBuffer.controller.error(
|
|
249
|
+
new DataStreamError(
|
|
250
|
+
`Encryption type mismatch for stream ${chunk.streamId}. Expected ${encryptionType}, got ${textBuffer.info.encryptionType}`,
|
|
251
|
+
DataStreamErrorReason.EncryptionTypeMismatch,
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
this.textStreamControllers.delete(chunk.streamId);
|
|
255
|
+
} else if (chunk.content.length > 0) {
|
|
222
256
|
textBuffer.controller.enqueue(chunk);
|
|
223
257
|
}
|
|
224
258
|
}
|
|
225
259
|
}
|
|
226
260
|
|
|
227
|
-
private handleStreamTrailer(trailer: DataStream_Trailer) {
|
|
261
|
+
private handleStreamTrailer(trailer: DataStream_Trailer, encryptionType: Encryption_Type) {
|
|
228
262
|
const textBuffer = this.textStreamControllers.get(trailer.streamId);
|
|
229
263
|
if (textBuffer) {
|
|
230
|
-
textBuffer.info.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
264
|
+
if (textBuffer.info.encryptionType !== encryptionType) {
|
|
265
|
+
textBuffer.controller.error(
|
|
266
|
+
new DataStreamError(
|
|
267
|
+
`Encryption type mismatch for stream ${trailer.streamId}. Expected ${encryptionType}, got ${textBuffer.info.encryptionType}`,
|
|
268
|
+
DataStreamErrorReason.EncryptionTypeMismatch,
|
|
269
|
+
),
|
|
270
|
+
);
|
|
271
|
+
} else {
|
|
272
|
+
textBuffer.info.attributes = { ...textBuffer.info.attributes, ...trailer.attributes };
|
|
273
|
+
textBuffer.controller.close();
|
|
274
|
+
this.textStreamControllers.delete(trailer.streamId);
|
|
275
|
+
}
|
|
236
276
|
}
|
|
237
277
|
|
|
238
278
|
const fileBuffer = this.byteStreamControllers.get(trailer.streamId);
|
|
239
279
|
if (fileBuffer) {
|
|
240
|
-
{
|
|
280
|
+
if (fileBuffer.info.encryptionType !== encryptionType) {
|
|
281
|
+
fileBuffer.controller.error(
|
|
282
|
+
new DataStreamError(
|
|
283
|
+
`Encryption type mismatch for stream ${trailer.streamId}. Expected ${encryptionType}, got ${fileBuffer.info.encryptionType}`,
|
|
284
|
+
DataStreamErrorReason.EncryptionTypeMismatch,
|
|
285
|
+
),
|
|
286
|
+
);
|
|
287
|
+
} else {
|
|
241
288
|
fileBuffer.info.attributes = { ...fileBuffer.info.attributes, ...trailer.attributes };
|
|
242
289
|
fileBuffer.controller.close();
|
|
243
|
-
this.byteStreamControllers.delete(trailer.streamId);
|
|
244
290
|
}
|
|
291
|
+
this.byteStreamControllers.delete(trailer.streamId);
|
|
245
292
|
}
|
|
246
293
|
}
|
|
247
294
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
DataStream_OperationType,
|
|
9
9
|
DataStream_TextHeader,
|
|
10
10
|
DataStream_Trailer,
|
|
11
|
+
Encryption_Type,
|
|
11
12
|
} from '@livekit/protocol';
|
|
12
13
|
import { type StructuredLogger } from '../../../logger';
|
|
13
14
|
import type RTCEngine from '../../RTCEngine';
|
|
@@ -104,6 +105,9 @@ export default class OutgoingDataStreamManager {
|
|
|
104
105
|
topic: options?.topic ?? '',
|
|
105
106
|
size: options?.totalSize,
|
|
106
107
|
attributes: options?.attributes,
|
|
108
|
+
encryptionType: this.engine.e2eeManager?.isDataChannelEncryptionEnabled
|
|
109
|
+
? Encryption_Type.GCM
|
|
110
|
+
: Encryption_Type.NONE,
|
|
107
111
|
};
|
|
108
112
|
const header = new DataStream_Header({
|
|
109
113
|
streamId,
|
|
@@ -231,6 +235,9 @@ export default class OutgoingDataStreamManager {
|
|
|
231
235
|
attributes: options?.attributes,
|
|
232
236
|
size: options?.totalSize,
|
|
233
237
|
name: options?.name ?? 'unknown',
|
|
238
|
+
encryptionType: this.engine.e2eeManager?.isDataChannelEncryptionEnabled
|
|
239
|
+
? Encryption_Type.GCM
|
|
240
|
+
: Encryption_Type.NONE,
|
|
234
241
|
};
|
|
235
242
|
|
|
236
243
|
const header = new DataStream_Header({
|
package/src/room/defaults.ts
CHANGED
|
@@ -42,6 +42,7 @@ export const roomOptionDefaults: InternalRoomOptions = {
|
|
|
42
42
|
reconnectPolicy: new DefaultReconnectPolicy(),
|
|
43
43
|
disconnectOnPageLeave: true,
|
|
44
44
|
webAudioMix: false,
|
|
45
|
+
singlePeerConnection: false,
|
|
45
46
|
} as const;
|
|
46
47
|
|
|
47
48
|
export const roomConnectOptionDefaults: InternalRoomConnectOptions = {
|
package/src/room/errors.ts
CHANGED
|
@@ -130,6 +130,9 @@ export enum DataStreamErrorReason {
|
|
|
130
130
|
|
|
131
131
|
// Unable to register a stream handler more than once.
|
|
132
132
|
HandlerAlreadyRegistered = 7,
|
|
133
|
+
|
|
134
|
+
// Encryption type mismatch.
|
|
135
|
+
EncryptionTypeMismatch = 8,
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
export class DataStreamError extends LivekitError {
|
|
@@ -1634,16 +1634,18 @@ export default class LocalParticipant extends Participant {
|
|
|
1634
1634
|
const destinationIdentities = options.destinationIdentities;
|
|
1635
1635
|
const topic = options.topic;
|
|
1636
1636
|
|
|
1637
|
+
let userPacket = new UserPacket({
|
|
1638
|
+
participantIdentity: this.identity,
|
|
1639
|
+
payload: data,
|
|
1640
|
+
destinationIdentities,
|
|
1641
|
+
topic,
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1637
1644
|
const packet = new DataPacket({
|
|
1638
1645
|
kind: kind,
|
|
1639
1646
|
value: {
|
|
1640
1647
|
case: 'user',
|
|
1641
|
-
value:
|
|
1642
|
-
participantIdentity: this.identity,
|
|
1643
|
-
payload: data,
|
|
1644
|
-
destinationIdentities,
|
|
1645
|
-
topic,
|
|
1646
|
-
}),
|
|
1648
|
+
value: userPacket,
|
|
1647
1649
|
},
|
|
1648
1650
|
});
|
|
1649
1651
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DataPacket_Kind,
|
|
3
|
+
Encryption_Type,
|
|
3
4
|
ParticipantInfo,
|
|
4
5
|
ParticipantInfo_State,
|
|
5
6
|
ParticipantInfo_Kind as ParticipantKind,
|
|
@@ -408,7 +409,11 @@ export type ParticipantEventCallbacks = {
|
|
|
408
409
|
localSenderCreated: (sender: RTCRtpSender, track: Track) => void;
|
|
409
410
|
participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void;
|
|
410
411
|
participantNameChanged: (name: string) => void;
|
|
411
|
-
dataReceived: (
|
|
412
|
+
dataReceived: (
|
|
413
|
+
payload: Uint8Array,
|
|
414
|
+
kind: DataPacket_Kind,
|
|
415
|
+
encryptionType?: Encryption_Type,
|
|
416
|
+
) => void;
|
|
412
417
|
sipDTMFReceived: (dtmf: SipDTMF) => void;
|
|
413
418
|
transcriptionReceived: (
|
|
414
419
|
transcription: TranscriptionSegment[],
|