livekit-client 0.16.6 → 0.17.0

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 (88) hide show
  1. package/dist/api/RequestQueue.js +6 -6
  2. package/dist/api/RequestQueue.js.map +1 -1
  3. package/dist/api/SignalClient.d.ts +3 -0
  4. package/dist/api/SignalClient.js +23 -3
  5. package/dist/api/SignalClient.js.map +1 -1
  6. package/dist/connect.js +1 -1
  7. package/dist/connect.js.map +1 -1
  8. package/dist/options.d.ts +7 -2
  9. package/dist/proto/livekit_models.d.ts +33 -0
  10. package/dist/proto/livekit_models.js +213 -3
  11. package/dist/proto/livekit_models.js.map +1 -1
  12. package/dist/proto/livekit_rtc.d.ts +15 -1
  13. package/dist/proto/livekit_rtc.js +128 -2
  14. package/dist/proto/livekit_rtc.js.map +1 -1
  15. package/dist/room/RTCEngine.d.ts +3 -2
  16. package/dist/room/RTCEngine.js +12 -8
  17. package/dist/room/RTCEngine.js.map +1 -1
  18. package/dist/room/Room.js +19 -8
  19. package/dist/room/Room.js.map +1 -1
  20. package/dist/room/events.d.ts +6 -1
  21. package/dist/room/events.js +6 -1
  22. package/dist/room/events.js.map +1 -1
  23. package/dist/room/participant/LocalParticipant.d.ts +3 -1
  24. package/dist/room/participant/LocalParticipant.js +17 -1
  25. package/dist/room/participant/LocalParticipant.js.map +1 -1
  26. package/dist/room/participant/RemoteParticipant.d.ts +2 -1
  27. package/dist/room/participant/RemoteParticipant.js +3 -3
  28. package/dist/room/participant/RemoteParticipant.js.map +1 -1
  29. package/dist/room/participant/publishUtils.d.ts +6 -0
  30. package/dist/room/participant/publishUtils.js +65 -24
  31. package/dist/room/participant/publishUtils.js.map +1 -1
  32. package/dist/room/participant/publishUtils.test.js +35 -5
  33. package/dist/room/participant/publishUtils.test.js.map +1 -1
  34. package/dist/room/track/LocalAudioTrack.d.ts +2 -0
  35. package/dist/room/track/LocalAudioTrack.js +23 -0
  36. package/dist/room/track/LocalAudioTrack.js.map +1 -1
  37. package/dist/room/track/LocalTrack.d.ts +4 -0
  38. package/dist/room/track/LocalTrack.js +34 -0
  39. package/dist/room/track/LocalTrack.js.map +1 -1
  40. package/dist/room/track/LocalVideoTrack.d.ts +1 -0
  41. package/dist/room/track/LocalVideoTrack.js +13 -0
  42. package/dist/room/track/LocalVideoTrack.js.map +1 -1
  43. package/dist/room/track/RemoteTrack.d.ts +1 -0
  44. package/dist/room/track/RemoteTrack.js +1 -0
  45. package/dist/room/track/RemoteTrack.js.map +1 -1
  46. package/dist/room/track/RemoteVideoTrack.d.ts +4 -2
  47. package/dist/room/track/RemoteVideoTrack.js +25 -11
  48. package/dist/room/track/RemoteVideoTrack.js.map +1 -1
  49. package/dist/room/track/Track.d.ts +4 -1
  50. package/dist/room/track/Track.js +20 -1
  51. package/dist/room/track/Track.js.map +1 -1
  52. package/dist/room/track/defaults.js +2 -2
  53. package/dist/room/track/defaults.js.map +1 -1
  54. package/dist/room/track/options.d.ts +65 -15
  55. package/dist/room/track/options.js +38 -0
  56. package/dist/room/track/options.js.map +1 -1
  57. package/dist/room/track/types.d.ts +11 -0
  58. package/dist/room/track/utils.d.ts +10 -0
  59. package/dist/room/track/utils.js +46 -1
  60. package/dist/room/track/utils.js.map +1 -1
  61. package/dist/room/utils.d.ts +1 -0
  62. package/dist/room/utils.js +5 -1
  63. package/dist/room/utils.js.map +1 -1
  64. package/package.json +1 -1
  65. package/src/api/RequestQueue.ts +7 -7
  66. package/src/api/SignalClient.ts +31 -4
  67. package/src/connect.ts +1 -1
  68. package/src/options.ts +12 -3
  69. package/src/proto/livekit_models.ts +249 -0
  70. package/src/proto/livekit_rtc.ts +155 -0
  71. package/src/room/RTCEngine.ts +16 -9
  72. package/src/room/Room.ts +17 -7
  73. package/src/room/events.ts +6 -1
  74. package/src/room/participant/LocalParticipant.ts +23 -4
  75. package/src/room/participant/RemoteParticipant.ts +4 -4
  76. package/src/room/participant/publishUtils.test.ts +46 -6
  77. package/src/room/participant/publishUtils.ts +72 -27
  78. package/src/room/track/LocalAudioTrack.ts +19 -1
  79. package/src/room/track/LocalTrack.ts +36 -0
  80. package/src/room/track/LocalVideoTrack.ts +9 -1
  81. package/src/room/track/RemoteTrack.ts +2 -0
  82. package/src/room/track/RemoteVideoTrack.ts +20 -9
  83. package/src/room/track/Track.ts +14 -2
  84. package/src/room/track/defaults.ts +2 -2
  85. package/src/room/track/options.ts +55 -3
  86. package/src/room/track/types.ts +12 -0
  87. package/src/room/track/utils.ts +39 -0
  88. package/src/room/utils.ts +4 -0
