livekit-client 1.14.2 → 1.14.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/dist/livekit-client.esm.mjs +169 -115
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/room/PCTransport.d.ts.map +1 -1
  6. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  7. package/dist/src/room/Room.d.ts +1 -1
  8. package/dist/src/room/Room.d.ts.map +1 -1
  9. package/dist/src/room/events.d.ts +2 -0
  10. package/dist/src/room/events.d.ts.map +1 -1
  11. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  12. package/dist/src/room/participant/Participant.d.ts +1 -0
  13. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  14. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  15. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  16. package/dist/src/room/track/Track.d.ts.map +1 -1
  17. package/dist/src/room/track/options.d.ts +10 -4
  18. package/dist/src/room/track/options.d.ts.map +1 -1
  19. package/dist/ts4.2/src/room/Room.d.ts +1 -1
  20. package/dist/ts4.2/src/room/events.d.ts +2 -0
  21. package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -0
  22. package/dist/ts4.2/src/room/track/options.d.ts +10 -4
  23. package/package.json +4 -4
  24. package/src/room/PCTransport.ts +6 -4
  25. package/src/room/RTCEngine.ts +7 -7
  26. package/src/room/Room.ts +4 -2
  27. package/src/room/events.ts +4 -0
  28. package/src/room/participant/LocalParticipant.ts +7 -12
  29. package/src/room/participant/Participant.ts +1 -0
  30. package/src/room/participant/publishUtils.ts +6 -1
  31. package/src/room/track/RemoteAudioTrack.ts +7 -3
  32. package/src/room/track/Track.ts +6 -1
  33. package/src/room/track/options.ts +8 -2
@@ -723,6 +723,34 @@ var ScalarType;
723
723
  ScalarType[ScalarType["SINT32"] = 17] = "SINT32";
724
724
  ScalarType[ScalarType["SINT64"] = 18] = "SINT64";
725
725
  })(ScalarType || (ScalarType = {}));
726
+ /**
727
+ * JavaScript representation of fields with 64 bit integral types (int64, uint64,
728
+ * sint64, fixed64, sfixed64).
729
+ *
730
+ * This is a subset of google.protobuf.FieldOptions.JSType, which defines JS_NORMAL,
731
+ * JS_STRING, and JS_NUMBER. Protobuf-ES uses BigInt by default, but will use
732
+ * String if `[jstype = JS_STRING]` is specified.
733
+ *
734
+ * ```protobuf
735
+ * uint64 field_a = 1; // BigInt
736
+ * uint64 field_b = 2 [jstype = JS_NORMAL]; // BigInt
737
+ * uint64 field_b = 2 [jstype = JS_NUMBER]; // BigInt
738
+ * uint64 field_b = 2 [jstype = JS_STRING]; // String
739
+ * ```
740
+ */
741
+ var LongType;
742
+ (function (LongType) {
743
+ /**
744
+ * Use JavaScript BigInt.
745
+ */
746
+ LongType[LongType["BIGINT"] = 0] = "BIGINT";
747
+ /**
748
+ * Use JavaScript String.
749
+ *
750
+ * Field option `[jstype = JS_STRING]`.
751
+ */
752
+ LongType[LongType["STRING"] = 1] = "STRING";
753
+ })(LongType || (LongType = {}));
726
754
 
727
755
  // Copyright 2008 Google Inc. All rights reserved.
728
756
  //
