livekit-client 2.18.8 → 2.18.10
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 +5609 -644
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +2898 -2431
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.pt.worker.js +2 -0
- package/dist/livekit-client.pt.worker.js.map +1 -0
- package/dist/livekit-client.pt.worker.mjs +5834 -0
- package/dist/livekit-client.pt.worker.mjs.map +1 -0
- 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 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +8 -7
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +35 -8
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +5 -5
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/DataCryptor.d.ts +5 -5
- package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +21 -4
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/naluUtils.d.ts +1 -1
- package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/sifPayload.d.ts +7 -7
- package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/options.d.ts +7 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/packetTrailer/PacketTrailerManager.d.ts +49 -0
- package/dist/src/packetTrailer/PacketTrailerManager.d.ts.map +1 -0
- package/dist/src/packetTrailer/packetTrailer.d.ts +32 -0
- package/dist/src/packetTrailer/packetTrailer.d.ts.map +1 -0
- package/dist/src/packetTrailer/types.d.ts +57 -0
- package/dist/src/packetTrailer/types.d.ts.map +1 -0
- package/dist/src/packetTrailer/utils.d.ts +9 -0
- package/dist/src/packetTrailer/utils.d.ts.map +1 -0
- package/dist/src/packetTrailer/worker/packetTrailer.worker.d.ts +2 -0
- package/dist/src/packetTrailer/worker/packetTrailer.worker.d.ts.map +1 -0
- package/dist/src/room/RTCEngine.d.ts +2 -1
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +3 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-track/LocalDataTrack.d.ts +2 -1
- package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -1
- package/dist/src/room/data-track/RemoteDataTrack.d.ts +5 -1
- package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -1
- package/dist/src/room/data-track/depacketizer.d.ts +12 -4
- package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
- package/dist/src/room/data-track/frame.d.ts +3 -3
- package/dist/src/room/data-track/frame.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +3 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/pipeline.d.ts +4 -1
- package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +2 -2
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/types.d.ts +4 -3
- package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/extensions.d.ts +4 -4
- package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/index.d.ts +5 -5
- package/dist/src/room/data-track/packet/index.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/serializable.d.ts +1 -1
- package/dist/src/room/data-track/packet/serializable.d.ts.map +1 -1
- package/dist/src/room/data-track/types.d.ts +7 -0
- package/dist/src/room/data-track/types.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +2 -2
- package/dist/src/room/participant/LocalParticipant.d.ts +3 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +1 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/track/PacketTrailerExtractor.d.ts +19 -0
- package/dist/src/room/track/PacketTrailerExtractor.d.ts.map +1 -0
- package/dist/src/room/track/RemoteVideoTrack.d.ts +16 -0
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +1 -1
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +10 -0
- 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/utils.d.ts +4 -3
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/MockMediaStreamTrack.d.ts.map +1 -1
- package/dist/src/utils/dataPacketBuffer.d.ts +1 -1
- package/dist/src/utils/dataPacketBuffer.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/ts4.2/api/SignalClient.d.ts +2 -1
- package/dist/ts4.2/e2ee/E2eeManager.d.ts +8 -7
- package/dist/ts4.2/e2ee/types.d.ts +35 -8
- package/dist/ts4.2/e2ee/utils.d.ts +5 -5
- package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +5 -5
- package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +21 -4
- package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +1 -1
- package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +7 -7
- package/dist/ts4.2/index.d.ts +5 -1
- package/dist/ts4.2/options.d.ts +7 -0
- package/dist/ts4.2/packetTrailer/PacketTrailerManager.d.ts +49 -0
- package/dist/ts4.2/packetTrailer/packetTrailer.d.ts +32 -0
- package/dist/ts4.2/packetTrailer/types.d.ts +57 -0
- package/dist/ts4.2/packetTrailer/utils.d.ts +9 -0
- package/dist/ts4.2/packetTrailer/worker/packetTrailer.worker.d.ts +2 -0
- package/dist/ts4.2/room/RTCEngine.d.ts +2 -1
- package/dist/ts4.2/room/Room.d.ts +3 -1
- package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +2 -1
- package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +5 -1
- package/dist/ts4.2/room/data-track/depacketizer.d.ts +12 -4
- package/dist/ts4.2/room/data-track/frame.d.ts +3 -3
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +3 -1
- package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +4 -1
- package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +2 -2
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +4 -3
- package/dist/ts4.2/room/data-track/packet/extensions.d.ts +4 -4
- package/dist/ts4.2/room/data-track/packet/index.d.ts +5 -5
- package/dist/ts4.2/room/data-track/packet/serializable.d.ts +1 -1
- package/dist/ts4.2/room/data-track/types.d.ts +7 -0
- package/dist/ts4.2/room/events.d.ts +2 -2
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +3 -1
- package/dist/ts4.2/room/participant/Participant.d.ts +1 -1
- package/dist/ts4.2/room/track/PacketTrailerExtractor.d.ts +19 -0
- package/dist/ts4.2/room/track/RemoteVideoTrack.d.ts +16 -0
- package/dist/ts4.2/room/track/Track.d.ts +1 -1
- package/dist/ts4.2/room/track/options.d.ts +10 -0
- package/dist/ts4.2/room/utils.d.ts +4 -3
- package/dist/ts4.2/utils/dataPacketBuffer.d.ts +1 -1
- package/dist/ts4.2/version.d.ts +1 -1
- package/package.json +24 -16
- package/src/api/SignalClient.test.ts +102 -10
- package/src/api/SignalClient.ts +4 -2
- package/src/api/WebSocketStream.test.ts +0 -1
- package/src/e2ee/E2eeManager.ts +82 -30
- package/src/e2ee/types.ts +37 -8
- package/src/e2ee/utils.ts +7 -6
- package/src/e2ee/worker/DataCryptor.ts +6 -6
- package/src/e2ee/worker/FrameCryptor.test.ts +177 -4
- package/src/e2ee/worker/FrameCryptor.ts +94 -14
- package/src/e2ee/worker/ParticipantKeyHandler.test.ts +4 -4
- package/src/e2ee/worker/e2ee.worker.ts +13 -5
- package/src/e2ee/worker/naluUtils.ts +4 -4
- package/src/e2ee/worker/sifPayload.ts +10 -8
- package/src/index.ts +7 -0
- package/src/options.ts +8 -0
- package/src/packetTrailer/PacketTrailerManager.test.ts +172 -0
- package/src/packetTrailer/PacketTrailerManager.ts +250 -0
- package/src/packetTrailer/packetTrailer.test.ts +174 -0
- package/src/packetTrailer/packetTrailer.ts +276 -0
- package/src/packetTrailer/types.ts +75 -0
- package/src/packetTrailer/utils.test.ts +105 -0
- package/src/packetTrailer/utils.ts +50 -0
- package/src/packetTrailer/worker/packetTrailer.worker.ts +155 -0
- package/src/packetTrailer/worker/tsconfig.json +14 -0
- package/src/room/BackOffStrategy.test.ts +1 -1
- package/src/room/RTCEngine.test.ts +219 -0
- package/src/room/RTCEngine.ts +86 -20
- package/src/room/Room.test.ts +62 -1
- package/src/room/Room.ts +28 -5
- package/src/room/data-track/LocalDataTrack.ts +15 -7
- package/src/room/data-track/RemoteDataTrack.ts +8 -1
- package/src/room/data-track/depacketizer.test.ts +433 -1
- package/src/room/data-track/depacketizer.ts +79 -61
- package/src/room/data-track/frame.ts +2 -2
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +194 -0
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +21 -1
- package/src/room/data-track/incoming/pipeline.ts +13 -2
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +350 -198
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +9 -3
- package/src/room/data-track/outgoing/types.ts +4 -3
- package/src/room/data-track/packet/extensions.ts +2 -2
- package/src/room/data-track/packet/index.ts +6 -6
- package/src/room/data-track/packet/serializable.ts +1 -1
- package/src/room/data-track/types.ts +8 -0
- package/src/room/events.ts +2 -2
- package/src/room/participant/LocalParticipant.test.ts +81 -0
- package/src/room/participant/LocalParticipant.ts +48 -7
- package/src/room/participant/Participant.ts +1 -1
- package/src/room/participant/publishUtils.ts +1 -1
- package/src/room/track/PacketTrailerExtractor.ts +43 -0
- package/src/room/track/RemoteVideoTrack.ts +23 -2
- package/src/room/track/Track.ts +1 -1
- package/src/room/track/create.ts +0 -4
- package/src/room/track/options.ts +11 -0
- package/src/room/track/record.ts +1 -1
- package/src/room/track/utils.ts +4 -1
- package/src/room/utils.test.ts +14 -1
- package/src/room/utils.ts +17 -3
- package/src/test/MockMediaStreamTrack.ts +0 -1
- package/src/type-polyfills/non-shared-typed-arrays.d.ts +6 -0
- package/src/utils/dataPacketBuffer.ts +1 -1
- package/src/version.ts +1 -1
package/src/room/RTCEngine.ts
CHANGED
|
@@ -54,9 +54,14 @@ import {
|
|
|
54
54
|
toProtoSessionDescription,
|
|
55
55
|
} from '../api/SignalClient';
|
|
56
56
|
import type { BaseE2EEManager } from '../e2ee/E2eeManager';
|
|
57
|
-
import { asEncryptablePacket } from '../e2ee/utils';
|
|
57
|
+
import { asEncryptablePacket, isInsertableStreamSupported } from '../e2ee/utils';
|
|
58
58
|
import log, { LoggerNames, getLogger } from '../logger';
|
|
59
59
|
import type { InternalRoomOptions } from '../options';
|
|
60
|
+
import {
|
|
61
|
+
hasPacketTrailerPublishOptions,
|
|
62
|
+
isPacketTrailerSupported,
|
|
63
|
+
shouldUsePacketTrailerScriptTransform,
|
|
64
|
+
} from '../packetTrailer/utils';
|
|
60
65
|
import TypedPromise from '../utils/TypedPromise';
|
|
61
66
|
import { DataPacketBuffer } from '../utils/dataPacketBuffer';
|
|
62
67
|
import { TTLMap } from '../utils/ttlmap';
|
|
@@ -762,7 +767,14 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
762
767
|
): RTCConfiguration {
|
|
763
768
|
const rtcConfig = { ...this.rtcConfig };
|
|
764
769
|
|
|
765
|
-
|
|
770
|
+
// E2EE and packet trailer extraction both rely on encoded frame transforms.
|
|
771
|
+
// Only opt into the createEncodedStreams flavor when that path will be
|
|
772
|
+
// used; RTCRtpScriptTransform does not need the PeerConnection flag.
|
|
773
|
+
const needsInsertableStreams =
|
|
774
|
+
this.signalOpts?.e2eeEnabled ||
|
|
775
|
+
(this.options.packetTrailer?.worker && !shouldUsePacketTrailerScriptTransform());
|
|
776
|
+
|
|
777
|
+
if (needsInsertableStreams && isInsertableStreamSupported()) {
|
|
766
778
|
this.log.debug('E2EE - setting up transports with insertable streams');
|
|
767
779
|
// this makes sure that no data is sent before the transforms are ready
|
|
768
780
|
// @ts-ignore
|
|
@@ -940,8 +952,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
940
952
|
return;
|
|
941
953
|
}
|
|
942
954
|
const decryptedData = await this.e2eeManager?.handleEncryptedData(
|
|
943
|
-
dp.value.value.encryptedValue,
|
|
944
|
-
dp.value.value.iv,
|
|
955
|
+
dp.value.value.encryptedValue as NonSharedUint8Array,
|
|
956
|
+
dp.value.value.iv as NonSharedUint8Array,
|
|
945
957
|
dp.participantIdentity,
|
|
946
958
|
dp.value.value.keyIndex,
|
|
947
959
|
);
|
|
@@ -1004,16 +1016,17 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1004
1016
|
opts: TrackPublishOptions,
|
|
1005
1017
|
encodings?: RTCRtpEncodingParameters[],
|
|
1006
1018
|
) {
|
|
1019
|
+
let sender: RTCRtpSender;
|
|
1007
1020
|
if (supportsTransceiver()) {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
}
|
|
1011
|
-
if (supportsAddTrack()) {
|
|
1021
|
+
sender = await this.createTransceiverRTCRtpSender(track, opts, encodings);
|
|
1022
|
+
} else if (supportsAddTrack()) {
|
|
1012
1023
|
this.log.warn('using add-track fallback');
|
|
1013
|
-
|
|
1014
|
-
|
|
1024
|
+
sender = await this.createRTCRtpSender(track.mediaStreamTrack);
|
|
1025
|
+
} else {
|
|
1026
|
+
throw new UnexpectedConnectionState('Required webRTC APIs not supported on this device');
|
|
1015
1027
|
}
|
|
1016
|
-
|
|
1028
|
+
this.setupPacketTrailerSender(sender, opts);
|
|
1029
|
+
return sender;
|
|
1017
1030
|
}
|
|
1018
1031
|
|
|
1019
1032
|
async createSimulcastSender(
|
|
@@ -1022,16 +1035,67 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1022
1035
|
opts: TrackPublishOptions,
|
|
1023
1036
|
encodings?: RTCRtpEncodingParameters[],
|
|
1024
1037
|
) {
|
|
1025
|
-
|
|
1038
|
+
let sender: RTCRtpSender | undefined;
|
|
1026
1039
|
if (supportsTransceiver()) {
|
|
1027
|
-
|
|
1028
|
-
}
|
|
1029
|
-
if (supportsAddTrack()) {
|
|
1040
|
+
sender = await this.createSimulcastTransceiverSender(track, simulcastTrack, opts, encodings);
|
|
1041
|
+
} else if (supportsAddTrack()) {
|
|
1030
1042
|
this.log.debug('using add-track fallback');
|
|
1031
|
-
|
|
1043
|
+
sender = await this.createRTCRtpSender(track.mediaStreamTrack);
|
|
1044
|
+
} else {
|
|
1045
|
+
throw new UnexpectedConnectionState('Cannot stream on this device');
|
|
1032
1046
|
}
|
|
1047
|
+
if (sender) {
|
|
1048
|
+
this.setupPacketTrailerSender(sender, opts);
|
|
1049
|
+
}
|
|
1050
|
+
return sender;
|
|
1051
|
+
}
|
|
1033
1052
|
|
|
1034
|
-
|
|
1053
|
+
private setupPacketTrailerSender(sender: RTCRtpSender, opts: TrackPublishOptions = {}) {
|
|
1054
|
+
if (!this.options.packetTrailer?.worker || this.signalOpts?.e2eeEnabled) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const packetTrailer = opts.packetTrailer;
|
|
1059
|
+
const hasPacketTrailer = hasPacketTrailerPublishOptions(packetTrailer);
|
|
1060
|
+
|
|
1061
|
+
if (shouldUsePacketTrailerScriptTransform()) {
|
|
1062
|
+
if (hasPacketTrailer) {
|
|
1063
|
+
// @ts-ignore
|
|
1064
|
+
sender.transform = new RTCRtpScriptTransform(this.options.packetTrailer.worker, {
|
|
1065
|
+
kind: 'encode',
|
|
1066
|
+
packetTrailer,
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
if (
|
|
1073
|
+
!isPacketTrailerSupported(this.options.packetTrailer) ||
|
|
1074
|
+
!('createEncodedStreams' in sender)
|
|
1075
|
+
) {
|
|
1076
|
+
if (hasPacketTrailer) {
|
|
1077
|
+
this.log.warn('packet trailer transform not supported; skipping write', this.logContext);
|
|
1078
|
+
}
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// @ts-ignore
|
|
1083
|
+
const { readable, writable } = sender.createEncodedStreams();
|
|
1084
|
+
if (hasPacketTrailer) {
|
|
1085
|
+
this.options.packetTrailer.worker.postMessage(
|
|
1086
|
+
{
|
|
1087
|
+
kind: 'encode',
|
|
1088
|
+
data: {
|
|
1089
|
+
readableStream: readable,
|
|
1090
|
+
writableStream: writable,
|
|
1091
|
+
packetTrailer,
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
[readable, writable],
|
|
1095
|
+
);
|
|
1096
|
+
} else {
|
|
1097
|
+
readable.pipeTo(writable);
|
|
1098
|
+
}
|
|
1035
1099
|
}
|
|
1036
1100
|
|
|
1037
1101
|
private async createTransceiverRTCRtpSender(
|
|
@@ -1447,7 +1511,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1447
1511
|
if (this.e2eeManager && this.e2eeManager.isDataChannelEncryptionEnabled) {
|
|
1448
1512
|
const encryptablePacket = asEncryptablePacket(packet);
|
|
1449
1513
|
if (encryptablePacket) {
|
|
1450
|
-
const encryptedData = await this.e2eeManager.encryptData(
|
|
1514
|
+
const encryptedData = await this.e2eeManager.encryptData(
|
|
1515
|
+
encryptablePacket.toBinary() as NonSharedUint8Array,
|
|
1516
|
+
);
|
|
1451
1517
|
packet.value = {
|
|
1452
1518
|
case: 'encryptedPacket',
|
|
1453
1519
|
value: new EncryptedPacket({
|
|
@@ -1464,7 +1530,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1464
1530
|
this.reliableDataSequence += 1;
|
|
1465
1531
|
}
|
|
1466
1532
|
|
|
1467
|
-
const msg = packet.toBinary()
|
|
1533
|
+
const msg = packet.toBinary() as Uint8Array<ArrayBuffer>;
|
|
1468
1534
|
|
|
1469
1535
|
switch (kind) {
|
|
1470
1536
|
case DataChannelKind.LOSSY:
|
|
@@ -1491,7 +1557,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
|
1491
1557
|
|
|
1492
1558
|
/* @internal */
|
|
1493
1559
|
async sendLossyBytes(
|
|
1494
|
-
bytes:
|
|
1560
|
+
bytes: NonSharedUint8Array,
|
|
1495
1561
|
kind: Exclude<DataChannelKind, DataChannelKind.RELIABLE>,
|
|
1496
1562
|
bufferStatusLowBehavior: 'drop' | 'wait' = 'drop',
|
|
1497
1563
|
) {
|
package/src/room/Room.test.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientInfo_Capability, JoinResponse } from '@livekit/protocol';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
3
|
import Room from './Room';
|
|
4
|
+
import { roomConnectOptionDefaults, roomOptionDefaults } from './defaults';
|
|
3
5
|
import { RoomEvent } from './events';
|
|
4
6
|
|
|
5
7
|
describe('Active device switch', () => {
|
|
@@ -28,3 +30,62 @@ describe('Active device switch', () => {
|
|
|
28
30
|
expect(kind).toBe('audioinput');
|
|
29
31
|
});
|
|
30
32
|
});
|
|
33
|
+
|
|
34
|
+
describe('Room signaling options', () => {
|
|
35
|
+
it('advertises packet trailer capability when E2EE can handle trailers', async () => {
|
|
36
|
+
const room = new Room();
|
|
37
|
+
const join = vi.fn().mockResolvedValue({
|
|
38
|
+
joinResponse: new JoinResponse({
|
|
39
|
+
room: { name: 'test-room', sid: 'room-sid' },
|
|
40
|
+
participant: { sid: 'participant-sid', identity: 'test-user' },
|
|
41
|
+
}),
|
|
42
|
+
serverInfo: { version: '1.0.0' },
|
|
43
|
+
});
|
|
44
|
+
const engine = { join };
|
|
45
|
+
|
|
46
|
+
(
|
|
47
|
+
room as unknown as {
|
|
48
|
+
e2eeManager: unknown;
|
|
49
|
+
connectSignal: (
|
|
50
|
+
url: string,
|
|
51
|
+
token: string,
|
|
52
|
+
engine: unknown,
|
|
53
|
+
connectOptions: typeof roomConnectOptionDefaults,
|
|
54
|
+
roomOptions: typeof roomOptionDefaults,
|
|
55
|
+
abortController: AbortController,
|
|
56
|
+
) => Promise<JoinResponse>;
|
|
57
|
+
}
|
|
58
|
+
).e2eeManager = {};
|
|
59
|
+
|
|
60
|
+
await (
|
|
61
|
+
room as unknown as {
|
|
62
|
+
connectSignal: (
|
|
63
|
+
url: string,
|
|
64
|
+
token: string,
|
|
65
|
+
engine: unknown,
|
|
66
|
+
connectOptions: typeof roomConnectOptionDefaults,
|
|
67
|
+
roomOptions: typeof roomOptionDefaults,
|
|
68
|
+
abortController: AbortController,
|
|
69
|
+
) => Promise<JoinResponse>;
|
|
70
|
+
}
|
|
71
|
+
).connectSignal(
|
|
72
|
+
'wss://test.livekit.io',
|
|
73
|
+
'test-token',
|
|
74
|
+
engine,
|
|
75
|
+
roomConnectOptionDefaults,
|
|
76
|
+
roomOptionDefaults,
|
|
77
|
+
new AbortController(),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(join).toHaveBeenCalledWith(
|
|
81
|
+
'wss://test.livekit.io',
|
|
82
|
+
'test-token',
|
|
83
|
+
expect.objectContaining({
|
|
84
|
+
clientInfoCapabilities: [ClientInfo_Capability.CAP_PACKET_TRAILER],
|
|
85
|
+
e2eeEnabled: true,
|
|
86
|
+
}),
|
|
87
|
+
expect.any(AbortSignal),
|
|
88
|
+
false,
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
});
|
package/src/room/Room.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Mutex } from '@livekit/mutex';
|
|
2
2
|
import {
|
|
3
3
|
ChatMessage as ChatMessageModel,
|
|
4
|
+
ClientInfo_Capability,
|
|
4
5
|
ConnectionQualityUpdate,
|
|
5
6
|
type DataPacket,
|
|
6
7
|
DataPacket_Kind,
|
|
@@ -43,6 +44,8 @@ import type {
|
|
|
43
44
|
RoomConnectOptions,
|
|
44
45
|
RoomOptions,
|
|
45
46
|
} from '../options';
|
|
47
|
+
import { PacketTrailerManager } from '../packetTrailer/PacketTrailerManager';
|
|
48
|
+
import { isPacketTrailerSupported } from '../packetTrailer/utils';
|
|
46
49
|
import TypedPromise from '../utils/TypedPromise';
|
|
47
50
|
import { getBrowser } from '../utils/browserParser';
|
|
48
51
|
import { BackOffStrategy } from './BackOffStrategy';
|
|
@@ -187,6 +190,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
187
190
|
|
|
188
191
|
private e2eeManager: BaseE2EEManager | undefined;
|
|
189
192
|
|
|
193
|
+
private packetTrailerManager: PacketTrailerManager | undefined;
|
|
194
|
+
|
|
190
195
|
private e2eeStateMutex: Mutex = new Mutex();
|
|
191
196
|
|
|
192
197
|
private connectionReconcileInterval?: ReturnType<typeof setInterval>;
|
|
@@ -307,6 +312,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
307
312
|
this.outgoingDataTrackManager,
|
|
308
313
|
);
|
|
309
314
|
|
|
315
|
+
this.setupPacketTrailer();
|
|
316
|
+
|
|
310
317
|
if (this.options.e2ee || this.options.encryption) {
|
|
311
318
|
this.setupE2EE();
|
|
312
319
|
}
|
|
@@ -465,6 +472,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
465
472
|
}
|
|
466
473
|
}
|
|
467
474
|
|
|
475
|
+
private setupPacketTrailer() {
|
|
476
|
+
// The manager is always created so tracks that advertise packet trailer
|
|
477
|
+
// features can be wired up when the app passes a packet trailer worker.
|
|
478
|
+
this.packetTrailerManager = new PacketTrailerManager(this.options.packetTrailer);
|
|
479
|
+
this.packetTrailerManager.setup(this);
|
|
480
|
+
}
|
|
481
|
+
|
|
468
482
|
private get logContext() {
|
|
469
483
|
return {
|
|
470
484
|
room: this.name,
|
|
@@ -914,6 +928,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
914
928
|
autoSubscribe: connectOptions.autoSubscribe,
|
|
915
929
|
adaptiveStream:
|
|
916
930
|
typeof roomOptions.adaptiveStream === 'object' ? true : roomOptions.adaptiveStream,
|
|
931
|
+
clientInfoCapabilities:
|
|
932
|
+
isPacketTrailerSupported(roomOptions.packetTrailer) || !!this.e2eeManager
|
|
933
|
+
? [ClientInfo_Capability.CAP_PACKET_TRAILER]
|
|
934
|
+
: undefined,
|
|
917
935
|
maxRetries: connectOptions.maxRetries,
|
|
918
936
|
e2eeEnabled: !!this.e2eeManager,
|
|
919
937
|
websocketTimeout: connectOptions.websocketTimeout,
|
|
@@ -946,7 +964,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
946
964
|
|
|
947
965
|
if (this.e2eeManager) {
|
|
948
966
|
try {
|
|
949
|
-
this.e2eeManager.setSifTrailer(joinResponse.sifTrailer);
|
|
967
|
+
this.e2eeManager.setSifTrailer(joinResponse.sifTrailer as NonSharedUint8Array);
|
|
950
968
|
} catch (e: any) {
|
|
951
969
|
this.log.error(e instanceof Error ? e.message : 'Could not set SifTrailer', {
|
|
952
970
|
error: e,
|
|
@@ -1825,7 +1843,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
1825
1843
|
this.handleParticipantDisconnected(info.identity, remoteParticipant);
|
|
1826
1844
|
} else {
|
|
1827
1845
|
// create participant if doesn't exist
|
|
1828
|
-
|
|
1846
|
+
this.getOrCreateParticipant(info.identity, info);
|
|
1829
1847
|
}
|
|
1830
1848
|
}
|
|
1831
1849
|
|
|
@@ -2018,7 +2036,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2018
2036
|
) => {
|
|
2019
2037
|
this.emit(
|
|
2020
2038
|
RoomEvent.DataReceived,
|
|
2021
|
-
userPacket.payload,
|
|
2039
|
+
userPacket.payload as NonSharedUint8Array,
|
|
2022
2040
|
participant,
|
|
2023
2041
|
kind,
|
|
2024
2042
|
userPacket.topic,
|
|
@@ -2026,7 +2044,12 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2026
2044
|
);
|
|
2027
2045
|
|
|
2028
2046
|
// also emit on the participant
|
|
2029
|
-
participant?.emit(
|
|
2047
|
+
participant?.emit(
|
|
2048
|
+
ParticipantEvent.DataReceived,
|
|
2049
|
+
userPacket.payload as NonSharedUint8Array,
|
|
2050
|
+
kind,
|
|
2051
|
+
encryptionType,
|
|
2052
|
+
);
|
|
2030
2053
|
};
|
|
2031
2054
|
|
|
2032
2055
|
private handleSipDtmf = (participant: RemoteParticipant | undefined, dtmf: SipDTMF) => {
|
|
@@ -2874,7 +2897,7 @@ export type RoomEventCallbacks = {
|
|
|
2874
2897
|
activeSpeakersChanged: (speakers: Array<Participant>) => void;
|
|
2875
2898
|
roomMetadataChanged: (metadata: string) => void;
|
|
2876
2899
|
dataReceived: (
|
|
2877
|
-
payload:
|
|
2900
|
+
payload: NonSharedUint8Array,
|
|
2878
2901
|
participant?: RemoteParticipant,
|
|
2879
2902
|
kind?: DataPacket_Kind,
|
|
2880
2903
|
topic?: string,
|
|
@@ -4,7 +4,7 @@ import { type DataTrackFrame, DataTrackFrameInternal } from './frame';
|
|
|
4
4
|
import type { DataTrackHandle } from './handle';
|
|
5
5
|
import type OutgoingDataTrackManager from './outgoing/OutgoingDataTrackManager';
|
|
6
6
|
import { DataTrackPushFrameError } from './outgoing/errors';
|
|
7
|
-
import type { DataTrackOptions } from './outgoing/types';
|
|
7
|
+
import type { DataTrackOptions, EventPacketsFlushedChange } from './outgoing/types';
|
|
8
8
|
import {
|
|
9
9
|
DataTrackSymbol,
|
|
10
10
|
type IDataTrack,
|
|
@@ -32,6 +32,8 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
|
|
|
32
32
|
/** Resolves once the data track has sent all pending packets the rtc data channel buffer. */
|
|
33
33
|
protected flushedFuture = new Future<void, never>();
|
|
34
34
|
|
|
35
|
+
protected isFlushed = true;
|
|
36
|
+
|
|
35
37
|
/** @internal */
|
|
36
38
|
constructor(options: DataTrackOptions, manager: OutgoingDataTrackManager) {
|
|
37
39
|
this.options = options;
|
|
@@ -39,7 +41,7 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
|
|
|
39
41
|
|
|
40
42
|
this.log = getLogger(LoggerNames.DataTracks);
|
|
41
43
|
|
|
42
|
-
this.manager.on('
|
|
44
|
+
this.manager.on('packetsFlushedChange', this.handleManagerPacketsFlushedChange);
|
|
43
45
|
this.manager.on('reset', this.handleManagerReset);
|
|
44
46
|
}
|
|
45
47
|
|
|
@@ -47,15 +49,18 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
|
|
|
47
49
|
// When the associated manager resets, mark any in flight flushes as complete
|
|
48
50
|
// There's nothing actionable a user can do to get these to complete so no
|
|
49
51
|
// error is being thrown.
|
|
50
|
-
this.
|
|
52
|
+
this.flushedFuture.resolve?.();
|
|
51
53
|
|
|
52
|
-
this.manager.off('
|
|
54
|
+
this.manager.off('packetsFlushedChange', this.handleManagerPacketsFlushedChange);
|
|
53
55
|
this.manager.off('reset', this.handleManagerReset);
|
|
54
56
|
};
|
|
55
57
|
|
|
56
|
-
private
|
|
57
|
-
this.
|
|
58
|
-
|
|
58
|
+
private handleManagerPacketsFlushedChange = (event: EventPacketsFlushedChange) => {
|
|
59
|
+
this.isFlushed = event.isFlushed;
|
|
60
|
+
if (event.isFlushed) {
|
|
61
|
+
this.flushedFuture.resolve?.();
|
|
62
|
+
this.flushedFuture = new Future();
|
|
63
|
+
}
|
|
59
64
|
};
|
|
60
65
|
|
|
61
66
|
/** @internal */
|
|
@@ -152,6 +157,9 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
|
|
|
152
157
|
* ```
|
|
153
158
|
**/
|
|
154
159
|
async flush(): Promise<void> {
|
|
160
|
+
if (this.isFlushed) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
155
163
|
return this.flushedFuture.promise;
|
|
156
164
|
}
|
|
157
165
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type IRemoteTrack,
|
|
8
8
|
TrackSymbol,
|
|
9
9
|
} from './track-interfaces';
|
|
10
|
-
import { type DataTrackInfo } from './types';
|
|
10
|
+
import { type DataTrackInfo, type RemoteDataTrackPipelineOptions } from './types';
|
|
11
11
|
|
|
12
12
|
type RemoteDataTrackOptions = {
|
|
13
13
|
publisherIdentity: Participant['identity'];
|
|
@@ -80,4 +80,11 @@ export default class RemoteDataTrack implements IRemoteTrack, IDataTrack {
|
|
|
80
80
|
throw err;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
/** Configure how incoming frames for this track are processed before they are handed out to
|
|
85
|
+
* subscribers (the "pipeline"). These options apply to all current and future subscriptions
|
|
86
|
+
* of this track, and may be set at any time. */
|
|
87
|
+
setPipelineOptions(options: RemoteDataTrackPipelineOptions): void {
|
|
88
|
+
this.manager.setPipelineOptions(this.info.sid, options);
|
|
89
|
+
}
|
|
83
90
|
}
|