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.
Files changed (167) hide show
  1. package/README.md +7 -3
  2. package/dist/livekit-client.e2ee.worker.js +1 -1
  3. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  4. package/dist/livekit-client.e2ee.worker.mjs +389 -301
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +11279 -13498
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.umd.js +1 -1
  9. package/dist/livekit-client.umd.js.map +1 -1
  10. package/dist/src/api/SignalClient.d.ts +2 -2
  11. package/dist/src/api/SignalClient.d.ts.map +1 -1
  12. package/dist/src/connectionHelper/ConnectionCheck.d.ts +3 -2
  13. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  14. package/dist/src/connectionHelper/checks/Checker.d.ts +3 -2
  15. package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
  16. package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
  17. package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
  18. package/dist/src/e2ee/E2eeManager.d.ts +4 -2
  19. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  20. package/dist/src/e2ee/KeyProvider.d.ts +4 -2
  21. package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
  22. package/dist/src/e2ee/constants.d.ts +1 -0
  23. package/dist/src/e2ee/constants.d.ts.map +1 -1
  24. package/dist/src/e2ee/types.d.ts +1 -0
  25. package/dist/src/e2ee/types.d.ts.map +1 -1
  26. package/dist/src/e2ee/worker/FrameCryptor.d.ts +4 -2
  27. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  28. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +14 -3
  29. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  30. package/dist/src/index.d.ts +1 -1
  31. package/dist/src/index.d.ts.map +1 -1
  32. package/dist/src/proto/livekit_models_pb.d.ts +1264 -0
  33. package/dist/src/proto/livekit_models_pb.d.ts.map +1 -0
  34. package/dist/src/proto/livekit_rtc_pb.d.ts +1373 -0
  35. package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -0
  36. package/dist/src/room/PCTransport.d.ts +2 -1
  37. package/dist/src/room/PCTransport.d.ts.map +1 -1
  38. package/dist/src/room/RTCEngine.d.ts +9 -5
  39. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  40. package/dist/src/room/RegionUrlProvider.d.ts +4 -1
  41. package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
  42. package/dist/src/room/Room.d.ts +15 -11
  43. package/dist/src/room/Room.d.ts.map +1 -1
  44. package/dist/src/room/participant/LocalParticipant.d.ts +2 -2
  45. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  46. package/dist/src/room/participant/Participant.d.ts +5 -3
  47. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  48. package/dist/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
  49. package/dist/src/room/participant/ParticipantTrackPermission.d.ts.map +1 -1
  50. package/dist/src/room/participant/RemoteParticipant.d.ts +2 -3
  51. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  52. package/dist/src/room/participant/publishUtils.d.ts +1 -1
  53. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  54. package/dist/src/room/timers.d.ts +5 -4
  55. package/dist/src/room/timers.d.ts.map +1 -1
  56. package/dist/src/room/track/LocalTrack.d.ts +3 -0
  57. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  58. package/dist/src/room/track/LocalTrackPublication.d.ts +1 -1
  59. package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
  60. package/dist/src/room/track/LocalVideoTrack.d.ts +2 -2
  61. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  62. package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
  63. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  64. package/dist/src/room/track/Track.d.ts +6 -4
  65. package/dist/src/room/track/Track.d.ts.map +1 -1
  66. package/dist/src/room/track/TrackPublication.d.ts +7 -5
  67. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  68. package/dist/src/room/track/create.d.ts.map +1 -1
  69. package/dist/src/room/track/options.d.ts +8 -0
  70. package/dist/src/room/track/options.d.ts.map +1 -1
  71. package/dist/src/room/track/utils.d.ts +5 -1
  72. package/dist/src/room/track/utils.d.ts.map +1 -1
  73. package/dist/src/room/utils.d.ts +3 -1
  74. package/dist/src/room/utils.d.ts.map +1 -1
  75. package/dist/src/test/mocks.d.ts +4 -3
  76. package/dist/src/test/mocks.d.ts.map +1 -1
  77. package/dist/ts4.2/src/api/SignalClient.d.ts +2 -2
  78. package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +3 -2
  79. package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +3 -2
  80. package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +4 -2
  81. package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +4 -2
  82. package/dist/ts4.2/src/e2ee/constants.d.ts +1 -0
  83. package/dist/ts4.2/src/e2ee/types.d.ts +1 -0
  84. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +4 -2
  85. package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +14 -3
  86. package/dist/ts4.2/src/index.d.ts +1 -1
  87. package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +1264 -0
  88. package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +1373 -0
  89. package/dist/ts4.2/src/room/PCTransport.d.ts +2 -1
  90. package/dist/ts4.2/src/room/RTCEngine.d.ts +9 -5
  91. package/dist/ts4.2/src/room/RegionUrlProvider.d.ts +4 -1
  92. package/dist/ts4.2/src/room/Room.d.ts +15 -11
  93. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +2 -2
  94. package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -3
  95. package/dist/ts4.2/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
  96. package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +2 -3
  97. package/dist/ts4.2/src/room/participant/publishUtils.d.ts +1 -1
  98. package/dist/ts4.2/src/room/timers.d.ts +5 -4
  99. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -0
  100. package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -1
  101. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +2 -2
  102. package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
  103. package/dist/ts4.2/src/room/track/Track.d.ts +6 -4
  104. package/dist/ts4.2/src/room/track/TrackPublication.d.ts +7 -5
  105. package/dist/ts4.2/src/room/track/options.d.ts +8 -0
  106. package/dist/ts4.2/src/room/track/utils.d.ts +5 -1
  107. package/dist/ts4.2/src/room/utils.d.ts +3 -1
  108. package/dist/ts4.2/src/test/mocks.d.ts +4 -3
  109. package/package.json +10 -10
  110. package/src/api/SignalClient.ts +104 -101
  111. package/src/connectionHelper/ConnectionCheck.ts +3 -2
  112. package/src/connectionHelper/checks/Checker.ts +3 -3
  113. package/src/connectionHelper/checks/webrtc.ts +66 -2
  114. package/src/connectionHelper/checks/websocket.ts +4 -0
  115. package/src/e2ee/E2eeManager.ts +4 -3
  116. package/src/e2ee/KeyProvider.ts +3 -2
  117. package/src/e2ee/constants.ts +4 -0
  118. package/src/e2ee/types.ts +1 -0
  119. package/src/e2ee/worker/FrameCryptor.test.ts +1 -3
  120. package/src/e2ee/worker/FrameCryptor.ts +5 -5
  121. package/src/e2ee/worker/ParticipantKeyHandler.ts +37 -6
  122. package/src/e2ee/worker/e2ee.worker.ts +1 -1
  123. package/src/index.ts +1 -1
  124. package/src/proto/livekit_models_pb.ts +2096 -0
  125. package/src/proto/livekit_rtc_pb.ts +2332 -0
  126. package/src/room/PCTransport.ts +1 -1
  127. package/src/room/RTCEngine.ts +28 -22
  128. package/src/room/RegionUrlProvider.ts +11 -2
  129. package/src/room/Room.test.ts +1 -0
  130. package/src/room/Room.ts +158 -79
  131. package/src/room/participant/LocalParticipant.ts +43 -59
  132. package/src/room/participant/Participant.ts +6 -4
  133. package/src/room/participant/ParticipantTrackPermission.ts +3 -3
  134. package/src/room/participant/RemoteParticipant.ts +5 -6
  135. package/src/room/participant/publishUtils.test.ts +1 -0
  136. package/src/room/participant/publishUtils.ts +4 -2
  137. package/src/room/track/LocalTrack.ts +24 -9
  138. package/src/room/track/LocalTrackPublication.ts +1 -1
  139. package/src/room/track/LocalVideoTrack.test.ts +2 -1
  140. package/src/room/track/LocalVideoTrack.ts +28 -26
  141. package/src/room/track/RemoteTrackPublication.ts +12 -7
  142. package/src/room/track/RemoteVideoTrack.test.ts +5 -4
  143. package/src/room/track/Track.ts +9 -6
  144. package/src/room/track/TrackPublication.ts +7 -5
  145. package/src/room/track/create.ts +9 -17
  146. package/src/room/track/facingMode.test.ts +1 -0
  147. package/src/room/track/options.ts +23 -16
  148. package/src/room/track/utils.test.ts +1 -0
  149. package/src/room/track/utils.ts +44 -2
  150. package/src/room/utils.test.ts +16 -0
  151. package/src/room/utils.ts +20 -4
  152. package/src/test/mocks.ts +7 -5
  153. package/src/utils/AsyncQueue.test.ts +1 -0
  154. package/src/utils/browserParser.test.ts +33 -3
  155. package/src/utils/browserParser.ts +1 -1
  156. package/dist/src/proto/google/protobuf/timestamp.d.ts +0 -146
  157. package/dist/src/proto/google/protobuf/timestamp.d.ts.map +0 -1
  158. package/dist/src/proto/livekit_models.d.ts +0 -2399
  159. package/dist/src/proto/livekit_models.d.ts.map +0 -1
  160. package/dist/src/proto/livekit_rtc.d.ts +0 -14352
  161. package/dist/src/proto/livekit_rtc.d.ts.map +0 -1
  162. package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +0 -150
  163. package/dist/ts4.2/src/proto/livekit_models.d.ts +0 -2659
  164. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +0 -15764
  165. package/src/proto/google/protobuf/timestamp.ts +0 -230
  166. package/src/proto/livekit_models.ts +0 -4006
  167. package/src/proto/livekit_rtc.ts +0 -4672
