livekit-client 2.22.2 → 2.22.4

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 (245) hide show
  1. package/README.md +35 -0
  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 +488 -438
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +954 -301
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.fm.worker.js +1 -1
  9. package/dist/livekit-client.fm.worker.js.map +1 -1
  10. package/dist/livekit-client.fm.worker.mjs +10 -2
  11. package/dist/livekit-client.fm.worker.mjs.map +1 -1
  12. package/dist/livekit-client.umd.js +1 -1
  13. package/dist/livekit-client.umd.js.map +1 -1
  14. package/dist/src/api/SignalClient.d.ts +4 -2
  15. package/dist/src/api/SignalClient.d.ts.map +1 -1
  16. package/dist/src/api/WebSocketStream.d.ts.map +1 -1
  17. package/dist/src/api/utils.d.ts +1 -0
  18. package/dist/src/api/utils.d.ts.map +1 -1
  19. package/dist/src/e2ee/E2eeManager.d.ts +26 -0
  20. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  21. package/dist/src/e2ee/types.d.ts +15 -1
  22. package/dist/src/e2ee/types.d.ts.map +1 -1
  23. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
  24. package/dist/src/e2ee/worker/ErrorRateLimiter.d.ts +21 -0
  25. package/dist/src/e2ee/worker/ErrorRateLimiter.d.ts.map +1 -0
  26. package/dist/src/e2ee/worker/FrameCryptor.d.ts +1 -18
  27. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  28. package/dist/src/logger.d.ts +4 -0
  29. package/dist/src/logger.d.ts.map +1 -1
  30. package/dist/src/options.d.ts +14 -0
  31. package/dist/src/options.d.ts.map +1 -1
  32. package/dist/src/room/PCTransport.d.ts +78 -7
  33. package/dist/src/room/PCTransport.d.ts.map +1 -1
  34. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  35. package/dist/src/room/RTCEngine.d.ts +9 -0
  36. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  37. package/dist/src/room/Room.d.ts +1 -1
  38. package/dist/src/room/Room.d.ts.map +1 -1
  39. package/dist/src/room/defaults.d.ts.map +1 -1
  40. package/dist/src/room/errors.d.ts +18 -0
  41. package/dist/src/room/errors.d.ts.map +1 -1
  42. package/dist/src/room/events.d.ts +3 -2
  43. package/dist/src/room/events.d.ts.map +1 -1
  44. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  45. package/dist/src/room/participant/publishUtils.d.ts +16 -0
  46. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  47. package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -1
  48. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  49. package/dist/src/room/track/LocalVideoTrack.d.ts +9 -0
  50. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  51. package/dist/src/room/track/options.d.ts +1 -1
  52. package/dist/src/room/track/utils.d.ts +13 -0
  53. package/dist/src/room/track/utils.d.ts.map +1 -1
  54. package/dist/src/room/utils.d.ts +66 -0
  55. package/dist/src/room/utils.d.ts.map +1 -1
  56. package/dist/src/test/compressionStreamStubs.d.ts +12 -0
  57. package/dist/src/test/compressionStreamStubs.d.ts.map +1 -0
  58. package/dist/ts4.2/api/SignalClient.d.ts +187 -185
  59. package/dist/ts4.2/api/SignalClientStateMachine.d.ts +85 -85
  60. package/dist/ts4.2/api/WebSocketStream.d.ts +33 -33
  61. package/dist/ts4.2/api/utils.d.ts +9 -8
  62. package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +30 -30
  63. package/dist/ts4.2/connectionHelper/checks/Checker.d.ts +62 -62
  64. package/dist/ts4.2/connectionHelper/checks/cloudRegion.d.ts +18 -18
  65. package/dist/ts4.2/connectionHelper/checks/connectionProtocol.d.ts +20 -20
  66. package/dist/ts4.2/connectionHelper/checks/publishAudio.d.ts +6 -6
  67. package/dist/ts4.2/connectionHelper/checks/publishVideo.d.ts +7 -7
  68. package/dist/ts4.2/connectionHelper/checks/reconnect.d.ts +6 -6
  69. package/dist/ts4.2/connectionHelper/checks/turn.d.ts +6 -6
  70. package/dist/ts4.2/connectionHelper/checks/webrtc.d.ts +6 -6
  71. package/dist/ts4.2/connectionHelper/checks/websocket.d.ts +6 -6
  72. package/dist/ts4.2/e2ee/E2eeManager.d.ts +111 -85
  73. package/dist/ts4.2/e2ee/KeyProvider.d.ts +54 -54
  74. package/dist/ts4.2/e2ee/constants.d.ts +21 -21
  75. package/dist/ts4.2/e2ee/errors.d.ts +12 -12
  76. package/dist/ts4.2/e2ee/events.d.ts +39 -39
  77. package/dist/ts4.2/e2ee/index.d.ts +6 -6
  78. package/dist/ts4.2/e2ee/types.d.ts +231 -217
  79. package/dist/ts4.2/e2ee/utils.d.ts +32 -32
  80. package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +16 -16
  81. package/dist/ts4.2/e2ee/worker/ErrorRateLimiter.d.ts +21 -0
  82. package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +209 -226
  83. package/dist/ts4.2/e2ee/worker/ParticipantKeyHandler.d.ts +81 -84
  84. package/dist/ts4.2/e2ee/worker/e2ee.worker.d.ts +2 -2
  85. package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +28 -28
  86. package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +23 -23
  87. package/dist/ts4.2/frameMetadata/FrameMetadataManager.d.ts +53 -53
  88. package/dist/ts4.2/frameMetadata/frameMetadata.d.ts +33 -33
  89. package/dist/ts4.2/frameMetadata/types.d.ts +62 -62
  90. package/dist/ts4.2/frameMetadata/utils.d.ts +9 -9
  91. package/dist/ts4.2/frameMetadata/worker/frameMetadata.worker.d.ts +2 -2
  92. package/dist/ts4.2/index.d.ts +76 -76
  93. package/dist/ts4.2/logger.d.ts +66 -62
  94. package/dist/ts4.2/options.d.ts +163 -149
  95. package/dist/ts4.2/room/BackOffStrategy.d.ts +27 -27
  96. package/dist/ts4.2/room/DefaultReconnectPolicy.d.ts +8 -8
  97. package/dist/ts4.2/room/DeviceManager.d.ts +12 -12
  98. package/dist/ts4.2/room/PCTransport.d.ts +246 -175
  99. package/dist/ts4.2/room/PCTransportManager.d.ts +77 -77
  100. package/dist/ts4.2/room/RTCEngine.d.ts +269 -261
  101. package/dist/ts4.2/room/ReconnectPolicy.d.ts +24 -24
  102. package/dist/ts4.2/room/RegionUrlProvider.d.ts +36 -36
  103. package/dist/ts4.2/room/Room.d.ts +354 -365
  104. package/dist/ts4.2/room/attribute-typings.d.ts +35 -35
  105. package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +62 -62
  106. package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +94 -95
  107. package/dist/ts4.2/room/data-channel/LossyDataChannel.d.ts +41 -41
  108. package/dist/ts4.2/room/data-channel/ReliableDataChannel.d.ts +57 -57
  109. package/dist/ts4.2/room/data-channel/types.d.ts +12 -12
  110. package/dist/ts4.2/room/data-stream/compression.d.ts +49 -49
  111. package/dist/ts4.2/room/data-stream/constants.d.ts +18 -18
  112. package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +29 -29
  113. package/dist/ts4.2/room/data-stream/incoming/StreamReader.d.ts +80 -80
  114. package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +73 -73
  115. package/dist/ts4.2/room/data-stream/outgoing/StreamWriter.d.ts +16 -16
  116. package/dist/ts4.2/room/data-stream/outgoing/header-utils.d.ts +18 -18
  117. package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +79 -80
  118. package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +50 -50
  119. package/dist/ts4.2/room/data-track/depacketizer.d.ts +59 -59
  120. package/dist/ts4.2/room/data-track/frame.d.ts +22 -22
  121. package/dist/ts4.2/room/data-track/handle.d.ts +27 -27
  122. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +124 -124
  123. package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -24
  124. package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +41 -41
  125. package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -20
  126. package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +126 -126
  127. package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +74 -74
  128. package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +23 -23
  129. package/dist/ts4.2/room/data-track/outgoing/types.d.ts +48 -48
  130. package/dist/ts4.2/room/data-track/packet/constants.d.ts +20 -20
  131. package/dist/ts4.2/room/data-track/packet/errors.d.ts +40 -40
  132. package/dist/ts4.2/room/data-track/packet/extensions.d.ts +69 -69
  133. package/dist/ts4.2/room/data-track/packet/index.d.ts +98 -98
  134. package/dist/ts4.2/room/data-track/packet/serializable.d.ts +13 -13
  135. package/dist/ts4.2/room/data-track/packetizer.d.ts +43 -43
  136. package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -23
  137. package/dist/ts4.2/room/data-track/types.d.ts +22 -22
  138. package/dist/ts4.2/room/data-track/utils.d.ts +60 -60
  139. package/dist/ts4.2/room/debounce.d.ts +11 -11
  140. package/dist/ts4.2/room/defaults.d.ts +9 -9
  141. package/dist/ts4.2/room/errors.d.ts +180 -162
  142. package/dist/ts4.2/room/events.d.ts +638 -637
  143. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +287 -287
  144. package/dist/ts4.2/room/participant/Participant.d.ts +142 -144
  145. package/dist/ts4.2/room/participant/ParticipantTrackPermission.d.ts +26 -26
  146. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +85 -86
  147. package/dist/ts4.2/room/participant/publishUtils.d.ts +53 -37
  148. package/dist/ts4.2/room/rpc/client/RpcClientManager.d.ts +43 -43
  149. package/dist/ts4.2/room/rpc/client/events.d.ts +8 -8
  150. package/dist/ts4.2/room/rpc/index.d.ts +7 -7
  151. package/dist/ts4.2/room/rpc/server/RpcServerManager.d.ts +44 -44
  152. package/dist/ts4.2/room/rpc/server/events.d.ts +8 -8
  153. package/dist/ts4.2/room/rpc/utils.d.ts +131 -131
  154. package/dist/ts4.2/room/stats.d.ts +71 -71
  155. package/dist/ts4.2/room/statsSummary.d.ts +13 -13
  156. package/dist/ts4.2/room/timers.d.ts +12 -12
  157. package/dist/ts4.2/room/token-source/TokenSource.d.ts +82 -82
  158. package/dist/ts4.2/room/token-source/types.d.ts +70 -70
  159. package/dist/ts4.2/room/token-source/utils.d.ts +8 -8
  160. package/dist/ts4.2/room/track/FrameMetadataExtractor.d.ts +19 -19
  161. package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +39 -42
  162. package/dist/ts4.2/room/track/LocalTrack.d.ts +125 -125
  163. package/dist/ts4.2/room/track/LocalTrackPublication.d.ts +42 -42
  164. package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +102 -93
  165. package/dist/ts4.2/room/track/RemoteAudioTrack.d.ts +55 -55
  166. package/dist/ts4.2/room/track/RemoteTrack.d.ts +38 -38
  167. package/dist/ts4.2/room/track/RemoteTrackPublication.d.ts +72 -75
  168. package/dist/ts4.2/room/track/RemoteVideoTrack.d.ts +73 -76
  169. package/dist/ts4.2/room/track/Track.d.ts +156 -166
  170. package/dist/ts4.2/room/track/TrackPublication.d.ts +75 -81
  171. package/dist/ts4.2/room/track/create.d.ts +25 -25
  172. package/dist/ts4.2/room/track/facingMode.d.ts +41 -41
  173. package/dist/ts4.2/room/track/options.d.ts +403 -403
  174. package/dist/ts4.2/room/track/processor/types.d.ts +31 -31
  175. package/dist/ts4.2/room/track/record.d.ts +13 -13
  176. package/dist/ts4.2/room/track/types.d.ts +28 -28
  177. package/dist/ts4.2/room/track/utils.d.ts +60 -47
  178. package/dist/ts4.2/room/types.d.ts +127 -127
  179. package/dist/ts4.2/room/utils.d.ts +246 -180
  180. package/dist/ts4.2/test/MockMediaStreamTrack.d.ts +26 -26
  181. package/dist/ts4.2/test/compressionStreamStubs.d.ts +12 -0
  182. package/dist/ts4.2/test/mocks.d.ts +12 -12
  183. package/dist/ts4.2/test/promiseState.d.ts +12 -12
  184. package/dist/ts4.2/test/signalToken.d.ts +26 -26
  185. package/dist/ts4.2/type-polyfills/non-shared-typed-arrays.d.ts +2 -2
  186. package/dist/ts4.2/utils/AsyncQueue.d.ts +23 -23
  187. package/dist/ts4.2/utils/TypedPromise.d.ts +17 -17
  188. package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -13
  189. package/dist/ts4.2/utils/browserParser.d.ts +13 -13
  190. package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -8
  191. package/dist/ts4.2/utils/cloneDeep.d.ts +2 -2
  192. package/dist/ts4.2/utils/dataPacketBuffer.d.ts +34 -34
  193. package/dist/ts4.2/utils/deferrable-map.d.ts +32 -32
  194. package/dist/ts4.2/utils/machineInspector.d.ts +54 -54
  195. package/dist/ts4.2/utils/serializer.d.ts +48 -48
  196. package/dist/ts4.2/utils/subscribeToEvents.d.ts +15 -15
  197. package/dist/ts4.2/utils/ttlmap.d.ts +20 -20
  198. package/dist/ts4.2/version.d.ts +15 -15
  199. package/package.json +5 -4
  200. package/src/api/SignalClient.e2e.test.ts +1 -3
  201. package/src/api/SignalClient.test.ts +160 -10
  202. package/src/api/SignalClient.ts +75 -54
  203. package/src/api/WebSocketStream.ts +3 -8
  204. package/src/api/utils.ts +10 -0
  205. package/src/e2ee/E2eeManager.test.ts +196 -0
  206. package/src/e2ee/E2eeManager.ts +114 -19
  207. package/src/e2ee/types.ts +19 -1
  208. package/src/e2ee/worker/DataCryptor.ts +2 -1
  209. package/src/e2ee/worker/ErrorRateLimiter.test.ts +53 -0
  210. package/src/e2ee/worker/ErrorRateLimiter.ts +52 -0
  211. package/src/e2ee/worker/FrameCryptor.ts +20 -70
  212. package/src/e2ee/worker/e2ee.worker.ts +34 -11
  213. package/src/logger.ts +22 -0
  214. package/src/options.ts +16 -0
  215. package/src/room/PCTransport.test.ts +183 -7
  216. package/src/room/PCTransport.ts +197 -36
  217. package/src/room/PCTransportManager.ts +3 -1
  218. package/src/room/RTCEngine.test.ts +84 -0
  219. package/src/room/RTCEngine.ts +52 -12
  220. package/src/room/Room.test.ts +32 -6
  221. package/src/room/Room.ts +27 -7
  222. package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +16 -20
  223. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +5 -5
  224. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.test.ts +60 -39
  225. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +4 -4
  226. package/src/room/defaults.ts +1 -0
  227. package/src/room/errors.test.ts +36 -0
  228. package/src/room/errors.ts +27 -0
  229. package/src/room/events.ts +2 -1
  230. package/src/room/participant/LocalParticipant.ts +30 -14
  231. package/src/room/participant/publishUtils.test.ts +133 -0
  232. package/src/room/participant/publishUtils.ts +54 -38
  233. package/src/room/rpc/client/RpcClientManager.test.ts +51 -0
  234. package/src/room/rpc/client/RpcClientManager.ts +32 -15
  235. package/src/room/track/LocalTrack.ts +18 -7
  236. package/src/room/track/LocalVideoTrack.ts +25 -8
  237. package/src/room/track/RemoteAudioTrack.ts +3 -3
  238. package/src/room/track/RemoteVideoTrack.test.ts +210 -0
  239. package/src/room/track/RemoteVideoTrack.ts +29 -3
  240. package/src/room/track/options.ts +1 -1
  241. package/src/room/track/utils.test.ts +201 -2
  242. package/src/room/track/utils.ts +88 -0
  243. package/src/room/utils.test.ts +211 -0
  244. package/src/room/utils.ts +129 -0
  245. package/src/test/compressionStreamStubs.ts +53 -0
@@ -930,7 +930,10 @@ function isScalarZeroValue(type, value) {
930
930
  return value == 0;
931
931
  // Loose comparison matches 0n, 0 and "0"
932
932
  }
933
- }function _arrayLikeToArray(r, a) {
933
+ }function _OverloadYield(e, d) {
934
+ this.v = e, this.k = d;
935
+ }
936
+ function _arrayLikeToArray(r, a) {
934
937
  (null == a || a > r.length) && (a = r.length);
935
938
  for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
936
939
  return n;
@@ -1046,7 +1049,61 @@ function _unsupportedIterableToArray(r, a) {
1046
1049
  var t = {}.toString.call(r).slice(8, -1);
1047
1050
  return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
1048
1051
  }
1049
- }/* eslint-disable prefer-const,no-case-declarations,@typescript-eslint/restrict-plus-operands */
1052
+ }
1053
+ function _wrapAsyncGenerator(e) {
1054
+ return function () {
1055
+ return new AsyncGenerator(e.apply(this, arguments));
1056
+ };
1057
+ }
1058
+ function AsyncGenerator(e) {
1059
+ var t, n;
1060
+ function resume(t, n) {
1061
+ try {
1062
+ var r = e[t](n),
1063
+ o = r.value,
1064
+ u = o instanceof _OverloadYield;
1065
+ Promise.resolve(u ? o.v : o).then(function (n) {
1066
+ if (u) {
1067
+ var i = "return" === t && o.k ? t : "next";
1068
+ if (!o.k || n.done) return resume(i, n);
1069
+ n = e[i](n).value;
1070
+ }
1071
+ settle(!!r.done, n);
1072
+ }, function (e) {
1073
+ resume("throw", e);
1074
+ });
1075
+ } catch (e) {
1076
+ settle(2, e);
1077
+ }
1078
+ }
1079
+ function settle(e, r) {
1080
+ 2 === e ? t.reject(r) : t.resolve({
1081
+ value: r,
1082
+ done: e
1083
+ }), (t = t.next) ? resume(t.key, t.arg) : n = null;
1084
+ }
1085
+ this._invoke = function (e, r) {
1086
+ return new Promise(function (o, u) {
1087
+ var i = {
1088
+ key: e,
1089
+ arg: r,
1090
+ resolve: o,
1091
+ reject: u,
1092
+ next: null
1093
+ };
1094
+ n ? n = n.next = i : (t = n = i, resume(e, r));
1095
+ });
1096
+ }, "function" != typeof e.return && (this.return = void 0);
1097
+ }
1098
+ AsyncGenerator.prototype["function" == typeof Symbol && Symbol.asyncIterator || "@@asyncIterator"] = function () {
1099
+ return this;
1100
+ }, AsyncGenerator.prototype.next = function (e) {
1101
+ return this._invoke("next", e);
1102
+ }, AsyncGenerator.prototype.throw = function (e) {
1103
+ return this._invoke("throw", e);
1104
+ }, AsyncGenerator.prototype.return = function (e) {
1105
+ return this._invoke("return", e);
1106
+ };/* eslint-disable prefer-const,no-case-declarations,@typescript-eslint/restrict-plus-operands */
1050
1107
  /**
1051
1108
  * Protobuf binary format wire types.
1052
1109
  *
@@ -5416,6 +5473,9 @@ const ClientInfo_SDK = /* @__PURE__ */proto3.makeEnum("livekit.ClientInfo.SDK",
5416
5473
  }, {
5417
5474
  no: 14,
5418
5475
  name: "ESP32"
5476
+ }, {
5477
+ no: 15,
5478
+ name: "DOTNET"
5419
5479
  }]);
