livekit-client 1.12.1 → 1.12.3
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/README.md +7 -3
- 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 +389 -301
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +11279 -13498
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +2 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +3 -2
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +4 -2
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/KeyProvider.d.ts +4 -2
- package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts +1 -0
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +1 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +4 -2
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +14 -3
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/proto/livekit_models_pb.d.ts +1264 -0
- package/dist/src/proto/livekit_models_pb.d.ts.map +1 -0
- package/dist/src/proto/livekit_rtc_pb.d.ts +1373 -0
- package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -0
- package/dist/src/room/PCTransport.d.ts +2 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +9 -5
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts +4 -1
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +15 -11
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +2 -2
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +5 -3
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
- package/dist/src/room/participant/ParticipantTrackPermission.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +2 -3
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/timers.d.ts +5 -4
- package/dist/src/room/timers.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +3 -0
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +2 -2
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +6 -4
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +7 -5
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +8 -0
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts +5 -1
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +3 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/mocks.d.ts +4 -3
- package/dist/src/test/mocks.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +2 -2
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +3 -2
- package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/constants.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/types.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +14 -3
- package/dist/ts4.2/src/index.d.ts +1 -1
- package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +1264 -0
- package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +1373 -0
- package/dist/ts4.2/src/room/PCTransport.d.ts +2 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +9 -5
- package/dist/ts4.2/src/room/RegionUrlProvider.d.ts +4 -1
- package/dist/ts4.2/src/room/Room.d.ts +15 -11
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +2 -2
- package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -3
- package/dist/ts4.2/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +2 -3
- package/dist/ts4.2/src/room/participant/publishUtils.d.ts +1 -1
- package/dist/ts4.2/src/room/timers.d.ts +5 -4
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -0
- package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +2 -2
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/Track.d.ts +6 -4
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +7 -5
- package/dist/ts4.2/src/room/track/options.d.ts +8 -0
- package/dist/ts4.2/src/room/track/utils.d.ts +5 -1
- package/dist/ts4.2/src/room/utils.d.ts +3 -1
- package/dist/ts4.2/src/test/mocks.d.ts +4 -3
- package/package.json +10 -10
- package/src/api/SignalClient.ts +104 -101
- package/src/connectionHelper/ConnectionCheck.ts +3 -2
- package/src/connectionHelper/checks/Checker.ts +3 -3
- package/src/connectionHelper/checks/webrtc.ts +66 -2
- package/src/connectionHelper/checks/websocket.ts +4 -0
- package/src/e2ee/E2eeManager.ts +4 -3
- package/src/e2ee/KeyProvider.ts +3 -2
- package/src/e2ee/constants.ts +4 -0
- package/src/e2ee/types.ts +1 -0
- package/src/e2ee/worker/FrameCryptor.test.ts +1 -3
- package/src/e2ee/worker/FrameCryptor.ts +5 -5
- package/src/e2ee/worker/ParticipantKeyHandler.ts +37 -6
- package/src/e2ee/worker/e2ee.worker.ts +1 -1
- package/src/index.ts +1 -1
- package/src/proto/livekit_models_pb.ts +2096 -0
- package/src/proto/livekit_rtc_pb.ts +2332 -0
- package/src/room/PCTransport.ts +1 -1
- package/src/room/RTCEngine.ts +28 -22
- package/src/room/RegionUrlProvider.ts +11 -2
- package/src/room/Room.test.ts +1 -0
- package/src/room/Room.ts +158 -79
- package/src/room/participant/LocalParticipant.ts +43 -59
- package/src/room/participant/Participant.ts +6 -4
- package/src/room/participant/ParticipantTrackPermission.ts +3 -3
- package/src/room/participant/RemoteParticipant.ts +5 -6
- package/src/room/participant/publishUtils.test.ts +1 -0
- package/src/room/participant/publishUtils.ts +4 -2
- package/src/room/track/LocalTrack.ts +24 -9
- package/src/room/track/LocalTrackPublication.ts +1 -1
- package/src/room/track/LocalVideoTrack.test.ts +2 -1
- package/src/room/track/LocalVideoTrack.ts +28 -26
- package/src/room/track/RemoteTrackPublication.ts +12 -7
- package/src/room/track/RemoteVideoTrack.test.ts +5 -4
- package/src/room/track/Track.ts +9 -6
- package/src/room/track/TrackPublication.ts +7 -5
- package/src/room/track/create.ts +9 -17
- package/src/room/track/facingMode.test.ts +1 -0
- package/src/room/track/options.ts +23 -16
- package/src/room/track/utils.test.ts +1 -0
- package/src/room/track/utils.ts +44 -2
- package/src/room/utils.test.ts +16 -0
- package/src/room/utils.ts +20 -4
- package/src/test/mocks.ts +7 -5
- package/src/utils/AsyncQueue.test.ts +1 -0
- package/src/utils/browserParser.test.ts +33 -3
- package/src/utils/browserParser.ts +1 -1
- package/dist/src/proto/google/protobuf/timestamp.d.ts +0 -146
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +0 -1
- package/dist/src/proto/livekit_models.d.ts +0 -2399
- package/dist/src/proto/livekit_models.d.ts.map +0 -1
- package/dist/src/proto/livekit_rtc.d.ts +0 -14352
- package/dist/src/proto/livekit_rtc.d.ts.map +0 -1
- package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +0 -150
- package/dist/ts4.2/src/proto/livekit_models.d.ts +0 -2659
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +0 -15764
- package/src/proto/google/protobuf/timestamp.ts +0 -230
- package/src/proto/livekit_models.ts +0 -4006
- package/src/proto/livekit_rtc.ts +0 -4672
@@ -6,15 +6,17 @@ import {
|
|
6
6
|
Encryption_Type,
|
7
7
|
ParticipantInfo,
|
8
8
|
ParticipantPermission,
|
9
|
-
|
9
|
+
UserPacket,
|
10
|
+
} from '../../proto/livekit_models_pb';
|
10
11
|
import {
|
11
12
|
AddTrackRequest,
|
12
13
|
DataChannelInfo,
|
13
14
|
SignalTarget,
|
15
|
+
SimulcastCodec,
|
14
16
|
SubscribedQualityUpdate,
|
15
17
|
TrackPublishedResponse,
|
16
18
|
TrackUnpublishedResponse,
|
17
|
-
} from '../../proto/
|
19
|
+
} from '../../proto/livekit_rtc_pb';
|
18
20
|
import type RTCEngine from '../RTCEngine';
|
19
21
|
import { DeviceUnsupportedError, TrackInvalidError, UnexpectedConnectionState } from '../errors';
|
20
22
|
import { EngineEvent, ParticipantEvent, TrackEvent } from '../events';
|
@@ -32,7 +34,11 @@ import type {
|
|
32
34
|
VideoCaptureOptions,
|
33
35
|
} from '../track/options';
|
34
36
|
import { ScreenSharePresets, VideoPresets, isBackupCodec, isCodecEqual } from '../track/options';
|
35
|
-
import {
|
37
|
+
import {
|
38
|
+
constraintsForOptions,
|
39
|
+
mergeDefaultOptions,
|
40
|
+
screenCaptureToDisplayMediaStreamOptions,
|
41
|
+
} from '../track/utils';
|
36
42
|
import type { DataPublishOptions } from '../types';
|
37
43
|
import { Future, isFireFox, isSVCCodec, isSafari, isWeb, supportsAV1, supportsVP9 } from '../utils';
|
38
44
|
import Participant from './Participant';
|
@@ -311,6 +317,9 @@ export default class LocalParticipant extends Participant {
|
|
311
317
|
// revisit if we want to return an array of tracks instead for v2
|
312
318
|
[track] = publishedTracks;
|
313
319
|
} catch (e) {
|
320
|
+
localTracks?.forEach((tr) => {
|
321
|
+
tr.stop();
|
322
|
+
});
|
314
323
|
if (e instanceof Error && !(e instanceof TrackInvalidError)) {
|
315
324
|
this.emit(ParticipantEvent.MediaDevicesError, e);
|
316
325
|
}
|
@@ -433,36 +442,12 @@ export default class LocalParticipant extends Participant {
|
|
433
442
|
options.resolution = ScreenSharePresets.h1080fps15.resolution;
|
434
443
|
}
|
435
444
|
|
436
|
-
let videoConstraints: MediaTrackConstraints | boolean = true;
|
437
|
-
if (options.resolution) {
|
438
|
-
if (isSafari()) {
|
439
|
-
videoConstraints = {
|
440
|
-
width: { max: options.resolution.width },
|
441
|
-
height: { max: options.resolution.height },
|
442
|
-
frameRate: options.resolution.frameRate,
|
443
|
-
};
|
444
|
-
} else {
|
445
|
-
videoConstraints = {
|
446
|
-
width: { ideal: options.resolution.width },
|
447
|
-
height: { ideal: options.resolution.height },
|
448
|
-
frameRate: options.resolution.frameRate,
|
449
|
-
};
|
450
|
-
}
|
451
|
-
}
|
452
|
-
|
453
445
|
if (navigator.mediaDevices.getDisplayMedia === undefined) {
|
454
446
|
throw new DeviceUnsupportedError('getDisplayMedia not supported');
|
455
447
|
}
|
456
448
|
|
457
|
-
const
|
458
|
-
|
459
|
-
video: videoConstraints,
|
460
|
-
// @ts-expect-error support for experimental display media features
|
461
|
-
controller: options.controller,
|
462
|
-
selfBrowserSurface: options.selfBrowserSurface,
|
463
|
-
surfaceSwitching: options.surfaceSwitching,
|
464
|
-
systemAudio: options.systemAudio,
|
465
|
-
});
|
449
|
+
const constraints = screenCaptureToDisplayMediaStreamOptions(options);
|
450
|
+
const stream: MediaStream = await navigator.mediaDevices.getDisplayMedia(constraints);
|
466
451
|
|
467
452
|
const tracks = stream.getVideoTracks();
|
468
453
|
if (tracks.length === 0) {
|
@@ -585,7 +570,7 @@ export default class LocalParticipant extends Participant {
|
|
585
570
|
if (opts.source) {
|
586
571
|
track.source = opts.source;
|
587
572
|
}
|
588
|
-
const publishPromise = this.publish(track, opts,
|
573
|
+
const publishPromise = this.publish(track, opts, isStereo);
|
589
574
|
this.pendingPublishPromises.set(track, publishPromise);
|
590
575
|
try {
|
591
576
|
const publication = await publishPromise;
|
@@ -597,12 +582,7 @@ export default class LocalParticipant extends Participant {
|
|
597
582
|
}
|
598
583
|
}
|
599
584
|
|
600
|
-
private async publish(
|
601
|
-
track: LocalTrack,
|
602
|
-
opts: TrackPublishOptions,
|
603
|
-
options: TrackPublishOptions | undefined,
|
604
|
-
isStereo: boolean,
|
605
|
-
) {
|
585
|
+
private async publish(track: LocalTrack, opts: TrackPublishOptions, isStereo: boolean) {
|
606
586
|
const existingTrackOfSource = Array.from(this.tracks.values()).find(
|
607
587
|
(publishedTrack) => track instanceof LocalTrack && publishedTrack.source === track.source,
|
608
588
|
);
|
@@ -646,10 +626,10 @@ export default class LocalParticipant extends Participant {
|
|
646
626
|
track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
647
627
|
|
648
628
|
// create track publication from track
|
649
|
-
const req = AddTrackRequest
|
629
|
+
const req = new AddTrackRequest({
|
650
630
|
// get local track id for use during publishing
|
651
631
|
cid: track.mediaStreamTrack.id,
|
652
|
-
name:
|
632
|
+
name: opts.name,
|
653
633
|
type: Track.kindToProto(track.kind),
|
654
634
|
muted: track.isMuted,
|
655
635
|
source: Track.sourceToProto(track.source),
|
@@ -698,26 +678,26 @@ export default class LocalParticipant extends Participant {
|
|
698
678
|
simEncodings = computeTrackBackupEncodings(track, opts.backupCodec.codec, simOpts);
|
699
679
|
|
700
680
|
req.simulcastCodecs = [
|
701
|
-
{
|
681
|
+
new SimulcastCodec({
|
702
682
|
codec: opts.videoCodec,
|
703
683
|
cid: track.mediaStreamTrack.id,
|
704
684
|
enableSimulcastLayers: true,
|
705
|
-
},
|
706
|
-
{
|
685
|
+
}),
|
686
|
+
new SimulcastCodec({
|
707
687
|
codec: opts.backupCodec.codec,
|
708
688
|
cid: '',
|
709
689
|
enableSimulcastLayers: true,
|
710
|
-
},
|
690
|
+
}),
|
711
691
|
];
|
712
692
|
} else if (opts.videoCodec) {
|
713
693
|
// pass codec info to sfu so it can prefer codec for the client which don't support
|
714
694
|
// setCodecPreferences
|
715
695
|
req.simulcastCodecs = [
|
716
|
-
{
|
696
|
+
new SimulcastCodec({
|
717
697
|
codec: opts.videoCodec,
|
718
698
|
cid: track.mediaStreamTrack.id,
|
719
699
|
enableSimulcastLayers: opts.simulcast ?? false,
|
720
|
-
},
|
700
|
+
}),
|
721
701
|
];
|
722
702
|
}
|
723
703
|
}
|
@@ -879,7 +859,7 @@ export default class LocalParticipant extends Participant {
|
|
879
859
|
return;
|
880
860
|
}
|
881
861
|
const simulcastTrack = track.addSimulcastTrack(videoCodec, encodings);
|
882
|
-
const req = AddTrackRequest
|
862
|
+
const req = new AddTrackRequest({
|
883
863
|
cid: simulcastTrack.mediaStreamTrack.id,
|
884
864
|
type: Track.kindToProto(track.kind),
|
885
865
|
muted: track.isMuted,
|
@@ -1099,18 +1079,18 @@ export default class LocalParticipant extends Participant {
|
|
1099
1079
|
});
|
1100
1080
|
}
|
1101
1081
|
|
1102
|
-
const packet
|
1082
|
+
const packet = new DataPacket({
|
1103
1083
|
kind,
|
1104
1084
|
value: {
|
1105
|
-
|
1106
|
-
|
1085
|
+
case: 'user',
|
1086
|
+
value: new UserPacket({
|
1107
1087
|
participantSid: this.sid,
|
1108
1088
|
payload: data,
|
1109
1089
|
destinationSids: destinationSids,
|
1110
1090
|
topic,
|
1111
|
-
},
|
1091
|
+
}),
|
1112
1092
|
},
|
1113
|
-
};
|
1093
|
+
});
|
1114
1094
|
|
1115
1095
|
await this.engine.sendDataPacket(packet, kind);
|
1116
1096
|
}
|
@@ -1333,10 +1313,12 @@ export default class LocalParticipant extends Participant {
|
|
1333
1313
|
const infos: TrackPublishedResponse[] = [];
|
1334
1314
|
this.tracks.forEach((track: LocalTrackPublication) => {
|
1335
1315
|
if (track.track !== undefined) {
|
1336
|
-
infos.push(
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1316
|
+
infos.push(
|
1317
|
+
new TrackPublishedResponse({
|
1318
|
+
cid: track.track.mediaStreamID,
|
1319
|
+
track: track.trackInfo,
|
1320
|
+
}),
|
1321
|
+
);
|
1340
1322
|
}
|
1341
1323
|
});
|
1342
1324
|
return infos;
|
@@ -1347,11 +1329,13 @@ export default class LocalParticipant extends Participant {
|
|
1347
1329
|
const infos: DataChannelInfo[] = [];
|
1348
1330
|
const getInfo = (dc: RTCDataChannel | undefined, target: SignalTarget) => {
|
1349
1331
|
if (dc?.id !== undefined && dc.id !== null) {
|
1350
|
-
infos.push(
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1332
|
+
infos.push(
|
1333
|
+
new DataChannelInfo({
|
1334
|
+
label: dc.label,
|
1335
|
+
id: dc.id,
|
1336
|
+
target,
|
1337
|
+
}),
|
1338
|
+
);
|
1355
1339
|
}
|
1356
1340
|
};
|
1357
1341
|
getInfo(this.engine.dataChannelForKind(DataPacket_Kind.LOSSY), SignalTarget.PUBLISHER);
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
2
3
|
import log from '../../logger';
|
3
4
|
import {
|
4
5
|
DataPacket_Kind,
|
@@ -6,7 +7,7 @@ import {
|
|
6
7
|
ParticipantPermission,
|
7
8
|
ConnectionQuality as ProtoQuality,
|
8
9
|
SubscriptionError,
|
9
|
-
} from '../../proto/
|
10
|
+
} from '../../proto/livekit_models_pb';
|
10
11
|
import { ParticipantEvent, TrackEvent } from '../events';
|
11
12
|
import type LocalTrackPublication from '../track/LocalTrackPublication';
|
12
13
|
import type RemoteTrack from '../track/RemoteTrack';
|
@@ -34,7 +35,7 @@ function qualityFromProto(q: ProtoQuality): ConnectionQuality {
|
|
34
35
|
}
|
35
36
|
}
|
36
37
|
|
37
|
-
export default class Participant extends EventEmitter<ParticipantEventCallbacks> {
|
38
|
+
export default class Participant extends (EventEmitter as new () => TypedEmitter<ParticipantEventCallbacks>) {
|
38
39
|
protected participantInfo?: ParticipantInfo;
|
39
40
|
|
40
41
|
audioTracks: Map<string, TrackPublication>;
|
@@ -75,6 +76,7 @@ export default class Participant extends EventEmitter<ParticipantEventCallbacks>
|
|
75
76
|
/** @internal */
|
76
77
|
constructor(sid: string, identity: string, name?: string, metadata?: string) {
|
77
78
|
super();
|
79
|
+
this.setMaxListeners(100);
|
78
80
|
this.sid = sid;
|
79
81
|
this.identity = identity;
|
80
82
|
this.name = name;
|
@@ -141,7 +143,7 @@ export default class Participant extends EventEmitter<ParticipantEventCallbacks>
|
|
141
143
|
/** when participant joined the room */
|
142
144
|
get joinedAt(): Date | undefined {
|
143
145
|
if (this.participantInfo) {
|
144
|
-
return new Date(this.participantInfo.joinedAt * 1000);
|
146
|
+
return new Date(Number.parseInt(this.participantInfo.joinedAt.toString()) * 1000);
|
145
147
|
}
|
146
148
|
return new Date();
|
147
149
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
1
|
+
import { TrackPermission } from '../../proto/livekit_rtc_pb';
|
2
2
|
|
3
3
|
export interface ParticipantTrackPermission {
|
4
4
|
/**
|
@@ -33,10 +33,10 @@ export function trackPermissionToProto(perms: ParticipantTrackPermission): Track
|
|
33
33
|
'Invalid track permission, must provide at least one of participantIdentity and participantSid',
|
34
34
|
);
|
35
35
|
}
|
36
|
-
return {
|
36
|
+
return new TrackPermission({
|
37
37
|
participantIdentity: perms.participantIdentity ?? '',
|
38
38
|
participantSid: perms.participantSid ?? '',
|
39
39
|
allTracks: perms.allowAll ?? false,
|
40
40
|
trackSids: perms.allowedTrackSids || [],
|
41
|
-
};
|
41
|
+
});
|
42
42
|
}
|
@@ -1,8 +1,7 @@
|
|
1
|
-
import type EventEmitter from 'eventemitter3';
|
2
1
|
import type { SignalClient } from '../../api/SignalClient';
|
3
2
|
import log from '../../logger';
|
4
|
-
import type { ParticipantInfo, SubscriptionError } from '../../proto/
|
5
|
-
import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/
|
3
|
+
import type { ParticipantInfo, SubscriptionError } from '../../proto/livekit_models_pb';
|
4
|
+
import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc_pb';
|
6
5
|
import { ParticipantEvent, TrackEvent } from '../events';
|
7
6
|
import RemoteAudioTrack from '../track/RemoteAudioTrack';
|
8
7
|
import type RemoteTrack from '../track/RemoteTrack';
|
@@ -350,9 +349,9 @@ export default class RemoteParticipant extends Participant {
|
|
350
349
|
}
|
351
350
|
|
352
351
|
/** @internal */
|
353
|
-
emit<
|
354
|
-
event:
|
355
|
-
...args:
|
352
|
+
emit<E extends keyof ParticipantEventCallbacks>(
|
353
|
+
event: E,
|
354
|
+
...args: Parameters<ParticipantEventCallbacks[E]>
|
356
355
|
): boolean {
|
357
356
|
log.trace('participant event', { participant: this.sid, event, args });
|
358
357
|
return super.emit(event, ...args);
|
@@ -3,13 +3,13 @@ import { TrackInvalidError } from '../errors';
|
|
3
3
|
import LocalAudioTrack from '../track/LocalAudioTrack';
|
4
4
|
import LocalVideoTrack from '../track/LocalVideoTrack';
|
5
5
|
import { Track } from '../track/Track';
|
6
|
-
import { ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43 } from '../track/options';
|
7
6
|
import type {
|
8
7
|
BackupVideoCodec,
|
9
8
|
TrackPublishOptions,
|
10
9
|
VideoCodec,
|
11
10
|
VideoEncoding,
|
12
11
|
} from '../track/options';
|
12
|
+
import { ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43 } from '../track/options';
|
13
13
|
import { getReactNativeOs, isFireFox, isReactNative, isSVCCodec } from '../utils';
|
14
14
|
|
15
15
|
/** @internal */
|
@@ -240,7 +240,9 @@ export function determineAppropriateEncoding(
|
|
240
240
|
}
|
241
241
|
// presets are based on the assumption of vp8 as a codec
|
242
242
|
// for other codecs we adjust the maxBitrate if no specific videoEncoding has been provided
|
243
|
-
//
|
243
|
+
// users should override these with ones that are optimized for their use case
|
244
|
+
// NOTE: SVC codec bitrates are inclusive of all scalability layers. while
|
245
|
+
// bitrate for non-SVC codecs does not include other simulcast layers.
|
244
246
|
if (codec) {
|
245
247
|
switch (codec) {
|
246
248
|
case 'av1':
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { debounce } from 'ts-debounce';
|
1
2
|
import log from '../../logger';
|
2
3
|
import { getBrowser } from '../../utils/browserParser';
|
3
4
|
import DeviceManager from '../DeviceManager';
|
@@ -108,8 +109,8 @@ export default abstract class LocalTrack extends Track {
|
|
108
109
|
detachTrack(this._mediaStreamTrack, el);
|
109
110
|
});
|
110
111
|
this._mediaStreamTrack.removeEventListener('ended', this.handleEnded);
|
111
|
-
this._mediaStreamTrack.removeEventListener('mute', this.
|
112
|
-
this._mediaStreamTrack.removeEventListener('unmute', this.
|
112
|
+
this._mediaStreamTrack.removeEventListener('mute', this.handleTrackMuteEvent);
|
113
|
+
this._mediaStreamTrack.removeEventListener('unmute', this.handleTrackUnmuteEvent);
|
113
114
|
if (!this.providedByUser && this._mediaStreamTrack !== newTrack) {
|
114
115
|
this._mediaStreamTrack.stop();
|
115
116
|
}
|
@@ -123,8 +124,8 @@ export default abstract class LocalTrack extends Track {
|
|
123
124
|
// the track is "muted"
|
124
125
|
// note this is different from LocalTrack.mute because we do not want to
|
125
126
|
// touch MediaStreamTrack.enabled
|
126
|
-
newTrack.addEventListener('mute', this.
|
127
|
-
newTrack.addEventListener('unmute', this.
|
127
|
+
newTrack.addEventListener('mute', this.handleTrackMuteEvent);
|
128
|
+
newTrack.addEventListener('unmute', this.handleTrackUnmuteEvent);
|
128
129
|
this._constraints = newTrack.getConstraints();
|
129
130
|
}
|
130
131
|
if (this.sender) {
|
@@ -274,18 +275,32 @@ export default abstract class LocalTrack extends Track {
|
|
274
275
|
log.debug(`visibility changed, is in Background: ${this.isInBackground}`);
|
275
276
|
|
276
277
|
if (!this.isInBackground && this.needsReAcquisition && !this.isUserProvided && !this.isMuted) {
|
277
|
-
log.debug(`track needs to be
|
278
|
+
log.debug(`track needs to be reacquired, restarting ${this.source}`);
|
278
279
|
await this.restart();
|
279
280
|
this.reacquireTrack = false;
|
280
281
|
}
|
281
282
|
}
|
282
283
|
|
284
|
+
private handleTrackMuteEvent = () =>
|
285
|
+
this.debouncedTrackMuteHandler().catch(() =>
|
286
|
+
log.debug('track mute bounce got cancelled by an unmute event'),
|
287
|
+
);
|
288
|
+
|
289
|
+
private debouncedTrackMuteHandler = debounce(async () => {
|
290
|
+
await this.pauseUpstream();
|
291
|
+
}, 5000);
|
292
|
+
|
293
|
+
private handleTrackUnmuteEvent = async () => {
|
294
|
+
this.debouncedTrackMuteHandler.cancel('unmute');
|
295
|
+
await this.resumeUpstream();
|
296
|
+
};
|
297
|
+
|
283
298
|
private handleEnded = () => {
|
284
299
|
if (this.isInBackground) {
|
285
300
|
this.reacquireTrack = true;
|
286
301
|
}
|
287
|
-
this._mediaStreamTrack.removeEventListener('mute', this.
|
288
|
-
this._mediaStreamTrack.removeEventListener('unmute', this.
|
302
|
+
this._mediaStreamTrack.removeEventListener('mute', this.handleTrackMuteEvent);
|
303
|
+
this._mediaStreamTrack.removeEventListener('unmute', this.handleTrackUnmuteEvent);
|
289
304
|
this.emit(TrackEvent.Ended, this);
|
290
305
|
};
|
291
306
|
|
@@ -293,8 +308,8 @@ export default abstract class LocalTrack extends Track {
|
|
293
308
|
super.stop();
|
294
309
|
|
295
310
|
this._mediaStreamTrack.removeEventListener('ended', this.handleEnded);
|
296
|
-
this._mediaStreamTrack.removeEventListener('mute', this.
|
297
|
-
this._mediaStreamTrack.removeEventListener('unmute', this.
|
311
|
+
this._mediaStreamTrack.removeEventListener('mute', this.handleTrackMuteEvent);
|
312
|
+
this._mediaStreamTrack.removeEventListener('unmute', this.handleTrackUnmuteEvent);
|
298
313
|
this.processor?.destroy();
|
299
314
|
this.processor = undefined;
|
300
315
|
}
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { describe, expect, it } from 'vitest';
|
2
|
+
import { VideoQuality } from '../../proto/livekit_models_pb';
|
2
3
|
import { videoLayersFromEncodings } from './LocalVideoTrack';
|
3
4
|
|
4
5
|
describe('videoLayersFromEncodings', () => {
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import type { SignalClient } from '../../api/SignalClient';
|
2
2
|
import log from '../../logger';
|
3
|
-
import { VideoLayer, VideoQuality } from '../../proto/
|
4
|
-
import
|
3
|
+
import { VideoLayer, VideoQuality } from '../../proto/livekit_models_pb';
|
4
|
+
import { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc_pb';
|
5
5
|
import { ScalabilityMode } from '../participant/publishUtils';
|
6
|
-
import { computeBitrate, monitorFrequency } from '../stats';
|
7
6
|
import type { VideoSenderStats } from '../stats';
|
7
|
+
import { computeBitrate, monitorFrequency } from '../stats';
|
8
8
|
import { Mutex, isFireFox, isMobile, isWeb, unwrapConstraint } from '../utils';
|
9
9
|
import LocalTrack from './LocalTrack';
|
10
10
|
import { Track } from './Track';
|
@@ -173,10 +173,12 @@ export default class LocalVideoTrack extends LocalTrack {
|
|
173
173
|
setPublishingQuality(maxQuality: VideoQuality) {
|
174
174
|
const qualities: SubscribedQuality[] = [];
|
175
175
|
for (let q = VideoQuality.LOW; q <= VideoQuality.HIGH; q += 1) {
|
176
|
-
qualities.push(
|
177
|
-
|
178
|
-
|
179
|
-
|
176
|
+
qualities.push(
|
177
|
+
new SubscribedQuality({
|
178
|
+
quality: q,
|
179
|
+
enabled: q <= maxQuality,
|
180
|
+
}),
|
181
|
+
);
|
180
182
|
}
|
181
183
|
log.debug(`setting publishing quality. max quality ${maxQuality}`);
|
182
184
|
this.setPublishingLayers(qualities);
|
@@ -358,7 +360,7 @@ async function setPublishingLayersForSender(
|
|
358
360
|
let hasChanged = false;
|
359
361
|
|
360
362
|
/* disable closable spatial layer as it has video blur / frozen issue with current server / client
|
361
|
-
1. chrome 113: when switching to up layer with scalability Mode change, it will generate a
|
363
|
+
1. chrome 113: when switching to up layer with scalability Mode change, it will generate a
|
362
364
|
low resolution frame and recover very quickly, but noticable
|
363
365
|
2. livekit sfu: additional pli request cause video frozen for a few frames, also noticable */
|
364
366
|
const closableSpatial = false;
|
@@ -456,7 +458,7 @@ export function videoQualityForRid(rid: string): VideoQuality {
|
|
456
458
|
case 'q':
|
457
459
|
return VideoQuality.LOW;
|
458
460
|
default:
|
459
|
-
return VideoQuality.
|
461
|
+
return VideoQuality.HIGH;
|
460
462
|
}
|
461
463
|
}
|
462
464
|
|
@@ -469,29 +471,32 @@ export function videoLayersFromEncodings(
|
|
469
471
|
// default to a single layer, HQ
|
470
472
|
if (!encodings) {
|
471
473
|
return [
|
472
|
-
{
|
474
|
+
new VideoLayer({
|
473
475
|
quality: VideoQuality.HIGH,
|
474
476
|
width,
|
475
477
|
height,
|
476
478
|
bitrate: 0,
|
477
479
|
ssrc: 0,
|
478
|
-
},
|
480
|
+
}),
|
479
481
|
];
|
480
482
|
}
|
481
483
|
|
482
484
|
if (svc) {
|
483
485
|
// svc layers
|
484
486
|
/* @ts-ignore */
|
485
|
-
const
|
487
|
+
const encodingSM = encodings[0].scalabilityMode as string;
|
488
|
+
const sm = new ScalabilityMode(encodingSM);
|
486
489
|
const layers = [];
|
487
490
|
for (let i = 0; i < sm.spatial; i += 1) {
|
488
|
-
layers.push(
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
491
|
+
layers.push(
|
492
|
+
new VideoLayer({
|
493
|
+
quality: VideoQuality.HIGH - i,
|
494
|
+
width: Math.ceil(width / 2 ** i),
|
495
|
+
height: Math.ceil(height / 2 ** i),
|
496
|
+
bitrate: encodings[0].maxBitrate ? Math.ceil(encodings[0].maxBitrate / 3 ** i) : 0,
|
497
|
+
ssrc: 0,
|
498
|
+
}),
|
499
|
+
);
|
495
500
|
}
|
496
501
|
return layers;
|
497
502
|
}
|
@@ -499,15 +504,12 @@ export function videoLayersFromEncodings(
|
|
499
504
|
return encodings.map((encoding) => {
|
500
505
|
const scale = encoding.scaleResolutionDownBy ?? 1;
|
501
506
|
let quality = videoQualityForRid(encoding.rid ?? '');
|
502
|
-
|
503
|
-
quality = VideoQuality.HIGH;
|
504
|
-
}
|
505
|
-
return {
|
507
|
+
return new VideoLayer({
|
506
508
|
quality,
|
507
|
-
width: width / scale,
|
508
|
-
height: height / scale,
|
509
|
+
width: Math.ceil(width / scale),
|
510
|
+
height: Math.ceil(height / scale),
|
509
511
|
bitrate: encoding.maxBitrate ?? 0,
|
510
512
|
ssrc: 0,
|
511
|
-
};
|
513
|
+
});
|
512
514
|
});
|
513
515
|
}
|
@@ -1,6 +1,11 @@
|
|
1
1
|
import log from '../../logger';
|
2
|
-
import {
|
3
|
-
|
2
|
+
import {
|
3
|
+
ParticipantTracks,
|
4
|
+
SubscriptionError,
|
5
|
+
TrackInfo,
|
6
|
+
VideoQuality,
|
7
|
+
} from '../../proto/livekit_models_pb';
|
8
|
+
import { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc_pb';
|
4
9
|
import { TrackEvent } from '../events';
|
5
10
|
import type RemoteTrack from './RemoteTrack';
|
6
11
|
import RemoteVideoTrack from './RemoteVideoTrack';
|
@@ -46,18 +51,18 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
46
51
|
this.allowed = true;
|
47
52
|
}
|
48
53
|
|
49
|
-
const sub
|
54
|
+
const sub = new UpdateSubscription({
|
50
55
|
trackSids: [this.trackSid],
|
51
56
|
subscribe: this.subscribed,
|
52
57
|
participantTracks: [
|
53
|
-
{
|
58
|
+
new ParticipantTracks({
|
54
59
|
// sending an empty participant id since TrackPublication doesn't keep it
|
55
60
|
// this is filled in by the participant that receives this message
|
56
61
|
participantSid: '',
|
57
62
|
trackSids: [this.trackSid],
|
58
|
-
},
|
63
|
+
}),
|
59
64
|
],
|
60
|
-
};
|
65
|
+
});
|
61
66
|
this.emit(TrackEvent.UpdateSubscription, sub);
|
62
67
|
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
63
68
|
this.emitPermissionUpdateIfChanged(prevPermission);
|
@@ -286,7 +291,7 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
286
291
|
|
287
292
|
/* @internal */
|
288
293
|
emitTrackUpdate() {
|
289
|
-
const settings: UpdateTrackSettings = UpdateTrackSettings
|
294
|
+
const settings: UpdateTrackSettings = new UpdateTrackSettings({
|
290
295
|
trackSids: [this.trackSid],
|
291
296
|
disabled: this.disabled,
|
292
297
|
fps: this.fps,
|
@@ -1,9 +1,10 @@
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
1
2
|
import MockMediaStreamTrack from '../../test/MockMediaStreamTrack';
|
2
3
|
import { TrackEvent } from '../events';
|
3
4
|
import RemoteVideoTrack, { ElementInfo } from './RemoteVideoTrack';
|
4
5
|
import type { Track } from './Track';
|
5
6
|
|
6
|
-
|
7
|
+
vi.useFakeTimers();
|
7
8
|
|
8
9
|
describe('RemoteVideoTrack', () => {
|
9
10
|
let track: RemoteVideoTrack;
|
@@ -73,7 +74,7 @@ describe('RemoteVideoTrack', () => {
|
|
73
74
|
elementInfo.setDimensions(100, 100);
|
74
75
|
|
75
76
|
track.observeElementInfo(elementInfo);
|
76
|
-
|
77
|
+
vi.runAllTimers();
|
77
78
|
|
78
79
|
expect(events).toHaveLength(1);
|
79
80
|
expect(events[0].width).toBe(100);
|
@@ -85,10 +86,10 @@ describe('RemoteVideoTrack', () => {
|
|
85
86
|
elementInfo.setDimensions(100, 100);
|
86
87
|
|
87
88
|
track.observeElementInfo(elementInfo);
|
88
|
-
|
89
|
+
vi.runAllTimers();
|
89
90
|
|
90
91
|
elementInfo.setDimensions(200, 200);
|
91
|
-
|
92
|
+
vi.runAllTimers();
|
92
93
|
|
93
94
|
expect(events).toHaveLength(2);
|
94
95
|
expect(events[1].width).toBe(200);
|
package/src/room/track/Track.ts
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEventEmitter from 'typed-emitter';
|
2
3
|
import type { SignalClient } from '../../api/SignalClient';
|
3
4
|
import log from '../../logger';
|
4
|
-
import { TrackSource, TrackType } from '../../proto/
|
5
|
-
import { StreamState as ProtoStreamState } from '../../proto/
|
5
|
+
import { TrackSource, TrackType } from '../../proto/livekit_models_pb';
|
6
|
+
import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc_pb';
|
6
7
|
import { TrackEvent } from '../events';
|
7
8
|
import { isFireFox, isSafari, isWeb } from '../utils';
|
8
9
|
|
@@ -12,7 +13,7 @@ const BACKGROUND_REACTION_DELAY = 5000;
|
|
12
13
|
// Safari tracks which audio elements have been "blessed" by the user.
|
13
14
|
const recycledElements: Array<HTMLAudioElement> = [];
|
14
15
|
|
15
|
-
export abstract class Track extends EventEmitter<TrackEventCallbacks> {
|
16
|
+
export abstract class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEventCallbacks>) {
|
16
17
|
kind: Track.Kind;
|
17
18
|
|
18
19
|
attachedElements: HTMLMediaElement[] = [];
|
@@ -51,6 +52,7 @@ export abstract class Track extends EventEmitter<TrackEventCallbacks> {
|
|
51
52
|
|
52
53
|
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind) {
|
53
54
|
super();
|
55
|
+
this.setMaxListeners(100);
|
54
56
|
this.kind = kind;
|
55
57
|
this._mediaStreamTrack = mediaTrack;
|
56
58
|
this._mediaStreamID = mediaTrack.id;
|
@@ -368,7 +370,8 @@ export namespace Track {
|
|
368
370
|
case Kind.Video:
|
369
371
|
return TrackType.VIDEO;
|
370
372
|
default:
|
371
|
-
|
373
|
+
// FIXME this was UNRECOGNIZED before
|
374
|
+
return TrackType.DATA;
|
372
375
|
}
|
373
376
|
}
|
374
377
|
|
@@ -396,7 +399,7 @@ export namespace Track {
|
|
396
399
|
case Source.ScreenShareAudio:
|
397
400
|
return TrackSource.SCREEN_SHARE_AUDIO;
|
398
401
|
default:
|
399
|
-
return TrackSource.
|
402
|
+
return TrackSource.UNKNOWN;
|
400
403
|
}
|
401
404
|
}
|
402
405
|
|