livekit-client 1.12.1 → 1.12.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|