5420
5480
  const ClientInfo_Capability = /* @__PURE__ */proto3.makeEnum("livekit.ClientInfo.Capability", [{
5421
5481
  no: 0,
@@ -5784,6 +5844,9 @@ const EncodingOptionsPreset = /* @__PURE__ */proto3.makeEnum("livekit.EncodingOp
5784
5844
  }, {
5785
5845
  no: 7,
5786
5846
  name: "PORTRAIT_H264_1080P_60"
5847
+ }, {
5848
+ no: 8,
5849
+ name: "PASSTHROUGH"
5787
5850
  }]);
5788
5851
  const EncodedFileType = /* @__PURE__ */proto3.makeEnum("livekit.EncodedFileType", [{
5789
5852
  no: 0,
@@ -7768,6 +7831,11 @@ const SyncState = /* @__PURE__ */proto3.makeMessageType("livekit.SyncState", ()
7768
7831
  kind: "message",
7769
7832
  T: PublishDataTrackResponse,
7770
7833
  repeated: true
7834
+ }, {
7835
+ no: 9,
7836
+ name: "data_subscription",
7837
+ kind: "message",
7838
+ T: UpdateDataSubscription
7771
7839
  }]);
7772
7840
  const DataChannelReceiveState = /* @__PURE__ */proto3.makeMessageType("livekit.DataChannelReceiveState", () => [{
7773
7841
  no: 1,
@@ -8625,7 +8693,21 @@ function setLogExtension(extension, logger) {
8625
8693
  logR.setLevel(logR.getLevel());
8626
8694
  });
8627
8695
  }
8628
- const workerLogger = loglevelExports.getLogger(LoggerNames.E2EE);const maxRetryDelay = 7000;
8696
+ const workerLogger = loglevelExports.getLogger(LoggerNames.E2EE);
8697
+ const workerLogLevelListeners = new Set();
8698
+ const originalWorkerSetLevel = workerLogger.setLevel.bind(workerLogger);
8699
+ workerLogger.setLevel = (level, persist) => {
8700
+ originalWorkerSetLevel(level, persist);
8701
+ const numeric = workerLogger.getLevel();
8702
+ workerLogLevelListeners.forEach(cb => cb(numeric));
8703
+ };
8704
+ /** @internal Subscribe to workerLogger level changes (so E2EE workers can be kept in sync). */
8705
+ function onWorkerLogLevelChanged(cb) {
8706
+ workerLogLevelListeners.add(cb);
8707
+ return () => {
8708
+ workerLogLevelListeners.delete(cb);
8709
+ };
8710
+ }const maxRetryDelay = 7000;
8629
8711
  const DEFAULT_RETRY_DELAYS_IN_MS = [0, 300, 2 * 2 * 300, 3 * 3 * 300, 4 * 4 * 300, maxRetryDelay, maxRetryDelay, maxRetryDelay, maxRetryDelay, maxRetryDelay];
8630
8712
  class DefaultReconnectPolicy {
8631
8713
  constructor(retryDelays) {
@@ -12208,7 +12290,7 @@ function getMatch(exp, ua) {
12208
12290
  }
12209
12291
  function getOSVersion(ua) {
12210
12292
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
12211
- }var version$1 = "2.22.2";const version = version$1;
12293
+ }var version$1 = "2.22.4";const version = version$1;
12212
12294
  const protocolVersion = 17;
12213
12295
  /** Initial client protocol. */
12214
12296
  const CLIENT_PROTOCOL_DEFAULT = 0;
@@ -12289,6 +12371,32 @@ class ConnectionError extends LivekitReasonedError {
12289
12371
  return new ConnectionError(message, ConnectionErrorReason.ServiceNotFound, undefined, serviceName);
12290
12372
  }
12291
12373
  }
12374
+ /**
12375
+ * Whether a failed connection attempt may be retried against a different LiveKit Cloud region.
12376
+ *
12377
+ * LiveKit Cloud signals project-level region pinning by returning 403 on the RTC paths when the
12378
+ * project is not allowed in the region the client geo-routed to. `/settings/regions` is
12379
+ * deliberately left reachable so the client can discover its allowed regions and connect there, so
12380
+ * a 403 must not be treated as terminal.
12381
+ *
12382
+ * A 401 stays terminal: no other region will accept a token this one rejected. So does the 404
12383
+ * "requested room does not exist" case, which is also reported as `NotAllowed`.
12384
+ *
12385
+ * We key on the status rather than the server's error message because that message is an
12386
+ * unversioned human-readable string; matching it would let a copy edit break already-shipped
12387
+ * clients. If a 403 really was a permissions failure rather than region pinning, every region
12388
+ * attempt fails the same way and the original error still surfaces — at the cost of one extra
12389
+ * region lookup.
12390
+ */
12391
+ function canFailOverToAnotherRegion(error) {
12392
+ if (error.reason === ConnectionErrorReason.Cancelled) {
12393
+ return false;
12394
+ }
12395
+ if (error.reason === ConnectionErrorReason.NotAllowed) {
12396
+ return error.status === 403;
12397
+ }
12398
+ return true;
12399
+ }
12292
12400
  class DeviceUnsupportedError extends LivekitError {
12293
12401
  constructor(message) {
12294
12402
  super(21, message !== null && message !== void 0 ? message : 'device is unsupported');
@@ -12622,7 +12730,7 @@ var RoomEvent;
12622
12730
  * Data packets provides the ability to use LiveKit to send/receive arbitrary payloads.
12623
12731
  * All participants in the room will receive the messages sent to the room.
12624
12732
  *
12625
- * args: (payload: NonSharedUint8Array, participant: [[Participant]], kind: [[DataPacket_Kind]], topic?: string)
12733
+ * args: (payload: NonSharedUint8Array, participant: [[Participant]] | undefined, kind: [[DataPacket_Kind]], topic?: string, encryptionType?: Encryption_Type, participantIdentity?: string)
12626
12734
  */
12627
12735
  RoomEvent["DataReceived"] = "dataReceived";
12628
12736
  /**
@@ -12980,6 +13088,7 @@ var EngineEvent;
12980
13088
  EngineEvent["Joined"] = "joined";
12981
13089
  EngineEvent["TokenRefreshed"] = "tokenRefreshed";
12982
13090
  EngineEvent["ServerRegionsReported"] = "serverRegionsReported";
13091
+ EngineEvent["RequestSubscribedCodecRefresh"] = "requestSubscribedCodecRefresh";
12983
13092
  })(EngineEvent || (EngineEvent = {}));
12984
13093
  var TrackEvent;
12985
13094
  (function (TrackEvent) {
@@ -13674,6 +13783,85 @@ function areDimensionsSmaller(a, b) {
13674
13783
  function layerDimensionsFor(trackInfo, quality) {
13675
13784
  var _a;
13676
13785
  return (_a = trackInfo.layers) === null || _a === void 0 ? void 0 : _a.find(l => l.quality === quality);
13786
+ }
13787
+ /**
13788
+ * Waits until `track` has actually produced a video frame.
13789
+ *
13790
+ * iOS keeps reporting the camera's sensor frame from `getSettings()` until the camera has
13791
+ * delivered its first picture, so a portrait capture initially reads as landscape. The arrival
13792
+ * of that first frame is the only reliable signal that the reported settings describe reality.
13793
+ *
13794
+ * Best effort, never throws. Resolves `true` when a frame was observed, `false` when no frame
13795
+ * can be observed (the track is already ended, muted or disabled) or `timeoutMs` elapsed first.
13796
+ *
13797
+ * @internal
13798
+ */
13799
+ function waitForFirstVideoFrame(track_1, timeoutMs_1) {
13800
+ return __awaiter(this, arguments, void 0, function (track, timeoutMs) {
13801
+ let candidateElements = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
13802
+ return function* () {
13803
+ var _a;
13804
+ if (typeof document === 'undefined') {
13805
+ return false;
13806
+ }
13807
+ if (track.readyState === 'ended' || track.muted || !track.enabled) {
13808
+ // no frame is ever going to arrive, don't hold the caller up for the full timeout
13809
+ return false;
13810
+ }
13811
+ // requestVideoFrameCallback (Safari 15.4 and up) is a platform capability, so the answer is the
13812
+ // same for every element
13813
+ const supportsFrameCallback = typeof HTMLVideoElement.prototype.requestVideoFrameCallback === 'function';
13814
+ // prefer an element that is already rendering this track (a local preview, typically) over
13815
+ // spinning up a second decode just to observe a frame
13816
+ const existingElement = candidateElements.find(el => el instanceof HTMLVideoElement && el.srcObject instanceof MediaStream && el.srcObject.getVideoTracks().includes(track) && (
13817
+ // without requestVideoFrameCallback we depend on `loadeddata`, which is one-shot: an element
13818
+ // that already decoded a frame (readyState >= HAVE_CURRENT_DATA) never fires it again.
13819
+ // Leave those alone and set up our own element, which starts out empty.
13820
+ supportsFrameCallback || el.readyState < 2));
13821
+ const element = existingElement !== null && existingElement !== void 0 ? existingElement : document.createElement('video');
13822
+ if (!existingElement) {
13823
+ element.playsInline = true;
13824
+ element.muted = true;
13825
+ element.srcObject = new MediaStream([track]);
13826
+ // the element is deliberately never added to the DOM, it only exists to pull frames.
13827
+ // WebKit delivers frames to a paused element too, so a rejected play() (Safari's low power
13828
+ // mode, for instance) is not a reason to stop waiting
13829
+ element.play().catch(error => {
13830
+ livekitLogger.debug('could not play element while waiting for the first video frame', {
13831
+ error
13832
+ });
13833
+ });
13834
+ }
13835
+ let timer;
13836
+ let frameHandle;
13837
+ let onLoadedData;
13838
+ try {
13839
+ return yield new Promise(resolve => {
13840
+ timer = setTimeout(() => resolve(false), timeoutMs);
13841
+ if (supportsFrameCallback) {
13842
+ frameHandle = element.requestVideoFrameCallback(() => resolve(true));
13843
+ } else {
13844
+ // Safari below 15.4 has no requestVideoFrameCallback. `loadeddata` fires once the first
13845
+ // frame has been decoded, which is the same signal a few milliseconds earlier.
13846
+ onLoadedData = () => resolve(true);
13847
+ element.addEventListener('loadeddata', onLoadedData);
13848
+ }
13849
+ });
13850
+ } finally {
13851
+ clearTimeout(timer);
13852
+ if (frameHandle !== undefined) {
13853
+ (_a = element.cancelVideoFrameCallback) === null || _a === void 0 ? void 0 : _a.call(element, frameHandle);
13854
+ }
13855
+ if (onLoadedData) {
13856
+ element.removeEventListener('loadeddata', onLoadedData);
13857
+ }
13858
+ if (!existingElement) {
13859
+ element.pause();
13860
+ element.srcObject = null;
13861
+ }
13862
+ }
13863
+ }();
13864
+ });
13677
13865
  }const BACKGROUND_REACTION_DELAY = 5000;
13678
13866
  // keep old audio elements when detached, we would re-use them since on iOS
13679
13867
  // Safari tracks which audio elements have been "blessed" by the user.
@@ -14216,6 +14404,56 @@ function negotiateDependencyDescriptor(transceiver) {
14216
14404
  return false;
14217
14405
  }
14218
14406
  }
14407
+ /**
14408
+ * VP9 and AV1 are published as SVC (a single RTP stream carrying every spatial layer)
14409
+ * by default. They can instead be published as real, rid based simulcast — one
14410
+ * independent stream per rid, each carrying a single spatial layer — when the caller
14411
+ * opts in with `simulcast: true` and a single spatial layer scalability mode (`L1Tx`).
14412
+ *
14413
+ * The SFU has to be told about this: without an explicit
14414
+ * `SimulcastCodec.videoLayerMode` it assumes `MULTIPLE_SPATIAL_LAYERS_PER_STREAM` for
14415
+ * any SVC capable codec.
14416
+ */
14417
+ function isSVCSimulcast(codec, options) {
14418
+ var _a;
14419
+ return isSVCCodec(codec) && !!(options === null || options === void 0 ? void 0 : options.simulcast) && !!((_a = options.scalabilityMode) === null || _a === void 0 ? void 0 : _a.startsWith('L1T'));
14420
+ }
14421
+ /**
14422
+ * Whether the browser reads multiple encodings on an SVC capable codec as *legacy SVC*
14423
+ * rather than as real simulcast.
14424
+ *
14425
+ * Before Chrome M113, supplying more than one encoding for VP9/AV1 selected SVC mode;
14426
+ * only from M113 does libwebrtc treat such encodings as simulcast, and only when each
14427
+ * one carries its own scalabilityMode. Safari (and anything WebKit based, i. e. every
14428
+ * browser on iOS) still uses the old interpretation, as does React Native's libwebrtc.
14429
+ * Announced at https://groups.google.com/g/discuss-webrtc/c/-QQ3pxrl-fw
14430
+ *
14431
+ * Where this is true the rids would not exist on the wire, so VP9/AV1 must be published
14432
+ * as SVC no matter what the caller asked for.
14433
+ */
14434
+ function usesLegacySVCEncodings() {
14435
+ const browser = getBrowser();
14436
+ return isSafariBased() ||
14437
+ // Even tho RN runs M114, it does not produce SVC layers when a single encoding
14438
+ // is provided. So we'll use the legacy SVC specification for now.
14439
+ // TODO: when we upstream libwebrtc, this will need additional verification
14440
+ isReactNative() || (browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && compareVersions(browser.version, '113') < 0;
14441
+ }
14442
+ /**
14443
+ * Last server version that doesn't support vp9/av1 simulcast.
14444
+ */
14445
+ const svcSimulcastMinServerVersion = '1.13.6';
14446
+ /**
14447
+ * Whether the connected server honours `SimulcastCodec.videoLayerMode`, i. e. whether
14448
+ * VP9/AV1 can be published as rid based simulcast. An unknown version is treated as
14449
+ * unsupported so the publish falls back to SVC.
14450
+ */
14451
+ function isSVCSimulcastSupportedByServer(serverVersion) {
14452
+ if (!serverVersion) {
14453
+ return false;
14454
+ }
14455
+ return compareVersions(serverVersion, svcSimulcastMinServerVersion) > 0;
14456
+ }
14219
14457
  function supportsSetSinkId(elm) {
14220
14458
  if (!document || isSafariBased()) {
14221
14459
  return false;
@@ -14263,6 +14501,32 @@ function isSafariBased() {
14263
14501
  const b = getBrowser();
14264
14502
  return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' || (b === null || b === void 0 ? void 0 : b.os) === 'iOS';
14265
14503
  }
14504
+ /**
14505
+ * iPadOS has sent the desktop Macintosh user agent by default since iPadOS 13, so `getBrowser()`
14506
+ * reports `os: 'macOS'` there and every `os === 'iOS'` check misses it. macOS Safari implements no
14507
+ * touch events at all (`TouchEvent` undefined, `maxTouchPoints` 0) regardless of any touchscreen
14508
+ * attached, so touch capability separates the two. Measured on an iPad 6 / iPadOS 17
14509
+ * (`maxTouchPoints` 5) against macOS Safari 26.4 (`maxTouchPoints` 0).
14510
+ *
14511
+ * Deliberately a separate helper rather than a fix in `browserParser`: iPadOS spoofs the Mac
14512
+ * version too, so reporting `os: 'iOS'` there would leave `osVersion` at `10.15.7` and silently
14513
+ * break {@link isSafari17Based} and {@link isSafariSvcApi}, which compare it.
14514
+ */
14515
+ function isIPadOS() {
14516
+ if (!isWeb()) {
14517
+ return false;
14518
+ }
14519
+ const b = getBrowser();
14520
+ return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' && (b === null || b === void 0 ? void 0 : b.os) === 'macOS' && navigator.maxTouchPoints > 1;
14521
+ }
14522
+ /**
14523
+ * iPhone, iPad and iOS-hosted browsers — everything running the Apple camera capture pipeline,
14524
+ * including iPads that present themselves as a Mac.
14525
+ */
14526
+ function isAppleMobile() {
14527
+ var _a;
14528
+ return ((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.os) === 'iOS' || isIPadOS();
14529
+ }
14266
14530
  function isSafari17Based() {
14267
14531
  const b = getBrowser();
14268
14532
  return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' && b.version.startsWith('17.') || (b === null || b === void 0 ? void 0 : b.os) === 'iOS' && !!(b === null || b === void 0 ? void 0 : b.osVersion) && compareVersions(b.osVersion, '17') >= 0;
@@ -14754,6 +15018,35 @@ function extractMaxAgeFromRequestHeaders(headers) {
14754
15018
  function isCompressionStreamSupported() {
14755
15019
  return typeof CompressionStream !== 'undefined';
14756
15020
  }
15021
+ let deflateRawCompressionSupported = null;
15022
+ /**
15023
+ * Whether this runtime can compress AND decompress the `deflate-raw` format, which is a stricter
15024
+ * requirement than {@link isCompressionStreamSupported}: Chromium 80-102 included
15025
+ * `CompressionStream` supporting only `gzip` / `deflate`, and `deflate-raw` was added in Chromium
15026
+ * 103 (Firefox 113, Safari 16.4).
15027
+ */
15028
+ function isDeflateRawCompressionSupported() {
15029
+ if (typeof deflateRawCompressionSupported !== 'boolean') {
15030
+ deflateRawCompressionSupported = probeDeflateRawCompressionSupport();
15031
+ }
15032
+ return deflateRawCompressionSupported;
15033
+ }
15034
+ function probeDeflateRawCompressionSupport() {
15035
+ if (typeof CompressionStream === 'undefined' || typeof DecompressionStream === 'undefined') {
15036
+ return false;
15037
+ }
15038
+ try {
15039
+ // There is no api to query format support, and constructing with an unsupported format throws
15040
+ // synchronously, so constructing one of each is the probe. Both are checked because the
15041
+ // deflate-raw capability advertised to peers is as much about decompressing what they send us
15042
+ // as it is about compressing what we send them.
15043
+ new CompressionStream('deflate-raw');
15044
+ new DecompressionStream('deflate-raw');
15045
+ return true;
15046
+ } catch (_a) {
15047
+ return false;
15048
+ }
15049
+ }
14757
15050
  function isPublisherOfferWithJoinSupported() {
14758
15051
  // we have connectivity issue about publisher offer with join on firefox #1919
14759
15052
  return isCompressionStreamSupported() && !isFireFox();
@@ -14824,6 +15117,15 @@ function getAbortReasonAsString(signal) {
14824
15117
  default:
14825
15118
  return 'toString' in reason ? reason.toString() : defaultMessage;
14826
15119
  }
15120
+ }
15121
+ function getErrorDescription(error, errorCategory) {
15122
+ if (error instanceof Error) {
15123
+ if (error.name && error.message) {
15124
+ return "".concat(error.name, ": ").concat(error.message);
15125
+ }
15126
+ return error.name;
15127
+ }
15128
+ return "Encountered unknown ".concat(errorCategory, " error: ").concat(String(error));
14827
15129
  }const ENCRYPTION_ALGORITHM = 'AES-GCM';
14828
15130
  // How many consecutive frames can fail decrypting before a particular key gets marked as invalid
14829
15131
  const DECRYPTION_FAILURE_TOLERANCE = 10;
@@ -15747,11 +16049,38 @@ class HTMLElementInfo {
15747
16049
  }
15748
16050
  }
15749
16051
  function isElementInPiP(el) {
15750
- var _a, _b;
16052
+ var _a;
15751
16053
  // Simple video PiP
15752
16054
  if (document.pictureInPictureElement === el) return true;
15753
- // Document PiP
15754
- if ((_a = window.documentPictureInPicture) === null || _a === void 0 ? void 0 : _a.window) return isElementInViewport(el, (_b = window.documentPictureInPicture) === null || _b === void 0 ? void 0 : _b.window);
16055
+ // Document PiP: the element has to actually live in the PiP window, otherwise an
16056
+ // opener-document element whose coordinates happen to fall inside the PiP window's
16057
+ // bounds is reported as being in PiP.
16058
+ const pipWindow = (_a = window.documentPictureInPicture) === null || _a === void 0 ? void 0 : _a.window;
16059
+ if (pipWindow) {
16060
+ return isDocumentInPiPWindow(el.ownerDocument, pipWindow) && isElementInViewport(el, pipWindow);
16061
+ }
16062
+ return false;
16063
+ }
16064
+ // Whether the document is the Document PiP window's own document, or the document of a frame
16065
+ // nested inside it. The spec allows frames in a PiP window, so an element in one is in PiP:
16066
+ // https://wicg.github.io/document-picture-in-picture/#iframes
16067
+ function isDocumentInPiPWindow(doc, pipWindow) {
16068
+ var _a, _b, _c;
16069
+ // A cross-origin frame anywhere between the element and the PiP window hides frameElement
16070
+ // from the walk below, but top stays readable across origins and identifies the top-level
16071
+ // window the element belongs to. For an opener element that is the opener, not the PiP window.
16072
+ if (((_a = doc.defaultView) === null || _a === void 0 ? void 0 : _a.top) === pipWindow) return true;
16073
+ let current = doc;
16074
+ while (current) {
16075
+ if (current === pipWindow.document) return true;
16076
+ try {
16077
+ // Reading frameElement across a cross-origin boundary throws, which means we cannot
16078
+ // establish the ancestry and must not claim the element is in PiP.
16079
+ current = (_c = (_b = current.defaultView) === null || _b === void 0 ? void 0 : _b.frameElement) === null || _c === void 0 ? void 0 : _c.ownerDocument;
16080
+ } catch (_d) {
16081
+ return false;
16082
+ }
16083
+ }
15755
16084
  return false;
15756
16085
  }
15757
16086
  // does not account for occlusion by other elements or opacity property
@@ -15775,10 +16104,18 @@ function isElementInViewport(el, win) {
15775
16104
  * @experimental
15776
16105
  */
15777
16106
  class E2EEManager extends eventsExports.EventEmitter {
16107
+ get logContext() {
16108
+ var _a, _b;
16109
+ return {
16110
+ room: (_a = this.room) === null || _a === void 0 ? void 0 : _a.name,
16111
+ participant: (_b = this.room) === null || _b === void 0 ? void 0 : _b.localParticipant.identity
16112
+ };
16113
+ }
15778
16114
  constructor(options, dcEncryptionEnabled) {
15779
16115
  super();
15780
16116
  this.decryptDataRequests = new Map();
15781
16117
  this.encryptDataRequests = new Map();
16118
+ this.log = getLogger(LoggerNames.E2EE, () => this.logContext);
15782
16119
  this.onWorkerMessage = ev => {
15783
16120
  var _a, _b;
15784
16121
  const _ev$data = ev.data,
@@ -15786,22 +16123,22 @@ class E2EEManager extends eventsExports.EventEmitter {
15786
16123
  data = _ev$data.data;
15787
16124
  switch (kind) {
15788
16125
  case 'error':
15789
- livekitLogger.error(data.error.message);
15790
- // If error has uuid, it's from an async operation (encrypt/decrypt)
15791
- // Reject the corresponding future
16126
+ // If error has uuid, it's from an async operation (encrypt/decrypt).
16127
+ // Reject the corresponding future and let the caller decide how to log/handle;
16128
+ // logging here would duplicate whatever the caller does.
15792
16129
  if (data.uuid) {
15793
16130
  const decryptFuture = this.decryptDataRequests.get(data.uuid);
15794
16131
  if (decryptFuture === null || decryptFuture === void 0 ? void 0 : decryptFuture.reject) {
15795
16132
  decryptFuture.reject(data.error);
15796
- break; // Don't emit general error if it's handled by future
16133
+ break;
15797
16134
  }
15798
16135
  const encryptFuture = this.encryptDataRequests.get(data.uuid);
15799
16136
  if (encryptFuture === null || encryptFuture === void 0 ? void 0 : encryptFuture.reject) {
15800
16137
  encryptFuture.reject(data.error);
15801
- break; // Don't emit general error if it's handled by future
16138
+ break;
15802
16139
  }
15803
16140
  }
15804
- // Emit general error event for unhandled errors
16141
+ this.log.error(data.error.message);
15805
16142
  this.emit(EncryptionEvent.EncryptionError, data.error, data.participantIdentity);
15806
16143
  break;
15807
16144
  case 'initAck':
@@ -15846,10 +16183,13 @@ class E2EEManager extends eventsExports.EventEmitter {
15846
16183
  case 'packetTrailerMetadata':
15847
16184
  this.handleFrameMetadata(data.trackId, data.rtpTimestamp, data.ssrc, data.metadata);
15848
16185
  break;
16186
+ case 'log':
16187
+ workerLogger[data.level](data.msg, data.context);
16188
+ break;
15849
16189
  }
15850
16190
  };
15851
16191
  this.onWorkerError = ev => {
15852
- livekitLogger.error('e2ee worker encountered an error:', {
16192
+ this.log.error('e2ee worker encountered an error:', {
15853
16193
  error: ev.error
15854
16194
  });
15855
16195
  this.emit(EncryptionEvent.EncryptionError, ev.error, undefined);
@@ -15872,7 +16212,7 @@ class E2EEManager extends eventsExports.EventEmitter {
15872
16212
  if (!isE2EESupported()) {
15873
16213
  throw new DeviceUnsupportedError('tried to setup end-to-end encryption on an unsupported browser');
15874
16214
  }
15875
- livekitLogger.info('setting up e2ee');
16215
+ this.log.info('setting up e2ee');
15876
16216
  if (room !== this.room) {
15877
16217
  this.room = room;
15878
16218
  this.setupEventListeners(room, this.keyProvider);
@@ -15885,20 +16225,94 @@ class E2EEManager extends eventsExports.EventEmitter {
15885
16225
  }
15886
16226
  };
15887
16227
  if (this.worker) {
15888
- livekitLogger.info("initializing worker", {
16228
+ this.log.info("initializing worker", {
15889
16229
  worker: this.worker
15890
16230
  });
15891
16231
  this.worker.onmessage = this.onWorkerMessage;
15892
16232
  this.worker.onerror = this.onWorkerError;
15893
16233
  this.worker.postMessage(msg);
16234
+ this.subscribeToLogLevelChanges();
15894
16235
  }
15895
16236
  }
15896
16237
  }
16238
+ /**
16239
+ * Subscribe the current worker to main-thread `workerLogger` level changes,
16240
+ * without strongly retaining `this` or `this.worker` from the module-global
16241
+ * listener Set on the logger. See {@link disposeRegistry}.
16242
+ */
16243
+ subscribeToLogLevelChanges() {
16244
+ var _a;
16245
+ // Guard against duplicate registration on re-setup.
16246
+ (_a = this.unsubscribeLogLevel) === null || _a === void 0 ? void 0 : _a.call(this);
16247
+ let unsub;
16248
+ if (E2EEManager.disposeRegistry) {
16249
+ // Modern engines: hold the worker weakly so the module-global listener Set
16250
+ // on the logger can't retain this manager, and clean up the entry on GC.
16251
+ const workerRef = new WeakRef(this.worker);
16252
+ unsub = onWorkerLogLevelChanged(level => {
16253
+ const worker = workerRef.deref();
16254
+ if (!worker) {
16255
+ unsub === null || unsub === void 0 ? void 0 : unsub();
16256
+ return;
16257
+ }
16258
+ worker.postMessage({
16259
+ kind: 'setLogLevel',
16260
+ data: {
16261
+ level
16262
+ }
16263
+ });
16264
+ });
16265
+ E2EEManager.disposeRegistry.register(this, unsub, this);
16266
+ } else {
16267
+ // Safari <14.1 and similar: no WeakRef. Fall back to a strong reference;
16268
+ // the leak lives until the consumer calls `dispose()`.
16269
+ const worker = this.worker;
16270
+ unsub = onWorkerLogLevelChanged(level => {
16271
+ worker.postMessage({
16272
+ kind: 'setLogLevel',
16273
+ data: {
16274
+ level
16275
+ }
16276
+ });
16277
+ });
16278
+ }
16279
+ this.unsubscribeLogLevel = unsub;
16280
+ }
16281
+ /**
16282
+ * @internal
16283
+ * Release the log-level subscription, reject any pending encrypt/decrypt
16284
+ * futures, and detach the worker message handlers. The worker itself is
16285
+ * caller-owned and is not terminated. Idempotent.
16286
+ */
16287
+ dispose() {
16288
+ var _a, _b, _c;
16289
+ (_a = this.unsubscribeLogLevel) === null || _a === void 0 ? void 0 : _a.call(this);
16290
+ this.unsubscribeLogLevel = undefined;
16291
+ if (E2EEManager.disposeRegistry) {
16292
+ E2EEManager.disposeRegistry.unregister(this);
16293
+ }
16294
+ // Reject pending futures BEFORE detaching worker handlers, so any late
16295
+ // response can't resolve one after we've cut the pipe. Each future's
16296
+ // `onFinally` deletes its own map entry, so both maps drain themselves.
16297
+ // Snapshot before iterating in case a rejection handler mutates the map.
16298
+ const disposalError = new CryptorError('E2EEManager disposed', CryptorErrorReason.InternalError);
16299
+ for (const future of [...this.encryptDataRequests.values()]) {
16300
+ (_b = future.reject) === null || _b === void 0 ? void 0 : _b.call(future, disposalError);
16301
+ }
16302
+ for (const future of [...this.decryptDataRequests.values()]) {
16303
+ (_c = future.reject) === null || _c === void 0 ? void 0 : _c.call(future, disposalError);
16304
+ }
16305
+ if (this.worker) {
16306
+ this.worker.onmessage = null;
16307
+ this.worker.onerror = null;
16308
+ }
16309
+ this.removeAllListeners();
16310
+ }
15897
16311
  /**
15898
16312
  * @internal
15899
16313
  */
15900
16314
  setParticipantCryptorEnabled(enabled, participantIdentity) {
15901
- livekitLogger.debug("set e2ee to ".concat(enabled, " for participant ").concat(participantIdentity));
16315
+ this.log.debug("set e2ee to ".concat(enabled, " for participant ").concat(participantIdentity));
15902
16316
  this.postEnable(enabled, participantIdentity);
15903
16317
  }
15904
16318
  /**
@@ -15906,7 +16320,7 @@ class E2EEManager extends eventsExports.EventEmitter {
15906
16320
  */
15907
16321
  setSifTrailer(trailer) {
15908
16322
  if (!trailer || trailer.length === 0) {
15909
- livekitLogger.warn("ignoring server sent trailer as it's empty");
16323
+ this.log.warn("ignoring server sent trailer as it's empty");
15910
16324
  } else {
15911
16325
  this.postSifTrailer(trailer);
15912
16326
  }
@@ -16113,8 +16527,7 @@ class E2EEManager extends eventsExports.EventEmitter {
16113
16527
  */
16114
16528
  setParticipantCryptorEnabledForPublication(pub, participantIdentity) {
16115
16529
  if (!pub.trackInfo) {
16116
- livekitLogger.warn('skipping e2ee enabled update for publication without trackInfo', {
16117
- participant: participantIdentity,
16530
+ this.log.warn('skipping e2ee enabled update for publication without trackInfo', {
16118
16531
  trackSid: pub.trackSid
16119
16532
  });
16120
16533
  return;
@@ -16134,7 +16547,7 @@ class E2EEManager extends eventsExports.EventEmitter {
16134
16547
  setupE2EESender(track, sender) {
16135
16548
  var _a, _b, _c;
16136
16549
  if (!isLocalTrack(track) || !sender) {
16137
- if (!sender) livekitLogger.warn('early return because sender is not ready');
16550
+ if (!sender) this.log.warn('early return because sender is not ready');
16138
16551
  return;
16139
16552
  }
16140
16553
  this.handleSender(sender, track.mediaStreamID, undefined, isVideoTrack(track) ? (_b = (_a = track.publishOptions) === null || _a === void 0 ? void 0 : _a.frameMetadata) !== null && _b !== void 0 ? _b : (_c = track.publishOptions) === null || _c === void 0 ? void 0 : _c.packetTrailer : undefined);
@@ -16227,7 +16640,7 @@ class E2EEManager extends eventsExports.EventEmitter {
16227
16640
  throw TypeError('local identity needs to be known in order to set up encrypted sender');
16228
16641
  }
16229
16642
  if (isScriptTransformSupportedForWorker()) {
16230
- livekitLogger.info('initialize script transform');
16643
+ this.log.info('initialize script transform');
16231
16644
  const options = {
16232
16645
  kind: 'encode',
16233
16646
  participantIdentity: this.room.localParticipant.identity,
@@ -16239,7 +16652,7 @@ class E2EEManager extends eventsExports.EventEmitter {
16239
16652
  // @ts-ignore
16240
16653
  sender.transform = new RTCRtpScriptTransform(this.worker, options);
16241
16654
  } else {
16242
- livekitLogger.info('initialize encoded streams');
16655
+ this.log.info('initialize encoded streams');
16243
16656
  // @ts-ignore
16244
16657
  const senderStreams = sender.createEncodedStreams();
16245
16658
  const msg = {
@@ -16259,7 +16672,15 @@ class E2EEManager extends eventsExports.EventEmitter {
16259
16672
  // @ts-ignore
16260
16673
  sender[E2EE_FLAG] = true;
16261
16674
  }
16262
- }const MAX_ENTRIES = 300;
16675
+ }
16676
+ /**
16677
+ * Runs a cleanup callback once this manager is garbage collected. Lets the
16678
+ * log-level listener (held in a module-global Set on the main-thread logger)
16679
+ * fall out of scope even when the consumer forgets to call `dispose()`.
16680
+ */
16681
+ E2EEManager.disposeRegistry = typeof FinalizationRegistry !== 'undefined' && typeof WeakRef !== 'undefined' && new FinalizationRegistry(cleanup => {
16682
+ cleanup();
16683
+ });const MAX_ENTRIES = 300;
16263
16684
  /**
16264
16685
  * Caches frame metadata extracted from received video frames,
16265
16686
  * keyed by RTP timestamp so it can be looked up when the frame is displayed.
@@ -18070,9 +18491,9 @@ class WebSocketStream {
18070
18491
  let data = _ref2.data;
18071
18492
  return controller.enqueue(data);
18072
18493
  };
18073
- ws.onerror = e => controller.error(ConnectionError.websocket(e instanceof Error ? "".concat(e.name, ": ").concat(e.message) : "Encountered unknown websocket error: ".concat(String(e))));
18494
+ ws.onerror = e => controller.error(ConnectionError.websocket(getErrorDescription(e, 'websocket')));
18074
18495
  ws.onclose = ev => {
18075
- if (ev.wasClean) {
18496
+ if (ev.wasClean || ev.code === 1000) {
18076
18497
  controller.close();
18077
18498
  } else {
18078
18499
  controller.error(ConnectionError.websocket("WS closed unexpectedly with code ".concat(ev.code)));
@@ -18354,7 +18775,7 @@ class SignalClient {
18354
18775
  const rtcUrl = createRtcUrl(url, params, useV0Path).toString();
18355
18776
  const validateUrl = createValidateUrl(rtcUrl).toString();
18356
18777
  return new Promise((resolve, reject) => __awaiter(_this2, void 0, void 0, function* () {
18357
- var _a, _b;
18778
+ var _a, _b, _c, _d, _e;
18358
18779
  try {
18359
18780
  let alreadyAborted = false;
18360
18781
  const abortHandler = eventOrError => __awaiter(this, void 0, void 0, function* () {
@@ -18461,38 +18882,56 @@ class SignalClient {
18461
18882
  const signalReader = connection.readable.getReader();
18462
18883
  this.streamWriter = connection.writable.getWriter();
18463
18884
  // wsTimeout only guarded the upgrade; guard the first-message read with
18464
- // its own timeout so a silent server can't hang join() forever.
18465
- let firstMessage;
18466
- let firstMessageTimeout;
18885
+ // its own deadline so a silent server can't hang join() forever.
18886
+ // During reconnect, drop any stray messages that arrive before the
18887
+ // ReconnectResponse so we do not declare the channel reconnected on the
18888
+ // wrong signal.
18889
+ let firstSignalResponse;
18467
18890
  try {
18468
- firstMessage = yield Promise.race([signalReader.read(), new Promise((_, rejectRead) => {
18469
- firstMessageTimeout = setTimeout(() => {
18470
- rejectRead(ConnectionError.timeout('signal connection timed out while waiting for the first message'));
18471
- }, JOIN_RESPONSE_TIMEOUT);
18472
- })]);
18891
+ const deadline = Date.now() + JOIN_RESPONSE_TIMEOUT;
18892
+ // eslint-disable-next-line no-constant-condition
18893
+ while (true) {
18894
+ const remaining = deadline - Date.now();
18895
+ if (remaining <= 0) {
18896
+ throw ConnectionError.timeout('signal connection timed out while waiting for the first message');
18897
+ }
18898
+ let readTimeout;
18899
+ const readResult = yield Promise.race([signalReader.read(), new Promise((_, rejectRead) => {
18900
+ readTimeout = setTimeout(() => {
18901
+ rejectRead(ConnectionError.timeout('signal connection timed out while waiting for the first message'));
18902
+ }, remaining);
18903
+ })]);
18904
+ clearTimeout(readTimeout);
18905
+ if (!readResult.value) {
18906
+ throw ConnectionError.internal('no message received as first message');
18907
+ }
18908
+ const parsed = parseSignalResponse(readResult.value);
18909
+ if (this.lifecycleState === 'reconnecting' && ((_a = parsed.message) === null || _a === void 0 ? void 0 : _a.case) !== 'reconnect' && ((_b = parsed.message) === null || _b === void 0 ? void 0 : _b.case) !== 'leave') {
18910
+ this.log.warn('dropping signal message while awaiting reconnect response', {
18911
+ messageCase: (_c = parsed.message) === null || _c === void 0 ? void 0 : _c.case
18912
+ });
18913
+ continue;
18914
+ }
18915
+ firstSignalResponse = parsed;
18916
+ break;
18917
+ }
18473
18918
  } catch (e) {
18474
- // No first message in time: release the reader and tear down the ws
18919
+ // No usable first message in time: release the reader and tear down the ws
18475
18920
  // so we surface the timeout instead of leaking an open connection.
18476
18921
  signalReader.releaseLock();
18477
18922
  reject(e);
18478
18923
  this.close();
18479
18924
  return;
18480
- } finally {
18481
- clearTimeout(firstMessageTimeout);
18482
18925
  }
18483
18926
  signalReader.releaseLock();
18484
- if (!firstMessage.value) {
18485
- throw ConnectionError.internal('no message received as first message');
18486
- }
18487
- const firstSignalResponse = parseSignalResponse(firstMessage.value);
18488
18927
  // Validate the first message
18489
- const validation = this.validateFirstMessage(firstSignalResponse, (_a = opts.reconnect) !== null && _a !== void 0 ? _a : false);
18928
+ const validation = this.validateFirstMessage(firstSignalResponse, (_d = opts.reconnect) !== null && _d !== void 0 ? _d : false);
18490
18929
  if (!validation.isValid) {
18491
18930
  reject(validation.error);
18492
18931
  return;
18493
18932
  }
18494
18933
  // Handle join response
18495
- if (((_b = firstSignalResponse.message) === null || _b === void 0 ? void 0 : _b.case) === 'join') {
18934
+ if (((_e = firstSignalResponse.message) === null || _e === void 0 ? void 0 : _e.case) === 'join') {
18496
18935
  // Set up ping configuration
18497
18936
  this.pingTimeoutDuration = firstSignalResponse.message.value.pingTimeout;
18498
18937
  this.pingIntervalDuration = firstSignalResponse.message.value.pingInterval;
@@ -18506,9 +18945,7 @@ class SignalClient {
18506
18945
  this.onJoined(firstSignalResponse.message.value);
18507
18946
  }
18508
18947
  }
18509
- // Handle successful connection
18510
- const firstMessageToProcess = validation.shouldProcessFirstMessage ? firstSignalResponse : undefined;
18511
- this.handleSignalConnected(connection, wsTimeout, attemptId, firstMessageToProcess);
18948
+ this.handleSignalConnected(connection, wsTimeout, attemptId);
18512
18949
  resolve(validation.response);
18513
18950
  } catch (e) {
18514
18951
  reject(e);
@@ -18522,11 +18959,8 @@ class SignalClient {
18522
18959
  }();
18523
18960
  });
18524
18961
  }
18525
- startReadingLoop(signalReader, firstMessage) {
18962
+ startReadingLoop(signalReader) {
18526
18963
  return __awaiter(this, void 0, void 0, function* () {
18527
- if (firstMessage) {
18528
- this.handleSignalResponse(firstMessage);
18529
- }
18530
18964
  const attemptId = this.attemptId;
18531
18965
  while (true) {
18532
18966
  if (this.signalLatency) {
@@ -18962,6 +19396,7 @@ class SignalClient {
18962
19396
  return __awaiter(this, arguments, void 0, function (reason) {
18963
19397
  var _this6 = this;
18964
19398
  let attemptId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.attemptId;
19399
+ let simulateReconnectDelay = arguments.length > 2 ? arguments[2] : undefined;
18965
19400
  return function* () {
18966
19401
  const onCloseCallback = _this6.onClose;
18967
19402
  if (!_this6.sendLifecycleInput({
@@ -18983,6 +19418,10 @@ class SignalClient {
18983
19418
  _this6.log.info("websocket connection closed: ".concat(reason), {
18984
19419
  reason
18985
19420
  });
19421
+ if (simulateReconnectDelay) {
19422
+ _this6.log.info("waiting for ".concat(simulateReconnectDelay, "ms until reconnecting signal"));
19423
+ yield sleep(simulateReconnectDelay);
19424
+ }
18986
19425
  if (onCloseCallback) {
18987
19426
  onCloseCallback(reason);
18988
19427
  }
@@ -19043,7 +19482,7 @@ class SignalClient {
19043
19482
  * @param firstMessage Optional first message to process
19044
19483
  * @internal
19045
19484
  */
19046
- handleSignalConnected(connection, timeoutHandle, attemptId, firstMessage) {
19485
+ handleSignalConnected(connection, timeoutHandle, attemptId) {
19047
19486
  clearTimeout(timeoutHandle);
19048
19487
  const established = this.sendLifecycleInput(this.lifecycleState === 'reconnecting' ? {
19049
19488
  type: 'reconnectComplete',
@@ -19065,7 +19504,7 @@ class SignalClient {
19065
19504
  }
19066
19505
  this.log.info('signal connected');
19067
19506
  this.startPingInterval();
19068
- this.startReadingLoop(connection.readable.getReader(), firstMessage);
19507
+ this.startReadingLoop(connection.readable.getReader());
19069
19508
  }
19070
19509
  /**
19071
19510
  * Validates the first message received from the signal server
@@ -19075,28 +19514,18 @@ class SignalClient {
19075
19514
  * @internal
19076
19515
  */
19077
19516
  validateFirstMessage(firstSignalResponse, isReconnect) {
19078
- var _a, _b, _c, _d, _e;
19517
+ var _a, _b, _c, _d;
19079
19518
  if (((_a = firstSignalResponse.message) === null || _a === void 0 ? void 0 : _a.case) === 'join') {
19080
19519
  return {
19081
19520
  isValid: true,
19082
19521
  response: firstSignalResponse.message.value
19083
19522
  };
19084
- } else if (this.lifecycleState === 'reconnecting' && ((_b = firstSignalResponse.message) === null || _b === void 0 ? void 0 : _b.case) !== 'leave') {
19085
- if (((_c = firstSignalResponse.message) === null || _c === void 0 ? void 0 : _c.case) === 'reconnect') {
19086
- return {
19087
- isValid: true,
19088
- response: firstSignalResponse.message.value
19089
- };
19090
- } else {
19091
- // in reconnecting, any message received means signal reconnected and we still need to process it
19092
- this.log.debug('declaring signal reconnected without reconnect response received');
19093
- return {
19094
- isValid: true,
19095
- response: undefined,
19096
- shouldProcessFirstMessage: true
19097
- };
19098
- }
19099
- } else if (this.isEstablishingConnection && ((_d = firstSignalResponse.message) === null || _d === void 0 ? void 0 : _d.case) === 'leave') {
19523
+ } else if (this.lifecycleState === 'reconnecting' && ((_b = firstSignalResponse.message) === null || _b === void 0 ? void 0 : _b.case) === 'reconnect') {
19524
+ return {
19525
+ isValid: true,
19526
+ response: firstSignalResponse.message.value
19527
+ };
19528
+ } else if (this.isEstablishingConnection && ((_c = firstSignalResponse.message) === null || _c === void 0 ? void 0 : _c.case) === 'leave') {
19100
19529
  return {
19101
19530
  isValid: false,
19102
19531
  error: ConnectionError.leaveRequest('Received leave request while trying to (re)connect', firstSignalResponse.message.value.reason)
@@ -19105,7 +19534,7 @@ class SignalClient {
19105
19534
  // non-reconnect case, should receive join response first
19106
19535
  return {
19107
19536
  isValid: false,
19108
- error: ConnectionError.internal("did not receive join response, got ".concat((_e = firstSignalResponse.message) === null || _e === void 0 ? void 0 : _e.case, " instead"))
19537
+ error: ConnectionError.internal("did not receive join response, got ".concat((_d = firstSignalResponse.message) === null || _d === void 0 ? void 0 : _d.case, " instead"))
19109
19538
  };
19110
19539
  }
19111
19540
  return {
@@ -19210,6 +19639,12 @@ function createConnectionParams(token, info, opts) {
19210
19639
  if (opts.adaptiveStream) {
19211
19640
  params.set('adaptive_stream', '1');
19212
19641
  }
19642
+ if (opts.disableIceLite) {
19643
+ params.set('disable_ice_lite', '1');
19644
+ }
19645
+ if (typeof opts.subscriberAllowPause !== 'undefined') {
19646
+ params.set('subscriber_allow_pause', opts.subscriberAllowPause ? '1' : '0');
19647
+ }
19213
19648
  if (opts.reconnectReason) {
19214
19649
  params.set('reconnect_reason', opts.reconnectReason.toString());
19215
19650
  }
@@ -19228,7 +19663,9 @@ function createJoinRequestConnectionParams(token, info, opts, publisherOffer) {
19228
19663
  clientInfo: info,
19229
19664
  connectionSettings: new ConnectionSettings({
19230
19665
  autoSubscribe: !!opts.autoSubscribe,
19231
- adaptiveStream: !!opts.adaptiveStream
19666
+ adaptiveStream: !!opts.adaptiveStream,
19667
+ disableIceLite: !!opts.disableIceLite,
19668
+ subscriberAllowPause: opts.subscriberAllowPause
19232
19669
  }),
19233
19670
  reconnect: !!opts.reconnect,
19234
19671
  participantSid: opts.sid ? opts.sid : undefined,
@@ -20069,40 +20506,112 @@ function requireLib() {
20069
20506
  const startBitrateMultiplier = 0.9;
20070
20507
  /** Maximum x-google-start-bitrate in kbps. 1 Mbps prevents BWE from starting too aggressively. */
20071
20508
  const maxStartBitrateKbps = 1000;
20509
+ /**
20510
+ * Minimum target bitrate in kbps for the start bitrate hint. Below this, seeding above the
20511
+ * real capacity costs more than the ramp it saves, so libwebrtc's default is left in place.
20512
+ */
20513
+ const minTargetBitrateKbps = 300;
20072
20514
  const debounceInterval = 20;
20073
20515
  /**
20074
- * Applies the configured start bitrate when this media section belongs to `cid`.
20075
- * This SDP munging is used for a bitrate setting that cannot be applied through
20076
- * `RTCRtpEncodingParameters`.
20516
+ * Codec payload for `codec` in this media section, when the section carries `cid`.
20077
20517
  *
20078
- * Returns `undefined` when the section does not belong to the track, `0` when
20079
- * it does but does not offer the requested codec, and the codec payload when the
20080
- * requested codec is present (whether the bitrate was added or already set).
20518
+ * Returns `undefined` when the section does not belong to the track, `0` when it does but
20519
+ * does not offer the requested codec, and the codec payload otherwise.
20081
20520
  *
20082
20521
  * @internal
20083
20522
  */
20084
- function applyVideoStartBitrate(media, cid, codec, maxbr) {
20085
- let isScreenShare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
20523
+ function findTrackCodecPayload(media, cid, codec) {
20086
20524
  var _a, _b, _c;
20087
20525
  if (!((_a = media.msid) === null || _a === void 0 ? void 0 : _a.includes(cid))) {
20088
20526
  return undefined;
20089
20527
  }
20090
- const codecPayload = (_c = (_b = media.rtp.find(rtp => rtp.codec.toUpperCase() === codec.toUpperCase())) === null || _b === void 0 ? void 0 : _b.payload) !== null && _c !== void 0 ? _c : 0;
20091
- if (codecPayload === 0) {
20092
- return 0;
20528
+ return (_c = (_b = media.rtp.find(rtp => rtp.codec.toUpperCase() === codec.toUpperCase())) === null || _b === void 0 ? void 0 : _b.payload) !== null && _c !== void 0 ? _c : 0;
20529
+ }
20530
+ /**
20531
+ * Start bitrate hinted for a single track, or `undefined` when its target is too low to
20532
+ * be worth seeding.
20533
+ *
20534
+ * 90% of the target leaves ~10% headroom for the estimator to settle. The same multiplier
20535
+ * is used for every codec because the target already reflects the codec's efficiency.
20536
+ * Camera is capped at 1 Mbps so the estimator does not open too aggressively on a
20537
+ * high-bitrate track; screen share is exempt, because its content needs the bitrate
20538
+ * immediately to stay legible.
20539
+ *
20540
+ * TODO: adjust dynamically from network conditions (e.g. a previous BWE estimate) rather
20541
+ * than a fixed cap.
20542
+ *
20543
+ * @internal
20544
+ */
20545
+ function computeTrackStartBitrate(trackbr) {
20546
+ if (trackbr.maxbr < minTargetBitrateKbps) {
20547
+ return undefined;
20093
20548
  }
20094
- // Use 90% of target bitrate, capped at 1 Mbps for camera to prevent BWE
20095
- // from starting too aggressively. Screen share is not capped since text/UI
20096
- // clarity requires high bitrate from the start.
20097
- // TODO: dynamically adjust start bitrate based on network conditions (e.g., previous BWE estimate)
20098
- const calculatedStartBitrate = Math.round(maxbr * startBitrateMultiplier);
20099
- const startBitrate = isScreenShare ? calculatedStartBitrate : Math.min(calculatedStartBitrate, maxStartBitrateKbps);
20549
+ const calculated = Math.round(trackbr.maxbr * startBitrateMultiplier);
20550
+ return trackbr.isScreenShare ? calculated : Math.min(calculated, maxStartBitrateKbps);
20551
+ }
20552
+ /**
20553
+ * The single start bitrate for this peer connection: the largest hint among the video
20554
+ * m-sections of `media` that map to a published track.
20555
+ *
20556
+ * libwebrtc reads `x-google-start-bitrate` per m-section but applies it to the shared
20557
+ * `Call` (`WebRtcVideoSendChannel::ApplyChangedParams` -> `SetSdpBitrateParameters`), where
20558
+ * `RtpBitrateConfigurator` holds one config for the whole connection. Differing per-section
20559
+ * values are therefore last-writer-wins, decided by m-section order, so every video section
20560
+ * gets the same number instead.
20561
+ *
20562
+ * Only sections that can send local media are considered. `trackBitrates` is append-only and an
20563
+ * unpublished section keeps its `a=msid`, so matching on msid alone would still pair a stale
20564
+ * entry with its old section — letting an uncapped screen-share target seed a connection that
20565
+ * now carries only a camera, or consuming the one-shot hint on a section that sends nothing.
20566
+ *
20567
+ * The direction is the discriminator, as an exclusion rather than a match: `recvonly` and
20568
+ * `inactive` are the only directions that cannot carry local media, and they are exactly the
20569
+ * two a dead section lands on — unpublish sets `inactive` (explicitly in
20570
+ * `LocalParticipant.unpublishTrack`, and via `removeTrack`'s sendonly -> inactive transition for
20571
+ * the simulcast senders), `removeTrack` on a `sendrecv` transceiver leaves `recvonly`, and the
20572
+ * pre-populated placeholders are `recvonly`. Everything else sends: `sendonly` from the
20573
+ * `addTransceiver` path, `sendrecv` from the legacy `addTrack` fallback (which reuses a
20574
+ * transceiver rather than creating a sendonly one), and a section with no direction attribute,
20575
+ * which SDP defaults to `sendrecv`.
20576
+ *
20577
+ * @internal
20578
+ */
20579
+ function computeConnectionStartBitrate(media, trackBitrates) {
20580
+ let connectionStartBitrate;
20581
+ for (const m of media) {
20582
+ if (m.type !== 'video' || m.direction === 'recvonly' || m.direction === 'inactive') {
20583
+ continue;
20584
+ }
20585
+ for (const trackbr of trackBitrates) {
20586
+ if (!trackbr.cid) {
20587
+ continue;
20588
+ }
20589
+ const codecPayload = findTrackCodecPayload(m, trackbr.cid, trackbr.codec);
20590
+ if (codecPayload === undefined) {
20591
+ continue;
20592
+ }
20593
+ const startBitrate = codecPayload > 0 ? computeTrackStartBitrate(trackbr) : undefined;
20594
+ if (startBitrate !== undefined && (connectionStartBitrate === undefined || startBitrate > connectionStartBitrate)) {
20595
+ connectionStartBitrate = startBitrate;
20596
+ }
20597
+ break;
20598
+ }
20599
+ }
20600
+ return connectionStartBitrate;
20601
+ }
20602
+ /**
20603
+ * Declares `x-google-start-bitrate` on `codecPayload`'s fmtp. This SDP munging is used for
20604
+ * a bitrate setting that cannot be applied through `RTCRtpEncodingParameters`.
20605
+ *
20606
+ * Returns whether the section now carries the hint.
20607
+ *
20608
+ * @internal
20609
+ */
20610
+ function applyVideoStartBitrate(media, codecPayload, startBitrate) {
20100
20611
  const fmtp = media.fmtp.find(entry => entry.payload === codecPayload);
20101
20612
  if (fmtp) {
20102
- // If another track's fmtp already has a start bitrate, it cannot be
20103
- // overridden here because the payload type is shared across the bundle.
20104
- // This forces every track sharing that payload to use the initial track's
20105
- // start bitrate.
20613
+ // A payload type is shared across the bundle, so a value written for one section is
20614
+ // already the connection-level one; leave it rather than rewrite it.
20106
20615
  if (!fmtp.config.includes('x-google-start-bitrate')) {
20107
20616
  fmtp.config += ";x-google-start-bitrate=".concat(startBitrate);
20108
20617
  }
@@ -20113,7 +20622,7 @@ function applyVideoStartBitrate(media, cid, codec, maxbr) {
20113
20622
  config: "x-google-start-bitrate=".concat(startBitrate)
20114
20623
  });
20115
20624
  }
20116
- return codecPayload;
20625
+ return true;
20117
20626
  }
20118
20627
  const PCEvents = {
20119
20628
  NegotiationStarted: 'negotiationStarted',
@@ -20141,11 +20650,20 @@ class PCTransport extends eventsExports.EventEmitter {
20141
20650
  this.iceLog = livekitLogger;
20142
20651
  this.ddExtID = 0;
20143
20652
  this.latestOfferId = 0;
20653
+ this.answeredOfferId = 0;
20144
20654
  this.latestAcknowledgedOfferId = 0;
20145
20655
  this.pendingCandidates = [];
20146
20656
  this.restartingIce = false;
20147
20657
  this.renegotiate = false;
20148
20658
  this.trackBitrates = [];
20659
+ /**
20660
+ * Whether an offer carrying the connection-level `x-google-start-bitrate` has been
20661
+ * accepted locally. The hint is written once per peer connection: libwebrtc retains
20662
+ * `start_bitrate_bps` in `RtpBitrateConfigurator` and re-applies it on network route
20663
+ * changes, so a later rewrite is at best a no-op and at worst restarts a converged
20664
+ * bandwidth estimator. A new peer connection (full reconnect) seeds a new estimator.
20665
+ */
20666
+ this.hasAppliedVideoStartBitrate = false;
20149
20667
  this.remoteStereoMids = [];
20150
20668
  this.remoteNackMids = [];
20151
20669
  // debounced negotiate interface
@@ -20254,7 +20772,7 @@ class PCTransport extends eventsExports.EventEmitter {
20254
20772
  }
20255
20773
  setRemoteDescription(sd, offerId) {
20256
20774
  return __awaiter(this, void 0, void 0, function* () {
20257
- var _a, _b;
20775
+ var _a;
20258
20776
  if (sd.type === 'answer' && this.latestOfferId > 0 && offerId > 0 && offerId !== this.latestOfferId) {
20259
20777
  this.log.warn('ignoring answer for old offer', {
20260
20778
  offerId,
@@ -20262,6 +20780,29 @@ class PCTransport extends eventsExports.EventEmitter {
20262
20780
  });
20263
20781
  return false;
20264
20782
  }
20783
+ if (sd.type === 'answer') {
20784
+ if (isDuplicateAnswer(offerId, this.answerInFlight, this.answeredOfferId, (_a = this._pc) === null || _a === void 0 ? void 0 : _a.signalingState, this.pendingInitialOffer !== undefined)) {
20785
+ // a repeat of an answer already applied or being applied (e.g.
20786
+ // duplicated by a proxy): there is no local offer left to answer
20787
+ this.log.warn('ignoring duplicate answer', {
20788
+ offerId
20789
+ });
20790
+ return false;
20791
+ }
20792
+ // claimed before any await, so a copy arriving mid-apply is caught too;
20793
+ // released however the apply ends, so a genuine retry is still possible
20794
+ this.answerInFlight = offerId;
20795
+ }
20796
+ try {
20797
+ return yield this.applyRemoteDescription(sd, offerId);
20798
+ } finally {
20799
+ if (sd.type === 'answer') this.answerInFlight = undefined;
20800
+ }
20801
+ });
20802
+ }
20803
+ applyRemoteDescription(sd, offerId) {
20804
+ return __awaiter(this, void 0, void 0, function* () {
20805
+ var _a, _b;
20265
20806
  let mungedSDP = undefined;
20266
20807
  if (sd.type === 'offer') {
20267
20808
  let _extractStereoAndNack = extractStereoAndNackAudioFromOffer(sd),
@@ -20335,6 +20876,8 @@ class PCTransport extends eventsExports.EventEmitter {
20335
20876
  mungedSDP = libExports.write(sdpParsed);
20336
20877
  }
20337
20878
  yield this.setMungedSDP(sd, mungedSDP, true);
20879
+ // only a successfully applied answer makes later copies duplicates
20880
+ if (sd.type === 'answer' && offerId > 0) this.answeredOfferId = offerId;
20338
20881
  if (this.pendingCandidates.length > 0) {
20339
20882
  this.iceLog.debug('flushing queued ICE candidates', {
20340
20883
  count: this.pendingCandidates.length
@@ -20442,6 +20985,12 @@ class PCTransport extends eventsExports.EventEmitter {
20442
20985
  sdp: offer.sdp
20443
20986
  });
20444
20987
  const sdpParsed = libExports.parse((_a = offer.sdp) !== null && _a !== void 0 ? _a : '');
20988
+ // One value for every video m-section, written only on the first offer that carries
20989
+ // local video: the hint is connection-level in libwebrtc, so differing per-section
20990
+ // values would be last-writer-wins on m-section order. Offers before any video is
20991
+ // published (data channel or audio only) find no target and leave the latch unset.
20992
+ const connectionStartBitrate = this.hasAppliedVideoStartBitrate ? undefined : computeConnectionStartBitrate(sdpParsed.media, this.trackBitrates);
20993
+ let appliedVideoStartBitrate = false;
20445
20994
  sdpParsed.media.forEach(media => {
20446
20995
  ensureIPAddrMatchVersion(media);
20447
20996
  if (media.type === 'audio') {
@@ -20451,12 +21000,17 @@ class PCTransport extends eventsExports.EventEmitter {
20451
21000
  if (!trackbr.cid) {
20452
21001
  return false;
20453
21002
  }
20454
- const codecPayload = applyVideoStartBitrate(media, trackbr.cid, trackbr.codec, trackbr.maxbr, trackbr.isScreenShare);
21003
+ const codecPayload = findTrackCodecPayload(media, trackbr.cid, trackbr.codec);
20455
21004
  if (codecPayload === undefined) {
20456
21005
  return false;
20457
21006
  }
20458
- if (codecPayload > 0 && isSVCCodec(trackbr.codec) && !isSafari()) {
20459
- this.ddExtID = ensureVideoDDExtension(media, sdpParsed, this.ddExtID);
21007
+ if (codecPayload > 0) {
21008
+ if (connectionStartBitrate !== undefined) {
21009
+ appliedVideoStartBitrate = applyVideoStartBitrate(media, codecPayload, connectionStartBitrate) || appliedVideoStartBitrate;
21010
+ }
21011
+ if (isSVCCodec(trackbr.codec) && !isSafari()) {
21012
+ this.ddExtID = ensureVideoDDExtension(media, sdpParsed, this.ddExtID);
21013
+ }
20460
21014
  }
20461
21015
  return true;
20462
21016
  });
@@ -20478,7 +21032,13 @@ class PCTransport extends eventsExports.EventEmitter {
20478
21032
  });
20479
21033
  return;
20480
21034
  }
20481
- yield this.setMungedSDP(offer, libExports.write(sdpParsed));
21035
+ const mungedSdp = libExports.write(sdpParsed);
21036
+ yield this.setMungedSDP(offer, mungedSdp);
21037
+ // setMungedSDP falls back to the unmunged SDP on rejection. Only consume the
21038
+ // one-shot hint once the SDP carrying it has been accepted locally.
21039
+ if (appliedVideoStartBitrate && offer.sdp === mungedSdp) {
21040
+ this.hasAppliedVideoStartBitrate = true;
21041
+ }
20482
21042
  this.onOffer(offer, this.latestOfferId);
20483
21043
  } finally {
20484
21044
  unlock();
@@ -20874,6 +21434,19 @@ function conformBundledCodecFmtp(media, isPlaceholder) {
20874
21434
  }
20875
21435
  }
20876
21436
  /** @internal */
21437
+ /**
21438
+ * Whether an incoming answer repeats one already applied or being applied.
21439
+ * Any answer for the offer whose answer is in flight is a duplicate, numbered
21440
+ * or not. With an offerId (current servers) an answer is also a duplicate if
21441
+ * that offer's answer was already applied. Without one, fall back to the
21442
+ * signaling state: in `stable` no local offer is pending, unless the initial
21443
+ * offer is still deferred (it is set locally only once its answer arrives).
21444
+ */
21445
+ function isDuplicateAnswer(offerId, answerInFlight, answeredOfferId, signalingState, hasPendingInitialOffer) {
21446
+ if (answerInFlight === offerId) return true;
21447
+ if (offerId > 0) return offerId === answeredOfferId;
21448
+ return signalingState === 'stable' && !hasPendingInitialOffer;
21449
+ }
20877
21450
  function extractStereoAndNackAudioFromOffer(offer) {
20878
21451
  var _a;
20879
21452
  const stereoMids = [];
@@ -20965,6 +21538,7 @@ const roomOptionDefaults = {
20965
21538
  };
20966
21539
  const roomConnectOptionDefaults = {
20967
21540
  autoSubscribe: true,
21541
+ disableIceLite: false,
20968
21542
  maxRetries: 1,
20969
21543
  peerConnectionTimeout: 15000,
20970
21544
  websocketTimeout: 15000
@@ -21178,7 +21752,9 @@ class PCTransportManager {
21178
21752
  try {
21179
21753
  if (this.isPublisherConnectionRequired && this.publisher.getConnectionState() !== 'connected' && this.publisher.getConnectionState() !== 'connecting') {
21180
21754
  this.log.debug('negotiation required, start negotiating');
21181
- this.publisher.negotiate();
21755
+ this.publisher.negotiate().catch(err => {
21756
+ this.log.error(err);
21757
+ });
21182
21758
  }
21183
21759
  yield Promise.all((_a = this.requiredTransports) === null || _a === void 0 ? void 0 : _a.map(transport => this.ensureTransportConnected(transport, abortController, timeout)));
21184
21760
  } finally {
@@ -22216,21 +22792,29 @@ class LocalTrack extends Track {
22216
22792
  var _this = this;
22217
22793
  let timeout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_DIMENSIONS_TIMEOUT;
22218
22794
  return function* () {
22219
- var _a;
22220
22795
  if (_this.kind === Track.Kind.Audio) {
22221
22796
  throw new Error('cannot get dimensions for audio tracks');
22222
22797
  }
22223
- if (((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.os) === 'iOS') {
22224
- // browsers report wrong initial resolution on iOS.
22225
- // when slightly delaying the call to .getSettings(), the correct resolution is being reported
22226
- yield sleep(10);
22227
- }
22228
22798
  const started = Date.now();
22229
- while (Date.now() - started < timeout) {
22799
+ if (isAppleMobile()) {
22800
+ // iOS and iPadOS keep reporting the camera's sensor frame from getSettings() until the
22801
+ // camera has delivered its first picture, so a portrait capture initially reads as
22802
+ // landscape. Wait for that first frame instead of guessing at a delay.
22803
+ // Gated to the Apple capture pipeline rather than run everywhere: elsewhere getSettings() is
22804
+ // already right and the first frame can be hundreds of milliseconds out (measured ~500ms on
22805
+ // macOS Safari), which would be pure added publish latency.
22806
+ // https://github.com/livekit/client-sdk-js/issues/2099
22807
+ // https://github.com/livekit/client-sdk-js/issues/2107
22808
+ yield waitForFirstVideoFrame(_this._mediaStreamTrack, timeout, _this.attachedElements);
22809
+ }
22810
+ while (true) {
22230
22811
  const dims = _this.dimensions;
22231
22812
  if (dims) {
22232
22813
  return dims;
22233
22814
  }
22815
+ if (Date.now() - started >= timeout) {
22816
+ break;
22817
+ }
22234
22818
  yield sleep(50);
22235
22819
  }
22236
22820
  throw new TrackInvalidError('unable to get track dimensions after timeout');
@@ -22933,24 +23517,6 @@ const computeDefaultScreenShareSimulcastPresets = fromPreset => {
22933
23517
  return new VideoPreset(Math.floor(fromPreset.width / t.scaleResolutionDownBy), Math.floor(fromPreset.height / t.scaleResolutionDownBy), Math.max(150000, Math.floor(fromPreset.encoding.maxBitrate / (Math.pow(t.scaleResolutionDownBy, 2) * (((_a = fromPreset.encoding.maxFramerate) !== null && _a !== void 0 ? _a : 30) / ((_b = t.fps) !== null && _b !== void 0 ? _b : 30))))), t.fps, fromPreset.encoding.priority);
22934
23518
  });
22935
23519
  };
22936
- // /**
22937
- // *
22938
- // * @internal
22939
- // * @experimental
22940
- // */
22941
- // const computeDefaultMultiCodecSimulcastEncodings = (width: number, height: number) => {
22942
- // // use vp8 as a default
22943
- // const vp8 = determineAppropriateEncoding(false, width, height);
22944
- // const vp9 = { ...vp8, maxBitrate: vp8.maxBitrate * 0.9 };
22945
- // const h264 = { ...vp8, maxBitrate: vp8.maxBitrate * 1.1 };
22946
- // const av1 = { ...vp8, maxBitrate: vp8.maxBitrate * 0.7 };
22947
- // return {
22948
- // vp8,
22949
- // vp9,
22950
- // h264,
22951
- // av1,
22952
- // };
22953
- // };
22954
23520
  const videoRids = ['q', 'h', 'f'];
22955
23521
  /* @internal */
22956
23522
  function computeVideoEncodings(isScreenShare, width, height, options) {
@@ -22962,6 +23528,8 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
22962
23528
  const useSimulcast = options === null || options === void 0 ? void 0 : options.simulcast;
22963
23529
  const scalabilityMode = options === null || options === void 0 ? void 0 : options.scalabilityMode;
22964
23530
  const videoCodec = options === null || options === void 0 ? void 0 : options.videoCodec;
23531
+ // VP9/AV1 published as rid based simulcast rather than SVC, see isSVCSimulcast
23532
+ const useSVCSimulcast = isSVCSimulcast(videoCodec, options);
22965
23533
  if (!videoEncoding && !useSimulcast && !scalabilityMode || !width || !height) {
22966
23534
  // when we aren't simulcasting or svc, will need to return a single encoding without
22967
23535
  // capping bandwidth. we always require a encoding for dynacast
@@ -22974,23 +23542,16 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
22974
23542
  }
22975
23543
  const sourceFramerate = videoEncoding.maxFramerate;
22976
23544
  const original = new VideoPreset(width, height, videoEncoding.maxBitrate, videoEncoding.maxFramerate, videoEncoding.priority);
22977
- if (scalabilityMode && isSVCCodec(videoCodec)) {
23545
+ if (scalabilityMode && isSVCCodec(videoCodec) && !useSVCSimulcast) {
22978
23546
  const sm = new ScalabilityMode(scalabilityMode);
22979
23547
  const encodings = [];
22980
23548
  if (sm.spatial > 3) {
22981
23549
  throw new Error("unsupported scalabilityMode: ".concat(scalabilityMode));
22982
23550
  }
22983
- // Before M113 in Chrome, defining multiple encodings with an SVC codec indicated
22984
- // that SVC mode should be used. Safari still works this way.
22985
- // This is a bit confusing but is due to how libwebrtc interpreted the encodings field
22986
- // before M113.
22987
- // Announced here: https://groups.google.com/g/discuss-webrtc/c/-QQ3pxrl-fw?pli=1
23551
+ // Browsers that read multiple encodings on an SVC codec as SVC rather than as
23552
+ // simulcast, see usesLegacySVCEncodings
22988
23553
  const browser = getBrowser();
22989
- if (isSafariBased() ||
22990
- // Even tho RN runs M114, it does not produce SVC layers when a single encoding
22991
- // is provided. So we'll use the legacy SVC specification for now.
22992
- // TODO: when we upstream libwebrtc, this will need additional verification
22993
- isReactNative() || (browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && compareVersions(browser === null || browser === void 0 ? void 0 : browser.version, '113') < 0) {
23554
+ if (usesLegacySVCEncodings()) {
22994
23555
  const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
22995
23556
  // safari 18.4 uses a different svc API that requires scaleResolutionDownBy to be set.
22996
23557
  const requireScale = isSafariSvcApi(browser);
@@ -23026,6 +23587,18 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
23026
23587
  if (!useSimulcast) {
23027
23588
  return [videoEncoding];
23028
23589
  }
23590
+ // Chrome M113+ only treats multiple encodings on an SVC capable codec as real
23591
+ // simulcast when every encoding carries an explicit scalabilityMode next to its
23592
+ // scaleResolutionDownBy. Without it the encodings are interpreted as legacy SVC.
23593
+ const applySVCSimulcastMode = encodings => {
23594
+ if (useSVCSimulcast) {
23595
+ encodings.forEach(encoding => {
23596
+ /* @ts-ignore */
23597
+ encoding.scalabilityMode = scalabilityMode;
23598
+ });
23599
+ }
23600
+ return encodings;
23601
+ };
23029
23602
  let presets;
23030
23603
  if (isScreenShare) {
23031
23604
  presets = (_a = sortPresets(options === null || options === void 0 ? void 0 : options.screenShareSimulcastLayers)) !== null && _a !== void 0 ? _a : defaultSimulcastLayers(isScreenShare, original);
@@ -23051,13 +23624,38 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
23051
23624
  // based on other conditions.
23052
23625
  const size = Math.max(width, height);
23053
23626
  if (size >= 960 && midPreset) {
23054
- return encodingsFromPresets(width, height, [lowPreset, midPreset, original], sourceFramerate);
23627
+ return applySVCSimulcastMode(encodingsFromPresets(width, height, [lowPreset, midPreset, original], sourceFramerate));
23055
23628
  }
23056
23629
  if (size >= 480) {
23057
- return encodingsFromPresets(width, height, [lowPreset, original], sourceFramerate);
23630
+ return applySVCSimulcastMode(encodingsFromPresets(width, height, [lowPreset, original], sourceFramerate));
23058
23631
  }
23059
23632
  }
23060
- return encodingsFromPresets(width, height, [original]);
23633
+ return applySVCSimulcastMode(encodingsFromPresets(width, height, [original]));
23634
+ }
23635
+ /**
23636
+ * Bitrate to hint to the bandwidth estimator through `x-google-start-bitrate`, so that
23637
+ * a publish does not spend its first seconds ramping up from a very low rate.
23638
+ *
23639
+ * It has to be the total the encoder will put on the wire, which means picking the
23640
+ * encoding that carries the inclusive bitrate:
23641
+ * - SVC publishes a single stream with the layers built in. `encodings[0]` holds the
23642
+ * full bitrate — the legacy SVC shape orders its encodings `f`..`q`, so that holds
23643
+ * for both SVC shapes.
23644
+ * - Simulcast publishes independent streams ordered `q`..`f`, so the total is the sum.
23645
+ * This includes VP9/AV1 published as rid based simulcast, where `encodings[0]` is
23646
+ * the *smallest* layer even though the codec is SVC capable.
23647
+ *
23648
+ * @internal
23649
+ */
23650
+ function computeStartTargetBitrate(codec, options, encodings) {
23651
+ var _a, _b;
23652
+ if (isSVCCodec(codec) && !isSVCSimulcast(codec, options)) {
23653
+ return (_b = (_a = encodings[0]) === null || _a === void 0 ? void 0 : _a.maxBitrate) !== null && _b !== void 0 ? _b : 0;
23654
+ }
23655
+ return encodings.reduce((sum, enc) => {
23656
+ var _a;
23657
+ return sum + ((_a = enc.maxBitrate) !== null && _a !== void 0 ? _a : 0);
23658
+ }, 0);
23061
23659
  }
23062
23660
  function computeTrackBackupEncodings(track, videoCodec, opts) {
23063
23661
  var _a, _b, _c, _d;
@@ -23511,6 +24109,15 @@ class LocalVideoTrack extends LocalTrack {
23511
24109
  return items;
23512
24110
  });
23513
24111
  }
24112
+ /**
24113
+ * Whether `codec` is being published as SVC (a single stream carrying all spatial
24114
+ * layers) as opposed to rid based simulcast. VP9/AV1 are SVC unless the publisher
24115
+ * opted into simulcast, in which case each rid is an independent stream and the
24116
+ * layers can be enabled/disabled individually.
24117
+ */
24118
+ isSvcPublish(codec) {
24119
+ return isSVCCodec(codec) && !isSVCSimulcast(codec, this.publishOptions);
24120
+ }
23514
24121
  setPublishingQuality(maxQuality) {
23515
24122
  const qualities = [];
23516
24123
  for (let q = VideoQuality.LOW; q <= VideoQuality.HIGH; q += 1) {
@@ -23520,7 +24127,7 @@ class LocalVideoTrack extends LocalTrack {
23520
24127
  }));
23521
24128
  }
23522
24129
  this.log.debug("setting publishing quality. max quality ".concat(maxQuality), this.logContext);
23523
- this.setPublishingLayers(isSVCCodec(this.codec), qualities);
24130
+ this.setPublishingLayers(this.isSvcPublish(this.codec), qualities);
23524
24131
  }
23525
24132
  restartTrack(options) {
23526
24133
  return __awaiter(this, void 0, void 0, function* () {
@@ -23764,12 +24371,20 @@ class LocalVideoTrack extends LocalTrack {
23764
24371
  // browser will reenable disabled codec/layers after new codec has been published,
23765
24372
  // so refresh subscribedCodecs after publish a new codec
23766
24373
  setTimeout(() => {
23767
- if (this.subscribedCodecs) {
23768
- this.setPublishingCodecs(this.subscribedCodecs);
23769
- }
24374
+ this.refreshSubscribedCodecs();
23770
24375
  }, refreshSubscribedCodecAfterNewCodec);
23771
24376
  });
23772
24377
  }
24378
+ /** @interal */
24379
+ refreshSubscribedCodecs() {
24380
+ return __awaiter(this, void 0, void 0, function* () {
24381
+ if (this.subscribedCodecs) {
24382
+ return this.setPublishingCodecs(this.subscribedCodecs).catch(e => this.log.warn("failed to refresh codec for track", Object.assign(Object.assign({}, this.logContext), {
24383
+ error: e
24384
+ })));
24385
+ }
24386
+ });
24387
+ }
23773
24388
  /**
23774
24389
  * @internal
23775
24390
  * Sets codecs that should be publishing, returns new codecs that have not yet
@@ -23785,7 +24400,7 @@ class LocalVideoTrack extends LocalTrack {
23785
24400
  }));
23786
24401
  // only enable simulcast codec for preference codec setted
23787
24402
  if (!this.codec && codecs.length > 0) {
23788
- yield this.setPublishingLayers(isSVCCodec(codecs[0].codec), codecs[0].qualities);
24403
+ yield this.setPublishingLayers(this.isSvcPublish(codecs[0].codec), codecs[0].qualities);
23789
24404
  return [];
23790
24405
  }
23791
24406
  this.subscribedCodecs = codecs;
@@ -23796,7 +24411,7 @@ class LocalVideoTrack extends LocalTrack {
23796
24411
  _a = false;
23797
24412
  const codec = _d;
23798
24413
  if (!this.codec || this.codec === codec.codec) {
23799
- yield this.setPublishingLayers(isSVCCodec(codec.codec), codec.qualities);
24414
+ yield this.setPublishingLayers(this.isSvcPublish(codec.codec), codec.qualities);
23800
24415
  } else {
23801
24416
  const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec);
23802
24417
  this.log.debug("try setPublishingCodec for ".concat(codec.codec), Object.assign(Object.assign({}, this.logContext), {
@@ -23811,7 +24426,7 @@ class LocalVideoTrack extends LocalTrack {
23811
24426
  }
23812
24427
  } else if (simulcastCodecInfo.encodings) {
23813
24428
  this.log.debug("try setPublishingLayersForSender ".concat(codec.codec), this.logContext);
23814
- yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, isSVCCodec(codec.codec), this.log, this.logContext);
24429
+ yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, this.isSvcPublish(codec.codec), this.log, this.logContext);
23815
24430
  }
23816
24431
  }
23817
24432
  }
@@ -24071,6 +24686,10 @@ class RTCEngine extends eventsExports.EventEmitter {
24071
24686
  get pendingReconnect() {
24072
24687
  return !!this.reconnectTimeout;
24073
24688
  }
24689
+ get serverVersion() {
24690
+ var _a, _b, _c;
24691
+ return ((_b = (_a = this.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.serverInfo) === null || _b === void 0 ? void 0 : _b.version) || ((_c = this.latestJoinResponse) === null || _c === void 0 ? void 0 : _c.serverVersion) || undefined;
24692
+ }
24074
24693
  get reliableChannel() {
24075
24694
  return this.dataChannels.reliable;
24076
24695
  }
@@ -24110,6 +24729,13 @@ class RTCEngine extends eventsExports.EventEmitter {
24110
24729
  this.midToTrackId = {};
24111
24730
  /** used to indicate whether the browser is currently waiting to reconnect */
24112
24731
  this.isWaitingForNetworkReconnect = false;
24732
+ /**
24733
+ * Abort handlers for the in-flight `negotiate()` calls, which a single central
24734
+ * Closing/Restarting listener pair fans out to. Registering a listener pair per call instead
24735
+ * accumulates them on the engine emitter and trips its max-listener warning under
24736
+ * renegotiation bursts.
24737
+ */
24738
+ this.pendingNegotiationAborts = new Set();
24113
24739
  this.handleDataChannel = _a => __awaiter(this, [_a], void 0, function (_ref) {
24114
24740
  var _this = this;
24115
24741
  let channel = _ref.channel;
@@ -24123,7 +24749,7 @@ class RTCEngine extends eventsExports.EventEmitter {
24123
24749
  }();
24124
24750
  });
24125
24751
  this.handleDataMessage = message => __awaiter(this, void 0, void 0, function* () {
24126
- var _a, _b, _c, _d, _e;
24752
+ var _a, _b, _c, _d;
24127
24753
  // make sure to respect incoming data message order by processing message events one after the other
24128
24754
  const unlock = yield this.dataProcessLock.lock();
24129
24755
  try {
@@ -24148,20 +24774,29 @@ class RTCEngine extends eventsExports.EventEmitter {
24148
24774
  this.log.error('Received encrypted packet but E2EE not set up');
24149
24775
  return;
24150
24776
  }
24151
- const decryptedData = yield (_c = this.e2eeManager) === null || _c === void 0 ? void 0 : _c.handleEncryptedData(dp.value.value.encryptedValue, dp.value.value.iv, dp.participantIdentity, dp.value.value.keyIndex);
24777
+ let decryptedData;
24778
+ try {
24779
+ decryptedData = yield this.e2eeManager.handleEncryptedData(dp.value.value.encryptedValue, dp.value.value.iv, dp.participantIdentity, dp.value.value.keyIndex);
24780
+ } catch (err) {
24781
+ this.log.debug('failed to decrypt data packet', {
24782
+ error: err,
24783
+ participantIdentity: dp.participantIdentity
24784
+ });
24785
+ return;
24786
+ }
24152
24787
  const decryptedPacket = EncryptedPacketPayload.fromBinary(decryptedData.payload);
24153
24788
  const newDp = new DataPacket({
24154
24789
  value: decryptedPacket.value,
24155
24790
  participantIdentity: dp.participantIdentity,
24156
24791
  participantSid: dp.participantSid
24157
24792
  });
24158
- if (((_d = newDp.value) === null || _d === void 0 ? void 0 : _d.case) === 'user') {
24793
+ if (((_c = newDp.value) === null || _c === void 0 ? void 0 : _c.case) === 'user') {
24159
24794
  // compatibility
24160
24795
  applyUserDataCompat(newDp, newDp.value.value);
24161
24796
  }
24162
24797
  this.emit(EngineEvent.DataPacketReceived, newDp, dp.value.value.encryptionType);
24163
24798
  } else {
24164
- if (((_e = dp.value) === null || _e === void 0 ? void 0 : _e.case) === 'user') {
24799
+ if (((_d = dp.value) === null || _d === void 0 ? void 0 : _d.case) === 'user') {
24165
24800
  // compatibility
24166
24801
  applyUserDataCompat(dp, dp.value.value);
24167
24802
  }
@@ -24346,6 +24981,14 @@ class RTCEngine extends eventsExports.EventEmitter {
24346
24981
  this.client.onRequestResponse = response => this.emit(EngineEvent.SignalRequestResponse, response);
24347
24982
  this.client.onParticipantUpdate = updates => this.emit(EngineEvent.ParticipantUpdate, updates);
24348
24983
  this.client.onJoined = joinResponse => this.emit(EngineEvent.Joined, joinResponse);
24984
+ const abortPendingNegotiations = () => {
24985
+ // Iterate a copy: each handler removes itself from the set as its negotiation settles.
24986
+ for (const abort of Array.from(this.pendingNegotiationAborts)) {
24987
+ abort();
24988
+ }
24989
+ };
24990
+ this.on(EngineEvent.Closing, abortPendingNegotiations);
24991
+ this.on(EngineEvent.Restarting, abortPendingNegotiations);
24349
24992
  }
24350
24993
  /** @internal */
24351
24994
  get logContext() {
@@ -24662,8 +25305,13 @@ class RTCEngine extends eventsExports.EventEmitter {
24662
25305
  sdp: sd.sdp,
24663
25306
  midToTrackId
24664
25307
  });
24665
- this.midToTrackId = midToTrackId;
25308
+ // in dual PC mode the publisher answer carries no mapping (the server's publisher
25309
+ // transport has no sending tracks) and must not clobber the subscriber offer mapping
25310
+ if (this.pcManager.mode === 'publisher-only') {
25311
+ this.midToTrackId = midToTrackId;
25312
+ }
24666
25313
  yield this.pcManager.setPublisherAnswer(sd, offerId);
25314
+ this.emit(EngineEvent.RequestSubscribedCodecRefresh);
24667
25315
  });
24668
25316
  // add candidate on trickle
24669
25317
  this.client.onTrickle = (candidate, target) => {
@@ -24730,7 +25378,9 @@ class RTCEngine extends eventsExports.EventEmitter {
24730
25378
  };
24731
25379
  this.client.onMediaSectionsRequirement = requirement => {
24732
25380
  this.addMediaSections(requirement.numAudios, requirement.numVideos);
24733
- this.negotiate();
25381
+ this.negotiate().catch(err => {
25382
+ this.log.error(err);
25383
+ });
24734
25384
  };
24735
25385
  this.client.onPublishDataTrackResponse = event => {
24736
25386
  this.emit(EngineEvent.PublishDataTrackResponse, event);
@@ -25483,8 +26133,7 @@ class RTCEngine extends eventsExports.EventEmitter {
25483
26133
  if (this.isClosed) {
25484
26134
  reject(new NegotiationError('cannot negotiate on closed engine'));
25485
26135
  }
25486
- this.on(EngineEvent.Closing, handleClosed);
25487
- this.on(EngineEvent.Restarting, handleClosed);
26136
+ this.pendingNegotiationAborts.add(handleClosed);
25488
26137
  this.pcManager.publisher.off(PCEvents.RTPVideoPayloadTypes, this.onRtpMapAvailable);
25489
26138
  this.pcManager.publisher.once(PCEvents.RTPVideoPayloadTypes, this.onRtpMapAvailable);
25490
26139
  try {
@@ -25507,8 +26156,7 @@ class RTCEngine extends eventsExports.EventEmitter {
25507
26156
  reject(new Error(String(e)));
25508
26157
  }
25509
26158
  } finally {
25510
- this.off(EngineEvent.Closing, handleClosed);
25511
- this.off(EngineEvent.Restarting, handleClosed);
26159
+ this.pendingNegotiationAborts.delete(handleClosed);
25512
26160
  }
25513
26161
  }));
25514
26162
  });
@@ -26309,10 +26957,10 @@ class TextStreamReader extends BaseStreamReader {
26309
26957
  let compressed;
26310
26958
  switch (streamHeader.compression) {
26311
26959
  case DataStream_CompressionType.DEFLATE_RAW:
26312
- if (!isCompressionStreamSupported()) {
26960
+ if (!isDeflateRawCompressionSupported()) {
26313
26961
  // NOTE: this shouldn't really ever happen, if this warning is logged then the sender
26314
26962
  // isn't properly abiding by the data streams v2 protocol.
26315
- livekitLogger.warn("Data stream ".concat(streamHeader.streamId, " received with deflate-raw compression, but this browser does not have support for DecompressionStream. Dropping..."));
26963
+ livekitLogger.warn("Data stream ".concat(streamHeader.streamId, " received with deflate-raw compression, but this browser does not support deflate-raw decompression. Dropping..."));
26316
26964
  return;
26317
26965
  }
26318
26966
  compressed = true;
@@ -26380,10 +27028,10 @@ class TextStreamReader extends BaseStreamReader {
26380
27028
  let compressed;
26381
27029
  switch (streamHeader.compression) {
26382
27030
  case DataStream_CompressionType.DEFLATE_RAW:
26383
- if (!isCompressionStreamSupported()) {
27031
+ if (!isDeflateRawCompressionSupported()) {
26384
27032
  // NOTE: this shouldn't really ever happen, if this warning is logged then the sender
26385
27033
  // isn't properly abiding by the data streams v2 protocol.
26386
- livekitLogger.warn("Data stream ".concat(streamHeader.streamId, " received with deflate-raw compression, but this browser does not have support for DecompressionStream. Dropping..."));
27034
+ livekitLogger.warn("Data stream ".concat(streamHeader.streamId, " received with deflate-raw compression, but this browser does not support deflate-raw decompression. Dropping..."));
26387
27035
  return;
26388
27036
  }
26389
27037
  compressed = true;
@@ -26748,7 +27396,7 @@ class OutgoingDataStreamManager {
26748
27396
  attributes: options === null || options === void 0 ? void 0 : options.attributes,
26749
27397
  encryptionType: ((_c = this.engine.e2eeManager) === null || _c === void 0 ? void 0 : _c.isDataChannelEncryptionEnabled) ? Encryption_Type.GCM : Encryption_Type.NONE
26750
27398
  };
26751
- const compressEligible = compress && isCompressionStreamSupported() && this.allRecipientsSupportV2(options === null || options === void 0 ? void 0 : options.destinationIdentities) && this.allRecipientsSupportCompression(options === null || options === void 0 ? void 0 : options.destinationIdentities);
27399
+ const compressEligible = compress && isDeflateRawCompressionSupported() && this.allRecipientsSupportV2(options === null || options === void 0 ? void 0 : options.destinationIdentities) && this.allRecipientsSupportCompression(options === null || options === void 0 ? void 0 : options.destinationIdentities);
26752
27400
  let compressedStream = compressEligible ? MaybeCollectedStream.fromStream(readableFromBytes(textInBytes).pipeThrough(deflateRawTransform())) : null;
26753
27401
  // Phase 1: Try to send as a single packet data stream
26754
27402
  const noAttachments = !(options === null || options === void 0 ? void 0 : options.attachments) || options.attachments.length === 0;
@@ -26857,7 +27505,7 @@ class OutgoingDataStreamManager {
26857
27505
  encryptionType: ((_e = this.engine.e2eeManager) === null || _e === void 0 ? void 0 : _e.isDataChannelEncryptionEnabled) ? Encryption_Type.GCM : Encryption_Type.NONE
26858
27506
  };
26859
27507
  const progressMonitorTap = progressReportingStream(bytes.length, options === null || options === void 0 ? void 0 : options.onProgress);
26860
- const compressEligible = compress && isCompressionStreamSupported() && this.allRecipientsSupportV2(destinationIdentities) && this.allRecipientsSupportCompression(destinationIdentities);
27508
+ const compressEligible = compress && isDeflateRawCompressionSupported() && this.allRecipientsSupportV2(destinationIdentities) && this.allRecipientsSupportCompression(destinationIdentities);
26861
27509
  let compressedStream = compressEligible ? MaybeCollectedStream.fromStream(readableFromBytes(bytes).pipeThrough(progressMonitorTap).pipeThrough(deflateRawTransform())) : null;
26862
27510
  // Phase 1: Try to send as a single packet data stream
26863
27511
  if (this.allRecipientsSupportV2(destinationIdentities)) {
@@ -27054,7 +27702,7 @@ class OutgoingDataStreamManager {
27054
27702
  return __awaiter(this, void 0, void 0, function* () {
27055
27703
  var _a, _b, _c, _d, _e;
27056
27704
  const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
27057
- const compress = ((_a = options === null || options === void 0 ? void 0 : options.compress) !== null && _a !== void 0 ? _a : true) && isCompressionStreamSupported() && this.allRecipientsSupportV2(destinationIdentities) && this.allRecipientsSupportCompression(destinationIdentities);
27705
+ const compress = ((_a = options === null || options === void 0 ? void 0 : options.compress) !== null && _a !== void 0 ? _a : true) && isDeflateRawCompressionSupported() && this.allRecipientsSupportV2(destinationIdentities) && this.allRecipientsSupportCompression(destinationIdentities);
27058
27706
  const info = {
27059
27707
  id: streamId,
27060
27708
  name: file.name,
@@ -29725,20 +30373,37 @@ class RpcClientManager extends eventsExports.EventEmitter {
29725
30373
  completionFuture,
29726
30374
  participantIdentity: destinationIdentity
29727
30375
  });
29728
- yield _this.publishRpcRequest(destinationIdentity, id, method, payload, effectiveTimeoutMs, remoteClientProtocol);
29729
- responseTimeoutId = setTimeout(() => {
29730
- var _a;
29731
- _this.pendingResponses.delete(id);
29732
- (_a = completionFuture.reject) === null || _a === void 0 ? void 0 : _a.call(completionFuture, RpcError.builtIn('RESPONSE_TIMEOUT'));
29733
- }, responseTimeoutMs);
30376
+ // Observe the future before publishing: a slow send can outlast the ack timer.
30377
+ // Keep the returned promise rejected for callers, while marking its rejection
30378
+ // handled until performRpc has finished publishing and can return it.
29734
30379
  const completionPromise = completionFuture.promise.finally(() => {
29735
- clearTimeout(responseTimeoutId);
30380
+ if (responseTimeoutId !== null) {
30381
+ clearTimeout(responseTimeoutId);
30382
+ }
29736
30383
  if (_this.pendingAcks.has(id)) {
29737
30384
  _this.log.warn('RPC response received before ack', id);
29738
30385
  _this.pendingAcks.delete(id);
29739
30386
  clearTimeout(ackTimeoutId);
29740
30387
  }
29741
30388
  });
30389
+ completionPromise.catch(() => {});
30390
+ try {
30391
+ yield _this.publishRpcRequest(destinationIdentity, id, method, payload, effectiveTimeoutMs, remoteClientProtocol);
30392
+ } catch (e) {
30393
+ // The caller receives the publish error. Do not leave an ack timer that
30394
+ // later rejects a future the caller never received.
30395
+ clearTimeout(ackTimeoutId);
30396
+ _this.pendingAcks.delete(id);
30397
+ _this.pendingResponses.delete(id);
30398
+ throw e;
30399
+ }
30400
+ if (_this.pendingResponses.has(id)) {
30401
+ responseTimeoutId = setTimeout(() => {
30402
+ var _a;
30403
+ _this.pendingResponses.delete(id);
30404
+ (_a = completionFuture.reject) === null || _a === void 0 ? void 0 : _a.call(completionFuture, RpcError.builtIn('RESPONSE_TIMEOUT'));
30405
+ }, responseTimeoutMs);
30406
+ }
29742
30407
  return [id, completionPromise];
29743
30408
  }();
29744
30409
  });
@@ -30091,7 +30756,7 @@ class RpcServerManager extends eventsExports.EventEmitter {
30091
30756
  * gets the volume of attached audio elements (loudest)
30092
30757
  */
30093
30758
  getVolume() {
30094
- if (this.elementVolume) {
30759
+ if (this.elementVolume !== undefined) {
30095
30760
  return this.elementVolume;
30096
30761
  }
30097
30762
  if (isReactNative()) {
@@ -30140,7 +30805,7 @@ class RpcServerManager extends eventsExports.EventEmitter {
30140
30805
  element.volume = 0;
30141
30806
  element.muted = true;
30142
30807
  }
30143
- if (this.elementVolume) {
30808
+ if (this.elementVolume !== undefined) {
30144
30809
  // make sure volume setting is being applied to the newly attached element
30145
30810
  this.setVolume(this.elementVolume);
30146
30811
  }
@@ -30200,7 +30865,7 @@ class RpcServerManager extends eventsExports.EventEmitter {
30200
30865
  this.gainNode = context.createGain();
30201
30866
  lastNode.connect(this.gainNode);
30202
30867
  this.gainNode.connect(context.destination);
30203
- if (this.elementVolume) {
30868
+ if (this.elementVolume !== undefined) {
30204
30869
  this.gainNode.gain.setTargetAtTime(this.elementVolume, 0, 0.1);
30205
30870
  }
30206
30871
  // try to resume the context if it isn't running already
@@ -31773,7 +32438,7 @@ class Participant extends eventsExports.EventEmitter {
31773
32438
  }
31774
32439
  publish(track, opts, isStereo) {
31775
32440
  return __awaiter(this, void 0, void 0, function* () {
31776
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
32441
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
31777
32442
  if (!this.hasPermissionsToPublish(track)) {
31778
32443
  throw new PublishTrackError('failed to publish track, insufficient permissions', 403);
31779
32444
  }
@@ -31876,7 +32541,12 @@ class Participant extends eventsExports.EventEmitter {
31876
32541
  req.height = dims.height;
31877
32542
  // for svc codecs, disable simulcast and use vp8 for backup codec
31878
32543
  if (isLocalVideoTrack(track)) {
31879
- if (isSVCCodec(videoCodec)) {
32544
+ if (isSVCSimulcast(videoCodec, opts) && (usesLegacySVCEncodings() || !isSVCSimulcastSupportedByServer((_e = this.engine) === null || _e === void 0 ? void 0 : _e.serverVersion))) {
32545
+ opts.simulcast = false;
32546
+ this.log.info('SVC simulcast is not supported, disabling simulcast.', getLogContextFromTrack(track));
32547
+ }
32548
+ const svcSimulcast = isSVCSimulcast(videoCodec, opts);
32549
+ if (isSVCCodec(videoCodec) && !svcSimulcast) {
31880
32550
  if (track.source === Track.Source.ScreenShare) {
31881
32551
  // vp9 svc with screenshare cannot encode multiple spatial layers
31882
32552
  // doing so reduces publish resolution to minimal resolution
@@ -31892,12 +32562,16 @@ class Participant extends eventsExports.EventEmitter {
31892
32562
  }
31893
32563
  }
31894
32564
  // set scalabilityMode to 'L3T3_KEY' by default
31895
- opts.scalabilityMode = (_e = opts.scalabilityMode) !== null && _e !== void 0 ? _e : 'L3T3_KEY';
32565
+ opts.scalabilityMode = (_f = opts.scalabilityMode) !== null && _f !== void 0 ? _f : 'L3T3_KEY';
31896
32566
  }
31897
- req.simulcastCodecs = [new SimulcastCodec({
32567
+ const primaryCodec = new SimulcastCodec({
31898
32568
  codec: videoCodec,
31899
32569
  cid: track.mediaStreamTrack.id
31900
- })];
32570
+ });
32571
+ if (svcSimulcast) {
32572
+ primaryCodec.videoLayerMode = VideoLayer_Mode.ONE_SPATIAL_LAYER_PER_STREAM;
32573
+ }
32574
+ req.simulcastCodecs = [primaryCodec];
31901
32575
  // set up backup
31902
32576
  if (opts.backupCodec === true) {
31903
32577
  opts.backupCodec = {
@@ -31918,19 +32592,19 @@ class Participant extends eventsExports.EventEmitter {
31918
32592
  }
31919
32593
  }
31920
32594
  encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
31921
- req.layers = videoLayersFromEncodings(req.width, req.height, encodings, isSVCCodec(opts.videoCodec));
32595
+ req.layers = videoLayersFromEncodings(req.width, req.height, encodings, isSVCCodec(opts.videoCodec) && !isSVCSimulcast(opts.videoCodec, opts));
31922
32596
  } else if (track.kind === Track.Kind.Audio) {
31923
32597
  encodings = [{
31924
- maxBitrate: (_f = opts.audioPreset) === null || _f === void 0 ? void 0 : _f.maxBitrate,
31925
- priority: (_h = (_g = opts.audioPreset) === null || _g === void 0 ? void 0 : _g.priority) !== null && _h !== void 0 ? _h : 'high',
31926
- networkPriority: (_k = (_j = opts.audioPreset) === null || _j === void 0 ? void 0 : _j.priority) !== null && _k !== void 0 ? _k : 'high'
32598
+ maxBitrate: (_g = opts.audioPreset) === null || _g === void 0 ? void 0 : _g.maxBitrate,
32599
+ priority: (_j = (_h = opts.audioPreset) === null || _h === void 0 ? void 0 : _h.priority) !== null && _j !== void 0 ? _j : 'high',
32600
+ networkPriority: (_l = (_k = opts.audioPreset) === null || _k === void 0 ? void 0 : _k.priority) !== null && _l !== void 0 ? _l : 'high'
31927
32601
  }];
31928
32602
  }
31929
32603
  if (!this.engine || this.engine.isClosed) {
31930
32604
  throw new UnexpectedConnectionState('cannot publish track when not connected');
31931
32605
  }
31932
32606
  const negotiate = () => __awaiter(this, void 0, void 0, function* () {
31933
- var _a, _b, _c, _d;
32607
+ var _a, _b;
31934
32608
  if (!this.engine.pcManager) {
31935
32609
  throw new UnexpectedConnectionState('pcManager is not ready');
31936
32610
  }
@@ -31967,13 +32641,9 @@ class Participant extends eventsExports.EventEmitter {
31967
32641
  });
31968
32642
  }
31969
32643
  } else if (track.codec && isVideoCodec(track.codec)) {
31970
- // Apply start bitrate for all video codecs to prevent initial blurriness.
31971
- // - SVC codecs: use first encoding's bitrate (single stream with built-in layers)
31972
- // - Simulcast: sum all encoding bitrates (independent streams, BWE needs total)
31973
- const targetBitrate = isSVCCodec(track.codec) ? (_d = (_c = encodings[0]) === null || _c === void 0 ? void 0 : _c.maxBitrate) !== null && _d !== void 0 ? _d : 0 : encodings.reduce((sum, enc) => {
31974
- var _a;
31975
- return sum + ((_a = enc.maxBitrate) !== null && _a !== void 0 ? _a : 0);
31976
- }, 0);
32644
+ // Apply start bitrate for all video codecs to prevent initial blurriness,
32645
+ // see computeStartTargetBitrate
32646
+ const targetBitrate = computeStartTargetBitrate(track.codec, opts, encodings);
31977
32647
  if (targetBitrate > 0) {
31978
32648
  this.engine.pcManager.publisher.setTrackCodecBitrate({
31979
32649
  cid: req.cid,
@@ -33446,7 +34116,7 @@ class Room extends eventsExports.EventEmitter {
33446
34116
  this.abortController = undefined;
33447
34117
  resolve();
33448
34118
  } catch (error) {
33449
- if (this.regionUrlProvider && error instanceof ConnectionError && error.reason !== ConnectionErrorReason.Cancelled && error.reason !== ConnectionErrorReason.NotAllowed) {
34119
+ if (this.regionUrlProvider && error instanceof ConnectionError && canFailOverToAnotherRegion(error)) {
33450
34120
  let nextUrl = null;
33451
34121
  try {
33452
34122
  this.log.debug('Fetching next region');
@@ -33495,6 +34165,8 @@ class Room extends eventsExports.EventEmitter {
33495
34165
  const _yield$engine$join = yield engine.join(url, token, {
33496
34166
  autoSubscribe: connectOptions.autoSubscribe,
33497
34167
  adaptiveStream: typeof roomOptions.adaptiveStream === 'object' ? true : roomOptions.adaptiveStream,
34168
+ disableIceLite: connectOptions.disableIceLite,
34169
+ subscriberAllowPause: connectOptions.subscriberAllowPause,
33498
34170
  clientInfoCapabilities: this.getClientInfoCapabilities(roomOptions),
33499
34171
  maxRetries: connectOptions.maxRetries,
33500
34172
  e2eeEnabled: !!this.e2eeManager,
@@ -33933,7 +34605,7 @@ class Room extends eventsExports.EventEmitter {
33933
34605
  // find the participant
33934
34606
  const participant = this.remoteParticipants.get(packet.participantIdentity);
33935
34607
  if (packet.value.case === 'user') {
33936
- this.handleUserPacket(participant, packet.value.value, packet.kind, encryptionType);
34608
+ this.handleUserPacket(participant, packet.value.value, packet.kind, encryptionType, packet.participantIdentity);
33937
34609
  } else if (packet.value.case === 'transcription') {
33938
34610
  this.handleTranscription(participant, packet.value.value);
33939
34611
  } else if (packet.value.case === 'sipDtmf') {
@@ -33964,8 +34636,8 @@ class Room extends eventsExports.EventEmitter {
33964
34636
  this.rpcClientManager.handleIncomingRpcAck(packet.value.value.requestId);
33965
34637
  }
33966
34638
  };
33967
- this.handleUserPacket = (participant, userPacket, kind, encryptionType) => {
33968
- this.emit(RoomEvent.DataReceived, userPacket.payload, participant, kind, userPacket.topic, encryptionType);
34639
+ this.handleUserPacket = (participant, userPacket, kind, encryptionType, participantIdentity) => {
34640
+ this.emit(RoomEvent.DataReceived, userPacket.payload, participant, kind, userPacket.topic, encryptionType, participantIdentity);
33969
34641
  // also emit on the participant
33970
34642
  participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.DataReceived, userPacket.payload, kind, encryptionType);
33971
34643
  };
@@ -34212,8 +34884,8 @@ class Room extends eventsExports.EventEmitter {
34212
34884
  });
34213
34885
  this.registerRpcDataStreamHandler();
34214
34886
  this.rpcClientManager = new RpcClientManager(this.log, this.outgoingDataStreamManager, this.getRemoteParticipantClientProtocol, () => {
34215
- var _a, _b;
34216
- return (_b = (_a = this.engine.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.serverInfo) === null || _b === void 0 ? void 0 : _b.version;
34887
+ var _a;
34888
+ return (_a = this.engine) === null || _a === void 0 ? void 0 : _a.serverVersion;
34217
34889
  });
34218
34890
  this.rpcClientManager.on('sendDataPacket', _ref2 => {
34219
34891
  let packet = _ref2.packet;
@@ -34540,6 +35212,12 @@ class Room extends eventsExports.EventEmitter {
34540
35212
  updatedAtInMs: Date.now(),
34541
35213
  maxAgeInMs: DEFAULT_MAX_AGE_MS
34542
35214
  });
35215
+ }).on(EngineEvent.RequestSubscribedCodecRefresh, () => {
35216
+ for (const videoPub of this.localParticipant.videoTrackPublications.values()) {
35217
+ if (isLocalVideoTrack(videoPub.track)) {
35218
+ videoPub.track.refreshSubscribedCodecs();
35219
+ }
35220
+ }
34543
35221
  });
34544
35222
  if (this.localParticipant) {
34545
35223
  this.localParticipant.setupEngine(this.engine);
@@ -34636,8 +35314,9 @@ class Room extends eventsExports.EventEmitter {
34636
35314
  let req;
34637
35315
  switch (scenario) {
34638
35316
  case 'signal-reconnect':
35317
+ const reconnectDelay = typeof arg === 'number' ? arg : 0;
34639
35318
  // @ts-expect-error function is private
34640
- yield this.engine.client.handleOnClose('simulate disconnect');
35319
+ yield this.engine.client.handleOnClose('simulate disconnect', undefined, reconnectDelay);
34641
35320
  break;
34642
35321
  case 'fail-on-v1-path':
34643
35322
  this.engine.failNextV1Path();
@@ -35318,7 +35997,7 @@ class Room extends eventsExports.EventEmitter {
35318
35997
  }
35319
35998
  // Advertise deflate-raw decompression support so peers know they can send us compressed data
35320
35999
  // streams (gated separately from clientProtocol — see the data streams v2 spec).
35321
- if (isCompressionStreamSupported()) {
36000
+ if (isDeflateRawCompressionSupported()) {
35322
36001
  capabilities.push(ClientInfo_Capability.CAP_COMPRESSION_DEFLATE_RAW);
35323
36002
  }
35324
36003
  return capabilities;
@@ -36368,212 +37047,186 @@ class TokenSourceFixed {}
36368
37047
  * and {@link TokenSourceCustom}.
36369
37048
  */
36370
37049
  class TokenSourceConfigurable {}new TextEncoder();
36371
- const decoder = new TextDecoder();function decodeBase64(encoded) {
36372
- if (Uint8Array.fromBase64) {
36373
- return Uint8Array.fromBase64(encoded);
36374
- }
36375
- const binary = atob(encoded);
36376
- const bytes = new Uint8Array(binary.length);
36377
- for (let i = 0; i < binary.length; i++) {
36378
- bytes[i] = binary.charCodeAt(i);
37050
+ const decoder = new TextDecoder(),
37051
+ strictDecoder = new TextDecoder("utf-8", {
37052
+ fatal: true
37053
+ });
37054
+ function decodeBase64(encoded) {
37055
+ let url = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
37056
+ if (Uint8Array.fromBase64) return Uint8Array.fromBase64(encoded, {
37057
+ alphabet: url ? "base64url" : "base64"
37058
+ });
37059
+ if (url) {
37060
+ if (encoded.includes("+") || encoded.includes("/")) throw new TypeError("Invalid base64url");
37061
+ encoded = encoded.replace(/-/g, "+").replace(/_/g, "/");
36379
37062
  }
37063
+ const binary = atob(encoded),
37064
+ bytes = new Uint8Array(binary.length);
37065
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
36380
37066
  return bytes;
36381
- }function decode(input) {
36382
- if (Uint8Array.fromBase64) {
36383
- return Uint8Array.fromBase64(typeof input === 'string' ? input : decoder.decode(input), {
36384
- alphabet: 'base64url'
36385
- });
36386
- }
36387
- let encoded = input;
36388
- if (encoded instanceof Uint8Array) {
36389
- encoded = decoder.decode(encoded);
36390
- }
36391
- encoded = encoded.replace(/-/g, '+').replace(/_/g, '/');
36392
- try {
36393
- return decodeBase64(encoded);
36394
- } catch (_unused) {
36395
- throw new TypeError('The input to be decoded is not correctly encoded.');
36396
- }
36397
37067
  }class JOSEError extends Error {
36398
37068
  constructor(message, options) {
36399
37069
  var _Error$captureStackTr;
36400
- super(message, options);
36401
- _defineProperty(this, "code", 'ERR_JOSE_GENERIC');
36402
- this.name = this.constructor.name;
36403
- (_Error$captureStackTr = Error.captureStackTrace) === null || _Error$captureStackTr === void 0 || _Error$captureStackTr.call(Error, this, this.constructor);
37070
+ super(message, options), _defineProperty(this, "code", "ERR_JOSE_GENERIC"), this.name = this.constructor.name, (_Error$captureStackTr = Error.captureStackTrace) === null || _Error$captureStackTr === void 0 ? void 0 : _Error$captureStackTr.call(Error, this, this.constructor);
36404
37071
  }
36405
37072
  }
36406
- _defineProperty(JOSEError, "code", 'ERR_JOSE_GENERIC');
37073
+ _defineProperty(JOSEError, "code", "ERR_JOSE_GENERIC");
36407
37074
  class JWTClaimValidationFailed extends JOSEError {
36408
37075
  constructor(message, payload) {
36409
- let claim = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'unspecified';
36410
- let reason = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'unspecified';
37076
+ let claim = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "unspecified";
37077
+ let reason = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "unspecified";
36411
37078
  super(message, {
36412
37079
  cause: {
36413
37080
  claim,
36414
37081
  reason,
36415
37082
  payload
36416
37083
  }
36417
- });
36418
- _defineProperty(this, "code", 'ERR_JWT_CLAIM_VALIDATION_FAILED');
36419
- _defineProperty(this, "claim", void 0);
36420
- _defineProperty(this, "reason", void 0);
36421
- _defineProperty(this, "payload", void 0);
36422
- this.claim = claim;
36423
- this.reason = reason;
36424
- this.payload = payload;
37084
+ }), _defineProperty(this, "code", "ERR_JWT_CLAIM_VALIDATION_FAILED"), _defineProperty(this, "claim", void 0), _defineProperty(this, "reason", void 0), _defineProperty(this, "payload", void 0), this.claim = claim, this.reason = reason, this.payload = payload;
36425
37085
  }
36426
37086
  }
36427
- _defineProperty(JWTClaimValidationFailed, "code", 'ERR_JWT_CLAIM_VALIDATION_FAILED');
37087
+ _defineProperty(JWTClaimValidationFailed, "code", "ERR_JWT_CLAIM_VALIDATION_FAILED");
36428
37088
  class JWTExpired extends JOSEError {
36429
37089
  constructor(message, payload) {
36430
- let claim = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'unspecified';
36431
- let reason = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'unspecified';
37090
+ let claim = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "unspecified";
37091
+ let reason = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "unspecified";
36432
37092
  super(message, {
36433
37093
  cause: {
36434
37094
  claim,
36435
37095
  reason,
36436
37096
  payload
36437
37097
  }
36438
- });
36439
- _defineProperty(this, "code", 'ERR_JWT_EXPIRED');
36440
- _defineProperty(this, "claim", void 0);
36441
- _defineProperty(this, "reason", void 0);
36442
- _defineProperty(this, "payload", void 0);
36443
- this.claim = claim;
36444
- this.reason = reason;
36445
- this.payload = payload;
37098
+ }), _defineProperty(this, "code", "ERR_JWT_EXPIRED"), _defineProperty(this, "claim", void 0), _defineProperty(this, "reason", void 0), _defineProperty(this, "payload", void 0), this.claim = claim, this.reason = reason, this.payload = payload;
36446
37099
  }
36447
37100
  }
36448
- _defineProperty(JWTExpired, "code", 'ERR_JWT_EXPIRED');
37101
+ _defineProperty(JWTExpired, "code", "ERR_JWT_EXPIRED");
36449
37102
  class JOSEAlgNotAllowed extends JOSEError {
36450
37103
  constructor() {
36451
37104
  super(...arguments);
36452
- _defineProperty(this, "code", 'ERR_JOSE_ALG_NOT_ALLOWED');
37105
+ _defineProperty(this, "code", "ERR_JOSE_ALG_NOT_ALLOWED");
36453
37106
  }
36454
37107
  }
36455
- _defineProperty(JOSEAlgNotAllowed, "code", 'ERR_JOSE_ALG_NOT_ALLOWED');
37108
+ _defineProperty(JOSEAlgNotAllowed, "code", "ERR_JOSE_ALG_NOT_ALLOWED");
36456
37109
  class JOSENotSupported extends JOSEError {
36457
37110
  constructor() {
36458
37111
  super(...arguments);
36459
- _defineProperty(this, "code", 'ERR_JOSE_NOT_SUPPORTED');
37112
+ _defineProperty(this, "code", "ERR_JOSE_NOT_SUPPORTED");
36460
37113
  }
36461
37114
  }
36462
- _defineProperty(JOSENotSupported, "code", 'ERR_JOSE_NOT_SUPPORTED');
37115
+ _defineProperty(JOSENotSupported, "code", "ERR_JOSE_NOT_SUPPORTED");
36463
37116
  class JWEDecryptionFailed extends JOSEError {
36464
37117
  constructor() {
36465
- let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'decryption operation failed';
37118
+ let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "decryption operation failed";
36466
37119
  let options = arguments.length > 1 ? arguments[1] : undefined;
36467
37120
  super(message, options);
36468
- _defineProperty(this, "code", 'ERR_JWE_DECRYPTION_FAILED');
37121
+ _defineProperty(this, "code", "ERR_JWE_DECRYPTION_FAILED");
36469
37122
  }
36470
37123
  }
36471
- _defineProperty(JWEDecryptionFailed, "code", 'ERR_JWE_DECRYPTION_FAILED');
37124
+ _defineProperty(JWEDecryptionFailed, "code", "ERR_JWE_DECRYPTION_FAILED");
36472
37125
  class JWEInvalid extends JOSEError {
36473
37126
  constructor() {
36474
37127
  super(...arguments);
36475
- _defineProperty(this, "code", 'ERR_JWE_INVALID');
37128
+ _defineProperty(this, "code", "ERR_JWE_INVALID");
36476
37129
  }
36477
37130
  }
36478
- _defineProperty(JWEInvalid, "code", 'ERR_JWE_INVALID');
37131
+ _defineProperty(JWEInvalid, "code", "ERR_JWE_INVALID");
36479
37132
  class JWSInvalid extends JOSEError {
36480
37133
  constructor() {
36481
37134
  super(...arguments);
36482
- _defineProperty(this, "code", 'ERR_JWS_INVALID');
37135
+ _defineProperty(this, "code", "ERR_JWS_INVALID");
36483
37136
  }
36484
37137
  }
36485
- _defineProperty(JWSInvalid, "code", 'ERR_JWS_INVALID');
37138
+ _defineProperty(JWSInvalid, "code", "ERR_JWS_INVALID");
36486
37139
  class JWTInvalid extends JOSEError {
36487
37140
  constructor() {
36488
37141
  super(...arguments);
36489
- _defineProperty(this, "code", 'ERR_JWT_INVALID');
37142
+ _defineProperty(this, "code", "ERR_JWT_INVALID");
36490
37143
  }
36491
37144
  }
36492
- _defineProperty(JWTInvalid, "code", 'ERR_JWT_INVALID');
37145
+ _defineProperty(JWTInvalid, "code", "ERR_JWT_INVALID");
36493
37146
  class JWKInvalid extends JOSEError {
36494
37147
  constructor() {
36495
37148
  super(...arguments);
36496
- _defineProperty(this, "code", 'ERR_JWK_INVALID');
37149
+ _defineProperty(this, "code", "ERR_JWK_INVALID");
36497
37150
  }
36498
37151
  }
36499
- _defineProperty(JWKInvalid, "code", 'ERR_JWK_INVALID');
37152
+ _defineProperty(JWKInvalid, "code", "ERR_JWK_INVALID");
36500
37153
  class JWKSInvalid extends JOSEError {
36501
37154
  constructor() {
36502
37155
  super(...arguments);
36503
- _defineProperty(this, "code", 'ERR_JWKS_INVALID');
37156
+ _defineProperty(this, "code", "ERR_JWKS_INVALID");
36504
37157
  }
36505
37158
  }
36506
- _defineProperty(JWKSInvalid, "code", 'ERR_JWKS_INVALID');
37159
+ _defineProperty(JWKSInvalid, "code", "ERR_JWKS_INVALID");
36507
37160
  class JWKSNoMatchingKey extends JOSEError {
36508
37161
  constructor() {
36509
- let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'no applicable key found in the JSON Web Key Set';
37162
+ let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "no applicable key found in the JSON Web Key Set";
36510
37163
  let options = arguments.length > 1 ? arguments[1] : undefined;
36511
37164
  super(message, options);
36512
- _defineProperty(this, "code", 'ERR_JWKS_NO_MATCHING_KEY');
37165
+ _defineProperty(this, "code", "ERR_JWKS_NO_MATCHING_KEY");
36513
37166
  }
36514
37167
  }
36515
- _defineProperty(JWKSNoMatchingKey, "code", 'ERR_JWKS_NO_MATCHING_KEY');
37168
+ _defineProperty(JWKSNoMatchingKey, "code", "ERR_JWKS_NO_MATCHING_KEY");
36516
37169
  class JWKSMultipleMatchingKeys extends JOSEError {
36517
37170
  constructor() {
36518
- let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'multiple matching keys found in the JSON Web Key Set';
37171
+ let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "multiple matching keys found in the JSON Web Key Set";
36519
37172
  let options = arguments.length > 1 ? arguments[1] : undefined;
36520
37173
  super(message, options);
36521
- _defineProperty(this, Symbol.asyncIterator, void 0);
36522
- _defineProperty(this, "code", 'ERR_JWKS_MULTIPLE_MATCHING_KEYS');
37174
+ _defineProperty(this, Symbol.asyncIterator, _wrapAsyncGenerator(function* () {}));
37175
+ _defineProperty(this, "code", "ERR_JWKS_MULTIPLE_MATCHING_KEYS");
36523
37176
  }
36524
37177
  }
36525
- _defineProperty(JWKSMultipleMatchingKeys, "code", 'ERR_JWKS_MULTIPLE_MATCHING_KEYS');
37178
+ _defineProperty(JWKSMultipleMatchingKeys, "code", "ERR_JWKS_MULTIPLE_MATCHING_KEYS");
36526
37179
  class JWKSTimeout extends JOSEError {
36527
37180
  constructor() {
36528
- let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'request timed out';
37181
+ let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "request timed out";
36529
37182
  let options = arguments.length > 1 ? arguments[1] : undefined;
36530
37183
  super(message, options);
36531
- _defineProperty(this, "code", 'ERR_JWKS_TIMEOUT');
37184
+ _defineProperty(this, "code", "ERR_JWKS_TIMEOUT");
36532
37185
  }
36533
37186
  }
36534
- _defineProperty(JWKSTimeout, "code", 'ERR_JWKS_TIMEOUT');
37187
+ _defineProperty(JWKSTimeout, "code", "ERR_JWKS_TIMEOUT");
36535
37188
  class JWSSignatureVerificationFailed extends JOSEError {
36536
37189
  constructor() {
36537
- let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'signature verification failed';
37190
+ let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "signature verification failed";
36538
37191
  let options = arguments.length > 1 ? arguments[1] : undefined;
36539
37192
  super(message, options);
36540
- _defineProperty(this, "code", 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED');
37193
+ _defineProperty(this, "code", "ERR_JWS_SIGNATURE_VERIFICATION_FAILED");
36541
37194
  }
36542
37195
  }
36543
- _defineProperty(JWSSignatureVerificationFailed, "code", 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED');const isObjectLike = value => typeof value === 'object' && value !== null;
36544
- function isObject(input) {
36545
- if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') {
36546
- return false;
36547
- }
36548
- if (Object.getPrototypeOf(input) === null) {
36549
- return true;
36550
- }
36551
- let proto = input;
36552
- while (Object.getPrototypeOf(proto) !== null) {
36553
- proto = Object.getPrototypeOf(proto);
37196
+ _defineProperty(JWSSignatureVerificationFailed, "code", "ERR_JWS_SIGNATURE_VERIFICATION_FAILED");const invalid = "The input to be decoded is not correctly encoded.";
37197
+ function decode(input) {
37198
+ try {
37199
+ return decodeBase64(typeof input == "string" ? input : decoder.decode(input), !0);
37200
+ } catch (cause) {
37201
+ throw new TypeError(invalid, {
37202
+ cause
37203
+ });
36554
37204
  }
36555
- return Object.getPrototypeOf(input) === proto;
37205
+ }function isObject(input) {
37206
+ if (typeof input != "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]") return false;
37207
+ const prototype = Object.getPrototypeOf(input);
37208
+ return prototype === null || Object.getPrototypeOf(prototype) === null;
36556
37209
  }function decodeJwt(jwt) {
36557
- if (typeof jwt !== 'string') throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
36558
- const _jwt$split = jwt.split('.'),
37210
+ if (typeof jwt != "string") throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
37211
+ const _jwt$split = jwt.split("."),
36559
37212
  payload = _jwt$split[1],
36560
37213
  length = _jwt$split.length;
36561
- if (length === 5) throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
36562
- if (length !== 3) throw new JWTInvalid('Invalid JWT');
36563
- if (!payload) throw new JWTInvalid('JWTs must contain a payload');
37214
+ if (length === 5) throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");
37215
+ if (length !== 3) throw new JWTInvalid("Invalid JWT");
37216
+ if (!payload) throw new JWTInvalid("JWTs must contain a payload");
36564
37217
  let decoded;
36565
37218
  try {
36566
37219
  decoded = decode(payload);
36567
37220
  } catch (_unused) {
36568
- throw new JWTInvalid('Failed to base64url decode the payload');
37221
+ throw new JWTInvalid("Failed to base64url decode the payload");
36569
37222
  }
36570
37223
  let result;
36571
37224
  try {
36572
- result = JSON.parse(decoder.decode(decoded));
37225
+ result = JSON.parse(strictDecoder.decode(decoded));
36573
37226
  } catch (_unused2) {
36574
- throw new JWTInvalid('Failed to parse the decoded payload as JSON');
37227
+ throw new JWTInvalid("Failed to parse the decoded payload as JSON");
36575
37228
  }
36576
- if (!isObject(result)) throw new JWTInvalid('Invalid JWT Claims Set');
37229
+ if (!isObject(result)) throw new JWTInvalid("Invalid JWT Claims Set");
36577
37230
  return result;
36578
37231
  }const ONE_SECOND_IN_MILLISECONDS = 1000;
36579
37232
  const ONE_MINUTE_IN_MILLISECONDS = 60 * ONE_SECOND_IN_MILLISECONDS;
@@ -36981,4 +37634,4 @@ const serializers = {
36981
37634
  json,
36982
37635
  raw,
36983
37636
  custom
36984
- };export{AudioPresets,BackupCodecPolicy,BaseKeyProvider,CLIENT_PROTOCOL_DATA_STREAM_RPC,CLIENT_PROTOCOL_DATA_STREAM_V2,CLIENT_PROTOCOL_DEFAULT,CheckStatus,Checker,ConnectionCheck,ConnectionError,ConnectionErrorReason,ConnectionQuality,ConnectionState,CriticalTimers,CryptorError,CryptorErrorReason,CryptorEvent,DataPacket_Kind,DataStreamError,DataStreamErrorReason,DataTrackPacket,DefaultReconnectPolicy,DeviceUnsupportedError,DisconnectReason,EncryptionEvent,Encryption_Type,EngineEvent,ExternalE2EEKeyProvider,FrameMetadataManager,KeyHandlerEvent,KeyProviderEvent,LivekitError,LivekitReasonedError,LocalAudioTrack,LocalDataTrack,LocalParticipant,LocalTrack,LocalTrackPublication,LocalTrackRecorder,LocalVideoTrack,LogLevel,LoggerNames,MediaDeviceFailure,_ as Mutex,NegotiationError,PacketTrailerManager,Participant,ParticipantEvent,ParticipantInfo_Kind as ParticipantKind,PublishDataError,PublishTrackError,RemoteAudioTrack,RemoteDataTrack,RemoteParticipant,RemoteTrack,RemoteTrackPublication,RemoteVideoTrack,Room,RoomEvent,RpcError,ScreenSharePresets,SignalReconnectError,SignalRequestError,SimulatedError,SubscriptionError,TokenSource,TokenSourceConfigurable,TokenSourceFixed,Track,TrackEvent,TrackInvalidError,TrackPublication,TrackType,UnexpectedConnectionState,UnsupportedServer,VideoPreset,VideoPresets,VideoPresets43,VideoQuality,areTokenSourceFetchOptionsEqual,asEncryptablePacket,attachToElement,attributeTypings as attributes,audioCodecs,clientProtocol,compareVersions,createAudioAnalyser,createE2EEKey,createKeyMaterialFromBuffer,createKeyMaterialFromString,createLocalAudioTrack,createLocalScreenTracks,createLocalTracks,createLocalVideoTrack,decodeTokenPayload,deriveKeys,detachTrack,facingModeFromDeviceLabel,facingModeFromLocalTrack,getBrowser,getEmptyAudioStreamTrack,getEmptyVideoStreamTrack,getLogger,importKey,isAudioCodec,isAudioTrack,isBackupCodec,isBackupVideoCodec,isBrowserSupported,isE2EESupported,isInsertableStreamSupported,isLocalParticipant,isLocalTrack,isRemoteParticipant,isRemoteTrack,isSVCCodec,isScriptTransformSupported,isSerializer,isVideoCodec,isVideoFrame,isVideoTrack,needsRbspUnescaping,parseRbsp,protocolVersion,ratchet,serializers,setLogExtension,setLogLevel,supportsAV1,supportsAdaptiveStream,supportsAudioOutputSelection,supportsDynacast,supportsH265,supportsVP9,version,videoCodecs,writeRbsp};//# sourceMappingURL=livekit-client.esm.mjs.map
37637
+ };export{AudioPresets,BackupCodecPolicy,BaseKeyProvider,CLIENT_PROTOCOL_DATA_STREAM_RPC,CLIENT_PROTOCOL_DATA_STREAM_V2,CLIENT_PROTOCOL_DEFAULT,CheckStatus,Checker,ConnectionCheck,ConnectionError,ConnectionErrorReason,ConnectionQuality,ConnectionState,CriticalTimers,CryptorError,CryptorErrorReason,CryptorEvent,DataPacket_Kind,DataStreamError,DataStreamErrorReason,DataTrackPacket,DefaultReconnectPolicy,DeviceUnsupportedError,DisconnectReason,EncryptionEvent,Encryption_Type,EngineEvent,ExternalE2EEKeyProvider,FrameMetadataManager,KeyHandlerEvent,KeyProviderEvent,LivekitError,LivekitReasonedError,LocalAudioTrack,LocalDataTrack,LocalParticipant,LocalTrack,LocalTrackPublication,LocalTrackRecorder,LocalVideoTrack,LogLevel,LoggerNames,MediaDeviceFailure,_ as Mutex,NegotiationError,PacketTrailerManager,Participant,ParticipantEvent,ParticipantInfo_Kind as ParticipantKind,PublishDataError,PublishTrackError,RemoteAudioTrack,RemoteDataTrack,RemoteParticipant,RemoteTrack,RemoteTrackPublication,RemoteVideoTrack,Room,RoomEvent,RpcError,ScreenSharePresets,SignalReconnectError,SignalRequestError,SimulatedError,SubscriptionError,TokenSource,TokenSourceConfigurable,TokenSourceFixed,Track,TrackEvent,TrackInvalidError,TrackPublication,TrackType,UnexpectedConnectionState,UnsupportedServer,VideoPreset,VideoPresets,VideoPresets43,VideoQuality,areTokenSourceFetchOptionsEqual,asEncryptablePacket,attachToElement,attributeTypings as attributes,audioCodecs,canFailOverToAnotherRegion,clientProtocol,compareVersions,createAudioAnalyser,createE2EEKey,createKeyMaterialFromBuffer,createKeyMaterialFromString,createLocalAudioTrack,createLocalScreenTracks,createLocalTracks,createLocalVideoTrack,decodeTokenPayload,deriveKeys,detachTrack,facingModeFromDeviceLabel,facingModeFromLocalTrack,getBrowser,getEmptyAudioStreamTrack,getEmptyVideoStreamTrack,getLogger,importKey,isAudioCodec,isAudioTrack,isBackupCodec,isBackupVideoCodec,isBrowserSupported,isE2EESupported,isInsertableStreamSupported,isLocalParticipant,isLocalTrack,isRemoteParticipant,isRemoteTrack,isSVCCodec,isScriptTransformSupported,isSerializer,isVideoCodec,isVideoFrame,isVideoTrack,needsRbspUnescaping,parseRbsp,protocolVersion,ratchet,serializers,setLogExtension,setLogLevel,supportsAV1,supportsAdaptiveStream,supportsAudioOutputSelection,supportsDynacast,supportsH265,supportsVP9,version,videoCodecs,writeRbsp};//# sourceMappingURL=livekit-client.esm.mjs.map