@@ -6,15 +6,17 @@ import {
6
6
  Encryption_Type,
7
7
  ParticipantInfo,
8
8
  ParticipantPermission,
9
- } from '../../proto/livekit_models';
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/livekit_rtc';
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 { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
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 stream: MediaStream = await navigator.mediaDevices.getDisplayMedia({
458
- audio: options.audio ?? false,
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, options, isStereo);
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.fromPartial({
629
+ const req = new AddTrackRequest({
650
630
  // get local track id for use during publishing
651
631
  cid: track.mediaStreamTrack.id,
652
- name: options?.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.fromPartial({
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: DataPacket = {
1082
+ const packet = new DataPacket({
1103
1083
  kind,
1104
1084
  value: {
1105
- $case: 'user',
1106
- user: {
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
- cid: track.track.mediaStreamID,
1338
- track: track.trackInfo,
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
- label: dc.label,
1352
- id: dc.id,
1353
- target,
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 'eventemitter3';
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/livekit_models';
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 type { TrackPermission } from '../../proto/livekit_rtc';
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/livekit_models';
5
- import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc';
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<T extends EventEmitter.EventNames<ParticipantEventCallbacks>>(
354
- event: T,
355
- ...args: EventEmitter.EventArgs<ParticipantEventCallbacks, T>
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);
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43 } from '../track/options';
2
3
  import {
3
4
  computeDefaultScreenShareSimulcastPresets,
@@ -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
- // TODO make the bitrate multipliers configurable per codec
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.pauseUpstream);
112
- this._mediaStreamTrack.removeEventListener('unmute', this.resumeUpstream);
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.pauseUpstream);
127
- newTrack.addEventListener('unmute', this.resumeUpstream);
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 reaquired, restarting ${this.source}`);
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.pauseUpstream);
288
- this._mediaStreamTrack.removeEventListener('unmute', this.resumeUpstream);
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.pauseUpstream);
297
- this._mediaStreamTrack.removeEventListener('unmute', this.resumeUpstream);
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,4 @@
1
- import type { TrackInfo } from '../../proto/livekit_models';
1
+ import type { TrackInfo } from '../../proto/livekit_models_pb';
2
2
  import { TrackEvent } from '../events';
3
3
  import type LocalAudioTrack from './LocalAudioTrack';
4
4
  import type LocalTrack from './LocalTrack';
@@ -1,4 +1,5 @@
1
- import { VideoQuality } from '../../proto/livekit_models';
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/livekit_models';
4
- import type { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc';
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
- quality: q,
178
- enabled: q <= maxQuality,
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.UNRECOGNIZED;
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 sm = new ScalabilityMode(encodings[0].scalabilityMode);
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
- quality: VideoQuality.HIGH - i,
490
- width: width / 2 ** i,
491
- height: height / 2 ** i,
492
- bitrate: encodings[0].maxBitrate ? encodings[0].maxBitrate / 3 ** i : 0,
493
- ssrc: 0,
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
- if (quality === VideoQuality.UNRECOGNIZED && encodings.length === 1) {
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 { SubscriptionError, TrackInfo, VideoQuality } from '../../proto/livekit_models';
3
- import { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc';
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: UpdateSubscription = {
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.fromPartial({
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
- jest.useFakeTimers();
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
- jest.runAllTimers();
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
- jest.runAllTimers();
89
+ vi.runAllTimers();
89
90
 
90
91
  elementInfo.setDimensions(200, 200);
91
- jest.runAllTimers();
92
+ vi.runAllTimers();
92
93
 
93
94
  expect(events).toHaveLength(2);
94
95
  expect(events[1].width).toBe(200);
@@ -1,8 +1,9 @@
1
- import EventEmitter from 'eventemitter3';
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/livekit_models';
5
- import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc';
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
- return TrackType.UNRECOGNIZED;
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.UNRECOGNIZED;
402
+ return TrackSource.UNKNOWN;
400
403
  }
401
404
  }
402
405