livekit-client 2.15.6 → 2.15.8
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 +1892 -153
- 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/connectionHelper/checks/publishVideo.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 +4 -2
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +5 -2
- 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/errors.d.ts +2 -1
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -3
- 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 +2 -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/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} +4 -2
- package/dist/ts4.2/{src/room → room}/RTCEngine.d.ts +5 -2
- 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/LocalParticipant.d.ts +1 -3
- 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 +2 -1
- package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -0
- package/package.json +14 -12
- package/src/connectionHelper/checks/publishVideo.ts +5 -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 +14 -1
- package/src/logger.ts +1 -0
- package/src/options.ts +8 -2
- package/src/room/PCTransport.ts +14 -5
- package/src/room/RTCEngine.ts +55 -6
- package/src/room/Room.ts +39 -17
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +64 -17
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -0
- package/src/room/errors.ts +3 -0
- package/src/room/participant/LocalParticipant.ts +17 -29
- 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.ts +35 -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 +8 -4
- package/src/utils/camelToSnakeCase.ts +16 -0
- /package/dist/ts4.2/{src/api → api}/SignalClient.d.ts +0 -0
- /package/dist/ts4.2/{src/api → api}/utils.d.ts +0 -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}/PCTransport.d.ts +0 -0
- /package/dist/ts4.2/{src/room → room}/PCTransportManager.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/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/PCTransport.ts
CHANGED
@@ -165,10 +165,11 @@ export default class PCTransport extends EventEmitter {
|
|
165
165
|
} else if (sd.type === 'answer') {
|
166
166
|
const sdpParsed = parse(sd.sdp ?? '');
|
167
167
|
sdpParsed.media.forEach((media) => {
|
168
|
+
const mid = getMidString(media.mid!);
|
168
169
|
if (media.type === 'audio') {
|
169
170
|
// mung sdp for opus bitrate settings
|
170
171
|
this.trackBitrates.some((trackbr): boolean => {
|
171
|
-
if (!trackbr.transceiver ||
|
172
|
+
if (!trackbr.transceiver || mid != trackbr.transceiver.mid) {
|
172
173
|
return false;
|
173
174
|
}
|
174
175
|
|
@@ -593,6 +594,9 @@ function ensureAudioNackAndStereo(
|
|
593
594
|
stereoMids: string[],
|
594
595
|
nackMids: string[],
|
595
596
|
) {
|
597
|
+
// sdp-transform types don't include number however the parser outputs mids as numbers in some cases
|
598
|
+
const mid = getMidString(media.mid!);
|
599
|
+
|
596
600
|
// found opus codec to add nack fb
|
597
601
|
let opusPayload = 0;
|
598
602
|
media.rtp.some((rtp): boolean => {
|
@@ -610,7 +614,7 @@ function ensureAudioNackAndStereo(
|
|
610
614
|
}
|
611
615
|
|
612
616
|
if (
|
613
|
-
nackMids.includes(
|
617
|
+
nackMids.includes(mid) &&
|
614
618
|
!media.rtcpFb.some((fb) => fb.payload === opusPayload && fb.type === 'nack')
|
615
619
|
) {
|
616
620
|
media.rtcpFb.push({
|
@@ -619,7 +623,7 @@ function ensureAudioNackAndStereo(
|
|
619
623
|
});
|
620
624
|
}
|
621
625
|
|
622
|
-
if (stereoMids.includes(
|
626
|
+
if (stereoMids.includes(mid)) {
|
623
627
|
media.fmtp.some((fmtp): boolean => {
|
624
628
|
if (fmtp.payload === opusPayload) {
|
625
629
|
if (!fmtp.config.includes('stereo=1')) {
|
@@ -642,6 +646,7 @@ function extractStereoAndNackAudioFromOffer(offer: RTCSessionDescriptionInit): {
|
|
642
646
|
const sdpParsed = parse(offer.sdp ?? '');
|
643
647
|
let opusPayload = 0;
|
644
648
|
sdpParsed.media.forEach((media) => {
|
649
|
+
const mid = getMidString(media.mid!);
|
645
650
|
if (media.type === 'audio') {
|
646
651
|
media.rtp.some((rtp): boolean => {
|
647
652
|
if (rtp.codec === 'opus') {
|
@@ -652,13 +657,13 @@ function extractStereoAndNackAudioFromOffer(offer: RTCSessionDescriptionInit): {
|
|
652
657
|
});
|
653
658
|
|
654
659
|
if (media.rtcpFb?.some((fb) => fb.payload === opusPayload && fb.type === 'nack')) {
|
655
|
-
nackMids.push(
|
660
|
+
nackMids.push(mid);
|
656
661
|
}
|
657
662
|
|
658
663
|
media.fmtp.some((fmtp): boolean => {
|
659
664
|
if (fmtp.payload === opusPayload) {
|
660
665
|
if (fmtp.config.includes('sprop-stereo=1')) {
|
661
|
-
stereoMids.push(
|
666
|
+
stereoMids.push(mid);
|
662
667
|
}
|
663
668
|
return true;
|
664
669
|
}
|
@@ -682,3 +687,7 @@ function ensureIPAddrMatchVersion(media: MediaDescription) {
|
|
682
687
|
}
|
683
688
|
}
|
684
689
|
}
|
690
|
+
|
691
|
+
function getMidString(mid: string | number) {
|
692
|
+
return typeof mid === 'number' ? mid.toFixed(0) : mid;
|
693
|
+
}
|
package/src/room/RTCEngine.ts
CHANGED
@@ -9,6 +9,9 @@ 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,
|
@@ -43,6 +46,8 @@ import {
|
|
43
46
|
SignalConnectionState,
|
44
47
|
toProtoSessionDescription,
|
45
48
|
} from '../api/SignalClient';
|
49
|
+
import type { BaseE2EEManager } from '../e2ee/E2eeManager';
|
50
|
+
import { asEncryptablePacket } from '../e2ee/utils';
|
46
51
|
import log, { LoggerNames, getLogger } from '../logger';
|
47
52
|
import type { InternalRoomOptions } from '../options';
|
48
53
|
import { DataPacketBuffer } from '../utils/dataPacketBuffer';
|
@@ -116,6 +121,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
116
121
|
*/
|
117
122
|
latestRemoteOfferId: number = 0;
|
118
123
|
|
124
|
+
/** @internal */
|
125
|
+
e2eeManager: BaseE2EEManager | undefined;
|
126
|
+
|
119
127
|
get isClosed() {
|
120
128
|
return this._isClosed;
|
121
129
|
}
|
@@ -203,7 +211,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
203
211
|
this.client = new SignalClient(undefined, this.loggerOptions);
|
204
212
|
this.client.signalLatency = this.options.expSignalLatency;
|
205
213
|
this.reconnectPolicy = this.options.reconnectPolicy;
|
206
|
-
this.registerOnLineListener();
|
207
214
|
this.closingLock = new Mutex();
|
208
215
|
this.dataProcessLock = new Mutex();
|
209
216
|
this.dcBufferStatus = new Map([
|
@@ -260,9 +267,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
260
267
|
|
261
268
|
// create offer
|
262
269
|
if (!this.subscriberPrimary || joinResponse.fastPublish) {
|
263
|
-
this.negotiate()
|
270
|
+
this.negotiate().catch((err) => {
|
271
|
+
log.error(err, this.logContext);
|
272
|
+
});
|
264
273
|
}
|
265
274
|
|
275
|
+
this.registerOnLineListener();
|
266
276
|
this.clientConfiguration = joinResponse.clientConfiguration;
|
267
277
|
this.emit(EngineEvent.SignalConnected, joinResponse);
|
268
278
|
return joinResponse;
|
@@ -445,7 +455,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
445
455
|
}
|
446
456
|
} else if (connectionState === PCTransportState.FAILED) {
|
447
457
|
// on Safari, PeerConnection will switch to 'disconnected' during renegotiation
|
448
|
-
if (this.pcState === PCState.Connected) {
|
458
|
+
if (this.pcState === PCState.Connected || this.pcState === PCState.Reconnecting) {
|
449
459
|
this.pcState = PCState.Disconnected;
|
450
460
|
|
451
461
|
this.handleDisconnect(
|
@@ -710,12 +720,32 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
710
720
|
if (dp.value?.case === 'speaker') {
|
711
721
|
// dispatch speaker updates
|
712
722
|
this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.value.speakers);
|
723
|
+
} else if (dp.value?.case === 'encryptedPacket') {
|
724
|
+
if (!this.e2eeManager) {
|
725
|
+
this.log.error('Received encrypted packet but E2EE not set up', this.logContext);
|
726
|
+
return;
|
727
|
+
}
|
728
|
+
const decryptedData = await this.e2eeManager?.handleEncryptedData(
|
729
|
+
dp.value.value.encryptedValue,
|
730
|
+
dp.value.value.iv,
|
731
|
+
dp.participantIdentity,
|
732
|
+
dp.value.value.keyIndex,
|
733
|
+
);
|
734
|
+
const decryptedPacket = EncryptedPacketPayload.fromBinary(decryptedData.payload);
|
735
|
+
const newDp = new DataPacket({
|
736
|
+
value: decryptedPacket.value,
|
737
|
+
});
|
738
|
+
if (newDp.value?.case === 'user') {
|
739
|
+
// compatibility
|
740
|
+
applyUserDataCompat(newDp, newDp.value.value);
|
741
|
+
}
|
742
|
+
this.emit(EngineEvent.DataPacketReceived, newDp, dp.value.value.encryptionType);
|
713
743
|
} else {
|
714
744
|
if (dp.value?.case === 'user') {
|
715
745
|
// compatibility
|
716
746
|
applyUserDataCompat(dp, dp.value.value);
|
717
747
|
}
|
718
|
-
this.emit(EngineEvent.DataPacketReceived, dp);
|
748
|
+
this.emit(EngineEvent.DataPacketReceived, dp, Encryption_Type.NONE);
|
719
749
|
}
|
720
750
|
} finally {
|
721
751
|
unlock();
|
@@ -1191,11 +1221,28 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1191
1221
|
// make sure we do have a data connection
|
1192
1222
|
await this.ensurePublisherConnected(kind);
|
1193
1223
|
|
1224
|
+
if (this.e2eeManager && this.e2eeManager.isDataChannelEncryptionEnabled) {
|
1225
|
+
const encryptablePacket = asEncryptablePacket(packet);
|
1226
|
+
if (encryptablePacket) {
|
1227
|
+
const encryptedData = await this.e2eeManager.encryptData(encryptablePacket.toBinary());
|
1228
|
+
packet.value = {
|
1229
|
+
case: 'encryptedPacket',
|
1230
|
+
value: new EncryptedPacket({
|
1231
|
+
encryptedValue: encryptedData.payload,
|
1232
|
+
iv: encryptedData.iv,
|
1233
|
+
keyIndex: encryptedData.keyIndex,
|
1234
|
+
}),
|
1235
|
+
};
|
1236
|
+
}
|
1237
|
+
}
|
1238
|
+
|
1194
1239
|
if (kind === DataPacket_Kind.RELIABLE) {
|
1195
1240
|
packet.sequence = this.reliableDataSequence;
|
1196
1241
|
this.reliableDataSequence += 1;
|
1197
1242
|
}
|
1243
|
+
|
1198
1244
|
const msg = packet.toBinary();
|
1245
|
+
|
1199
1246
|
const dc = this.dataChannelForKind(kind);
|
1200
1247
|
if (dc) {
|
1201
1248
|
if (kind === DataPacket_Kind.RELIABLE) {
|
@@ -1293,7 +1340,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1293
1340
|
}
|
1294
1341
|
if (needNegotiation) {
|
1295
1342
|
// start negotiation
|
1296
|
-
this.negotiate()
|
1343
|
+
this.negotiate().catch((err) => {
|
1344
|
+
log.error(err, this.logContext);
|
1345
|
+
});
|
1297
1346
|
}
|
1298
1347
|
|
1299
1348
|
const targetChannel = this.dataChannelForKind(kind, subscriber);
|
@@ -1558,7 +1607,7 @@ export type EngineEventCallbacks = {
|
|
1558
1607
|
receiver: RTCRtpReceiver,
|
1559
1608
|
) => void;
|
1560
1609
|
activeSpeakersUpdate: (speakers: Array<SpeakerInfo>) => void;
|
1561
|
-
dataPacketReceived: (packet: DataPacket) => void;
|
1610
|
+
dataPacketReceived: (packet: DataPacket, encryptionType: Encryption_Type) => void;
|
1562
1611
|
transcriptionReceived: (transcription: Transcription) => void;
|
1563
1612
|
transportsCreated: (publisher: PCTransport, subscriber: PCTransport) => void;
|
1564
1613
|
/** @internal */
|
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,16 @@ 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;
|
361
373
|
} else {
|
362
|
-
this.e2eeManager = new E2EEManager(
|
374
|
+
this.e2eeManager = new E2EEManager(e2eeOptions, dcEncryptionEnabled);
|
363
375
|
}
|
364
376
|
this.e2eeManager.on(
|
365
377
|
EncryptionEvent.ParticipantEncryptionStatusChanged,
|
@@ -443,6 +455,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
443
455
|
}
|
444
456
|
|
445
457
|
this.engine = new RTCEngine(this.options);
|
458
|
+
this.engine.e2eeManager = this.e2eeManager;
|
446
459
|
|
447
460
|
this.engine
|
448
461
|
.on(EngineEvent.ParticipantUpdate, this.handleParticipantUpdates)
|
@@ -789,7 +802,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
789
802
|
this.localParticipant.identity = pi.identity;
|
790
803
|
this.localParticipant.setEnabledPublishCodecs(joinResponse.enabledPublishCodecs);
|
791
804
|
|
792
|
-
if (this.
|
805
|
+
if (this.e2eeManager) {
|
793
806
|
try {
|
794
807
|
this.e2eeManager.setSifTrailer(joinResponse.sifTrailer);
|
795
808
|
} catch (e: any) {
|
@@ -970,7 +983,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
970
983
|
* @internal for testing
|
971
984
|
*/
|
972
985
|
async simulateScenario(scenario: SimulationScenario, arg?: any) {
|
973
|
-
let postAction = () => {};
|
986
|
+
let postAction = async () => {};
|
974
987
|
let req: SimulateScenario | undefined;
|
975
988
|
switch (scenario) {
|
976
989
|
case 'signal-reconnect':
|
@@ -1711,11 +1724,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1711
1724
|
pub.setSubscriptionError(update.err);
|
1712
1725
|
};
|
1713
1726
|
|
1714
|
-
private handleDataPacket = (packet: DataPacket) => {
|
1727
|
+
private handleDataPacket = (packet: DataPacket, encryptionType: Encryption_Type) => {
|
1715
1728
|
// find the participant
|
1716
1729
|
const participant = this.remoteParticipants.get(packet.participantIdentity);
|
1717
1730
|
if (packet.value.case === 'user') {
|
1718
|
-
this.handleUserPacket(participant, packet.value.value, packet.kind);
|
1731
|
+
this.handleUserPacket(participant, packet.value.value, packet.kind, encryptionType);
|
1719
1732
|
} else if (packet.value.case === 'transcription') {
|
1720
1733
|
this.handleTranscription(participant, packet.value.value);
|
1721
1734
|
} else if (packet.value.case === 'sipDtmf') {
|
@@ -1729,7 +1742,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1729
1742
|
packet.value.case === 'streamChunk' ||
|
1730
1743
|
packet.value.case === 'streamTrailer'
|
1731
1744
|
) {
|
1732
|
-
this.handleDataStream(packet);
|
1745
|
+
this.handleDataStream(packet, encryptionType);
|
1733
1746
|
} else if (packet.value.case === 'rpcRequest') {
|
1734
1747
|
const rpc = packet.value.value;
|
1735
1748
|
this.handleIncomingRpcRequest(
|
@@ -1747,11 +1760,19 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1747
1760
|
participant: RemoteParticipant | undefined,
|
1748
1761
|
userPacket: UserPacket,
|
1749
1762
|
kind: DataPacket_Kind,
|
1763
|
+
encryptionType: Encryption_Type,
|
1750
1764
|
) => {
|
1751
|
-
this.emit(
|
1765
|
+
this.emit(
|
1766
|
+
RoomEvent.DataReceived,
|
1767
|
+
userPacket.payload,
|
1768
|
+
participant,
|
1769
|
+
kind,
|
1770
|
+
userPacket.topic,
|
1771
|
+
encryptionType,
|
1772
|
+
);
|
1752
1773
|
|
1753
1774
|
// also emit on the participant
|
1754
|
-
participant?.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
|
1775
|
+
participant?.emit(ParticipantEvent.DataReceived, userPacket.payload, kind, encryptionType);
|
1755
1776
|
};
|
1756
1777
|
|
1757
1778
|
private handleSipDtmf = (participant: RemoteParticipant | undefined, dtmf: SipDTMF) => {
|
@@ -1791,8 +1812,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1791
1812
|
this.emit(RoomEvent.MetricsReceived, metrics, participant);
|
1792
1813
|
};
|
1793
1814
|
|
1794
|
-
private handleDataStream = (packet: DataPacket) => {
|
1795
|
-
this.incomingDataStreamManager.handleDataStreamPacket(packet);
|
1815
|
+
private handleDataStream = (packet: DataPacket, encryptionType: Encryption_Type) => {
|
1816
|
+
this.incomingDataStreamManager.handleDataStreamPacket(packet, encryptionType);
|
1796
1817
|
};
|
1797
1818
|
|
1798
1819
|
private async handleIncomingRpcRequest(
|
@@ -2596,6 +2617,7 @@ export type RoomEventCallbacks = {
|
|
2596
2617
|
participant?: RemoteParticipant,
|
2597
2618
|
kind?: DataPacket_Kind,
|
2598
2619
|
topic?: string,
|
2620
|
+
encryptionType?: Encryption_Type,
|
2599
2621
|
) => void;
|
2600
2622
|
sipDTMFReceived: (dtmf: SipDTMF, participant?: RemoteParticipant) => void;
|
2601
2623
|
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/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 {
|
@@ -9,7 +9,6 @@ import {
|
|
9
9
|
Encryption_Type,
|
10
10
|
JoinResponse,
|
11
11
|
ParticipantInfo,
|
12
|
-
ParticipantPermission,
|
13
12
|
RequestResponse,
|
14
13
|
RequestResponse_Reason,
|
15
14
|
RpcAck,
|
@@ -453,16 +452,6 @@ export default class LocalParticipant extends Participant {
|
|
453
452
|
return this.setTrackEnabled(Track.Source.ScreenShare, enabled, options, publishOptions);
|
454
453
|
}
|
455
454
|
|
456
|
-
/** @internal */
|
457
|
-
setPermissions(permissions: ParticipantPermission): boolean {
|
458
|
-
const prevPermissions = this.permissions;
|
459
|
-
const changed = super.setPermissions(permissions);
|
460
|
-
if (changed && prevPermissions) {
|
461
|
-
this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
|
462
|
-
}
|
463
|
-
return changed;
|
464
|
-
}
|
465
|
-
|
466
455
|
/** @internal */
|
467
456
|
async setE2EEEnabled(enabled: boolean) {
|
468
457
|
this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
|
@@ -841,19 +830,20 @@ export default class LocalParticipant extends Participant {
|
|
841
830
|
return existingPublication;
|
842
831
|
}
|
843
832
|
|
833
|
+
const opts: TrackPublishOptions = {
|
834
|
+
...this.roomOptions.publishDefaults,
|
835
|
+
...options,
|
836
|
+
};
|
844
837
|
const isStereoInput =
|
845
838
|
('channelCount' in track.mediaStreamTrack.getSettings() &&
|
846
839
|
// @ts-ignore `channelCount` on getSettings() is currently only available for Safari, but is generally the best way to determine a stereo track https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings/channelCount
|
847
840
|
track.mediaStreamTrack.getSettings().channelCount === 2) ||
|
848
841
|
track.mediaStreamTrack.getConstraints().channelCount === 2;
|
849
|
-
const isStereo =
|
842
|
+
const isStereo = opts.forceStereo ?? isStereoInput;
|
850
843
|
|
851
844
|
// disable dtx for stereo track if not enabled explicitly
|
852
845
|
if (isStereo) {
|
853
|
-
if (
|
854
|
-
options = {};
|
855
|
-
}
|
856
|
-
if (options.dtx === undefined) {
|
846
|
+
if (opts.dtx === undefined) {
|
857
847
|
this.log.info(
|
858
848
|
`Opus DTX will be disabled for stereo tracks by default. Enable them explicitly to make it work.`,
|
859
849
|
{
|
@@ -862,18 +852,14 @@ export default class LocalParticipant extends Participant {
|
|
862
852
|
},
|
863
853
|
);
|
864
854
|
}
|
865
|
-
if (
|
855
|
+
if (opts.red === undefined) {
|
866
856
|
this.log.info(
|
867
857
|
`Opus RED will be disabled for stereo tracks by default. Enable them explicitly to make it work.`,
|
868
858
|
);
|
869
859
|
}
|
870
|
-
|
871
|
-
|
860
|
+
opts.dtx ??= false;
|
861
|
+
opts.red ??= false;
|
872
862
|
}
|
873
|
-
const opts: TrackPublishOptions = {
|
874
|
-
...this.roomOptions.publishDefaults,
|
875
|
-
...options,
|
876
|
-
};
|
877
863
|
|
878
864
|
if (!isE2EESimulcastSupported() && this.roomOptions.e2ee) {
|
879
865
|
this.log.info(
|
@@ -1648,16 +1634,18 @@ export default class LocalParticipant extends Participant {
|
|
1648
1634
|
const destinationIdentities = options.destinationIdentities;
|
1649
1635
|
const topic = options.topic;
|
1650
1636
|
|
1637
|
+
let userPacket = new UserPacket({
|
1638
|
+
participantIdentity: this.identity,
|
1639
|
+
payload: data,
|
1640
|
+
destinationIdentities,
|
1641
|
+
topic,
|
1642
|
+
});
|
1643
|
+
|
1651
1644
|
const packet = new DataPacket({
|
1652
1645
|
kind: kind,
|
1653
1646
|
value: {
|
1654
1647
|
case: 'user',
|
1655
|
-
value:
|
1656
|
-
participantIdentity: this.identity,
|
1657
|
-
payload: data,
|
1658
|
-
destinationIdentities,
|
1659
|
-
topic,
|
1660
|
-
}),
|
1648
|
+
value: userPacket,
|
1661
1649
|
},
|
1662
1650
|
});
|
1663
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[],
|