livekit-client 2.22.3 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) 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 +71 -8
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +1746 -547
  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 +2 -1
  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 +1 -0
  17. package/dist/src/api/WebSocketStream.d.ts.map +1 -1
  18. package/dist/src/api/utils.d.ts +1 -1
  19. package/dist/src/api/utils.d.ts.map +1 -1
  20. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  21. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  22. package/dist/src/e2ee/worker/naluUtils.d.ts +12 -1
  23. package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -1
  24. package/dist/src/index.d.ts +2 -2
  25. package/dist/src/index.d.ts.map +1 -1
  26. package/dist/src/options.d.ts +14 -0
  27. package/dist/src/options.d.ts.map +1 -1
  28. package/dist/src/room/DefaultReconnectPolicy.d.ts.map +1 -1
  29. package/dist/src/room/PCTransport.d.ts +100 -7
  30. package/dist/src/room/PCTransport.d.ts.map +1 -1
  31. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  32. package/dist/src/room/RTCEngine.d.ts +16 -0
  33. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  34. package/dist/src/room/Room.d.ts +54 -1
  35. package/dist/src/room/Room.d.ts.map +1 -1
  36. package/dist/src/room/data-channel/DataChannelManager.d.ts +9 -8
  37. package/dist/src/room/data-channel/DataChannelManager.d.ts.map +1 -1
  38. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts +25 -12
  39. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts.map +1 -1
  40. package/dist/src/room/data-channel/LossyDataChannel.d.ts.map +1 -1
  41. package/dist/src/room/data-channel/ReliableDataChannel.d.ts.map +1 -1
  42. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  43. package/dist/src/room/defaults.d.ts.map +1 -1
  44. package/dist/src/room/errors.d.ts +18 -0
  45. package/dist/src/room/errors.d.ts.map +1 -1
  46. package/dist/src/room/events.d.ts +3 -2
  47. package/dist/src/room/events.d.ts.map +1 -1
  48. package/dist/src/room/participant/LocalParticipant.d.ts +4 -0
  49. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  50. package/dist/src/room/participant/Participant.d.ts +1 -1
  51. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  52. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  53. package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -1
  54. package/dist/src/room/rpc/utils.d.ts.map +1 -1
  55. package/dist/src/room/track/LocalAudioTrack.d.ts +24 -1
  56. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  57. package/dist/src/room/track/LocalTrack.d.ts +18 -0
  58. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  59. package/dist/src/room/track/LocalVideoTrack.d.ts +9 -0
  60. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  61. package/dist/src/room/track/RemoteAudioTrack.d.ts +6 -0
  62. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  63. package/dist/src/room/track/processor/types.d.ts +11 -0
  64. package/dist/src/room/track/processor/types.d.ts.map +1 -1
  65. package/dist/src/room/track/utils.d.ts +13 -0
  66. package/dist/src/room/track/utils.d.ts.map +1 -1
  67. package/dist/src/room/utils.d.ts +60 -1
  68. package/dist/src/room/utils.d.ts.map +1 -1
  69. package/dist/src/test/compressionStreamStubs.d.ts +12 -0
  70. package/dist/src/test/compressionStreamStubs.d.ts.map +1 -0
  71. package/dist/ts4.2/api/SignalClient.d.ts +187 -185
  72. package/dist/ts4.2/api/SignalClientStateMachine.d.ts +85 -85
  73. package/dist/ts4.2/api/WebSocketStream.d.ts +34 -33
  74. package/dist/ts4.2/api/utils.d.ts +9 -9
  75. package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +30 -30
  76. package/dist/ts4.2/connectionHelper/checks/Checker.d.ts +62 -62
  77. package/dist/ts4.2/connectionHelper/checks/cloudRegion.d.ts +18 -18
  78. package/dist/ts4.2/connectionHelper/checks/connectionProtocol.d.ts +20 -20
  79. package/dist/ts4.2/connectionHelper/checks/publishAudio.d.ts +6 -6
  80. package/dist/ts4.2/connectionHelper/checks/publishVideo.d.ts +7 -7
  81. package/dist/ts4.2/connectionHelper/checks/reconnect.d.ts +6 -6
  82. package/dist/ts4.2/connectionHelper/checks/turn.d.ts +6 -6
  83. package/dist/ts4.2/connectionHelper/checks/webrtc.d.ts +6 -6
  84. package/dist/ts4.2/connectionHelper/checks/websocket.d.ts +6 -6
  85. package/dist/ts4.2/e2ee/E2eeManager.d.ts +111 -111
  86. package/dist/ts4.2/e2ee/KeyProvider.d.ts +54 -54
  87. package/dist/ts4.2/e2ee/constants.d.ts +21 -21
  88. package/dist/ts4.2/e2ee/errors.d.ts +12 -12
  89. package/dist/ts4.2/e2ee/events.d.ts +39 -39
  90. package/dist/ts4.2/e2ee/index.d.ts +6 -6
  91. package/dist/ts4.2/e2ee/types.d.ts +231 -231
  92. package/dist/ts4.2/e2ee/utils.d.ts +32 -32
  93. package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +16 -16
  94. package/dist/ts4.2/e2ee/worker/ErrorRateLimiter.d.ts +21 -21
  95. package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +209 -209
  96. package/dist/ts4.2/e2ee/worker/ParticipantKeyHandler.d.ts +81 -84
  97. package/dist/ts4.2/e2ee/worker/e2ee.worker.d.ts +2 -2
  98. package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +39 -28
  99. package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +23 -23
  100. package/dist/ts4.2/frameMetadata/FrameMetadataManager.d.ts +53 -53
  101. package/dist/ts4.2/frameMetadata/frameMetadata.d.ts +33 -33
  102. package/dist/ts4.2/frameMetadata/types.d.ts +62 -62
  103. package/dist/ts4.2/frameMetadata/utils.d.ts +9 -9
  104. package/dist/ts4.2/frameMetadata/worker/frameMetadata.worker.d.ts +2 -2
  105. package/dist/ts4.2/index.d.ts +76 -76
  106. package/dist/ts4.2/logger.d.ts +66 -66
  107. package/dist/ts4.2/options.d.ts +163 -149
  108. package/dist/ts4.2/room/BackOffStrategy.d.ts +27 -27
  109. package/dist/ts4.2/room/DefaultReconnectPolicy.d.ts +8 -8
  110. package/dist/ts4.2/room/DeviceManager.d.ts +12 -12
  111. package/dist/ts4.2/room/PCTransport.d.ts +268 -175
  112. package/dist/ts4.2/room/PCTransportManager.d.ts +77 -77
  113. package/dist/ts4.2/room/RTCEngine.d.ts +277 -262
  114. package/dist/ts4.2/room/ReconnectPolicy.d.ts +24 -24
  115. package/dist/ts4.2/room/RegionUrlProvider.d.ts +36 -36
  116. package/dist/ts4.2/room/Room.d.ts +407 -365
  117. package/dist/ts4.2/room/attribute-typings.d.ts +35 -35
  118. package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +63 -62
  119. package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +107 -95
  120. package/dist/ts4.2/room/data-channel/LossyDataChannel.d.ts +41 -41
  121. package/dist/ts4.2/room/data-channel/ReliableDataChannel.d.ts +57 -57
  122. package/dist/ts4.2/room/data-channel/types.d.ts +12 -12
  123. package/dist/ts4.2/room/data-stream/compression.d.ts +49 -49
  124. package/dist/ts4.2/room/data-stream/constants.d.ts +18 -18
  125. package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +29 -29
  126. package/dist/ts4.2/room/data-stream/incoming/StreamReader.d.ts +80 -80
  127. package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +73 -73
  128. package/dist/ts4.2/room/data-stream/outgoing/StreamWriter.d.ts +16 -16
  129. package/dist/ts4.2/room/data-stream/outgoing/header-utils.d.ts +18 -18
  130. package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +79 -80
  131. package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +50 -50
  132. package/dist/ts4.2/room/data-track/depacketizer.d.ts +59 -59
  133. package/dist/ts4.2/room/data-track/frame.d.ts +22 -22
  134. package/dist/ts4.2/room/data-track/handle.d.ts +27 -27
  135. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +124 -124
  136. package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -24
  137. package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +41 -41
  138. package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -20
  139. package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +126 -126
  140. package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +74 -74
  141. package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +23 -23
  142. package/dist/ts4.2/room/data-track/outgoing/types.d.ts +48 -48
  143. package/dist/ts4.2/room/data-track/packet/constants.d.ts +20 -20
  144. package/dist/ts4.2/room/data-track/packet/errors.d.ts +40 -40
  145. package/dist/ts4.2/room/data-track/packet/extensions.d.ts +69 -69
  146. package/dist/ts4.2/room/data-track/packet/index.d.ts +98 -98
  147. package/dist/ts4.2/room/data-track/packet/serializable.d.ts +13 -13
  148. package/dist/ts4.2/room/data-track/packetizer.d.ts +43 -43
  149. package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -23
  150. package/dist/ts4.2/room/data-track/types.d.ts +22 -22
  151. package/dist/ts4.2/room/data-track/utils.d.ts +60 -60
  152. package/dist/ts4.2/room/debounce.d.ts +11 -11
  153. package/dist/ts4.2/room/defaults.d.ts +9 -9
  154. package/dist/ts4.2/room/errors.d.ts +180 -162
  155. package/dist/ts4.2/room/events.d.ts +638 -637
  156. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +291 -287
  157. package/dist/ts4.2/room/participant/Participant.d.ts +142 -144
  158. package/dist/ts4.2/room/participant/ParticipantTrackPermission.d.ts +26 -26
  159. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +85 -86
  160. package/dist/ts4.2/room/participant/publishUtils.d.ts +53 -53
  161. package/dist/ts4.2/room/rpc/client/RpcClientManager.d.ts +43 -43
  162. package/dist/ts4.2/room/rpc/client/events.d.ts +8 -8
  163. package/dist/ts4.2/room/rpc/index.d.ts +7 -7
  164. package/dist/ts4.2/room/rpc/server/RpcServerManager.d.ts +44 -44
  165. package/dist/ts4.2/room/rpc/server/events.d.ts +8 -8
  166. package/dist/ts4.2/room/rpc/utils.d.ts +131 -131
  167. package/dist/ts4.2/room/stats.d.ts +71 -71
  168. package/dist/ts4.2/room/statsSummary.d.ts +13 -13
  169. package/dist/ts4.2/room/timers.d.ts +12 -12
  170. package/dist/ts4.2/room/token-source/TokenSource.d.ts +82 -82
  171. package/dist/ts4.2/room/token-source/types.d.ts +70 -70
  172. package/dist/ts4.2/room/token-source/utils.d.ts +8 -8
  173. package/dist/ts4.2/room/track/FrameMetadataExtractor.d.ts +19 -19
  174. package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +62 -42
  175. package/dist/ts4.2/room/track/LocalTrack.d.ts +143 -125
  176. package/dist/ts4.2/room/track/LocalTrackPublication.d.ts +42 -42
  177. package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +109 -100
  178. package/dist/ts4.2/room/track/RemoteAudioTrack.d.ts +61 -55
  179. package/dist/ts4.2/room/track/RemoteTrack.d.ts +38 -38
  180. package/dist/ts4.2/room/track/RemoteTrackPublication.d.ts +72 -75
  181. package/dist/ts4.2/room/track/RemoteVideoTrack.d.ts +73 -76
  182. package/dist/ts4.2/room/track/Track.d.ts +156 -166
  183. package/dist/ts4.2/room/track/TrackPublication.d.ts +75 -81
  184. package/dist/ts4.2/room/track/create.d.ts +25 -25
  185. package/dist/ts4.2/room/track/facingMode.d.ts +41 -41
  186. package/dist/ts4.2/room/track/options.d.ts +403 -403
  187. package/dist/ts4.2/room/track/processor/types.d.ts +42 -31
  188. package/dist/ts4.2/room/track/record.d.ts +13 -13
  189. package/dist/ts4.2/room/track/types.d.ts +28 -28
  190. package/dist/ts4.2/room/track/utils.d.ts +60 -47
  191. package/dist/ts4.2/room/types.d.ts +127 -127
  192. package/dist/ts4.2/room/utils.d.ts +273 -214
  193. package/dist/ts4.2/test/MockMediaStreamTrack.d.ts +26 -26
  194. package/dist/ts4.2/test/compressionStreamStubs.d.ts +12 -0
  195. package/dist/ts4.2/test/mocks.d.ts +12 -12
  196. package/dist/ts4.2/test/promiseState.d.ts +12 -12
  197. package/dist/ts4.2/test/signalToken.d.ts +26 -26
  198. package/dist/ts4.2/type-polyfills/non-shared-typed-arrays.d.ts +2 -2
  199. package/dist/ts4.2/utils/AsyncQueue.d.ts +23 -23
  200. package/dist/ts4.2/utils/TypedPromise.d.ts +17 -17
  201. package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -13
  202. package/dist/ts4.2/utils/browserParser.d.ts +13 -13
  203. package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -8
  204. package/dist/ts4.2/utils/cloneDeep.d.ts +2 -2
  205. package/dist/ts4.2/utils/dataPacketBuffer.d.ts +34 -34
  206. package/dist/ts4.2/utils/deferrable-map.d.ts +32 -32
  207. package/dist/ts4.2/utils/machineInspector.d.ts +54 -54
  208. package/dist/ts4.2/utils/serializer.d.ts +48 -48
  209. package/dist/ts4.2/utils/subscribeToEvents.d.ts +15 -15
  210. package/dist/ts4.2/utils/ttlmap.d.ts +20 -20
  211. package/dist/ts4.2/version.d.ts +15 -15
  212. package/package.json +5 -4
  213. package/src/api/SignalClient.e2e.test.ts +1 -3
  214. package/src/api/SignalClient.test.ts +394 -10
  215. package/src/api/SignalClient.ts +132 -76
  216. package/src/api/WebSocketStream.test.ts +11 -0
  217. package/src/api/WebSocketStream.ts +22 -1
  218. package/src/api/utils.ts +1 -1
  219. package/src/e2ee/E2eeManager.test.ts +43 -0
  220. package/src/e2ee/E2eeManager.ts +6 -2
  221. package/src/e2ee/worker/FrameCryptor.test.ts +212 -90
  222. package/src/e2ee/worker/FrameCryptor.ts +21 -4
  223. package/src/e2ee/worker/naluUtils.test.ts +123 -0
  224. package/src/e2ee/worker/naluUtils.ts +57 -2
  225. package/src/index.ts +2 -0
  226. package/src/options.ts +16 -0
  227. package/src/room/DefaultReconnectPolicy.test.ts +58 -0
  228. package/src/room/DefaultReconnectPolicy.ts +14 -2
  229. package/src/room/PCTransport.test.ts +327 -9
  230. package/src/room/PCTransport.ts +339 -36
  231. package/src/room/PCTransportManager.ts +3 -1
  232. package/src/room/RTCEngine.test.ts +318 -2
  233. package/src/room/RTCEngine.ts +126 -12
  234. package/src/room/Room.test.ts +148 -6
  235. package/src/room/Room.ts +312 -80
  236. package/src/room/data-channel/DataChannelManager.test.ts +24 -7
  237. package/src/room/data-channel/DataChannelManager.ts +43 -38
  238. package/src/room/data-channel/FlowControlledDataChannel.test.ts +60 -18
  239. package/src/room/data-channel/FlowControlledDataChannel.ts +40 -22
  240. package/src/room/data-channel/LossyDataChannel.test.ts +38 -0
  241. package/src/room/data-channel/LossyDataChannel.ts +10 -1
  242. package/src/room/data-channel/ReliableDataChannel.test.ts +72 -0
  243. package/src/room/data-channel/ReliableDataChannel.ts +16 -5
  244. package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +47 -20
  245. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +16 -5
  246. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.test.ts +60 -39
  247. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +4 -4
  248. package/src/room/defaults.ts +1 -0
  249. package/src/room/errors.test.ts +36 -0
  250. package/src/room/errors.ts +27 -0
  251. package/src/room/events.ts +2 -1
  252. package/src/room/participant/LocalParticipant.test.ts +136 -1
  253. package/src/room/participant/LocalParticipant.ts +66 -17
  254. package/src/room/participant/Participant.ts +6 -3
  255. package/src/room/participant/publishUtils.ts +0 -19
  256. package/src/room/participant/restartOnDefaultAudioDevice.test.ts +177 -0
  257. package/src/room/rpc/client/RpcClientManager.test.ts +51 -0
  258. package/src/room/rpc/client/RpcClientManager.ts +32 -15
  259. package/src/room/rpc/utils.test.ts +32 -0
  260. package/src/room/rpc/utils.ts +7 -0
  261. package/src/room/track/LocalAudioTrack.ts +101 -10
  262. package/src/room/track/LocalTrack.ts +87 -19
  263. package/src/room/track/LocalVideoTrack.ts +17 -3
  264. package/src/room/track/RemoteAudioTrack.ts +59 -14
  265. package/src/room/track/RemoteVideoTrack.test.ts +210 -0
  266. package/src/room/track/RemoteVideoTrack.ts +29 -3
  267. package/src/room/track/processor/types.ts +11 -0
  268. package/src/room/track/utils.test.ts +206 -2
  269. package/src/room/track/utils.ts +117 -5
  270. package/src/room/utils.test.ts +250 -0
  271. package/src/room/utils.ts +152 -19
  272. package/src/test/compressionStreamStubs.ts +53 -0
