livekit-client 0.18.6 → 1.0.2

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 (154) hide show
  1. package/README.md +1 -1
  2. package/dist/livekit-client.esm.mjs +1034 -438
  3. package/dist/livekit-client.esm.mjs.map +1 -1
  4. package/dist/livekit-client.umd.js +1 -1
  5. package/dist/livekit-client.umd.js.map +1 -1
  6. package/dist/{api → src/api}/RequestQueue.d.ts +0 -0
  7. package/dist/src/api/RequestQueue.d.ts.map +1 -0
  8. package/dist/{api → src/api}/SignalClient.d.ts +3 -3
  9. package/dist/src/api/SignalClient.d.ts.map +1 -0
  10. package/dist/{index.d.ts → src/index.d.ts} +3 -4
  11. package/dist/src/index.d.ts.map +1 -0
  12. package/dist/{logger.d.ts → src/logger.d.ts} +0 -0
  13. package/dist/src/logger.d.ts.map +1 -0
  14. package/dist/src/options.d.ts +61 -0
  15. package/dist/src/options.d.ts.map +1 -0
  16. package/dist/{proto → src/proto}/google/protobuf/timestamp.d.ts +0 -0
  17. package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -0
  18. package/dist/{proto → src/proto}/livekit_models.d.ts +80 -0
  19. package/dist/src/proto/livekit_models.d.ts.map +1 -0
  20. package/dist/{proto → src/proto}/livekit_rtc.d.ts +661 -0
  21. package/dist/src/proto/livekit_rtc.d.ts.map +1 -0
  22. package/dist/{room → src/room}/DeviceManager.d.ts +0 -0
  23. package/dist/src/room/DeviceManager.d.ts.map +1 -0
  24. package/dist/{room → src/room}/PCTransport.d.ts +0 -0
  25. package/dist/src/room/PCTransport.d.ts.map +1 -0
  26. package/dist/{room → src/room}/RTCEngine.d.ts +4 -2
  27. package/dist/src/room/RTCEngine.d.ts.map +1 -0
  28. package/dist/{room → src/room}/Room.d.ts +13 -7
  29. package/dist/src/room/Room.d.ts.map +1 -0
  30. package/dist/{room → src/room}/errors.d.ts +0 -0
  31. package/dist/src/room/errors.d.ts.map +1 -0
  32. package/dist/{room → src/room}/events.d.ts +11 -13
  33. package/dist/src/room/events.d.ts.map +1 -0
  34. package/dist/{room → src/room}/participant/LocalParticipant.d.ts +4 -1
  35. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -0
  36. package/dist/{room → src/room}/participant/Participant.d.ts +0 -4
  37. package/dist/src/room/participant/Participant.d.ts.map +1 -0
  38. package/dist/{room → src/room}/participant/ParticipantTrackPermission.d.ts +0 -0
  39. package/dist/src/room/participant/ParticipantTrackPermission.d.ts.map +1 -0
  40. package/dist/{room → src/room}/participant/RemoteParticipant.d.ts +3 -2
  41. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -0
  42. package/dist/{room → src/room}/participant/publishUtils.d.ts +0 -0
  43. package/dist/src/room/participant/publishUtils.d.ts.map +1 -0
  44. package/dist/{room → src/room}/stats.d.ts +1 -0
  45. package/dist/src/room/stats.d.ts.map +1 -0
  46. package/dist/{room → src/room}/track/LocalAudioTrack.d.ts +0 -0
  47. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -0
  48. package/dist/{room → src/room}/track/LocalTrack.d.ts +3 -0
  49. package/dist/src/room/track/LocalTrack.d.ts.map +1 -0
  50. package/dist/{room → src/room}/track/LocalTrackPublication.d.ts +0 -0
  51. package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -0
  52. package/dist/{room → src/room}/track/LocalVideoTrack.d.ts +17 -2
  53. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -0
  54. package/dist/{room → src/room}/track/RemoteAudioTrack.d.ts +0 -0
  55. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -0
  56. package/dist/{room → src/room}/track/RemoteTrack.d.ts +0 -1
  57. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -0
  58. package/dist/{room → src/room}/track/RemoteTrackPublication.d.ts +0 -0
  59. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -0
  60. package/dist/{room → src/room}/track/RemoteVideoTrack.d.ts +25 -1
  61. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -0
  62. package/dist/{room → src/room}/track/Track.d.ts +6 -1
  63. package/dist/src/room/track/Track.d.ts.map +1 -0
  64. package/dist/{room → src/room}/track/TrackPublication.d.ts +0 -0
  65. package/dist/src/room/track/TrackPublication.d.ts.map +1 -0
  66. package/dist/{room → src/room}/track/create.d.ts +0 -0
  67. package/dist/src/room/track/create.d.ts.map +1 -0
  68. package/dist/{room → src/room}/track/defaults.d.ts +0 -0
  69. package/dist/src/room/track/defaults.d.ts.map +1 -0
  70. package/dist/{room → src/room}/track/options.d.ts +2 -31
  71. package/dist/src/room/track/options.d.ts.map +1 -0
  72. package/dist/{room → src/room}/track/types.d.ts +5 -0
  73. package/dist/src/room/track/types.d.ts.map +1 -0
  74. package/dist/{room → src/room}/track/utils.d.ts +0 -0
  75. package/dist/src/room/track/utils.d.ts.map +1 -0
  76. package/dist/{room → src/room}/utils.d.ts +0 -0
  77. package/dist/src/room/utils.d.ts.map +1 -0
  78. package/dist/src/test/MockMediaStreamTrack.d.ts +26 -0
  79. package/dist/src/test/MockMediaStreamTrack.d.ts.map +1 -0
  80. package/dist/{test → src/test}/mocks.d.ts +0 -0
  81. package/dist/src/test/mocks.d.ts.map +1 -0
  82. package/dist/src/version.d.ts +3 -0
  83. package/dist/src/version.d.ts.map +1 -0
  84. package/package.json +6 -2
  85. package/src/api/SignalClient.ts +34 -9
  86. package/src/index.ts +4 -3
  87. package/src/options.ts +0 -82
  88. package/src/proto/livekit_models.ts +90 -0
  89. package/src/proto/livekit_rtc.ts +235 -1
  90. package/src/room/DeviceManager.ts +4 -1
  91. package/src/room/RTCEngine.ts +46 -9
  92. package/src/room/Room.ts +122 -53
  93. package/src/room/events.ts +12 -14
  94. package/src/room/participant/LocalParticipant.ts +108 -23
  95. package/src/room/participant/Participant.ts +0 -5
  96. package/src/room/participant/RemoteParticipant.ts +17 -5
  97. package/src/room/participant/publishUtils.test.ts +2 -2
  98. package/src/room/stats.ts +2 -0
  99. package/src/room/track/LocalAudioTrack.ts +4 -0
  100. package/src/room/track/LocalTrack.ts +12 -5
  101. package/src/room/track/LocalVideoTrack.ts +144 -56
  102. package/src/room/track/RemoteTrack.ts +0 -2
  103. package/src/room/track/RemoteVideoTrack.test.ts +149 -0
  104. package/src/room/track/RemoteVideoTrack.ts +118 -37
  105. package/src/room/track/Track.ts +23 -2
  106. package/src/room/track/create.ts +1 -1
  107. package/src/room/track/options.ts +2 -31
  108. package/src/room/track/types.ts +5 -0
  109. package/src/room/track/utils.test.ts +6 -6
  110. package/src/test/MockMediaStreamTrack.ts +83 -0
  111. package/src/version.ts +4 -2
  112. package/dist/api/RequestQueue.d.ts.map +0 -1
  113. package/dist/api/SignalClient.d.ts.map +0 -1
  114. package/dist/connect.d.ts +0 -24
  115. package/dist/connect.d.ts.map +0 -1
  116. package/dist/index.d.ts.map +0 -1
  117. package/dist/logger.d.ts.map +0 -1
  118. package/dist/options.d.ts +0 -128
  119. package/dist/options.d.ts.map +0 -1
  120. package/dist/proto/google/protobuf/timestamp.d.ts.map +0 -1
  121. package/dist/proto/livekit_models.d.ts.map +0 -1
  122. package/dist/proto/livekit_rtc.d.ts.map +0 -1
  123. package/dist/room/DeviceManager.d.ts.map +0 -1
  124. package/dist/room/PCTransport.d.ts.map +0 -1
  125. package/dist/room/RTCEngine.d.ts.map +0 -1
  126. package/dist/room/Room.d.ts.map +0 -1
  127. package/dist/room/errors.d.ts.map +0 -1
  128. package/dist/room/events.d.ts.map +0 -1
  129. package/dist/room/participant/LocalParticipant.d.ts.map +0 -1
  130. package/dist/room/participant/Participant.d.ts.map +0 -1
  131. package/dist/room/participant/ParticipantTrackPermission.d.ts.map +0 -1
  132. package/dist/room/participant/RemoteParticipant.d.ts.map +0 -1
  133. package/dist/room/participant/publishUtils.d.ts.map +0 -1
  134. package/dist/room/stats.d.ts.map +0 -1
  135. package/dist/room/track/LocalAudioTrack.d.ts.map +0 -1
  136. package/dist/room/track/LocalTrack.d.ts.map +0 -1
  137. package/dist/room/track/LocalTrackPublication.d.ts.map +0 -1
  138. package/dist/room/track/LocalVideoTrack.d.ts.map +0 -1
  139. package/dist/room/track/RemoteAudioTrack.d.ts.map +0 -1
  140. package/dist/room/track/RemoteTrack.d.ts.map +0 -1
  141. package/dist/room/track/RemoteTrackPublication.d.ts.map +0 -1
  142. package/dist/room/track/RemoteVideoTrack.d.ts.map +0 -1
  143. package/dist/room/track/Track.d.ts.map +0 -1
  144. package/dist/room/track/TrackPublication.d.ts.map +0 -1
  145. package/dist/room/track/create.d.ts.map +0 -1
  146. package/dist/room/track/defaults.d.ts.map +0 -1
  147. package/dist/room/track/options.d.ts.map +0 -1
  148. package/dist/room/track/types.d.ts.map +0 -1
  149. package/dist/room/track/utils.d.ts.map +0 -1
  150. package/dist/room/utils.d.ts.map +0 -1
  151. package/dist/test/mocks.d.ts.map +0 -1
  152. package/dist/version.d.ts +0 -3
  153. package/dist/version.d.ts.map +0 -1
  154. package/src/connect.ts +0 -98