@@ -6,6 +6,7 @@ import {
6
6
  TrackSource,
7
7
  Room,
8
8
  ParticipantInfo,
9
+ ClientConfiguration,
9
10
  TrackInfo,
10
11
  VideoQuality,
11
12
  ConnectionQuality,
@@ -192,6 +193,8 @@ export interface JoinResponse {
192
193
  * when this is set, the other fields will be largely empty
193
194
  */
194
195
  alternativeUrl: string;
196
+ clientConfiguration?: ClientConfiguration;
197
+ serverRegion: string;
195
198
  }
196
199
 
197
200
  export interface TrackPublishedResponse {
@@ -306,6 +309,12 @@ export interface SyncState {
306
309
  answer?: SessionDescription;
307
310
  subscription?: UpdateSubscription;
308
311
  publishTracks: TrackPublishedResponse[];
312
+ dataChannels: DataChannelInfo[];
313
+ }
314
+
315
+ export interface DataChannelInfo {
316
+ label: string;
317
+ id: number;
309
318
  }
310
319
 
311
320
  export interface SimulateScenario {
@@ -1405,6 +1414,7 @@ const baseJoinResponse: object = {
1405
1414
  serverVersion: "",
1406
1415
  subscriberPrimary: false,
1407
1416
  alternativeUrl: "",
1417
+ serverRegion: "",
1408
1418
  };
1409
1419
 
1410
1420
  export const JoinResponse = {
@@ -1436,6 +1446,15 @@ export const JoinResponse = {
1436
1446
  if (message.alternativeUrl !== "") {
1437
1447
  writer.uint32(58).string(message.alternativeUrl);
1438
1448
  }
1449
+ if (message.clientConfiguration !== undefined) {
1450
+ ClientConfiguration.encode(
1451
+ message.clientConfiguration,
1452
+ writer.uint32(66).fork()
1453
+ ).ldelim();
1454
+ }
1455
+ if (message.serverRegion !== "") {
1456
+ writer.uint32(74).string(message.serverRegion);
1457
+ }
1439
1458
  return writer;
1440
1459
  },
1441
1460
 
@@ -1471,6 +1490,15 @@ export const JoinResponse = {
1471
1490
  case 7:
1472
1491
  message.alternativeUrl = reader.string();
1473
1492
  break;
1493
+ case 8:
1494
+ message.clientConfiguration = ClientConfiguration.decode(
1495
+ reader,
1496
+ reader.uint32()
1497
+ );
1498
+ break;
1499
+ case 9:
1500
+ message.serverRegion = reader.string();
1501
+ break;
1474
1502
  default:
1475
1503
  reader.skipType(tag & 7);
1476
1504
  break;
@@ -1524,6 +1552,21 @@ export const JoinResponse = {
1524
1552
  } else {
1525
1553
  message.alternativeUrl = "";
1526
1554
  }
1555
+ if (
1556
+ object.clientConfiguration !== undefined &&
1557
+ object.clientConfiguration !== null
1558
+ ) {
1559
+ message.clientConfiguration = ClientConfiguration.fromJSON(
1560
+ object.clientConfiguration
1561
+ );
1562
+ } else {
1563
+ message.clientConfiguration = undefined;
1564
+ }
1565
+ if (object.serverRegion !== undefined && object.serverRegion !== null) {
1566
+ message.serverRegion = String(object.serverRegion);
1567
+ } else {
1568
+ message.serverRegion = "";
1569
+ }
1527
1570
  return message;
1528
1571
  },
1529
1572
 
@@ -1555,6 +1598,12 @@ export const JoinResponse = {
1555
1598
  (obj.subscriberPrimary = message.subscriberPrimary);
1556
1599
  message.alternativeUrl !== undefined &&
1557
1600
  (obj.alternativeUrl = message.alternativeUrl);
1601
+ message.clientConfiguration !== undefined &&
1602
+ (obj.clientConfiguration = message.clientConfiguration
1603
+ ? ClientConfiguration.toJSON(message.clientConfiguration)
1604
+ : undefined);
1605
+ message.serverRegion !== undefined &&
1606
+ (obj.serverRegion = message.serverRegion);
1558
1607
  return obj;
1559
1608
  },
1560
1609
 
@@ -1588,6 +1637,17 @@ export const JoinResponse = {
1588
1637
  }
1589
1638
  message.subscriberPrimary = object.subscriberPrimary ?? false;
1590
1639
  message.alternativeUrl = object.alternativeUrl ?? "";
1640
+ if (
1641
+ object.clientConfiguration !== undefined &&
1642
+ object.clientConfiguration !== null
1643
+ ) {
1644
+ message.clientConfiguration = ClientConfiguration.fromPartial(
1645
+ object.clientConfiguration
1646
+ );
1647
+ } else {
1648
+ message.clientConfiguration = undefined;
1649
+ }
1650
+ message.serverRegion = object.serverRegion ?? "";
1591
1651
  return message;
1592
1652
  },
1593
1653
  };
@@ -3197,6 +3257,9 @@ export const SyncState = {
3197
3257
  for (const v of message.publishTracks) {
3198
3258
  TrackPublishedResponse.encode(v!, writer.uint32(26).fork()).ldelim();
3199
3259
  }
3260
+ for (const v of message.dataChannels) {
3261
+ DataChannelInfo.encode(v!, writer.uint32(34).fork()).ldelim();
3262
+ }
3200
3263
  return writer;
3201
3264
  },
3202
3265
 
@@ -3205,6 +3268,7 @@ export const SyncState = {
3205
3268
  let end = length === undefined ? reader.len : reader.pos + length;
3206
3269
  const message = { ...baseSyncState } as SyncState;
3207
3270
  message.publishTracks = [];
3271
+ message.dataChannels = [];
3208
3272
  while (reader.pos < end) {
3209
3273
  const tag = reader.uint32();
3210
3274
  switch (tag >>> 3) {
@@ -3222,6 +3286,11 @@ export const SyncState = {
3222
3286
  TrackPublishedResponse.decode(reader, reader.uint32())
3223
3287
  );
3224
3288
  break;
3289
+ case 4:
3290
+ message.dataChannels.push(
3291
+ DataChannelInfo.decode(reader, reader.uint32())
3292
+ );
3293
+ break;
3225
3294
  default:
3226
3295
  reader.skipType(tag & 7);
3227
3296
  break;
@@ -3233,6 +3302,7 @@ export const SyncState = {
3233
3302
  fromJSON(object: any): SyncState {
3234
3303
  const message = { ...baseSyncState } as SyncState;
3235
3304
  message.publishTracks = [];
3305
+ message.dataChannels = [];
3236
3306
  if (object.answer !== undefined && object.answer !== null) {
3237
3307
  message.answer = SessionDescription.fromJSON(object.answer);
3238
3308
  } else {
@@ -3248,6 +3318,11 @@ export const SyncState = {
3248
3318
  message.publishTracks.push(TrackPublishedResponse.fromJSON(e));
3249
3319
  }
3250
3320
  }
3321
+ if (object.dataChannels !== undefined && object.dataChannels !== null) {
3322
+ for (const e of object.dataChannels) {
3323
+ message.dataChannels.push(DataChannelInfo.fromJSON(e));
3324
+ }
3325
+ }
3251
3326
  return message;
3252
3327
  },
3253
3328
 
@@ -3268,6 +3343,13 @@ export const SyncState = {
3268
3343
  } else {
3269
3344
  obj.publishTracks = [];
3270
3345
  }
3346
+ if (message.dataChannels) {
3347
+ obj.dataChannels = message.dataChannels.map((e) =>
3348
+ e ? DataChannelInfo.toJSON(e) : undefined
3349
+ );
3350
+ } else {
3351
+ obj.dataChannels = [];
3352
+ }
3271
3353
  return obj;
3272
3354
  },
3273
3355
 
@@ -3291,6 +3373,79 @@ export const SyncState = {
3291
3373
  message.publishTracks.push(TrackPublishedResponse.fromPartial(e));
3292
3374
  }
3293
3375
  }
3376
+ message.dataChannels = [];
3377
+ if (object.dataChannels !== undefined && object.dataChannels !== null) {
3378
+ for (const e of object.dataChannels) {
3379
+ message.dataChannels.push(DataChannelInfo.fromPartial(e));
3380
+ }
3381
+ }
3382
+ return message;
3383
+ },
3384
+ };
3385
+
3386
+ const baseDataChannelInfo: object = { label: "", id: 0 };
3387
+
3388
+ export const DataChannelInfo = {
3389
+ encode(
3390
+ message: DataChannelInfo,
3391
+ writer: _m0.Writer = _m0.Writer.create()
3392
+ ): _m0.Writer {
3393
+ if (message.label !== "") {
3394
+ writer.uint32(10).string(message.label);
3395
+ }
3396
+ if (message.id !== 0) {
3397
+ writer.uint32(16).uint32(message.id);
3398
+ }
3399
+ return writer;
3400
+ },
3401
+
3402
+ decode(input: _m0.Reader | Uint8Array, length?: number): DataChannelInfo {
3403
+ const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
3404
+ let end = length === undefined ? reader.len : reader.pos + length;
3405
+ const message = { ...baseDataChannelInfo } as DataChannelInfo;
3406
+ while (reader.pos < end) {
3407
+ const tag = reader.uint32();
3408
+ switch (tag >>> 3) {
3409
+ case 1:
3410
+ message.label = reader.string();
3411
+ break;
3412
+ case 2:
3413
+ message.id = reader.uint32();
3414
+ break;
3415
+ default:
3416
+ reader.skipType(tag & 7);
3417
+ break;
3418
+ }
3419
+ }
3420
+ return message;
3421
+ },
3422
+
3423
+ fromJSON(object: any): DataChannelInfo {
3424
+ const message = { ...baseDataChannelInfo } as DataChannelInfo;
3425
+ if (object.label !== undefined && object.label !== null) {
3426
+ message.label = String(object.label);
3427
+ } else {
3428
+ message.label = "";
3429
+ }
3430
+ if (object.id !== undefined && object.id !== null) {
3431
+ message.id = Number(object.id);
3432
+ } else {
3433
+ message.id = 0;
3434
+ }
3435
+ return message;
3436
+ },
3437
+
3438
+ toJSON(message: DataChannelInfo): unknown {
3439
+ const obj: any = {};
3440
+ message.label !== undefined && (obj.label = message.label);
3441
+ message.id !== undefined && (obj.id = message.id);
3442
+ return obj;
3443
+ },
3444
+
3445
+ fromPartial(object: DeepPartial<DataChannelInfo>): DataChannelInfo {
3446
+ const message = { ...baseDataChannelInfo } as DataChannelInfo;
3447
+ message.label = object.label ?? "";
3448
+ message.id = object.id ?? 0;
3294
3449
  return message;
3295
3450
  },
3296
3451
  };
@@ -3,6 +3,8 @@ import type TypedEventEmitter from 'typed-emitter';
3
3
  import { SignalClient, SignalOptions } from '../api/SignalClient';
4
4
  import log from '../logger';
5
5
  import {
6
+ ClientConfigSetting,
7
+ ClientConfiguration,
6
8
  DataPacket, DataPacket_Kind, SpeakerInfo, TrackInfo, UserPacket,
7
9
  } from '../proto/livekit_models';
8
10
  import {
@@ -70,7 +72,9 @@ export default class RTCEngine extends (
70
72
 
71
73
  private reconnectStart: number = 0;
72
74
 
73
- private fullReconnect: boolean = false;
75
+ private fullReconnectOnNext: boolean = false;
76
+
77
+ private clientConfiguration?: ClientConfiguration;
74
78
 
75
79
  private connectedServerAddr?: string;
76
80
 
@@ -96,6 +100,7 @@ export default class RTCEngine extends (
96
100
  if (!this.subscriberPrimary) {
97
101
  this.negotiate();
98
102
  }
103
+ this.clientConfiguration = joinResponse.clientConfiguration;
99
104
 
100
105
  return joinResponse;
101
106
  }
@@ -303,7 +308,7 @@ export default class RTCEngine extends (
303
308
 
304
309
  this.client.onLeave = (leave?: LeaveRequest) => {
305
310
  if (leave?.canReconnect) {
306
- this.fullReconnect = true;
311
+ this.fullReconnectOnNext = true;
307
312
  this.primaryPC = undefined;
308
313
  } else {
309
314
  this.emit(EngineEvent.Disconnected);
@@ -376,19 +381,19 @@ export default class RTCEngine extends (
376
381
  if (this.isClosed) {
377
382
  return;
378
383
  }
379
- if (isFireFox()) {
380
- // FF does not support DTLS restart.
381
- this.fullReconnect = true;
384
+ if (isFireFox() // TODO remove once clientConfiguration handles firefox case server side
385
+ || this.clientConfiguration?.resumeConnection === ClientConfigSetting.DISABLED) {
386
+ this.fullReconnectOnNext = true;
382
387
  }
383
388
 
384
389
  try {
385
- if (this.fullReconnect) {
390
+ if (this.fullReconnectOnNext) {
386
391
  await this.restartConnection();
387
392
  } else {
388
393
  await this.resumeConnection();
389
394
  }
390
395
  this.reconnectAttempts = 0;
391
- this.fullReconnect = false;
396
+ this.fullReconnectOnNext = false;
392
397
  } catch (e) {
393
398
  this.reconnectAttempts += 1;
394
399
  let recoverable = true;
@@ -398,7 +403,7 @@ export default class RTCEngine extends (
398
403
  recoverable = false;
399
404
  } else if (!(e instanceof SignalReconnectError)) {
400
405
  // cannot resume
401
- this.fullReconnect = true;
406
+ this.fullReconnectOnNext = true;
402
407
  }
403
408
 
404
409
  const duration = Date.now() - this.reconnectStart;
@@ -444,6 +449,7 @@ export default class RTCEngine extends (
444
449
  }
445
450
 
446
451
  await this.waitForPCConnected();
452
+ this.client.setReconnected();
447
453
 
448
454
  // reconnect success
449
455
  this.emit(EngineEvent.Restarted, joinResponse);
@@ -478,6 +484,7 @@ export default class RTCEngine extends (
478
484
  }
479
485
 
480
486
  await this.waitForPCConnected();
487
+ this.client.setReconnected();
481
488
 
482
489
  // resume success
483
490
  this.emit(EngineEvent.Resumed);
@@ -564,7 +571,7 @@ export default class RTCEngine extends (
564
571
  this.publisher.negotiate();
565
572
  }
566
573
 
567
- private dataChannelForKind(kind: DataPacket_Kind): RTCDataChannel | undefined {
574
+ dataChannelForKind(kind: DataPacket_Kind): RTCDataChannel | undefined {
568
575
  if (kind === DataPacket_Kind.LOSSY) {
569
576
  return this.lossyDC;
570
577
  } if (kind === DataPacket_Kind.RELIABLE) {
package/src/room/Room.ts CHANGED
@@ -28,7 +28,8 @@ import LocalTrackPublication from './track/LocalTrackPublication';
28
28
  import RemoteTrackPublication from './track/RemoteTrackPublication';
29
29
  import { Track } from './track/Track';
30
30
  import { TrackPublication } from './track/TrackPublication';
31
- import { RemoteTrack } from './track/types';
31
+ import { AdaptiveStreamSettings, RemoteTrack } from './track/types';
32
+ import { getNewAudioContext } from './track/utils';
32
33
  import { unpackStreamId } from './utils';
33
34
 
34
35
  export enum RoomState {
@@ -195,7 +196,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
195
196
 
196
197
  try {
197
198
  const joinResponse = await this.engine.join(url, token, opts);
198
- log.debug('connected to Livekit Server', joinResponse.serverVersion);
199
+ log.debug(`connected to Livekit Server version: ${joinResponse.serverVersion}, region: ${joinResponse.serverRegion}`);
199
200
 
200
201
  if (!joinResponse.serverVersion) {
201
202
  throw new UnsupportedServer('unknown server version');
@@ -437,12 +438,20 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
437
438
  if (!trackId || trackId === '') trackId = mediaTrack.id;
438
439
 
439
440
  const participant = this.getOrCreateParticipant(participantId);
441
+ let adaptiveStreamSettings: AdaptiveStreamSettings | undefined;
442
+ if (this.options.adaptiveStream) {
443
+ if (typeof this.options.adaptiveStream === 'object') {
444
+ adaptiveStreamSettings = this.options.adaptiveStream;
445
+ } else {
446
+ adaptiveStreamSettings = {};
447
+ }
448
+ }
440
449
  participant.addSubscribedMediaTrack(
441
450
  mediaTrack,
442
451
  trackId,
443
452
  stream,
444
453
  receiver,
445
- this.options.adaptiveStream,
454
+ adaptiveStreamSettings,
446
455
  );
447
456
  }
448
457
 
@@ -458,6 +467,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
458
467
  };
459
468
 
460
469
  private handleRestarted = async (joinResponse: JoinResponse) => {
470
+ log.debug('reconnected to server region', joinResponse.serverRegion);
461
471
  this.state = RoomState.Connected;
462
472
  this.emit(RoomEvent.Reconnected);
463
473
  this.emit(RoomEvent.StateChanged, this.state);
@@ -717,10 +727,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
717
727
  }
718
728
  // by using an AudioContext, it reduces lag on audio elements
719
729
  // https://stackoverflow.com/questions/9811429/html5-audio-tag-on-safari-has-a-delay/54119854#54119854
720
- // @ts-ignore
721
- const AudioContext = window.AudioContext || window.webkitAudioContext;
722
- if (AudioContext) {
723
- this.audioContext = new AudioContext();
730
+ const ctx = getNewAudioContext();
731
+ if (ctx) {
732
+ this.audioContext = ctx;
724
733
  }
725
734
  }
726
735
 
@@ -826,6 +835,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
826
835
  participantTracks: [],
827
836
  },
828
837
  publishTracks: this.localParticipant.publishedTracksInfo(),
838
+ dataChannels: this.localParticipant.dataChannelsInfo(),
829
839
  });
830
840
  }
831
841
 
@@ -185,7 +185,7 @@ export enum RoomEvent {
185
185
  ConnectionQualityChanged = 'connectionQualityChanged',
186
186
 
187
187
  /**
188
- * StreamState indicates if a subscribed track has been paused by the SFU
188
+ * StreamState indicates if a subscribed (remote) track has been paused by the SFU
189
189
  * (typically this happens because of subscriber's bandwidth constraints)
190
190
  *
191
191
  * When bandwidth conditions allow, the track will be resumed automatically.
@@ -400,6 +400,11 @@ export enum TrackEvent {
400
400
  AudioPlaybackStarted = 'audioPlaybackStarted',
401
401
  /** @internal */
402
402
  AudioPlaybackFailed = 'audioPlaybackFailed',
403
+ /**
404
+ * @internal
405
+ * Only fires on LocalAudioTrack instances
406
+ */
407
+ AudioSilenceDetected = 'audioSilenceDetected',
403
408
  /** @internal */
404
409
  VisibilityChanged = 'visibilityChanged',
405
410
  /** @internal */
@@ -3,7 +3,9 @@ import { RoomOptions } from '../../options';
3
3
  import {
4
4
  DataPacket, DataPacket_Kind,
5
5
  } from '../../proto/livekit_models';
6
- import { AddTrackRequest, SubscribedQualityUpdate, TrackPublishedResponse } from '../../proto/livekit_rtc';
6
+ import {
7
+ AddTrackRequest, DataChannelInfo, SubscribedQualityUpdate, TrackPublishedResponse,
8
+ } from '../../proto/livekit_rtc';
7
9
  import {
8
10
  TrackInvalidError,
9
11
  UnexpectedConnectionState,
@@ -17,7 +19,8 @@ import LocalVideoTrack, { videoLayersFromEncodings } from '../track/LocalVideoTr
17
19
  import {
18
20
  CreateLocalTracksOptions,
19
21
  ScreenShareCaptureOptions,
20
- TrackPublishOptions, VideoCodec, VideoPresets,
22
+ ScreenSharePresets,
23
+ TrackPublishOptions, VideoCodec,
21
24
  } from '../track/options';
22
25
  import { Track } from '../track/Track';
23
26
  import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
@@ -272,7 +275,7 @@ export default class LocalParticipant extends Participant {
272
275
  options = {};
273
276
  }
274
277
  if (options.resolution === undefined) {
275
- options.resolution = VideoPresets.fhd.resolution;
278
+ options.resolution = ScreenSharePresets.h720fps15.resolution;
276
279
  }
277
280
 
278
281
  let videoConstraints: MediaTrackConstraints | boolean = true;
@@ -525,7 +528,7 @@ export default class LocalParticipant extends Participant {
525
528
  destination?: RemoteParticipant[] | string[]) {
526
529
  const dest: string[] = [];
527
530
  if (destination !== undefined) {
528
- destination.forEach((val : any) => {
531
+ destination.forEach((val: any) => {
529
532
  if (val instanceof RemoteParticipant) {
530
533
  dest.push(val.sid);
531
534
  } else {
@@ -681,4 +684,20 @@ export default class LocalParticipant extends Participant {
681
684
  });
682
685
  return infos;
683
686
  }
687
+
688
+ /** @internal */
689
+ dataChannelsInfo(): DataChannelInfo[] {
690
+ const infos: DataChannelInfo[] = [];
691
+ const getInfo = (dc: RTCDataChannel | undefined) => {
692
+ if (dc?.id !== undefined && dc.id !== null) {
693
+ infos.push({
694
+ label: dc.label,
695
+ id: dc.id,
696
+ });
697
+ }
698
+ };
699
+ getInfo(this.engine.dataChannelForKind(DataPacket_Kind.LOSSY));
700
+ getInfo(this.engine.dataChannelForKind(DataPacket_Kind.RELIABLE));
701
+ return infos;
702
+ }
684
703
  }
@@ -10,7 +10,7 @@ import RemoteAudioTrack from '../track/RemoteAudioTrack';
10
10
  import RemoteTrackPublication from '../track/RemoteTrackPublication';
11
11
  import RemoteVideoTrack from '../track/RemoteVideoTrack';
12
12
  import { Track } from '../track/Track';
13
- import { RemoteTrack } from '../track/types';
13
+ import { AdaptiveStreamSettings, RemoteTrack } from '../track/types';
14
14
  import Participant, { ParticipantEventCallbacks } from './Participant';
15
15
 
16
16
  export default class RemoteParticipant extends Participant {
@@ -82,7 +82,7 @@ export default class RemoteParticipant extends Participant {
82
82
  sid: Track.SID,
83
83
  mediaStream: MediaStream,
84
84
  receiver?: RTCRtpReceiver,
85
- adaptiveStream?: boolean,
85
+ adaptiveStreamSettings?: AdaptiveStreamSettings,
86
86
  triesLeft?: number,
87
87
  ) {
88
88
  // find the track publication
@@ -114,7 +114,7 @@ export default class RemoteParticipant extends Participant {
114
114
  if (triesLeft === undefined) triesLeft = 20;
115
115
  setTimeout(() => {
116
116
  this.addSubscribedMediaTrack(mediaTrack, sid, mediaStream,
117
- receiver, adaptiveStream, triesLeft! - 1);
117
+ receiver, adaptiveStreamSettings, triesLeft! - 1);
118
118
  }, 150);
119
119
  return;
120
120
  }
@@ -122,7 +122,7 @@ export default class RemoteParticipant extends Participant {
122
122
  const isVideo = mediaTrack.kind === 'video';
123
123
  let track: RemoteTrack;
124
124
  if (isVideo) {
125
- track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStream);
125
+ track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStreamSettings);
126
126
  } else {
127
127
  track = new RemoteAudioTrack(mediaTrack, sid, receiver);
128
128
  }
@@ -1,11 +1,15 @@
1
- import { VideoPresets, VideoPresets43 } from '../track/options';
2
1
  import {
2
+ ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43,
3
+ } from '../track/options';
4
+ import {
5
+ computeDefaultScreenShareSimulcastPresets,
3
6
  computeVideoEncodings,
4
7
  determineAppropriateEncoding,
5
8
  presets169,
6
9
  presets43,
7
10
  presetsForResolution,
8
11
  presetsScreenShare,
12
+ sortPresets,
9
13
  } from './publishUtils';
10
14
 
11
15
  describe('presetsForResolution', () => {
@@ -63,7 +67,7 @@ describe('computeVideoEncodings', () => {
63
67
 
64
68
  // ensure they are what we expect
65
69
  expect(encodings![0].rid).toBe('q');
66
- expect(encodings![0].maxBitrate).toBe(VideoPresets.qvga.encoding.maxBitrate);
70
+ expect(encodings![0].maxBitrate).toBe(VideoPresets.h180.encoding.maxBitrate);
67
71
  expect(encodings![0].scaleResolutionDownBy).toBe(3);
68
72
  expect(encodings![1].rid).toBe('h');
69
73
  expect(encodings![1].scaleResolutionDownBy).toBe(1.5);
@@ -77,7 +81,7 @@ describe('computeVideoEncodings', () => {
77
81
  expect(encodings).toHaveLength(3);
78
82
  expect(encodings![0].scaleResolutionDownBy).toBe(3);
79
83
  expect(encodings![1].scaleResolutionDownBy).toBe(1.5);
80
- expect(encodings![2].maxBitrate).toBe(VideoPresets.qhd.encoding.maxBitrate);
84
+ expect(encodings![2].maxBitrate).toBe(VideoPresets.h540.encoding.maxBitrate);
81
85
  });
82
86
 
83
87
  it('returns two encodings for lower-res simulcast', () => {
@@ -88,9 +92,9 @@ describe('computeVideoEncodings', () => {
88
92
 
89
93
  // ensure they are what we expect
90
94
  expect(encodings![0].rid).toBe('q');
91
- expect(encodings![0].maxBitrate).toBe(VideoPresets.qvga.encoding.maxBitrate);
95
+ expect(encodings![0].maxBitrate).toBe(VideoPresets.h180.encoding.maxBitrate);
92
96
  expect(encodings![1].rid).toBe('h');
93
- expect(encodings![1].maxBitrate).toBe(VideoPresets.vga.encoding.maxBitrate);
97
+ expect(encodings![1].maxBitrate).toBe(VideoPresets.h360.encoding.maxBitrate);
94
98
  });
95
99
 
96
100
  it('respects provided min resolution', () => {
@@ -99,7 +103,43 @@ describe('computeVideoEncodings', () => {
99
103
  });
100
104
  expect(encodings).toHaveLength(1);
101
105
  expect(encodings![0].rid).toBe('q');
102
- expect(encodings![0].maxBitrate).toBe(VideoPresets43.qvga.encoding.maxBitrate);
106
+ expect(encodings![0].maxBitrate).toBe(VideoPresets43.h120.encoding.maxBitrate);
103
107
  expect(encodings![0].scaleResolutionDownBy).toBe(1);
104
108
  });
105
109
  });
110
+
111
+ describe('customSimulcastLayers', () => {
112
+ it('sorts presets from lowest to highest', () => {
113
+ const sortedPresets = sortPresets(
114
+ [VideoPresets.h1440, VideoPresets.h360, VideoPresets.h1080, VideoPresets.h90],
115
+ ) as Array<VideoPreset>;
116
+ expect(sortPresets).not.toBeUndefined();
117
+ expect(sortedPresets[0]).toBe(VideoPresets.h90);
118
+ expect(sortedPresets[1]).toBe(VideoPresets.h360);
119
+ expect(sortedPresets[2]).toBe(VideoPresets.h1080);
120
+ expect(sortedPresets[3]).toBe(VideoPresets.h1440);
121
+ });
122
+ it('sorts presets from lowest to highest, even when dimensions are the same', () => {
123
+ const sortedPresets = sortPresets([
124
+ new VideoPreset(1920, 1080, 3_000_000, 20),
125
+ new VideoPreset(1920, 1080, 2_000_000, 15),
126
+ new VideoPreset(1920, 1080, 3_000_000, 15),
127
+ ]) as Array<VideoPreset>;
128
+ expect(sortPresets).not.toBeUndefined();
129
+ expect(sortedPresets[0].encoding.maxBitrate).toBe(2_000_000);
130
+ expect(sortedPresets[1].encoding.maxFramerate).toBe(15);
131
+ expect(sortedPresets[2].encoding.maxFramerate).toBe(20);
132
+ });
133
+ });
134
+
135
+ describe('screenShareSimulcastDefaults', () => {
136
+ it('computes appropriate bitrate from original preset', () => {
137
+ const defaultSimulcastLayers = computeDefaultScreenShareSimulcastPresets(
138
+ ScreenSharePresets.h720fps15,
139
+ );
140
+ expect(defaultSimulcastLayers[0].width).toBe(640);
141
+ expect(defaultSimulcastLayers[0].height).toBe(360);
142
+ expect(defaultSimulcastLayers[0].encoding.maxFramerate).toBe(3);
143
+ expect(defaultSimulcastLayers[0].encoding.maxBitrate).toBe(150_000);
144
+ });
145
+ });