package/src/room/utils.ts CHANGED
@@ -300,6 +300,33 @@ export function isSafariBased(): boolean {
300
300
  return b?.name === 'Safari' || b?.os === 'iOS';
301
301
  }
302
302
 
303
+ /**
304
+ * iPadOS has sent the desktop Macintosh user agent by default since iPadOS 13, so `getBrowser()`
305
+ * reports `os: 'macOS'` there and every `os === 'iOS'` check misses it. macOS Safari implements no
306
+ * touch events at all (`TouchEvent` undefined, `maxTouchPoints` 0) regardless of any touchscreen
307
+ * attached, so touch capability separates the two. Measured on an iPad 6 / iPadOS 17
308
+ * (`maxTouchPoints` 5) against macOS Safari 26.4 (`maxTouchPoints` 0).
309
+ *
310
+ * Deliberately a separate helper rather than a fix in `browserParser`: iPadOS spoofs the Mac
311
+ * version too, so reporting `os: 'iOS'` there would leave `osVersion` at `10.15.7` and silently
312
+ * break {@link isSafari17Based} and {@link isSafariSvcApi}, which compare it.
313
+ */
314
+ export function isIPadOS(): boolean {
315
+ if (!isWeb()) {
316
+ return false;
317
+ }
318
+ const b = getBrowser();
319
+ return b?.name === 'Safari' && b?.os === 'macOS' && navigator.maxTouchPoints > 1;
320
+ }
321
+
322
+ /**
323
+ * iPhone, iPad and iOS-hosted browsers — everything running the Apple camera capture pipeline,
324
+ * including iPads that present themselves as a Mac.
325
+ */
326
+ export function isAppleMobile(): boolean {
327
+ return getBrowser()?.os === 'iOS' || isIPadOS();
328
+ }
329
+
303
330
  export function isSafari17Based(): boolean {
304
331
  const b = getBrowser();
305
332
  return (
@@ -532,6 +559,17 @@ export function createDummyVideoStreamTrack(
532
559
 
533
560
  let emptyAudioStreamTrack: MediaStreamTrack | undefined;
534
561
 
562
+ let emptyAudioStreamContext: AudioContext | undefined;
563
+
564
+ /** tracks handed out by `getEmptyAudioStreamTrack` that haven't been released yet */
565
+ const outstandingEmptyAudioStreamTracks = new Set<MediaStreamTrack>();
566
+
567
+ /**
568
+ * Returns a silent audio track, cloned off a shared `AudioContext` created on first use. Hand the
569
+ * clone back to `releaseEmptyAudioStreamTrack` rather than stopping it, so the context can close.
570
+ * A clone that is only stopped stays counted as outstanding, which holds the shared context open
571
+ * for the lifetime of the page.
572
+ */
535
573
  export function getEmptyAudioStreamTrack() {
536
574
  if (!emptyAudioStreamTrack) {
537
575
  // implementation adapted from https://blog.mozilla.org/webrtc/warm-up-with-replacetrack/
@@ -545,31 +583,37 @@ export function getEmptyAudioStreamTrack() {
545
583
  oscillator.start();
546
584
  [emptyAudioStreamTrack] = dst.stream.getAudioTracks();
547
585
  if (!emptyAudioStreamTrack) {
586
+ // don't hold on to a context we can't get a track out of
587
+ ctx.close().catch(() => {});
548
588
  throw Error('Could not get empty media stream audio track');
549
589
  }
550
590
  emptyAudioStreamTrack.enabled = false;
591
+ emptyAudioStreamContext = ctx;
551
592
  }
552
- return emptyAudioStreamTrack.clone();
593
+ const track = emptyAudioStreamTrack.clone();
594
+ outstandingEmptyAudioStreamTracks.add(track);
595
+ return track;
553
596
  }
554
597
 
555
- export function getStereoAudioStreamTrack() {
556
- const ctx = new AudioContext();
557
- const oscLeft = ctx.createOscillator();
558
- const oscRight = ctx.createOscillator();
559
- oscLeft.frequency.value = 440;
560
- oscRight.frequency.value = 220;
561
- const merger = ctx.createChannelMerger(2);
562
- oscLeft.connect(merger, 0, 0); // left channel
563
- oscRight.connect(merger, 0, 1); // right channel
564
- const dst = ctx.createMediaStreamDestination();
565
- merger.connect(dst);
566
- oscLeft.start();
567
- oscRight.start();
568
- const [stereoTrack] = dst.stream.getAudioTracks();
569
- if (!stereoTrack) {
570
- throw Error('Could not get stereo media stream audio track');
598
+ /**
599
+ * Stops a track obtained from `getEmptyAudioStreamTrack` and closes the shared `AudioContext`
600
+ * backing it once every track it handed out has been released. Does nothing for a track this
601
+ * module never handed out, or one that has been released already.
602
+ */
603
+ export async function releaseEmptyAudioStreamTrack(track: MediaStreamTrack) {
604
+ if (!outstandingEmptyAudioStreamTracks.delete(track)) {
605
+ // the track wasn't obtained from `getEmptyAudioStreamTrack`, or has been released before
606
+ return;
607
+ }
608
+ track.stop();
609
+ if (outstandingEmptyAudioStreamTracks.size > 0) {
610
+ return;
571
611
  }
572
- return stereoTrack;
612
+ const context = emptyAudioStreamContext;
613
+ emptyAudioStreamTrack?.stop();
614
+ emptyAudioStreamTrack = undefined;
615
+ emptyAudioStreamContext = undefined;
616
+ await context?.close();
573
617
  }
574
618
 
575
619
  /** An object that represents a serialized version of a `new Promise((resolve, reject) => {})`
@@ -681,11 +725,20 @@ export function createAudioAnalyser(
681
725
  return volume;
682
726
  };
683
727
 
728
+ let isCleanedUp = false;
729
+
684
730
  const cleanup = async () => {
685
- await audioContext.close();
731
+ if (isCleanedUp) {
732
+ // closing an already closed context would reject, make repeated cleanups a no-op instead
733
+ return;
734
+ }
735
+ isCleanedUp = true;
736
+ mediaStreamSource.disconnect();
737
+ analyser.disconnect();
686
738
  if (opts.cloneTrack) {
687
739
  streamTrack.stop();
688
740
  }
741
+ await audioContext.close();
689
742
  };
690
743
 
691
744
  return { calculateVolume, analyser, cleanup };
@@ -724,6 +777,43 @@ export function unwrapConstraint(constraint: ConstrainDOMString | ConstrainULong
724
777
  throw Error('could not unwrap constraint');
725
778
  }
726
779
 
780
+ /**
781
+ * The getUserMedia rejections that no softer constraint can recover from, because the user or the
782
+ * document refused capture outright. Retrying one of these would put a second prompt in front of
783
+ * someone who already said no. The last two names are legacy spellings still reported by older
784
+ * engines. Matched by `name` rather than with `instanceof`, because these are not reliably distinct
785
+ * constructors across environments. happy-dom, which the tests run under, aliases them to `Event`.
786
+ */
787
+ export function isPermissionDeniedError(e: unknown): boolean {
788
+ return (
789
+ typeof e === 'object' &&
790
+ e !== null &&
791
+ 'name' in e &&
792
+ (e.name === 'NotAllowedError' ||
793
+ e.name === 'SecurityError' ||
794
+ e.name === 'PermissionDeniedError' ||
795
+ e.name === 'PermissionDismissedError')
796
+ );
797
+ }
798
+
799
+ const deviceAcquisitionFailures = new WeakSet();
800
+
801
+ /** Records that `e` is the rejection getUserMedia itself produced. */
802
+ export function markDeviceAcquisitionFailure(e: unknown) {
803
+ if (typeof e === 'object' && e !== null) {
804
+ deviceAcquisitionFailures.add(e);
805
+ }
806
+ }
807
+
808
+ /**
809
+ * Whether a track restart failed at getUserMedia rather than in the work that adopts the new track.
810
+ * Recorded on the side rather than by wrapping, because callers of `restartTrack` match on the
811
+ * rejection's own name.
812
+ */
813
+ export function isDeviceAcquisitionFailure(e: unknown): boolean {
814
+ return typeof e === 'object' && e !== null && deviceAcquisitionFailures.has(e);
815
+ }
816
+
727
817
  export function toWebsocketUrl(url: string): string {
728
818
  if (url.startsWith('http')) {
729
819
  return url.replace(/^(http)/, 'ws');
@@ -961,6 +1051,49 @@ export function isCompressionStreamSupported() {
961
1051
  return typeof CompressionStream !== 'undefined';
962
1052
  }
963
1053
 
1054
+ let deflateRawCompressionSupported: boolean | null = null;
1055
+
1056
+ /**
1057
+ * Whether this runtime can compress AND decompress the `deflate-raw` format, which is a stricter
1058
+ * requirement than {@link isCompressionStreamSupported}: Chromium 80-102 included
1059
+ * `CompressionStream` supporting only `gzip` / `deflate`, and `deflate-raw` was added in Chromium
1060
+ * 103 (Firefox 113, Safari 16.4).
1061
+ */
1062
+ export function isDeflateRawCompressionSupported(): boolean {
1063
+ if (typeof deflateRawCompressionSupported !== 'boolean') {
1064
+ deflateRawCompressionSupported = probeDeflateRawCompressionSupport();
1065
+ }
1066
+ return deflateRawCompressionSupported;
1067
+ }
1068
+
1069
+ function probeDeflateRawCompressionSupport(): boolean {
1070
+ if (typeof CompressionStream === 'undefined' || typeof DecompressionStream === 'undefined') {
1071
+ return false;
1072
+ }
1073
+ try {
1074
+ // There is no api to query format support, and constructing with an unsupported format throws
1075
+ // synchronously, so constructing one of each is the probe. Both are checked because the
1076
+ // deflate-raw capability advertised to peers is as much about decompressing what they send us
1077
+ // as it is about compressing what we send them.
1078
+ new CompressionStream('deflate-raw');
1079
+ new DecompressionStream('deflate-raw');
1080
+ return true;
1081
+ } catch {
1082
+ return false;
1083
+ }
1084
+ }
1085
+
1086
+ /**
1087
+ * Clears the memoized {@link isDeflateRawCompressionSupported} probe result. A runtime doesn't
1088
+ * gain or lose compression formats while it is running, so this only exists for tests that swap
1089
+ * the global `CompressionStream` / `DecompressionStream`.
1090
+ *
1091
+ * @internal
1092
+ */
1093
+ export function resetDeflateRawCompressionSupportCache() {
1094
+ deflateRawCompressionSupported = null;
1095
+ }
1096
+
964
1097
  export function isPublisherOfferWithJoinSupported() {
965
1098
  // we have connectivity issue about publisher offer with join on firefox #1919
966
1099
  return isCompressionStreamSupported() && !isFireFox();
@@ -0,0 +1,53 @@
1
+ import { resetDeflateRawCompressionSupportCache } from '../room/utils';
2
+
3
+ /**
4
+ * Swaps the global `CompressionStream` / `DecompressionStream` away entirely, emulating a runtime
5
+ * that predates the compression streams api. Returns a function that restores them.
6
+ */
7
+ export function stubMissingCompressionStreams(): () => void {
8
+ return stubCompressionStreams(undefined, undefined);
9
+ }
10
+
11
+ /**
12
+ * Swaps in global `CompressionStream` / `DecompressionStream` constructors that reject the
13
+ * `deflate-raw` format the way Chromium 80-102 does (the api exists, but only `gzip` / `deflate`
14
+ * are supported). Returns a function that restores the real ones.
15
+ */
16
+ export function stubMissingDeflateRawSupport(): () => void {
17
+ return stubCompressionStreams(
18
+ legacyFormatsOnly('CompressionStream', CompressionStream),
19
+ legacyFormatsOnly('DecompressionStream', DecompressionStream),
20
+ );
21
+ }
22
+
23
+ type CompressionStreamCtor<T> = new (format: CompressionFormat) => T;
24
+
25
+ function legacyFormatsOnly<T>(
26
+ name: string,
27
+ Real: CompressionStreamCtor<T>,
28
+ ): CompressionStreamCtor<T> {
29
+ return function LegacyCompressionStream(format: CompressionFormat) {
30
+ if (format === 'deflate-raw') {
31
+ throw new TypeError(
32
+ `Failed to construct '${name}': Unsupported compression format: 'deflate-raw'`,
33
+ );
34
+ }
35
+ return new Real(format);
36
+ } as unknown as CompressionStreamCtor<T>;
37
+ }
38
+
39
+ function stubCompressionStreams(compression: unknown, decompression: unknown): () => void {
40
+ const originalCompressionStream = globalThis.CompressionStream;
41
+ const originalDecompressionStream = globalThis.DecompressionStream;
42
+ (globalThis as any).CompressionStream = compression;
43
+ (globalThis as any).DecompressionStream = decompression;
44
+ // The support probe memoizes, which is only safe because a real runtime never changes formats
45
+ // mid-session; swapping the globals has to invalidate it.
46
+ resetDeflateRawCompressionSupportCache();
47
+
48
+ return () => {
49
+ (globalThis as any).CompressionStream = originalCompressionStream;
50
+ (globalThis as any).DecompressionStream = originalDecompressionStream;
51
+ resetDeflateRawCompressionSupportCache();
52
+ };
53
+ }