@@ -1681,7 +1709,7 @@ function scalarEquals(type, a, b) {
1681
1709
  * Returns the default value for the given scalar type, following
1682
1710
  * proto3 semantics.
1683
1711
  */
1684
- function scalarDefaultValue(type) {
1712
+ function scalarDefaultValue(type, longType) {
1685
1713
  switch (type) {
1686
1714
  case ScalarType.BOOL:
1687
1715
  return false;
@@ -1690,7 +1718,8 @@ function scalarDefaultValue(type) {
1690
1718
  case ScalarType.INT64:
1691
1719
  case ScalarType.SFIXED64:
1692
1720
  case ScalarType.SINT64:
1693
- return protoInt64.zero;
1721
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison -- acceptable since it's covered by tests
1722
+ return longType == 0 ? protoInt64.zero : "0";
1694
1723
  case ScalarType.DOUBLE:
1695
1724
  case ScalarType.FLOAT:
1696
1725
  return 0.0;
@@ -1734,13 +1763,13 @@ function scalarTypeInfo(type, value) {
1734
1763
  wireType = WireType.Bit32;
1735
1764
  break;
1736
1765
  case ScalarType.INT64:
1737
- isIntrinsicDefault = isUndefined || value == 0;
1766
+ isIntrinsicDefault = isUndefined || value == 0; // Loose comparison matches 0n, 0 and "0"
1738
1767
  break;
1739
1768
  case ScalarType.UINT64:
1740
- isIntrinsicDefault = isUndefined || value == 0;
1769
+ isIntrinsicDefault = isUndefined || value == 0; // Loose comparison matches 0n, 0 and "0"
1741
1770
  break;
1742
1771
  case ScalarType.FIXED64:
1743
- isIntrinsicDefault = isUndefined || value == 0;
1772
+ isIntrinsicDefault = isUndefined || value == 0; // Loose comparison matches 0n, 0 and "0"
1744
1773
  wireType = WireType.Bit64;
1745
1774
  break;
1746
1775
  case ScalarType.BYTES:
@@ -1855,18 +1884,23 @@ function makeBinaryFormatCommon() {
1855
1884
  case "scalar":
1856
1885
  case "enum":
1857
1886
  const scalarType = field.kind == "enum" ? ScalarType.INT32 : field.T;
1887
+ let read = readScalar$1;
1888
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison -- acceptable since it's covered by tests
1889
+ if (field.kind == "scalar" && field.L > 0) {
1890
+ read = readScalarLTString;
1891
+ }
1858
1892
  if (repeated) {
1859
1893
  let arr = target[localName]; // safe to assume presence of array, oneof cannot contain repeated values
1860
1894
  if (wireType == WireType.LengthDelimited && scalarType != ScalarType.STRING && scalarType != ScalarType.BYTES) {
1861
1895
  let e = reader.uint32() + reader.pos;
1862
1896
  while (reader.pos < e) {
1863
- arr.push(readScalar$1(reader, scalarType));
1897
+ arr.push(read(reader, scalarType));
1864
1898
  }
1865
1899
  } else {
1866
- arr.push(readScalar$1(reader, scalarType));
1900
+ arr.push(read(reader, scalarType));
1867
1901
  }
1868
1902
  } else {
1869
- target[localName] = readScalar$1(reader, scalarType);
1903
+ target[localName] = read(reader, scalarType);
1870
1904
  }
1871
1905
  break;
1872
1906
  case "message":
@@ -1929,7 +1963,7 @@ function readMapEntry(field, reader, options) {
1929
1963
  }
1930
1964
  }
1931
1965
  if (key === undefined) {
1932
- let keyRaw = scalarDefaultValue(field.K);
1966
+ let keyRaw = scalarDefaultValue(field.K, LongType.BIGINT);
1933
1967
  key = field.K == ScalarType.BOOL ? keyRaw.toString() : keyRaw;
1934
1968
  }
1935
1969
  if (typeof key != "string" && typeof key != "number") {
@@ -1938,7 +1972,7 @@ function readMapEntry(field, reader, options) {
1938
1972
  if (val === undefined) {
1939
1973
  switch (field.V.kind) {
1940
1974
  case "scalar":
1941
- val = scalarDefaultValue(field.V.T);
1975
+ val = scalarDefaultValue(field.V.T, LongType.BIGINT);
1942
1976
  break;
1943
1977
  case "enum":
1944
1978
  val = 0;
@@ -1950,6 +1984,12 @@ function readMapEntry(field, reader, options) {
1950
1984
  }
1951
1985
  return [key, val];
1952
1986
  }
1987
+ // Read a scalar value, but return 64 bit integral types (int64, uint64,
1988
+ // sint64, fixed64, sfixed64) as string instead of bigint.
1989
+ function readScalarLTString(reader, type) {
1990
+ const v = readScalar$1(reader, type);
1991
+ return typeof v == "bigint" ? v.toString() : v;
1992
+ }
1953
1993
  // Does not use scalarTypeInfo() for better performance.
1954
1994
  function readScalar$1(reader, type) {
1955
1995
  switch (type) {
@@ -2340,7 +2380,7 @@ function makeJsonFormatCommon(makeWriteField) {
2340
2380
  break;
2341
2381
  case "scalar":
2342
2382
  try {
2343
- val = readScalar(field.T, jsonItem);
2383
+ val = readScalar(field.T, jsonItem, field.L);
2344
2384
  } catch (e) {
2345
2385
  let m = "cannot decode field ".concat(type.typeName, ".").concat(field.name, " from JSON: ").concat(this.debug(jsonItem));
2346
2386
  if (e instanceof Error && e.message.length > 0) {
@@ -2375,7 +2415,7 @@ function makeJsonFormatCommon(makeWriteField) {
2375
2415
  break;
2376
2416
  case "scalar":
2377
2417
  try {
2378
- val = readScalar(field.V.T, jsonMapValue);
2418
+ val = readScalar(field.V.T, jsonMapValue, LongType.BIGINT);
2379
2419
  } catch (e) {
2380
2420
  let m = "cannot decode map value for field ".concat(type.typeName, ".").concat(field.name, " from JSON: ").concat(this.debug(jsonValue));
2381
2421
  if (e instanceof Error && e.message.length > 0) {
@@ -2386,7 +2426,7 @@ function makeJsonFormatCommon(makeWriteField) {
2386
2426
  break;
2387
2427
  }
2388
2428
  try {
2389
- targetMap[readScalar(field.K, field.K == ScalarType.BOOL ? jsonMapKey == "true" ? true : jsonMapKey == "false" ? false : jsonMapKey : jsonMapKey).toString()] = val;
2429
+ targetMap[readScalar(field.K, field.K == ScalarType.BOOL ? jsonMapKey == "true" ? true : jsonMapKey == "false" ? false : jsonMapKey : jsonMapKey, LongType.BIGINT).toString()] = val;
2390
2430
  } catch (e) {
2391
2431
  let m = "cannot decode map key for field ".concat(type.typeName, ".").concat(field.name, " from JSON: ").concat(this.debug(jsonValue));
2392
2432
  if (e instanceof Error && e.message.length > 0) {
@@ -2422,7 +2462,7 @@ function makeJsonFormatCommon(makeWriteField) {
2422
2462
  break;
2423
2463
  case "scalar":
2424
2464
  try {
2425
- target[localName] = readScalar(field.T, jsonValue);
2465
+ target[localName] = readScalar(field.T, jsonValue, field.L);
2426
2466
  } catch (e) {
2427
2467
  let m = "cannot decode field ".concat(type.typeName, ".").concat(field.name, " from JSON: ").concat(this.debug(jsonValue));
2428
2468
  if (e instanceof Error && e.message.length > 0) {
@@ -2488,7 +2528,7 @@ function debugJsonValue(json) {
2488
2528
  }
2489
2529
  // May throw an error. If the error message is non-blank, it should be shown.
2490
2530
  // It is up to the caller to provide context.
2491
- function readScalar(type, json) {
2531
+ function readScalar(type, json, longType) {
2492
2532
  // every valid case in the switch below returns, and every fall
2493
2533
  // through is regarded as a failure.
2494
2534
  switch (type) {
@@ -2542,12 +2582,16 @@ function readScalar(type, json) {
2542
2582
  case ScalarType.SINT64:
2543
2583
  if (json === null) return protoInt64.zero;
2544
2584
  if (typeof json != "number" && typeof json != "string") break;
2545
- return protoInt64.parse(json);
2585
+ const long = protoInt64.parse(json);
2586
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
2587
+ return longType ? long.toString() : long;
2546
2588
  case ScalarType.FIXED64:
2547
2589
  case ScalarType.UINT64:
2548
2590
  if (json === null) return protoInt64.zero;
2549
2591
  if (typeof json != "number" && typeof json != "string") break;
2550
- return protoInt64.uParse(json);
2592
+ const uLong = protoInt64.uParse(json);
2593
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
2594
+ return longType ? uLong.toString() : uLong;
2551
2595
  // bool:
2552
2596
  case ScalarType.BOOL:
2553
2597
  if (json === null) return false;
@@ -3238,7 +3282,7 @@ const proto3 = makeProtoRuntime("proto3", makeJsonFormatProto3(), makeBinaryForm
3238
3282
  t[name] = {};
3239
3283
  break;
3240
3284
  case "scalar":
3241
- t[name] = scalarDefaultValue(member.T); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
3285
+ t[name] = scalarDefaultValue(member.T, member.L); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
3242
3286
  break;
3243
3287
  }
3244
3288
  }
@@ -3246,7 +3290,7 @@ const proto3 = makeProtoRuntime("proto3", makeJsonFormatProto3(), makeBinaryForm
3246
3290
  }));
3247
3291
  /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
3248
3292
  function normalizeFieldInfosProto3(fieldInfos) {
3249
- var _a, _b, _c;
3293
+ var _a, _b, _c, _d;
3250
3294
  const r = [];
3251
3295
  let o;
3252
3296
  for (const field of typeof fieldInfos == "function" ? fieldInfos() : fieldInfos) {
@@ -3254,12 +3298,15 @@ function normalizeFieldInfosProto3(fieldInfos) {
3254
3298
  f.localName = localFieldName(field.name, field.oneof !== undefined);
3255
3299
  f.jsonName = (_a = field.jsonName) !== null && _a !== void 0 ? _a : fieldJsonName(field.name);
3256
3300
  f.repeated = (_b = field.repeated) !== null && _b !== void 0 ? _b : false;
3301
+ if (field.kind == "scalar") {
3302
+ f.L = (_c = field.L) !== null && _c !== void 0 ? _c : LongType.BIGINT;
3303
+ }
3257
3304
  // From the proto3 language guide:
3258
3305
  // > In proto3, repeated fields of scalar numeric types are packed by default.
3259
3306
  // This information is incomplete - according to the conformance tests, BOOL
3260
3307
  // and ENUM are packed by default as well. This means only STRING and BYTES
3261
3308
  // are not packed by default, which makes sense because they are length-delimited.
3262
- f.packed = (_c = field.packed) !== null && _c !== void 0 ? _c : field.kind == "enum" || field.kind == "scalar" && field.T != ScalarType.BYTES && field.T != ScalarType.STRING;
3309
+ f.packed = (_d = field.packed) !== null && _d !== void 0 ? _d : field.kind == "enum" || field.kind == "scalar" && field.T != ScalarType.BYTES && field.T != ScalarType.STRING;
3263
3310
  // We do not surface options at this time
3264
3311
  // f.options = field.options ?? emptyReadonlyObject;
3265
3312
  if (field.oneof !== undefined) {
@@ -11998,7 +12045,7 @@ function getMatch(exp, ua) {
11998
12045
  return match && match.length >= id && match[id] || '';
11999
12046
  }
12000
12047
 
12001
- var version$1 = "1.14.2";
12048
+ var version$1 = "1.14.4";
12002
12049
 
12003
12050
  const version = version$1;
12004
12051
  const protocolVersion = 10;
@@ -12486,6 +12533,9 @@ var ParticipantEvent;
12486
12533
  // fired only on LocalParticipant
12487
12534
  /** @internal */
12488
12535
  ParticipantEvent["MediaDevicesError"] = "mediaDevicesError";
12536
+ // fired only on LocalParticipant
12537
+ /** @internal */
12538
+ ParticipantEvent["AudioStreamAcquired"] = "audioStreamAcquired";
12489
12539
  /**
12490
12540
  * A participant's permission has changed. Currently only fired on LocalParticipant.
12491
12541
  * args: (prevPermissions: [[ParticipantPermission]])
@@ -12782,7 +12832,12 @@ function attachToElement(track, element) {
12782
12832
  });
12783
12833
  mediaStream.addTrack(track);
12784
12834
  }
12785
- element.autoplay = true;
12835
+ if (!isSafari() || !(element instanceof HTMLVideoElement)) {
12836
+ // when in low power mode (applies to both macOS and iOS), Safari will show a play/pause overlay
12837
+ // when a video starts that has the `autoplay` attribute is set.
12838
+ // we work around this by _not_ setting the autoplay attribute on safari and instead call `setTimeout(() => el.play(),0)` further down
12839
+ element.autoplay = true;
12840
+ }
12786
12841
  // In case there are no audio tracks present on the mediastream, we set the element as muted to ensure autoplay works
12787
12842
  element.muted = mediaStream.getAudioTracks().length === 0;
12788
12843
  if (element instanceof HTMLVideoElement) {
@@ -16089,7 +16144,7 @@ class PCTransport extends eventsExports.EventEmitter {
16089
16144
  for (const fmtp of media.fmtp) {
16090
16145
  if (fmtp.payload === codecPayload) {
16091
16146
  if (!fmtp.config.includes('x-google-start-bitrate')) {
16092
- fmtp.config += ";x-google-start-bitrate=".concat(trackbr.maxbr * startBitrateForSVC);
16147
+ fmtp.config += ";x-google-start-bitrate=".concat(Math.round(trackbr.maxbr * startBitrateForSVC));
16093
16148
  }
16094
16149
  if (!fmtp.config.includes('x-google-max-bitrate')) {
16095
16150
  fmtp.config += ";x-google-max-bitrate=".concat(trackbr.maxbr);
@@ -16101,7 +16156,7 @@ class PCTransport extends eventsExports.EventEmitter {
16101
16156
  if (!fmtpFound) {
16102
16157
  media.fmtp.push({
16103
16158
  payload: codecPayload,
16104
- config: "x-google-start-bitrate=".concat(trackbr.maxbr * startBitrateForSVC, ";x-google-max-bitrate=").concat(trackbr.maxbr)
16159
+ config: "x-google-start-bitrate=".concat(Math.round(trackbr.maxbr * startBitrateForSVC), ";x-google-max-bitrate=").concat(trackbr.maxbr)
16105
16160
  });
16106
16161
  }
16107
16162
  return true;
@@ -16772,19 +16827,13 @@ class RTCEngine extends eventsExports.EventEmitter {
16772
16827
  this.regionUrlProvider = provider;
16773
16828
  }
16774
16829
  configure(joinResponse) {
16775
- var _a, _b;
16830
+ var _a;
16776
16831
  // already configured
16777
16832
  if (this.publisher || this.subscriber) {
16778
16833
  return;
16779
16834
  }
16780
16835
  this.participantSid = (_a = joinResponse.participant) === null || _a === void 0 ? void 0 : _a.sid;
16781
16836
  const rtcConfig = this.makeRTCConfiguration(joinResponse);
16782
- if ((_b = this.signalOpts) === null || _b === void 0 ? void 0 : _b.e2eeEnabled) {
16783
- livekitLogger.debug('E2EE - setting up transports with insertable streams');
16784
- // this makes sure that no data is sent before the transforms are ready
16785
- // @ts-ignore
16786
- rtcConfig.encodedInsertableStreams = true;
16787
- }
16788
16837
  const googConstraints = {
16789
16838
  optional: [{
16790
16839
  googDscp: true
@@ -16916,7 +16965,14 @@ class RTCEngine extends eventsExports.EventEmitter {
16916
16965
  };
16917
16966
  }
16918
16967
  makeRTCConfiguration(serverResponse) {
16968
+ var _a;
16919
16969
  const rtcConfig = Object.assign({}, this.rtcConfig);
16970
+ if ((_a = this.signalOpts) === null || _a === void 0 ? void 0 : _a.e2eeEnabled) {
16971
+ livekitLogger.debug('E2EE - setting up transports with insertable streams');
16972
+ // this makes sure that no data is sent before the transforms are ready
16973
+ // @ts-ignore
16974
+ rtcConfig.encodedInsertableStreams = true;
16975
+ }
16920
16976
  // update ICE servers before creating PeerConnection
16921
16977
  if (serverResponse.iceServers && !rtcConfig.iceServers) {
16922
16978
  const rtcIceServers = [];
@@ -17232,6 +17288,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17232
17288
  let message = '';
17233
17289
  if (e instanceof Error) {
17234
17290
  message = e.message;
17291
+ livekitLogger.error(e.message);
17235
17292
  }
17236
17293
  if (e instanceof ConnectionError && e.reason === 0 /* ConnectionErrorReason.NotAllowed */) {
17237
17294
  throw new UnexpectedConnectionState('could not reconnect, token might be expired');
@@ -17906,7 +17963,8 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
17906
17963
  }
17907
17964
  function computeTrackBackupEncodings(track, videoCodec, opts) {
17908
17965
  var _a, _b, _c, _d;
17909
- if (!opts.backupCodec || opts.backupCodec.codec === opts.videoCodec) {
17966
+ // backupCodec should not be true anymore, default codec is set in LocalParticipant.publish
17967
+ if (!opts.backupCodec || opts.backupCodec === true || opts.backupCodec.codec === opts.videoCodec) {
17910
17968
  // backup codec publishing is disabled
17911
17969
  return;
17912
17970
  }
@@ -18774,9 +18832,6 @@ class RemoteAudioTrack extends RemoteTrack {
18774
18832
  } else {
18775
18833
  super.attach(element);
18776
18834
  }
18777
- if (this.elementVolume) {
18778
- element.volume = this.elementVolume;
18779
- }
18780
18835
  if (this.sinkId && supportsSetSinkId(element)) {
18781
18836
  /* @ts-ignore */
18782
18837
  element.setSinkId(this.sinkId);
@@ -18787,6 +18842,10 @@ class RemoteAudioTrack extends RemoteTrack {
18787
18842
  element.volume = 0;
18788
18843
  element.muted = true;
18789
18844
  }
18845
+ if (this.elementVolume) {
18846
+ // make sure volume setting is being applied to the newly attached element
18847
+ this.setVolume(this.elementVolume);
18848
+ }
18790
18849
  return element;
18791
18850
  }
18792
18851
  detach(element) {
@@ -20485,6 +20544,7 @@ class LocalParticipant extends Participant {
20485
20544
  }
20486
20545
  if (constraints.audio) {
20487
20546
  this.microphoneError = undefined;
20547
+ this.emit(ParticipantEvent.AudioStreamAcquired);
20488
20548
  }
20489
20549
  if (constraints.video) {
20490
20550
  this.cameraError = undefined;
@@ -20531,6 +20591,7 @@ class LocalParticipant extends Participant {
20531
20591
  screenVideo.source = Track.Source.ScreenShare;
20532
20592
  const localTracks = [screenVideo];
20533
20593
  if (stream.getAudioTracks().length > 0) {
20594
+ this.emit(ParticipantEvent.AudioStreamAcquired);
20534
20595
  const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false, this.audioContext);
20535
20596
  screenAudio.source = Track.Source.ScreenShareAudio;
20536
20597
  localTracks.push(screenAudio);
@@ -20645,18 +20706,7 @@ class LocalParticipant extends Participant {
20645
20706
  return __awaiter(this, void 0, void 0, function* () {
20646
20707
  const existingTrackOfSource = Array.from(this.tracks.values()).find(publishedTrack => track instanceof LocalTrack && publishedTrack.source === track.source);
20647
20708
  if (existingTrackOfSource && track.source !== Track.Source.Unknown) {
20648
- try {
20649
- // throw an Error in order to capture the stack trace
20650
- throw Error("publishing a second track with the same source: ".concat(track.source));
20651
- } catch (e) {
20652
- if (e instanceof Error) {
20653
- livekitLogger.warn(e.message, {
20654
- oldTrack: existingTrackOfSource,
20655
- newTrack: track,
20656
- trace: e.stack
20657
- });
20658
- }
20659
- }
20709
+ livekitLogger.info("publishing a second track with the same source: ".concat(track.source));
20660
20710
  }
20661
20711
  if (opts.stopMicTrackOnMute && track instanceof LocalAudioTrack) {
20662
20712
  track.stopOnMute = true;
@@ -20735,7 +20785,13 @@ class LocalParticipant extends Participant {
20735
20785
  cid: track.mediaStreamTrack.id
20736
20786
  })];
20737
20787
  // set up backup
20788
+ if (opts.backupCodec === true) {
20789
+ opts.backupCodec = {
20790
+ codec: defaultVideoCodec
20791
+ };
20792
+ }
20738
20793
  if (opts.backupCodec && videoCodec !== opts.backupCodec.codec) {
20794
+ // multi-codec simulcast requires dynacast
20739
20795
  if (!this.roomOptions.dynacast) {
20740
20796
  this.roomOptions.dynacast = true;
20741
20797
  }
@@ -21415,6 +21471,70 @@ class Room extends eventsExports.EventEmitter {
21415
21471
  this.onPageLeave = () => __awaiter(this, void 0, void 0, function* () {
21416
21472
  yield this.disconnect();
21417
21473
  });
21474
+ /**
21475
+ * Browsers have different policies regarding audio playback. Most requiring
21476
+ * some form of user interaction (click/tap/etc).
21477
+ * In those cases, audio will be silent until a click/tap triggering one of the following
21478
+ * - `startAudio`
21479
+ * - `getUserMedia`
21480
+ */
21481
+ this.startAudio = () => __awaiter(this, void 0, void 0, function* () {
21482
+ const elements = [];
21483
+ const browser = getBrowser();
21484
+ if (browser && browser.os === 'iOS') {
21485
+ /**
21486
+ * iOS blocks audio element playback if
21487
+ * - user is not publishing audio themselves and
21488
+ * - no other audio source is playing
21489
+ *
21490
+ * as a workaround, we create an audio element with an empty track, so that
21491
+ * silent audio is always playing
21492
+ */
21493
+ const audioId = 'livekit-dummy-audio-el';
21494
+ let dummyAudioEl = document.getElementById(audioId);
21495
+ if (!dummyAudioEl) {
21496
+ dummyAudioEl = document.createElement('audio');
21497
+ dummyAudioEl.id = audioId;
21498
+ dummyAudioEl.autoplay = true;
21499
+ dummyAudioEl.hidden = true;
21500
+ const track = getEmptyAudioStreamTrack();
21501
+ track.enabled = true;
21502
+ const stream = new MediaStream([track]);
21503
+ dummyAudioEl.srcObject = stream;
21504
+ document.addEventListener('visibilitychange', () => {
21505
+ if (!dummyAudioEl) {
21506
+ return;
21507
+ }
21508
+ // set the srcObject to null on page hide in order to prevent lock screen controls to show up for it
21509
+ dummyAudioEl.srcObject = document.hidden ? null : stream;
21510
+ });
21511
+ document.body.append(dummyAudioEl);
21512
+ this.once(RoomEvent.Disconnected, () => {
21513
+ dummyAudioEl === null || dummyAudioEl === void 0 ? void 0 : dummyAudioEl.remove();
21514
+ });
21515
+ }
21516
+ elements.push(dummyAudioEl);
21517
+ }
21518
+ this.participants.forEach(p => {
21519
+ p.audioTracks.forEach(t => {
21520
+ if (t.track) {
21521
+ t.track.attachedElements.forEach(e => {
21522
+ elements.push(e);
21523
+ });
21524
+ }
21525
+ });
21526
+ });
21527
+ try {
21528
+ yield Promise.all([this.acquireAudioContext(), ...elements.map(e => {
21529
+ e.muted = false;
21530
+ return e.play();
21531
+ })]);
21532
+ this.handleAudioPlaybackStarted();
21533
+ } catch (err) {
21534
+ this.handleAudioPlaybackFailed(err);
21535
+ throw err;
21536
+ }
21537
+ });
21418
21538
  this.handleRestarting = () => {
21419
21539
  this.clearConnectionReconcile();
21420
21540
  // also unwind existing participants & existing subscriptions
@@ -21970,72 +22090,6 @@ class Room extends eventsExports.EventEmitter {
21970
22090
  }
21971
22091
  });
21972
22092
  }
21973
- /**
21974
- * Browsers have different policies regarding audio playback. Most requiring
21975
- * some form of user interaction (click/tap/etc).
21976
- * In those cases, audio will be silent until a click/tap triggering one of the following
21977
- * - `startAudio`
21978
- * - `getUserMedia`
21979
- */
21980
- startAudio() {
21981
- return __awaiter(this, void 0, void 0, function* () {
21982
- const elements = [];
21983
- const browser = getBrowser();
21984
- if (browser && browser.os === 'iOS') {
21985
- /**
21986
- * iOS blocks audio element playback if
21987
- * - user is not publishing audio themselves and
21988
- * - no other audio source is playing
21989
- *
21990
- * as a workaround, we create an audio element with an empty track, so that
21991
- * silent audio is always playing
21992
- */
21993
- const audioId = 'livekit-dummy-audio-el';
21994
- let dummyAudioEl = document.getElementById(audioId);
21995
- if (!dummyAudioEl) {
21996
- dummyAudioEl = document.createElement('audio');
21997
- dummyAudioEl.id = audioId;
21998
- dummyAudioEl.autoplay = true;
21999
- dummyAudioEl.hidden = true;
22000
- const track = getEmptyAudioStreamTrack();
22001
- track.enabled = true;
22002
- const stream = new MediaStream([track]);
22003
- dummyAudioEl.srcObject = stream;
22004
- document.addEventListener('visibilitychange', () => {
22005
- if (!dummyAudioEl) {
22006
- return;
22007
- }
22008
- // set the srcObject to null on page hide in order to prevent lock screen controls to show up for it
22009
- dummyAudioEl.srcObject = document.hidden ? null : stream;
22010
- });
22011
- document.body.append(dummyAudioEl);
22012
- this.once(RoomEvent.Disconnected, () => {
22013
- dummyAudioEl === null || dummyAudioEl === void 0 ? void 0 : dummyAudioEl.remove();
22014
- });
22015
- }
22016
- elements.push(dummyAudioEl);
22017
- }
22018
- this.participants.forEach(p => {
22019
- p.audioTracks.forEach(t => {
22020
- if (t.track) {
22021
- t.track.attachedElements.forEach(e => {
22022
- elements.push(e);
22023
- });
22024
- }
22025
- });
22026
- });
22027
- try {
22028
- yield Promise.all([this.acquireAudioContext(), ...elements.map(e => {
22029
- e.muted = false;
22030
- return e.play();
22031
- })]);
22032
- this.handleAudioPlaybackStarted();
22033
- } catch (err) {
22034
- this.handleAudioPlaybackFailed(err);
22035
- throw err;
22036
- }
22037
- });
22038
- }
22039
22093
  /**
22040
22094
  * Returns true if audio playback is enabled
22041
22095
  */
@@ -22132,7 +22186,7 @@ class Room extends eventsExports.EventEmitter {
22132
22186
  });
22133
22187
  }
22134
22188
  setupLocalParticipantEvents() {
22135
- this.localParticipant.on(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged).on(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged).on(ParticipantEvent.TrackMuted, this.onLocalTrackMuted).on(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted).on(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished).on(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished).on(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged).on(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError).on(ParticipantEvent.ParticipantPermissionsChanged, this.onLocalParticipantPermissionsChanged);
22189
+ this.localParticipant.on(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged).on(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged).on(ParticipantEvent.TrackMuted, this.onLocalTrackMuted).on(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted).on(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished).on(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished).on(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged).on(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError).on(ParticipantEvent.AudioStreamAcquired, this.startAudio).on(ParticipantEvent.ParticipantPermissionsChanged, this.onLocalParticipantPermissionsChanged);
22136
22190
  }
22137
22191
  recreateEngine() {
22138
22192
  var _a;
@@ -22220,7 +22274,7 @@ class Room extends eventsExports.EventEmitter {
22220
22274
  (_b = pub.track) === null || _b === void 0 ? void 0 : _b.stop();
22221
22275
  }
22222
22276
  });
22223
- this.localParticipant.off(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged).off(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged).off(ParticipantEvent.TrackMuted, this.onLocalTrackMuted).off(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted).off(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished).off(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished).off(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged).off(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError).off(ParticipantEvent.ParticipantPermissionsChanged, this.onLocalParticipantPermissionsChanged);
22277
+ this.localParticipant.off(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged).off(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged).off(ParticipantEvent.TrackMuted, this.onLocalTrackMuted).off(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted).off(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished).off(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished).off(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged).off(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError).off(ParticipantEvent.AudioStreamAcquired, this.startAudio).off(ParticipantEvent.ParticipantPermissionsChanged, this.onLocalParticipantPermissionsChanged);
22224
22278
  this.localParticipant.tracks.clear();
22225
22279
  this.localParticipant.videoTracks.clear();
22226
22280
  this.localParticipant.audioTracks.clear();