@@ -10,12 +10,15 @@ import {
10
10
  TrackUnpublishedResponse,
11
11
  } from '../../proto/livekit_rtc';
12
12
  import { TrackInvalidError, UnexpectedConnectionState } from '../errors';
13
- import { ParticipantEvent, TrackEvent } from '../events';
13
+ import { EngineEvent, ParticipantEvent, TrackEvent } from '../events';
14
14
  import RTCEngine from '../RTCEngine';
15
15
  import LocalAudioTrack from '../track/LocalAudioTrack';
16
16
  import LocalTrack from '../track/LocalTrack';
17
17
  import LocalTrackPublication from '../track/LocalTrackPublication';
18
- import LocalVideoTrack, { videoLayersFromEncodings } from '../track/LocalVideoTrack';
18
+ import LocalVideoTrack, {
19
+ SimulcastTrackInfo,
20
+ videoLayersFromEncodings,
21
+ } from '../track/LocalVideoTrack';
19
22
  import {
20
23
  CreateLocalTracksOptions,
21
24
  ScreenShareCaptureOptions,
@@ -31,6 +34,7 @@ import { ParticipantTrackPermission, trackPermissionToProto } from './Participan
31
34
  import { computeVideoEncodings, mediaTrackToLocalTrack } from './publishUtils';
32
35
  import RemoteParticipant from './RemoteParticipant';
33
36
 
37
+ const compatibleCodecForSVC = 'vp8';
34
38
  export default class LocalParticipant extends Participant {
35
39
  audioTracks: Map<string, LocalTrackPublication>;
36
40
 
@@ -47,6 +51,10 @@ export default class LocalParticipant extends Participant {
47
51
 
48
52
  private engine: RTCEngine;
49
53
 
54
+ private participantTrackPermissions: Array<ParticipantTrackPermission> = [];
55
+
56
+ private allParticipantsAllowedToSubscribe: boolean = true;
57
+
50
58
  // keep a pointer to room options
51
59
  private roomOptions?: RoomOptions;
52
60
 
@@ -74,6 +82,11 @@ export default class LocalParticipant extends Participant {
74
82
  this.engine.client.onSubscribedQualityUpdate = this.handleSubscribedQualityUpdate;
75
83
 
76
84
  this.engine.client.onLocalTrackUnpublished = this.handleLocalTrackUnpublished;
85
+
86
+ this.engine
87
+ .on(EngineEvent.Connected, this.updateTrackSubscriptionPermissions)
88
+ .on(EngineEvent.Restarted, this.updateTrackSubscriptionPermissions)
89
+ .on(EngineEvent.Resumed, this.updateTrackSubscriptionPermissions);
77
90
  }
78
91
 
79
92
  get lastCameraError(): Error | undefined {
@@ -384,7 +397,7 @@ export default class LocalParticipant extends Participant {
384
397
  // handle track actions
385
398
  track.on(TrackEvent.Muted, this.onTrackMuted);
386
399
  track.on(TrackEvent.Unmuted, this.onTrackUnmuted);
387
- track.on(TrackEvent.Ended, this.onTrackUnpublish);
400
+ track.on(TrackEvent.Ended, this.handleTrackEnded);
388
401
  track.on(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
389
402
  track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
390
403
 
@@ -401,6 +414,8 @@ export default class LocalParticipant extends Participant {
401
414
 
402
415
  // compute encodings and layers for video
403
416
  let encodings: RTCRtpEncodingParameters[] | undefined;
417
+ let simEncodings: RTCRtpEncodingParameters[] | undefined;
418
+ let simulcastTracks: SimulcastTrackInfo[] | undefined;
404
419
  if (track.kind === Track.Kind.Video) {
405
420
  // TODO: support react native, which doesn't expose getSettings
406
421
  const settings = track.mediaStreamTrack.getSettings();
@@ -409,16 +424,38 @@ export default class LocalParticipant extends Participant {
409
424
  // width and height should be defined for video
410
425
  req.width = width ?? 0;
411
426
  req.height = height ?? 0;
412
- // for svc codecs, disable simulcast and enable scalability L3T3
413
- // by default
414
- if (track instanceof LocalVideoTrack) {
415
- if (opts?.videoCodec === 'vp9' || opts?.videoCodec === 'av1') {
416
- opts.simulcast = false;
417
- opts.scalabilityMode = opts.scalabilityMode ?? 'L3T3';
418
- } else {
419
- // other codecs, unset scalability
420
- opts.scalabilityMode = undefined;
421
- }
427
+ // for svc codecs, disable simulcast and use vp8 for backup codec
428
+ if (
429
+ track instanceof LocalVideoTrack &&
430
+ (opts?.videoCodec === 'vp9' || opts?.videoCodec === 'av1')
431
+ ) {
432
+ // set scalabilityMode to 'L3T3' by default
433
+ opts.scalabilityMode = opts.scalabilityMode ?? 'L3T3';
434
+
435
+ // add backup codec track
436
+ const simOpts = { ...opts };
437
+ simOpts.simulcast = true;
438
+ simOpts.scalabilityMode = undefined;
439
+ simEncodings = computeVideoEncodings(
440
+ track.source === Track.Source.ScreenShare,
441
+ width,
442
+ height,
443
+ simOpts,
444
+ );
445
+ const simulcastTrack = track.addSimulcastTrack(compatibleCodecForSVC, simEncodings);
446
+ simulcastTracks = [simulcastTrack];
447
+ req.simulcastCodecs = [
448
+ {
449
+ codec: opts.videoCodec,
450
+ cid: track.mediaStreamTrack.id,
451
+ enableSimulcastLayers: true,
452
+ },
453
+ {
454
+ codec: simulcastTrack.codec,
455
+ cid: simulcastTrack.mediaStreamTrack.id,
456
+ enableSimulcastLayers: true,
457
+ },
458
+ ];
422
459
  }
423
460
 
424
461
  encodings = computeVideoEncodings(
@@ -427,7 +464,7 @@ export default class LocalParticipant extends Participant {
427
464
  height,
428
465
  opts,
429
466
  );
430
- req.layers = videoLayersFromEncodings(req.width, req.height, encodings);
467
+ req.layers = videoLayersFromEncodings(req.width, req.height, simEncodings ?? encodings);
431
468
  } else if (track.kind === Track.Kind.Audio && opts.audioBitrate) {
432
469
  encodings = [
433
470
  {
@@ -436,8 +473,14 @@ export default class LocalParticipant extends Participant {
436
473
  ];
437
474
  }
438
475
 
476
+ if (!this.engine || this.engine.isClosed) {
477
+ throw new UnexpectedConnectionState('cannot publish track when not connected');
478
+ }
479
+
439
480
  const ti = await this.engine.addTrack(req);
440
481
  const publication = new LocalTrackPublication(track.kind, ti, track);
482
+ // save options for when it needs to be republished again
483
+ publication.options = opts;
441
484
  track.sid = ti.sid;
442
485
 
443
486
  if (!this.engine.publisher) {
@@ -448,13 +491,32 @@ export default class LocalParticipant extends Participant {
448
491
  if (encodings) {
449
492
  transceiverInit.sendEncodings = encodings;
450
493
  }
451
- const transceiver = this.engine.publisher.pc.addTransceiver(
494
+ // addTransceiver for react-native is async. web is synchronous, but await won't effect it.
495
+ const transceiver = await this.engine.publisher.pc.addTransceiver(
452
496
  track.mediaStreamTrack,
453
497
  transceiverInit,
454
498
  );
455
- if (opts.videoCodec) {
499
+ if (track.kind === Track.Kind.Video && opts.videoCodec) {
456
500
  this.setPreferredCodec(transceiver, track.kind, opts.videoCodec);
501
+ track.codec = opts.videoCodec;
502
+ }
503
+
504
+ const localTrack = track as LocalVideoTrack;
505
+ if (simulcastTracks) {
506
+ for await (const simulcastTrack of simulcastTracks) {
507
+ const simTransceiverInit: RTCRtpTransceiverInit = { direction: 'sendonly' };
508
+ if (simulcastTrack.encodings) {
509
+ simTransceiverInit.sendEncodings = simulcastTrack.encodings;
510
+ }
511
+ const simTransceiver = await this.engine.publisher!.pc.addTransceiver(
512
+ simulcastTrack.mediaStreamTrack,
513
+ simTransceiverInit,
514
+ );
515
+ this.setPreferredCodec(simTransceiver, localTrack.kind, simulcastTrack.codec);
516
+ localTrack.setSimulcastTrackSender(simulcastTrack.codec, simTransceiver.sender);
517
+ }
457
518
  }
519
+
458
520
  this.engine.negotiate();
459
521
 
460
522
  // store RTPSender
@@ -464,6 +526,7 @@ export default class LocalParticipant extends Participant {
464
526
  } else if (track instanceof LocalAudioTrack) {
465
527
  track.startMonitor();
466
528
  }
529
+
467
530
  this.addTrackPublication(publication);
468
531
 
469
532
  // send event for publication
@@ -490,9 +553,10 @@ export default class LocalParticipant extends Participant {
490
553
 
491
554
  track = publication.track;
492
555
 
556
+ track.sender = undefined;
493
557
  track.off(TrackEvent.Muted, this.onTrackMuted);
494
558
  track.off(TrackEvent.Unmuted, this.onTrackUnmuted);
495
- track.off(TrackEvent.Ended, this.onTrackUnpublish);
559
+ track.off(TrackEvent.Ended, this.handleTrackEnded);
496
560
  track.off(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
497
561
  track.off(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
498
562
 
@@ -505,7 +569,7 @@ export default class LocalParticipant extends Participant {
505
569
 
506
570
  const { mediaStreamTrack } = track;
507
571
 
508
- if (this.engine.publisher) {
572
+ if (this.engine.publisher && this.engine.publisher.pc.connectionState !== 'closed') {
509
573
  const senders = this.engine.publisher.pc.getSenders();
510
574
  senders.forEach((sender) => {
511
575
  if (sender.track === mediaStreamTrack) {
@@ -609,11 +673,23 @@ export default class LocalParticipant extends Participant {
609
673
  allParticipantsAllowed: boolean,
610
674
  participantTrackPermissions: ParticipantTrackPermission[] = [],
611
675
  ) {
676
+ this.participantTrackPermissions = participantTrackPermissions;
677
+ this.allParticipantsAllowedToSubscribe = allParticipantsAllowed;
678
+ if (this.engine.client.isConnected) {
679
+ this.updateTrackSubscriptionPermissions();
680
+ }
681
+ }
682
+
683
+ private updateTrackSubscriptionPermissions = () => {
684
+ log.debug('updating track subscription permissions', {
685
+ allParticipantsAllowed: this.allParticipantsAllowedToSubscribe,
686
+ participantTrackPermissions: this.participantTrackPermissions,
687
+ });
612
688
  this.engine.client.sendUpdateSubscriptionPermissions(
613
- allParticipantsAllowed,
614
- participantTrackPermissions.map((p) => trackPermissionToProto(p)),
689
+ this.allParticipantsAllowedToSubscribe,
690
+ this.participantTrackPermissions.map((p) => trackPermissionToProto(p)),
615
691
  );
616
- }
692
+ };
617
693
 
618
694
  /** @internal */
619
695
  private onTrackUnmuted = (track: LocalTrack) => {
@@ -657,7 +733,11 @@ export default class LocalParticipant extends Participant {
657
733
  });
658
734
  return;
659
735
  }
660
- pub.videoTrack?.setPublishingLayers(update.subscribedQualities);
736
+ if (update.subscribedCodecs.length > 0) {
737
+ pub.videoTrack?.setPublishingCodecs(update.subscribedCodecs);
738
+ } else if (update.subscribedQualities.length > 0) {
739
+ pub.videoTrack?.setPublishingLayers(update.subscribedQualities);
740
+ }
661
741
  };
662
742
 
663
743
  private handleLocalTrackUnpublished = (unpublished: TrackUnpublishedResponse) => {
@@ -672,7 +752,10 @@ export default class LocalParticipant extends Participant {
672
752
  this.unpublishTrack(track.track!);
673
753
  };
674
754
 
675
- private onTrackUnpublish = (track: LocalTrack) => {
755
+ private handleTrackEnded = (track: LocalTrack) => {
756
+ log.debug('unpublishing local track due to TrackEnded', {
757
+ track: track.sid,
758
+ });
676
759
  this.unpublishTrack(track);
677
760
  };
678
761
 
@@ -710,6 +793,7 @@ export default class LocalParticipant extends Participant {
710
793
  }
711
794
  const cap = RTCRtpSender.getCapabilities(kind);
712
795
  if (!cap) return;
796
+ log.debug('get capabilities', cap);
713
797
  let selected: RTCRtpCodecCapability | undefined;
714
798
  const codecs: RTCRtpCodecCapability[] = [];
715
799
  cap.codecs.forEach((c) => {
@@ -734,6 +818,7 @@ export default class LocalParticipant extends Participant {
734
818
  }
735
819
  codecs.push(c);
736
820
  });
821
+
737
822
  if (selected && 'setCodecPreferences' in transceiver) {
738
823
  // @ts-ignore
739
824
  codecs.unshift(selected);
@@ -188,7 +188,6 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
188
188
  this.metadata = md;
189
189
 
190
190
  if (changed) {
191
- this.emit(ParticipantEvent.MetadataChanged, prevMetadata);
192
191
  this.emit(ParticipantEvent.ParticipantMetadataChanged, prevMetadata);
193
192
  }
194
193
  }
@@ -266,10 +265,6 @@ export type ParticipantEventCallbacks = {
266
265
  trackUnmuted: (publication: TrackPublication) => void;
267
266
  localTrackPublished: (publication: LocalTrackPublication) => void;
268
267
  localTrackUnpublished: (publication: LocalTrackPublication) => void;
269
- /**
270
- * @deprecated use [[participantMetadataChanged]] instead
271
- */
272
- metadataChanged: (prevMetadata: string | undefined, participant?: any) => void;
273
268
  participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void;
274
269
  dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void;
275
270
  isSpeakingChanged: (speaking: boolean) => void;
@@ -19,6 +19,8 @@ export default class RemoteParticipant extends Participant {
19
19
 
20
20
  signalClient: SignalClient;
21
21
 
22
+ private volume?: number;
23
+
22
24
  /** @internal */
23
25
  static fromParticipantInfo(signalClient: SignalClient, pi: ParticipantInfo): RemoteParticipant {
24
26
  const rp = new RemoteParticipant(signalClient, pi.sid, pi.identity);
@@ -40,6 +42,7 @@ export default class RemoteParticipant extends Participant {
40
42
 
41
43
  // register action events
42
44
  publication.on(TrackEvent.UpdateSettings, (settings: UpdateTrackSettings) => {
45
+ log.debug('send update settings', settings);
43
46
  this.signalClient.sendUpdateTrackSettings(settings);
44
47
  });
45
48
  publication.on(TrackEvent.UpdateSubscription, (sub: UpdateSubscription) => {
@@ -68,24 +71,26 @@ export default class RemoteParticipant extends Participant {
68
71
  }
69
72
 
70
73
  /**
71
- * sets the volume on the participant's microphone track if it exists.
74
+ * sets the volume on the participant's microphone track
75
+ * if no track exists the volume will be applied when the microphone track is added
72
76
  */
73
77
  setVolume(volume: number) {
78
+ this.volume = volume;
74
79
  const audioPublication = this.getTrack(Track.Source.Microphone);
75
- if (audioPublication) {
80
+ if (audioPublication && audioPublication.track) {
76
81
  (audioPublication.track as RemoteAudioTrack).setVolume(volume);
77
82
  }
78
83
  }
79
84
 
80
85
  /**
81
86
  * gets the volume on the participant's microphone track
82
- * returns undefined if no microphone track exists
83
87
  */
84
88
  getVolume() {
85
89
  const audioPublication = this.getTrack(Track.Source.Microphone);
86
- if (audioPublication) {
90
+ if (audioPublication && audioPublication.track) {
87
91
  return (audioPublication.track as RemoteAudioTrack).getVolume();
88
92
  }
93
+ return this.volume;
89
94
  }
90
95
 
91
96
  /** @internal */
@@ -153,7 +158,14 @@ export default class RemoteParticipant extends Participant {
153
158
  track.start();
154
159
 
155
160
  publication.setTrack(track);
156
-
161
+ // set participant volume on new microphone tracks
162
+ if (
163
+ this.volume !== undefined &&
164
+ track instanceof RemoteAudioTrack &&
165
+ track.source === Track.Source.Microphone
166
+ ) {
167
+ track.setVolume(this.volume);
168
+ }
157
169
  this.emit(ParticipantEvent.TrackSubscribed, track, publication);
158
170
 
159
171
  return publication;
@@ -28,11 +28,11 @@ describe('presetsForResolution', () => {
28
28
 
29
29
  describe('determineAppropriateEncoding', () => {
30
30
  it('uses higher encoding', () => {
31
- expect(determineAppropriateEncoding(false, 600, 300)).toEqual(VideoPresets.vga.encoding);
31
+ expect(determineAppropriateEncoding(false, 600, 300)).toEqual(VideoPresets.h360.encoding);
32
32
  });
33
33
 
34
34
  it('handles portrait', () => {
35
- expect(determineAppropriateEncoding(false, 300, 600)).toEqual(VideoPresets.vga.encoding);
35
+ expect(determineAppropriateEncoding(false, 300, 600)).toEqual(VideoPresets.h360.encoding);
36
36
  });
37
37
  });
38
38
 
package/src/room/stats.ts CHANGED
@@ -104,6 +104,8 @@ export interface VideoReceiverStats extends ReceiverStats {
104
104
  pliCount?: number;
105
105
 
106
106
  nackCount?: number;
107
+
108
+ decoderImplementation?: string;
107
109
  }
108
110
 
109
111
  export function computeBitrate<T extends ReceiverStats | SenderStats>(
@@ -1,6 +1,7 @@
1
1
  import log from '../../logger';
2
2
  import { TrackEvent } from '../events';
3
3
  import { AudioSenderStats, computeBitrate, monitorFrequency } from '../stats';
4
+ import { isWeb } from '../utils';
4
5
  import LocalTrack from './LocalTrack';
5
6
  import { AudioCaptureOptions } from './options';
6
7
  import { Track } from './Track';
@@ -68,6 +69,9 @@ export default class LocalAudioTrack extends LocalTrack {
68
69
 
69
70
  /* @internal */
70
71
  startMonitor() {
72
+ if (!isWeb()) {
73
+ return;
74
+ }
71
75
  setTimeout(() => {
72
76
  this.monitorSender();
73
77
  }, monitorFrequency);
@@ -2,6 +2,7 @@ import log from '../../logger';
2
2
  import DeviceManager from '../DeviceManager';
3
3
  import { TrackInvalidError } from '../errors';
4
4
  import { TrackEvent } from '../events';
5
+ import { VideoCodec } from './options';
5
6
  import { getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isMobile } from '../utils';
6
7
  import { attachToElement, detachTrack, Track } from './Track';
7
8
 
@@ -9,6 +10,9 @@ export default class LocalTrack extends Track {
9
10
  /** @internal */
10
11
  sender?: RTCRtpSender;
11
12
 
13
+ /** @internal */
14
+ codec?: VideoCodec;
15
+
12
16
  protected constraints: MediaTrackConstraints;
13
17
 
14
18
  protected wasMuted: boolean;
@@ -94,7 +98,9 @@ export default class LocalTrack extends Track {
94
98
  track.addEventListener('ended', this.handleEnded);
95
99
  log.debug('replace MediaStreamTrack');
96
100
 
97
- await this.sender.replaceTrack(track);
101
+ if (this.sender) {
102
+ await this.sender.replaceTrack(track);
103
+ }
98
104
  this._mediaStreamTrack = track;
99
105
 
100
106
  this.attachedElements.forEach((el) => {
@@ -106,9 +112,6 @@ export default class LocalTrack extends Track {
106
112
  }
107
113
 
108
114
  protected async restart(constraints?: MediaTrackConstraints): Promise<LocalTrack> {
109
- if (!this.sender) {
110
- throw new TrackInvalidError('unable to restart an unpublished track');
111
- }
112
115
  if (!constraints) {
113
116
  constraints = this.constraints;
114
117
  }
@@ -141,7 +144,11 @@ export default class LocalTrack extends Track {
141
144
  newTrack.addEventListener('ended', this.handleEnded);
142
145
  log.debug('re-acquired MediaStreamTrack');
143
146
 
144
- await this.sender.replaceTrack(newTrack);
147
+ if (this.sender) {
148
+ // Track can be restarted after it's unpublished
149
+ await this.sender.replaceTrack(newTrack);
150
+ }
151
+
145
152
  this._mediaStreamTrack = newTrack;
146
153
 
147
154
  this.attachedElements.forEach((el) => {