@videojs/spf 10.0.0-beta.3 → 10.0.0-beta.30

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 (580) hide show
  1. package/README.md +19 -38
  2. package/dist/default/core/actors/create-machine-actor.js +91 -0
  3. package/dist/default/core/actors/create-machine-actor.js.map +1 -0
  4. package/dist/default/core/actors/create-transition-actor.js +46 -0
  5. package/dist/default/core/actors/create-transition-actor.js.map +1 -0
  6. package/dist/default/core/composition/create-composition.js +85 -0
  7. package/dist/default/core/composition/create-composition.js.map +1 -0
  8. package/dist/default/core/composition/share-signals.js +39 -0
  9. package/dist/default/core/composition/share-signals.js.map +1 -0
  10. package/dist/default/core/machine.js +26 -0
  11. package/dist/default/core/machine.js.map +1 -0
  12. package/dist/default/core/reactors/create-machine-reactor.js +83 -0
  13. package/dist/default/core/reactors/create-machine-reactor.js.map +1 -0
  14. package/dist/default/core/signals/effect.js +41 -0
  15. package/dist/default/core/signals/effect.js.map +1 -0
  16. package/dist/default/core/signals/primitives.js +54 -0
  17. package/dist/default/core/signals/primitives.js.map +1 -0
  18. package/dist/default/core/signals/when.js +38 -0
  19. package/dist/default/core/signals/when.js.map +1 -0
  20. package/dist/default/core/tasks/delayed-reschedule.js +31 -0
  21. package/dist/default/core/tasks/delayed-reschedule.js.map +1 -0
  22. package/dist/default/core/tasks/task.js +321 -0
  23. package/dist/default/core/tasks/task.js.map +1 -0
  24. package/dist/default/dom.js +9 -0
  25. package/dist/default/hls-audio.js +3 -0
  26. package/dist/default/hls-background-video.js +3 -0
  27. package/dist/default/hls-video.js +4 -0
  28. package/dist/default/hls.js +8 -0
  29. package/dist/default/index.js +23 -0
  30. package/dist/default/index.js.map +1 -0
  31. package/dist/default/media/abr/quality-selection.js +21 -0
  32. package/dist/default/media/abr/quality-selection.js.map +1 -0
  33. package/dist/default/media/buffer/back-buffer.js +46 -0
  34. package/dist/default/media/buffer/back-buffer.js.map +1 -0
  35. package/dist/default/media/buffer/forward-buffer.js +119 -0
  36. package/dist/default/media/buffer/forward-buffer.js.map +1 -0
  37. package/dist/default/media/dom/capabilities.js +72 -0
  38. package/dist/default/media/dom/capabilities.js.map +1 -0
  39. package/dist/default/media/dom/mse/append-segment.js +60 -0
  40. package/dist/default/media/dom/mse/append-segment.js.map +1 -0
  41. package/dist/default/media/dom/mse/buffer-flusher.js +55 -0
  42. package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -0
  43. package/dist/default/media/dom/mse/duration.js +95 -0
  44. package/dist/default/media/dom/mse/duration.js.map +1 -0
  45. package/dist/default/media/dom/mse/end-of-stream.js +20 -0
  46. package/dist/default/media/dom/mse/end-of-stream.js.map +1 -0
  47. package/dist/default/media/dom/mse/mediasource-setup.js +242 -0
  48. package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -0
  49. package/dist/default/media/dom/screen.js +112 -0
  50. package/dist/default/media/dom/screen.js.map +1 -0
  51. package/dist/default/media/dom/text/resolve-vtt-segment.js +50 -0
  52. package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -0
  53. package/dist/default/media/dom/text/text-track-slots.js +69 -0
  54. package/dist/default/media/dom/text/text-track-slots.js.map +1 -0
  55. package/dist/default/media/errors.js +69 -0
  56. package/dist/default/media/errors.js.map +1 -0
  57. package/dist/default/media/hls/parse-attributes.js +148 -0
  58. package/dist/default/media/hls/parse-attributes.js.map +1 -0
  59. package/dist/default/media/hls/parse-media-playlist.js +261 -0
  60. package/dist/default/media/hls/parse-media-playlist.js.map +1 -0
  61. package/dist/default/media/hls/parse-multivariant.js +234 -0
  62. package/dist/default/media/hls/parse-multivariant.js.map +1 -0
  63. package/dist/default/media/hls/reload-policy.js +71 -0
  64. package/dist/default/media/hls/reload-policy.js.map +1 -0
  65. package/dist/default/media/hls/resolve-url.js +11 -0
  66. package/dist/default/media/hls/resolve-url.js.map +1 -0
  67. package/dist/default/media/live-window.js +36 -0
  68. package/dist/default/media/live-window.js.map +1 -0
  69. package/dist/default/media/media-tracks/media-tracks.js +101 -0
  70. package/dist/default/media/media-tracks/media-tracks.js.map +1 -0
  71. package/dist/default/media/mp4/box.js +58 -0
  72. package/dist/default/media/mp4/box.js.map +1 -0
  73. package/dist/default/media/mp4/timestamp-origin.js +107 -0
  74. package/dist/default/media/mp4/timestamp-origin.js.map +1 -0
  75. package/dist/default/media/primitives/resolution.js +29 -0
  76. package/dist/default/media/primitives/resolution.js.map +1 -0
  77. package/dist/default/media/primitives/select-tracks.js +107 -0
  78. package/dist/default/media/primitives/select-tracks.js.map +1 -0
  79. package/dist/default/media/text/parse-vtt-timestamp-map.js +42 -0
  80. package/dist/default/media/text/parse-vtt-timestamp-map.js.map +1 -0
  81. package/dist/default/media/text/resolve-vtt-metadata.js +23 -0
  82. package/dist/default/media/text/resolve-vtt-metadata.js.map +1 -0
  83. package/dist/default/media/types/index.js +50 -0
  84. package/dist/default/media/types/index.js.map +1 -0
  85. package/dist/default/media/utils/cdn.js +56 -0
  86. package/dist/default/media/utils/cdn.js.map +1 -0
  87. package/dist/default/media/utils/preload.js +22 -0
  88. package/dist/default/media/utils/preload.js.map +1 -0
  89. package/dist/default/media/utils/track-selection.js +47 -0
  90. package/dist/default/media/utils/track-selection.js.map +1 -0
  91. package/dist/default/media/utils/tracks.js +144 -0
  92. package/dist/default/media/utils/tracks.js.map +1 -0
  93. package/dist/default/media-tracks.js +2 -0
  94. package/dist/default/mux-audio.js +3 -0
  95. package/dist/default/mux-background-video.js +3 -0
  96. package/dist/default/mux-video.js +3 -0
  97. package/dist/default/network/bandwidth-estimator.js +96 -0
  98. package/dist/default/network/bandwidth-estimator.js.map +1 -0
  99. package/dist/default/network/chunked-stream-iterable.js +49 -0
  100. package/dist/default/network/chunked-stream-iterable.js.map +1 -0
  101. package/dist/default/network/ewma.js +71 -0
  102. package/dist/default/network/ewma.js.map +1 -0
  103. package/dist/default/network/fetch.js +115 -0
  104. package/dist/default/network/fetch.js.map +1 -0
  105. package/dist/default/playback/actors/dom/segment-loader.js +249 -0
  106. package/dist/default/playback/actors/dom/segment-loader.js.map +1 -0
  107. package/dist/default/playback/actors/dom/source-buffer.js +139 -0
  108. package/dist/default/playback/actors/dom/source-buffer.js.map +1 -0
  109. package/dist/default/playback/actors/dom/text-tracks.js +46 -0
  110. package/dist/default/playback/actors/dom/text-tracks.js.map +1 -0
  111. package/dist/default/playback/actors/text-track-segment-loader.js +149 -0
  112. package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -0
  113. package/dist/default/playback/adapters/hls-audio/adapter.js +186 -0
  114. package/dist/default/playback/adapters/hls-audio/adapter.js.map +1 -0
  115. package/dist/default/playback/adapters/hls-audio/media.js +9 -0
  116. package/dist/default/playback/adapters/hls-audio/media.js.map +1 -0
  117. package/dist/default/playback/adapters/hls-background-video/adapter.js +211 -0
  118. package/dist/default/playback/adapters/hls-background-video/adapter.js.map +1 -0
  119. package/dist/default/playback/adapters/hls-background-video/host.js +60 -0
  120. package/dist/default/playback/adapters/hls-background-video/host.js.map +1 -0
  121. package/dist/default/playback/adapters/hls-background-video/media.js +21 -0
  122. package/dist/default/playback/adapters/hls-background-video/media.js.map +1 -0
  123. package/dist/default/playback/adapters/hls-video/adapter.js +316 -0
  124. package/dist/default/playback/adapters/hls-video/adapter.js.map +1 -0
  125. package/dist/default/playback/adapters/hls-video/error-surface.js +51 -0
  126. package/dist/default/playback/adapters/hls-video/error-surface.js.map +1 -0
  127. package/dist/default/playback/adapters/hls-video/media-tracks.js +126 -0
  128. package/dist/default/playback/adapters/hls-video/media-tracks.js.map +1 -0
  129. package/dist/default/playback/adapters/hls-video/media.js +11 -0
  130. package/dist/default/playback/adapters/hls-video/media.js.map +1 -0
  131. package/dist/default/playback/adapters/mux-audio/media.js +26 -0
  132. package/dist/default/playback/adapters/mux-audio/media.js.map +1 -0
  133. package/dist/default/playback/adapters/mux-video/adapter.js +105 -0
  134. package/dist/default/playback/adapters/mux-video/adapter.js.map +1 -0
  135. package/dist/default/playback/adapters/mux-video/media.js +23 -0
  136. package/dist/default/playback/adapters/mux-video/media.js.map +1 -0
  137. package/dist/default/playback/behaviors/calculate-presentation-duration.js +36 -0
  138. package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  139. package/dist/default/playback/behaviors/collect-errors.js +111 -0
  140. package/dist/default/playback/behaviors/collect-errors.js.map +1 -0
  141. package/dist/default/playback/behaviors/derive-cdn-priority.js +68 -0
  142. package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -0
  143. package/dist/default/playback/behaviors/dom/airplay.js +211 -0
  144. package/dist/default/playback/behaviors/dom/airplay.js.map +1 -0
  145. package/dist/default/playback/behaviors/dom/apply-start-position.js +93 -0
  146. package/dist/default/playback/behaviors/dom/apply-start-position.js.map +1 -0
  147. package/dist/default/playback/behaviors/dom/end-of-stream.js +102 -0
  148. package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -0
  149. package/dist/default/playback/behaviors/dom/load-segments.js +181 -0
  150. package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -0
  151. package/dist/default/playback/behaviors/dom/recover-end-stall.js +77 -0
  152. package/dist/default/playback/behaviors/dom/recover-end-stall.js.map +1 -0
  153. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js +132 -0
  154. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
  155. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +205 -0
  156. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  157. package/dist/default/playback/behaviors/dom/setup-mediasource.js +114 -0
  158. package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  159. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +40 -0
  160. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  161. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js +26 -0
  162. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
  163. package/dist/default/playback/behaviors/dom/sync-text-tracks.js +124 -0
  164. package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  165. package/dist/default/playback/behaviors/dom/track-current-time.js +52 -0
  166. package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -0
  167. package/dist/default/playback/behaviors/dom/track-load-triggers.js +94 -0
  168. package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  169. package/dist/default/playback/behaviors/dom/track-playback-rate.js +37 -0
  170. package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  171. package/dist/default/playback/behaviors/dom/track-player-resolution.js +57 -0
  172. package/dist/default/playback/behaviors/dom/track-player-resolution.js.map +1 -0
  173. package/dist/default/playback/behaviors/dom/track-screen-resolution.js +29 -0
  174. package/dist/default/playback/behaviors/dom/track-screen-resolution.js.map +1 -0
  175. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +113 -0
  176. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  177. package/dist/default/playback/behaviors/establish-start-media-time.js +197 -0
  178. package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -0
  179. package/dist/default/playback/behaviors/resolve-presentation.js +85 -0
  180. package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -0
  181. package/dist/default/playback/behaviors/resolve-track.js +141 -0
  182. package/dist/default/playback/behaviors/resolve-track.js.map +1 -0
  183. package/dist/default/playback/behaviors/select-tracks.js +216 -0
  184. package/dist/default/playback/behaviors/select-tracks.js.map +1 -0
  185. package/dist/default/playback/behaviors/setup-failover-monitor.js +66 -0
  186. package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -0
  187. package/dist/default/playback/behaviors/sync-preload.js +59 -0
  188. package/dist/default/playback/behaviors/sync-preload.js.map +1 -0
  189. package/dist/default/playback/behaviors/track-switching.js +391 -0
  190. package/dist/default/playback/behaviors/track-switching.js.map +1 -0
  191. package/dist/default/playback/engines/hls/engine-audio-only.js +98 -0
  192. package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -0
  193. package/dist/default/playback/engines/hls/engine-background-video.js +92 -0
  194. package/dist/default/playback/engines/hls/engine-background-video.js.map +1 -0
  195. package/dist/default/playback/engines/hls/engine.js +147 -0
  196. package/dist/default/playback/engines/hls/engine.js.map +1 -0
  197. package/dist/default/playback/primitives/error-messages.js +51 -0
  198. package/dist/default/playback/primitives/error-messages.js.map +1 -0
  199. package/dist/default/playback/primitives/failover-fetch.js +42 -0
  200. package/dist/default/playback/primitives/failover-fetch.js.map +1 -0
  201. package/dist/default/playback/primitives/head-peek.js +44 -0
  202. package/dist/default/playback/primitives/head-peek.js.map +1 -0
  203. package/dist/default/playback/primitives/live-window.js +66 -0
  204. package/dist/default/playback/primitives/live-window.js.map +1 -0
  205. package/dist/default/playback/primitives/relocation-pipelines.js +203 -0
  206. package/dist/default/playback/primitives/relocation-pipelines.js.map +1 -0
  207. package/dist/default/playback/primitives/report-track-conditions.js +86 -0
  208. package/dist/default/playback/primitives/report-track-conditions.js.map +1 -0
  209. package/dist/default/playback/primitives/segment-load-pipeline.js +96 -0
  210. package/dist/default/playback/primitives/segment-load-pipeline.js.map +1 -0
  211. package/dist/default/playback/primitives/selection-rules.js +86 -0
  212. package/dist/default/playback/primitives/selection-rules.js.map +1 -0
  213. package/dist/default/playback/primitives/text-segment-load-pipeline.js +37 -0
  214. package/dist/default/playback/primitives/text-segment-load-pipeline.js.map +1 -0
  215. package/dist/default/playback/primitives/track-types.js +61 -0
  216. package/dist/default/playback/primitives/track-types.js.map +1 -0
  217. package/dist/dev/core/actors/actor.d.ts +26 -0
  218. package/dist/dev/core/actors/actor.d.ts.map +1 -0
  219. package/dist/dev/core/actors/create-machine-actor.d.ts +128 -0
  220. package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -0
  221. package/dist/dev/core/actors/create-machine-actor.js +91 -0
  222. package/dist/dev/core/actors/create-machine-actor.js.map +1 -0
  223. package/dist/dev/core/actors/create-transition-actor.d.ts +41 -0
  224. package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -0
  225. package/dist/dev/core/actors/create-transition-actor.js +46 -0
  226. package/dist/dev/core/actors/create-transition-actor.js.map +1 -0
  227. package/dist/dev/core/composition/create-composition.d.ts +273 -0
  228. package/dist/dev/core/composition/create-composition.d.ts.map +1 -0
  229. package/dist/dev/core/composition/create-composition.js +85 -0
  230. package/dist/dev/core/composition/create-composition.js.map +1 -0
  231. package/dist/dev/core/composition/share-signals.d.ts +46 -0
  232. package/dist/dev/core/composition/share-signals.d.ts.map +1 -0
  233. package/dist/dev/core/composition/share-signals.js +39 -0
  234. package/dist/dev/core/composition/share-signals.js.map +1 -0
  235. package/dist/dev/core/machine.d.ts +21 -0
  236. package/dist/dev/core/machine.d.ts.map +1 -0
  237. package/dist/dev/core/machine.js +26 -0
  238. package/dist/dev/core/machine.js.map +1 -0
  239. package/dist/dev/core/reactors/create-machine-reactor.d.ts +93 -0
  240. package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -0
  241. package/dist/dev/core/reactors/create-machine-reactor.js +83 -0
  242. package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -0
  243. package/dist/dev/core/signals/effect.d.ts +15 -0
  244. package/dist/dev/core/signals/effect.d.ts.map +1 -0
  245. package/dist/dev/core/signals/effect.js +41 -0
  246. package/dist/dev/core/signals/effect.js.map +1 -0
  247. package/dist/dev/core/signals/primitives.d.ts +46 -0
  248. package/dist/dev/core/signals/primitives.d.ts.map +1 -0
  249. package/dist/dev/core/signals/primitives.js +54 -0
  250. package/dist/dev/core/signals/primitives.js.map +1 -0
  251. package/dist/dev/core/signals/when.js +38 -0
  252. package/dist/dev/core/signals/when.js.map +1 -0
  253. package/dist/dev/core/tasks/delayed-reschedule.js +31 -0
  254. package/dist/dev/core/tasks/delayed-reschedule.js.map +1 -0
  255. package/dist/dev/core/tasks/task.d.ts +166 -0
  256. package/dist/dev/core/tasks/task.d.ts.map +1 -0
  257. package/dist/dev/core/tasks/task.js +321 -0
  258. package/dist/dev/core/tasks/task.js.map +1 -0
  259. package/dist/dev/dom.d.ts +9 -0
  260. package/dist/dev/dom.js +9 -0
  261. package/dist/dev/hls-audio.d.ts +3 -0
  262. package/dist/dev/hls-audio.js +3 -0
  263. package/dist/dev/hls-background-video.d.ts +4 -0
  264. package/dist/dev/hls-background-video.js +3 -0
  265. package/dist/dev/hls-video.d.ts +5 -0
  266. package/dist/dev/hls-video.js +4 -0
  267. package/dist/dev/hls.d.ts +9 -0
  268. package/dist/dev/hls.js +8 -0
  269. package/dist/dev/index.d.ts +24 -0
  270. package/dist/dev/index.d.ts.map +1 -0
  271. package/dist/dev/index.js +23 -0
  272. package/dist/dev/index.js.map +1 -0
  273. package/dist/dev/media/abr/quality-selection.d.ts +22 -0
  274. package/dist/dev/media/abr/quality-selection.d.ts.map +1 -0
  275. package/dist/dev/media/abr/quality-selection.js +21 -0
  276. package/dist/dev/media/abr/quality-selection.js.map +1 -0
  277. package/dist/dev/media/buffer/back-buffer.d.ts +14 -0
  278. package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -0
  279. package/dist/dev/media/buffer/back-buffer.js +46 -0
  280. package/dist/dev/media/buffer/back-buffer.js.map +1 -0
  281. package/dist/dev/media/buffer/forward-buffer.d.ts +14 -0
  282. package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -0
  283. package/dist/dev/media/buffer/forward-buffer.js +119 -0
  284. package/dist/dev/media/buffer/forward-buffer.js.map +1 -0
  285. package/dist/dev/media/dom/capabilities.js +72 -0
  286. package/dist/dev/media/dom/capabilities.js.map +1 -0
  287. package/dist/dev/media/dom/mse/append-segment.d.ts +18 -0
  288. package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -0
  289. package/dist/dev/media/dom/mse/append-segment.js +60 -0
  290. package/dist/dev/media/dom/mse/append-segment.js.map +1 -0
  291. package/dist/dev/media/dom/mse/buffer-flusher.d.ts +24 -0
  292. package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -0
  293. package/dist/dev/media/dom/mse/buffer-flusher.js +55 -0
  294. package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -0
  295. package/dist/dev/media/dom/mse/duration.js +95 -0
  296. package/dist/dev/media/dom/mse/duration.js.map +1 -0
  297. package/dist/dev/media/dom/mse/end-of-stream.js +20 -0
  298. package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -0
  299. package/dist/dev/media/dom/mse/mediasource-setup.js +242 -0
  300. package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -0
  301. package/dist/dev/media/dom/screen.d.ts +7 -0
  302. package/dist/dev/media/dom/screen.d.ts.map +1 -0
  303. package/dist/dev/media/dom/screen.js +112 -0
  304. package/dist/dev/media/dom/screen.js.map +1 -0
  305. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +6 -0
  306. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -0
  307. package/dist/dev/media/dom/text/resolve-vtt-segment.js +50 -0
  308. package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -0
  309. package/dist/dev/media/dom/text/text-track-slots.d.ts +30 -0
  310. package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -0
  311. package/dist/dev/media/dom/text/text-track-slots.js +69 -0
  312. package/dist/dev/media/dom/text/text-track-slots.js.map +1 -0
  313. package/dist/dev/media/errors.d.ts +82 -0
  314. package/dist/dev/media/errors.d.ts.map +1 -0
  315. package/dist/dev/media/errors.js +69 -0
  316. package/dist/dev/media/errors.js.map +1 -0
  317. package/dist/dev/media/hls/parse-attributes.js +148 -0
  318. package/dist/dev/media/hls/parse-attributes.js.map +1 -0
  319. package/dist/dev/media/hls/parse-media-playlist.js +261 -0
  320. package/dist/dev/media/hls/parse-media-playlist.js.map +1 -0
  321. package/dist/dev/media/hls/parse-multivariant.js +234 -0
  322. package/dist/dev/media/hls/parse-multivariant.js.map +1 -0
  323. package/dist/dev/media/hls/reload-policy.js +71 -0
  324. package/dist/dev/media/hls/reload-policy.js.map +1 -0
  325. package/dist/dev/media/hls/resolve-url.js +11 -0
  326. package/dist/dev/media/hls/resolve-url.js.map +1 -0
  327. package/dist/dev/media/live-window.js +36 -0
  328. package/dist/dev/media/live-window.js.map +1 -0
  329. package/dist/dev/media/media-tracks/media-tracks.d.ts +51 -0
  330. package/dist/dev/media/media-tracks/media-tracks.d.ts.map +1 -0
  331. package/dist/dev/media/media-tracks/media-tracks.js +101 -0
  332. package/dist/dev/media/media-tracks/media-tracks.js.map +1 -0
  333. package/dist/dev/media/mp4/box.js +58 -0
  334. package/dist/dev/media/mp4/box.js.map +1 -0
  335. package/dist/dev/media/mp4/timestamp-origin.js +107 -0
  336. package/dist/dev/media/mp4/timestamp-origin.js.map +1 -0
  337. package/dist/dev/media/primitives/resolution.d.ts +24 -0
  338. package/dist/dev/media/primitives/resolution.d.ts.map +1 -0
  339. package/dist/dev/media/primitives/resolution.js +29 -0
  340. package/dist/dev/media/primitives/resolution.js.map +1 -0
  341. package/dist/dev/media/primitives/select-tracks.js +107 -0
  342. package/dist/dev/media/primitives/select-tracks.js.map +1 -0
  343. package/dist/dev/media/text/parse-vtt-timestamp-map.js +42 -0
  344. package/dist/dev/media/text/parse-vtt-timestamp-map.js.map +1 -0
  345. package/dist/dev/media/text/resolve-vtt-metadata.js +23 -0
  346. package/dist/dev/media/text/resolve-vtt-metadata.js.map +1 -0
  347. package/dist/dev/media/types/index.d.ts +377 -0
  348. package/dist/dev/media/types/index.d.ts.map +1 -0
  349. package/dist/dev/media/types/index.js +50 -0
  350. package/dist/dev/media/types/index.js.map +1 -0
  351. package/dist/dev/media/utils/cdn.d.ts +13 -0
  352. package/dist/dev/media/utils/cdn.d.ts.map +1 -0
  353. package/dist/dev/media/utils/cdn.js +56 -0
  354. package/dist/dev/media/utils/cdn.js.map +1 -0
  355. package/dist/dev/media/utils/preload.js +22 -0
  356. package/dist/dev/media/utils/preload.js.map +1 -0
  357. package/dist/dev/media/utils/track-selection.js +47 -0
  358. package/dist/dev/media/utils/track-selection.js.map +1 -0
  359. package/dist/dev/media/utils/tracks.js +144 -0
  360. package/dist/dev/media/utils/tracks.js.map +1 -0
  361. package/dist/dev/media-tracks.d.ts +3 -0
  362. package/dist/dev/media-tracks.js +2 -0
  363. package/dist/dev/mux-audio.d.ts +4 -0
  364. package/dist/dev/mux-audio.js +3 -0
  365. package/dist/dev/mux-background-video.d.ts +4 -0
  366. package/dist/dev/mux-background-video.js +3 -0
  367. package/dist/dev/mux-video.d.ts +4 -0
  368. package/dist/dev/mux-video.js +3 -0
  369. package/dist/dev/network/bandwidth-estimator.d.ts +50 -0
  370. package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -0
  371. package/dist/dev/network/bandwidth-estimator.js +96 -0
  372. package/dist/dev/network/bandwidth-estimator.js.map +1 -0
  373. package/dist/dev/network/chunked-stream-iterable.js +49 -0
  374. package/dist/dev/network/chunked-stream-iterable.js.map +1 -0
  375. package/dist/dev/network/ewma.js +71 -0
  376. package/dist/dev/network/ewma.js.map +1 -0
  377. package/dist/dev/network/fetch.js +115 -0
  378. package/dist/dev/network/fetch.js.map +1 -0
  379. package/dist/dev/playback/actors/dom/segment-loader.d.ts +48 -0
  380. package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -0
  381. package/dist/dev/playback/actors/dom/segment-loader.js +249 -0
  382. package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -0
  383. package/dist/dev/playback/actors/dom/source-buffer.d.ts +47 -0
  384. package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -0
  385. package/dist/dev/playback/actors/dom/source-buffer.js +139 -0
  386. package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -0
  387. package/dist/dev/playback/actors/dom/text-tracks.d.ts +3 -0
  388. package/dist/dev/playback/actors/dom/text-tracks.js +46 -0
  389. package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -0
  390. package/dist/dev/playback/actors/text-track-segment-loader.d.ts +54 -0
  391. package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -0
  392. package/dist/dev/playback/actors/text-track-segment-loader.js +149 -0
  393. package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -0
  394. package/dist/dev/playback/actors/text-tracks.d.ts +33 -0
  395. package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -0
  396. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts +54 -0
  397. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts.map +1 -0
  398. package/dist/dev/playback/adapters/hls-audio/adapter.js +186 -0
  399. package/dist/dev/playback/adapters/hls-audio/adapter.js.map +1 -0
  400. package/dist/dev/playback/adapters/hls-audio/media.d.ts +10 -0
  401. package/dist/dev/playback/adapters/hls-audio/media.d.ts.map +1 -0
  402. package/dist/dev/playback/adapters/hls-audio/media.js +9 -0
  403. package/dist/dev/playback/adapters/hls-audio/media.js.map +1 -0
  404. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts +77 -0
  405. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts.map +1 -0
  406. package/dist/dev/playback/adapters/hls-background-video/adapter.js +211 -0
  407. package/dist/dev/playback/adapters/hls-background-video/adapter.js.map +1 -0
  408. package/dist/dev/playback/adapters/hls-background-video/host.d.ts +38 -0
  409. package/dist/dev/playback/adapters/hls-background-video/host.d.ts.map +1 -0
  410. package/dist/dev/playback/adapters/hls-background-video/host.js +60 -0
  411. package/dist/dev/playback/adapters/hls-background-video/host.js.map +1 -0
  412. package/dist/dev/playback/adapters/hls-background-video/media.d.ts +20 -0
  413. package/dist/dev/playback/adapters/hls-background-video/media.d.ts.map +1 -0
  414. package/dist/dev/playback/adapters/hls-background-video/media.js +21 -0
  415. package/dist/dev/playback/adapters/hls-background-video/media.js.map +1 -0
  416. package/dist/dev/playback/adapters/hls-video/adapter.d.ts +63 -0
  417. package/dist/dev/playback/adapters/hls-video/adapter.d.ts.map +1 -0
  418. package/dist/dev/playback/adapters/hls-video/adapter.js +316 -0
  419. package/dist/dev/playback/adapters/hls-video/adapter.js.map +1 -0
  420. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts +18 -0
  421. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts.map +1 -0
  422. package/dist/dev/playback/adapters/hls-video/error-surface.js +51 -0
  423. package/dist/dev/playback/adapters/hls-video/error-surface.js.map +1 -0
  424. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts +22 -0
  425. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts.map +1 -0
  426. package/dist/dev/playback/adapters/hls-video/media-tracks.js +126 -0
  427. package/dist/dev/playback/adapters/hls-video/media-tracks.js.map +1 -0
  428. package/dist/dev/playback/adapters/hls-video/media.d.ts +10 -0
  429. package/dist/dev/playback/adapters/hls-video/media.d.ts.map +1 -0
  430. package/dist/dev/playback/adapters/hls-video/media.js +11 -0
  431. package/dist/dev/playback/adapters/hls-video/media.js.map +1 -0
  432. package/dist/dev/playback/adapters/mux-audio/media.d.ts +28 -0
  433. package/dist/dev/playback/adapters/mux-audio/media.d.ts.map +1 -0
  434. package/dist/dev/playback/adapters/mux-audio/media.js +26 -0
  435. package/dist/dev/playback/adapters/mux-audio/media.js.map +1 -0
  436. package/dist/dev/playback/adapters/mux-video/adapter.d.ts +34 -0
  437. package/dist/dev/playback/adapters/mux-video/adapter.d.ts.map +1 -0
  438. package/dist/dev/playback/adapters/mux-video/adapter.js +105 -0
  439. package/dist/dev/playback/adapters/mux-video/adapter.js.map +1 -0
  440. package/dist/dev/playback/adapters/mux-video/media.d.ts +24 -0
  441. package/dist/dev/playback/adapters/mux-video/media.d.ts.map +1 -0
  442. package/dist/dev/playback/adapters/mux-video/media.js +23 -0
  443. package/dist/dev/playback/adapters/mux-video/media.js.map +1 -0
  444. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +26 -0
  445. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -0
  446. package/dist/dev/playback/behaviors/calculate-presentation-duration.js +36 -0
  447. package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  448. package/dist/dev/playback/behaviors/collect-errors.js +111 -0
  449. package/dist/dev/playback/behaviors/collect-errors.js.map +1 -0
  450. package/dist/dev/playback/behaviors/derive-cdn-priority.js +68 -0
  451. package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -0
  452. package/dist/dev/playback/behaviors/dom/airplay.js +211 -0
  453. package/dist/dev/playback/behaviors/dom/airplay.js.map +1 -0
  454. package/dist/dev/playback/behaviors/dom/apply-start-position.js +93 -0
  455. package/dist/dev/playback/behaviors/dom/apply-start-position.js.map +1 -0
  456. package/dist/dev/playback/behaviors/dom/end-of-stream.js +102 -0
  457. package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -0
  458. package/dist/dev/playback/behaviors/dom/load-segments.d.ts +72 -0
  459. package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -0
  460. package/dist/dev/playback/behaviors/dom/load-segments.js +181 -0
  461. package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -0
  462. package/dist/dev/playback/behaviors/dom/recover-end-stall.js +77 -0
  463. package/dist/dev/playback/behaviors/dom/recover-end-stall.js.map +1 -0
  464. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js +132 -0
  465. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
  466. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +205 -0
  467. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  468. package/dist/dev/playback/behaviors/dom/setup-mediasource.js +114 -0
  469. package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  470. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +28 -0
  471. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -0
  472. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +40 -0
  473. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  474. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js +26 -0
  475. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
  476. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +124 -0
  477. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  478. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +33 -0
  479. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -0
  480. package/dist/dev/playback/behaviors/dom/track-current-time.js +52 -0
  481. package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -0
  482. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +47 -0
  483. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -0
  484. package/dist/dev/playback/behaviors/dom/track-load-triggers.js +94 -0
  485. package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  486. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +33 -0
  487. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -0
  488. package/dist/dev/playback/behaviors/dom/track-playback-rate.js +37 -0
  489. package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  490. package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts +8 -0
  491. package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts.map +1 -0
  492. package/dist/dev/playback/behaviors/dom/track-player-resolution.js +57 -0
  493. package/dist/dev/playback/behaviors/dom/track-player-resolution.js.map +1 -0
  494. package/dist/dev/playback/behaviors/dom/track-screen-resolution.js +29 -0
  495. package/dist/dev/playback/behaviors/dom/track-screen-resolution.js.map +1 -0
  496. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +113 -0
  497. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  498. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +36 -0
  499. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts.map +1 -0
  500. package/dist/dev/playback/behaviors/establish-start-media-time.js +197 -0
  501. package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -0
  502. package/dist/dev/playback/behaviors/resolve-presentation.d.ts +8 -0
  503. package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -0
  504. package/dist/dev/playback/behaviors/resolve-presentation.js +85 -0
  505. package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -0
  506. package/dist/dev/playback/behaviors/resolve-track.js +141 -0
  507. package/dist/dev/playback/behaviors/resolve-track.js.map +1 -0
  508. package/dist/dev/playback/behaviors/select-tracks.d.ts +72 -0
  509. package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -0
  510. package/dist/dev/playback/behaviors/select-tracks.js +216 -0
  511. package/dist/dev/playback/behaviors/select-tracks.js.map +1 -0
  512. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +14 -0
  513. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -0
  514. package/dist/dev/playback/behaviors/setup-failover-monitor.js +66 -0
  515. package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -0
  516. package/dist/dev/playback/behaviors/sync-preload.js +59 -0
  517. package/dist/dev/playback/behaviors/sync-preload.js.map +1 -0
  518. package/dist/dev/playback/behaviors/track-switching.js +391 -0
  519. package/dist/dev/playback/behaviors/track-switching.js.map +1 -0
  520. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +173 -0
  521. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -0
  522. package/dist/dev/playback/engines/hls/engine-audio-only.js +98 -0
  523. package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -0
  524. package/dist/dev/playback/engines/hls/engine-background-video.d.ts +158 -0
  525. package/dist/dev/playback/engines/hls/engine-background-video.d.ts.map +1 -0
  526. package/dist/dev/playback/engines/hls/engine-background-video.js +92 -0
  527. package/dist/dev/playback/engines/hls/engine-background-video.js.map +1 -0
  528. package/dist/dev/playback/engines/hls/engine.d.ts +329 -0
  529. package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -0
  530. package/dist/dev/playback/engines/hls/engine.js +147 -0
  531. package/dist/dev/playback/engines/hls/engine.js.map +1 -0
  532. package/dist/dev/playback/primitives/derive-start-media-time.d.ts +16 -0
  533. package/dist/dev/playback/primitives/derive-start-media-time.d.ts.map +1 -0
  534. package/dist/dev/playback/primitives/error-messages.js +51 -0
  535. package/dist/dev/playback/primitives/error-messages.js.map +1 -0
  536. package/dist/dev/playback/primitives/failover-fetch.js +42 -0
  537. package/dist/dev/playback/primitives/failover-fetch.js.map +1 -0
  538. package/dist/dev/playback/primitives/head-peek.js +44 -0
  539. package/dist/dev/playback/primitives/head-peek.js.map +1 -0
  540. package/dist/dev/playback/primitives/live-window.js +66 -0
  541. package/dist/dev/playback/primitives/live-window.js.map +1 -0
  542. package/dist/dev/playback/primitives/relocation-pipelines.js +203 -0
  543. package/dist/dev/playback/primitives/relocation-pipelines.js.map +1 -0
  544. package/dist/dev/playback/primitives/report-track-conditions.d.ts +11 -0
  545. package/dist/dev/playback/primitives/report-track-conditions.d.ts.map +1 -0
  546. package/dist/dev/playback/primitives/report-track-conditions.js +86 -0
  547. package/dist/dev/playback/primitives/report-track-conditions.js.map +1 -0
  548. package/dist/dev/playback/primitives/segment-load-pipeline.d.ts +2 -0
  549. package/dist/dev/playback/primitives/segment-load-pipeline.js +96 -0
  550. package/dist/dev/playback/primitives/segment-load-pipeline.js.map +1 -0
  551. package/dist/dev/playback/primitives/selection-rules.d.ts +38 -0
  552. package/dist/dev/playback/primitives/selection-rules.d.ts.map +1 -0
  553. package/dist/dev/playback/primitives/selection-rules.js +86 -0
  554. package/dist/dev/playback/primitives/selection-rules.js.map +1 -0
  555. package/dist/dev/playback/primitives/source-buffer-messages.d.ts +43 -0
  556. package/dist/dev/playback/primitives/source-buffer-messages.d.ts.map +1 -0
  557. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts +65 -0
  558. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts.map +1 -0
  559. package/dist/dev/playback/primitives/text-segment-load-pipeline.js +37 -0
  560. package/dist/dev/playback/primitives/text-segment-load-pipeline.js.map +1 -0
  561. package/dist/dev/playback/primitives/text-track-messages.d.ts +14 -0
  562. package/dist/dev/playback/primitives/text-track-messages.d.ts.map +1 -0
  563. package/dist/dev/playback/primitives/track-types.js +61 -0
  564. package/dist/dev/playback/primitives/track-types.js.map +1 -0
  565. package/package.json +63 -26
  566. package/dist/adapter-CMw-rvbk.js +0 -3512
  567. package/dist/adapter-CMw-rvbk.js.map +0 -1
  568. package/dist/adapter-l8iJN_GI.d.ts +0 -689
  569. package/dist/adapter-l8iJN_GI.d.ts.map +0 -1
  570. package/dist/dom/index.d.ts +0 -243
  571. package/dist/dom/index.d.ts.map +0 -1
  572. package/dist/dom/index.js +0 -38
  573. package/dist/dom/index.js.map +0 -1
  574. package/dist/dom/playback-engine/index.d.ts +0 -2
  575. package/dist/dom/playback-engine/index.js +0 -4
  576. package/dist/index.d.ts +0 -13
  577. package/dist/index.d.ts.map +0 -1
  578. package/dist/index.js +0 -3
  579. package/dist/src-DRsS_H80.js +0 -14
  580. package/dist/src-DRsS_H80.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-text-tracks.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/sync-text-tracks.ts"],"sourcesContent":["/**\n * **Own the text-track slots on the host media element, mirroring the SPF\n * model.** When a presentation is resolved and a media element is\n * available, allocate one slot in `mediaElement.textTracks` per model text\n * track — via creating `<track>` children, since that's the only spec\n * mechanism for adding *and* removing entries to `textTracks` (no\n * `removeTextTrack` API exists). Once slots are provisioned, mirror the\n * resolved `selectedTextTrackId` into their `mode`s (one-way: state → DOM),\n * and propagate user-initiated DOM `change` events back to\n * `userTextTrackSelection` — the standing *intent* (a language-based partial,\n * or `'off'`) that `switchTextTrack` resolves into `selectedTextTrackId`. So\n * non-SPF consumers (host-page captions buttons, browser native UI, video.js\n * store) drive selection by expressing intent, not by writing the resolved id.\n *\n * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'sync-active'`):\n * the entry allocates the slots, applies the initial selection, attaches\n * the `change` listener, and opens a brief Chromium settling-window guard —\n * all transition-driven, fire-once on state entry, with paired cleanup on\n * state exit. A single `effects:` mirrors subsequent\n * `selectedTextTrackId` changes into `mode`s; that's the only\n * continuous-reactivity concern.\n *\n * State-exit cleanup also sends a `'clear'` message to the\n * `TextTracksActor` so its cue+segment cache (keyed by trackId) is\n * dropped alongside the DOM `<track>` slots. The actor itself is owned\n * by `setupTextTrackActors` and bound to mediaElement, not presentation,\n * so it survives source resets; clearing its context here keeps the\n * cache consistent with the DOM. Without this, a subsequent\n * presentation reusing a trackId would have `getSegmentsToLoad` treat\n * its segments as already-buffered and skip loading them.\n *\n * Single-writer separation: `selectedTextTrackId` is the resolved *output*\n * owned solely by `switchTextTrack`; this behavior only reads it (to mirror\n * modes). The write path here is `userTextTrackSelection` — the user-intent\n * *input* — so DOM action and the resolver never contend for one slot. The\n * intent isn't cleared on source unload (it's a standing preference, like\n * `userAudioTrackSelection`); `'off'` is written when the user disables all\n * tracks via native UI.\n *\n * Echo guard: `selectedTextTrackId` is exactly the id this behavior last drove\n * into the DOM, so a `change` event still showing it is our own echo (or a\n * resolver-driven correction — e.g. the picked track's CDN failed and the\n * resolver disabled it) and is ignored, never written back as a spurious user\n * action. Only a showing id that *differs* from the resolved id is a real user\n * pick. The settling-window guard additionally swallows Chromium's init-time\n * auto-selection before the resolved selection has settled.\n */\n\nimport { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, peek, type ReadonlySignal, type Signal } from '../../../core/signals/primitives';\nimport { syncTextTrackModes } from '../../../media/dom/text/text-track-slots';\nimport type { MaybeResolvedPresentation, PartiallyResolvedTextTrack, TextTrack } from '../../../media/types';\nimport { getTracksByType } from '../../../media/utils/tracks';\nimport type { TextTracksActor } from '../../actors/text-tracks';\n\ntype SyncTextTracksFsmState = 'preconditions-unmet' | 'sync-active';\n\nexport interface SyncTextTracksConfig {\n /**\n * Create and append SPF-owned `<track>` slots on `mediaElement`, one per\n * model text track. Implementation tags each element so the read/remove\n * helpers can scope to SPF-owned slots. **Required** — the behavior is\n * DOM-binding-neutral and the composing engine supplies the integration.\n */\n addSubtitlesTracksToMedia: (\n mediaElement: HTMLMediaElement,\n modelTextTracks: readonly (PartiallyResolvedTextTrack | TextTrack)[]\n ) => void;\n /**\n * Return the SPF-owned subtitle/caption `TextTrack` currently in `'showing'`\n * mode, or `undefined` if none. Used by the DOM `change` bridge to mirror\n * native-UI selection back into `selectedTextTrackId`.\n */\n getShowingSubtitlesTrackFromMedia: (mediaElement: HTMLMediaElement) => globalThis.TextTrack | undefined;\n /**\n * Remove every SPF-owned `<track>` child from `mediaElement`. Called on\n * state exit (source unload, behavior destroy) to evict slots.\n */\n removeAllSubtitlesTracksFromMedia: (mediaElement: HTMLMediaElement) => void;\n}\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaElement: HTMLMediaElement | undefined\n): SyncTextTracksFsmState {\n if (!mediaElement || !presentation) return 'preconditions-unmet';\n return getTracksByType(presentation, 'text').length > 0 ? 'sync-active' : 'preconditions-unmet';\n}\n\n/**\n * Map the DOM-showing track back to standing user intent. No showing track is an\n * explicit `'off'`. Otherwise prefer a language-based partial (so the pick\n * persists across source changes); fall back to `{ id }` for a track without a\n * language (precise within a source, just not portable).\n */\nfunction deriveTextTrackIntent(\n showingId: string | undefined,\n modelTextTracks: readonly (PartiallyResolvedTextTrack | TextTrack)[]\n): Partial<TextTrack> | 'off' {\n if (!showingId) return 'off';\n const language = modelTextTracks.find((track) => track.id === showingId)?.language;\n return language ? { language } : { id: showingId };\n}\n\nfunction syncTextTracksSetup({\n state,\n context,\n config,\n}: {\n state: {\n presentation: ReadonlySignal<MaybeResolvedPresentation | undefined>;\n // Read-only here: the resolved output owned by `switchTextTrack`, mirrored\n // into DOM modes and used as the echo-guard reference.\n selectedTextTrackId: ReadonlySignal<string | undefined>;\n // The write path: standing user intent the DOM bridge feeds.\n userTextTrackSelection: Signal<Partial<TextTrack> | 'off' | undefined>;\n };\n context: {\n mediaElement: ReadonlySignal<HTMLMediaElement | undefined>;\n textTracksActor: ReadonlySignal<TextTracksActor<VTTCue> | undefined>;\n };\n config: SyncTextTracksConfig;\n}): Reactor<SyncTextTracksFsmState | 'destroying' | 'destroyed'> {\n const { addSubtitlesTracksToMedia, getShowingSubtitlesTrackFromMedia, removeAllSubtitlesTracksFromMedia } = config;\n\n const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaElement.get()));\n\n return createMachineReactor<SyncTextTracksFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n 'sync-active': {\n // Allocate slots, apply the initial selection, attach the change\n // listener, and open the settling window — all transition-driven\n // (fire once per state entry). Entry is auto-untracked, so `.get()`\n // here doesn't subscribe; the returned cleanup detaches and evicts\n // on state exit (src reset through 'preconditions-unmet') and on\n // destroy.\n entry: () => {\n const mediaElement = context.mediaElement.get()!;\n // `getTracksByType('text', ...)` returns text tracks only — the\n // selection-set filter inside the helper ensures that — but its\n // declared return is the wide track union. Mirror the cast\n // pattern used by `track-switching` for the video branch.\n const modelTextTracks = getTracksByType(state.presentation.get()!, 'text') as readonly (\n | PartiallyResolvedTextTrack\n | TextTrack\n )[];\n\n addSubtitlesTracksToMedia(mediaElement, modelTextTracks);\n // Apply our selection synchronously so the change-event tasks\n // these mode writes queue land in the macrotask queue *before*\n // the settling-close `setTimeout(0)` queued below. This keeps\n // the settling window open long enough to swallow Chromium's\n // own auto-selection task (also scheduled in the next tick after\n // `<track>` insertion); if the order flipped, our handler would\n // treat Chromium's auto-pick as a user action. The `effects:`\n // block below handles subsequent `selectedTextTrackId` changes.\n syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());\n\n // Chromium re-applies its own selection across the next task tick\n // after `<track>` insertion (default-track auto-pick, language\n // preference). During this window, the `change` event may fire\n // with browser-chosen modes that don't reflect a real user action;\n // re-apply our modes silently rather than writing them back.\n let inSettlingWindow = true;\n const settlingTimeout = setTimeout(() => {\n inSettlingWindow = false;\n }, 0);\n\n const onChange = (): void => {\n if (inSettlingWindow) {\n syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());\n return;\n }\n const showingTrack = getShowingSubtitlesTrackFromMedia(mediaElement);\n // `showingTrack.id` matches the SPF id we set when the slot was\n // allocated. Empty-string ids fall through to `undefined`.\n const showingId = showingTrack?.id || undefined;\n // Echo guard: selectedTextTrackId is the id we last drove into the\n // DOM (mirror / resolver correction). A change still showing it is our\n // own echo — ignore it rather than write spurious intent.\n if (showingId === state.selectedTextTrackId.get()) return;\n // Genuine user action → write intent (resolved into selectedTextTrackId\n // by switchTextTrack), not the resolved id.\n state.userTextTrackSelection.set(deriveTextTrackIntent(showingId, modelTextTracks));\n };\n\n const unlisten = listen(mediaElement.textTracks, 'change', onChange);\n\n return () => {\n unlisten();\n clearTimeout(settlingTimeout);\n removeAllSubtitlesTracksFromMedia(mediaElement);\n // Clear the TextTracksActor's cue+segment cache, which is\n // keyed by trackId. If we don't, a subsequent presentation\n // reusing a trackId would have `getSegmentsToLoad` treat its\n // segments as already-buffered. The DOM cleanup above\n // already evicted the live cues; this drops the cache that\n // tracked them. The actor itself is owned by\n // `setupTextTrackActors` (mediaElement-bound lifecycle), so\n // we send rather than destroy.\n peek(context.textTracksActor)?.send({ type: 'clear' });\n };\n },\n\n // Mirror selection changes into mode. The state machine handles\n // mediaElement/presentation changes via 'preconditions-unmet'\n // round-trips, so we peek mediaElement here.\n effects: () => {\n const mediaElement = peek(context.mediaElement)!;\n syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());\n },\n },\n },\n });\n}\n\nexport const syncTextTracks = defineBehavior({\n stateKeys: ['presentation', 'selectedTextTrackId', 'userTextTrackSelection'],\n contextKeys: ['mediaElement', 'textTracksActor'],\n setup: syncTextTracksSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFA,SAAS,YACP,cACA,cACwB;CACxB,IAAI,CAAC,gBAAgB,CAAC,cAAc,OAAO;CAC3C,OAAO,gBAAgB,cAAc,MAAM,CAAC,CAAC,SAAS,IAAI,gBAAgB;AAC5E;;;;;;;AAQA,SAAS,sBACP,WACA,iBAC4B;CAC5B,IAAI,CAAC,WAAW,OAAO;CACvB,MAAM,WAAW,gBAAgB,MAAM,UAAU,MAAM,OAAO,SAAS,CAAC,EAAE;CAC1E,OAAO,WAAW,EAAE,SAAS,IAAI,EAAE,IAAI,UAAU;AACnD;AAEA,SAAS,oBAAoB,EAC3B,OACA,SACA,UAe+D;CAC/D,MAAM,EAAE,2BAA2B,mCAAmC,sCAAsC;CAE5G,MAAM,qBAAqB,eAAe,YAAY,MAAM,aAAa,IAAI,GAAG,QAAQ,aAAa,IAAI,CAAC,CAAC;CAE3G,OAAO,qBAA6C;EAClD,SAAS;EACT,eAAe,mBAAmB,IAAI;EACtC,QAAQ;GACN,uBAAuB,CAAC;GAExB,eAAe;IAOb,aAAa;KACX,MAAM,eAAe,QAAQ,aAAa,IAAI;KAK9C,MAAM,kBAAkB,gBAAgB,MAAM,aAAa,IAAI,GAAI,MAAM;KAKzE,0BAA0B,cAAc,eAAe;KASvD,mBAAmB,aAAa,YAAY,MAAM,oBAAoB,IAAI,CAAC;KAO3E,IAAI,mBAAmB;KACvB,MAAM,kBAAkB,iBAAiB;MACvC,mBAAmB;KACrB,GAAG,CAAC;KAEJ,MAAM,iBAAuB;MAC3B,IAAI,kBAAkB;OACpB,mBAAmB,aAAa,YAAY,MAAM,oBAAoB,IAAI,CAAC;OAC3E;MACF;MAIA,MAAM,YAHe,kCAAkC,YAG1B,CAAC,EAAE,MAAM,KAAA;MAItC,IAAI,cAAc,MAAM,oBAAoB,IAAI,GAAG;MAGnD,MAAM,uBAAuB,IAAI,sBAAsB,WAAW,eAAe,CAAC;KACpF;KAEA,MAAM,WAAW,OAAO,aAAa,YAAY,UAAU,QAAQ;KAEnE,aAAa;MACX,SAAS;MACT,aAAa,eAAe;MAC5B,kCAAkC,YAAY;MAS9C,KAAK,QAAQ,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;KACvD;IACF;IAKA,eAAe;KAEb,mBADqB,KAAK,QAAQ,YACJ,CAAC,CAAC,YAAY,MAAM,oBAAoB,IAAI,CAAC;IAC7E;GACF;EACF;CACF,CAAC;AACH;AAEA,MAAa,iBAAiB,eAAe;CAC3C,WAAW;EAAC;EAAgB;EAAuB;CAAwB;CAC3E,aAAa,CAAC,gBAAgB,iBAAiB;CAC/C,OAAO;AACT,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
2
+ import { effect } from "../../../core/signals/effect.js";
3
+ import { listen } from "@videojs/utils/dom";
4
+ //#region src/playback/behaviors/dom/track-current-time.ts
5
+ /**
6
+ * Mirror `mediaElement.currentTime` into reactive state. Listens for:
7
+ * - `timeupdate` — fires during playback (~4 Hz)
8
+ * - `seeking` — fires when a seek begins; per spec, `currentTime` is already
9
+ * at the new position when this event dispatches, so buffer management can
10
+ * react immediately rather than waiting for `timeupdate`, which does not
11
+ * fire while paused.
12
+ * - `emptied` — fires when the resource selection algorithm tears down the
13
+ * current media (e.g. a new `src` is set on the same element). Re-syncs so
14
+ * downstream state doesn't retain the stale playback position from the
15
+ * previous source when the engine is reused across src changes.
16
+ *
17
+ * Also syncs immediately when a media element becomes available.
18
+ *
19
+ * When no media element is attached, writes `config.defaultCurrentTime`
20
+ * (default-default `0`, matching the HTMLMediaElement spec) so consumers
21
+ * always see a defined position. Read-only mirror; does not push
22
+ * `state.currentTime` back to the element.
23
+ */
24
+ function trackCurrentTimeSetup({ state, context, config }) {
25
+ const defaultCurrentTime = config?.defaultCurrentTime ?? 0;
26
+ return effect(() => {
27
+ const mediaElement = context.mediaElement.get();
28
+ if (!mediaElement) {
29
+ state.currentTime.set(defaultCurrentTime);
30
+ return;
31
+ }
32
+ const sync = () => state.currentTime.set(mediaElement.currentTime);
33
+ sync();
34
+ const removeEmptied = listen(mediaElement, "emptied", sync);
35
+ const removeTimeupdate = listen(mediaElement, "timeupdate", sync);
36
+ const removeSeeking = listen(mediaElement, "seeking", sync);
37
+ return () => {
38
+ removeEmptied();
39
+ removeTimeupdate();
40
+ removeSeeking();
41
+ };
42
+ });
43
+ }
44
+ const trackCurrentTime = defineBehavior({
45
+ stateKeys: ["currentTime"],
46
+ contextKeys: ["mediaElement"],
47
+ setup: trackCurrentTimeSetup
48
+ });
49
+ //#endregion
50
+ export { trackCurrentTime };
51
+
52
+ //# sourceMappingURL=track-current-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-current-time.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-current-time.ts"],"sourcesContent":["/**\n * Mirror `mediaElement.currentTime` into reactive state. Listens for:\n * - `timeupdate` — fires during playback (~4 Hz)\n * - `seeking` — fires when a seek begins; per spec, `currentTime` is already\n * at the new position when this event dispatches, so buffer management can\n * react immediately rather than waiting for `timeupdate`, which does not\n * fire while paused.\n * - `emptied` — fires when the resource selection algorithm tears down the\n * current media (e.g. a new `src` is set on the same element). Re-syncs so\n * downstream state doesn't retain the stale playback position from the\n * previous source when the engine is reused across src changes.\n *\n * Also syncs immediately when a media element becomes available.\n *\n * When no media element is attached, writes `config.defaultCurrentTime`\n * (default-default `0`, matching the HTMLMediaElement spec) so consumers\n * always see a defined position. Read-only mirror; does not push\n * `state.currentTime` back to the element.\n */\nimport { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport type { ReadonlySignal, Signal } from '../../../core/signals/primitives';\n\nexport interface CurrentTimeState {\n currentTime?: number;\n}\n\nexport interface CurrentTimeContext {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\nexport interface TrackCurrentTimeConfig {\n /**\n * Value written to `state.currentTime` when no media element is attached.\n * Defaults to `0` — the HTMLMediaElement spec default.\n */\n defaultCurrentTime?: number;\n}\n\nfunction trackCurrentTimeSetup({\n state,\n context,\n config,\n}: {\n state: { currentTime: Signal<CurrentTimeState['currentTime']> };\n context: { mediaElement: ReadonlySignal<CurrentTimeContext['mediaElement']> };\n config?: TrackCurrentTimeConfig;\n}): () => void {\n const defaultCurrentTime = config?.defaultCurrentTime ?? 0;\n\n return effect(() => {\n const mediaElement = context.mediaElement.get();\n if (!mediaElement) {\n state.currentTime.set(defaultCurrentTime);\n return;\n }\n\n const sync = () => state.currentTime.set(mediaElement.currentTime);\n sync();\n const removeEmptied = listen(mediaElement, 'emptied', sync);\n const removeTimeupdate = listen(mediaElement, 'timeupdate', sync);\n const removeSeeking = listen(mediaElement, 'seeking', sync);\n return () => {\n removeEmptied();\n removeTimeupdate();\n removeSeeking();\n };\n });\n}\n\nexport const trackCurrentTime = defineBehavior({\n stateKeys: ['currentTime'],\n contextKeys: ['mediaElement'],\n setup: trackCurrentTimeSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAS,sBAAsB,EAC7B,OACA,SACA,UAKa;CACb,MAAM,qBAAqB,QAAQ,sBAAsB;CAEzD,OAAO,aAAa;EAClB,MAAM,eAAe,QAAQ,aAAa,IAAI;EAC9C,IAAI,CAAC,cAAc;GACjB,MAAM,YAAY,IAAI,kBAAkB;GACxC;EACF;EAEA,MAAM,aAAa,MAAM,YAAY,IAAI,aAAa,WAAW;EACjE,KAAK;EACL,MAAM,gBAAgB,OAAO,cAAc,WAAW,IAAI;EAC1D,MAAM,mBAAmB,OAAO,cAAc,cAAc,IAAI;EAChE,MAAM,gBAAgB,OAAO,cAAc,WAAW,IAAI;EAC1D,aAAa;GACX,cAAc;GACd,iBAAiB;GACjB,cAAc;EAChB;CACF,CAAC;AACH;AAEA,MAAa,mBAAmB,eAAe;CAC7C,WAAW,CAAC,aAAa;CACzB,aAAa,CAAC,cAAc;CAC5B,OAAO;AACT,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { computed } from "../../../core/signals/primitives.js";
2
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
3
+ import { createMachineReactor } from "../../../core/reactors/create-machine-reactor.js";
4
+ import { listen } from "@videojs/utils/dom";
5
+ //#region src/playback/behaviors/dom/track-load-triggers.ts
6
+ /**
7
+ * Slot-driven FSM. The slot `loadActivated` is the canonical externally-
8
+ * observable state; `deriveState` reads it (and the preconditions) to
9
+ * derive the local FSM state. The slot is *both* the state and the data —
10
+ * no separate internal bookkeeping.
11
+ *
12
+ * ```
13
+ * 'preconditions-unmet' ⟷ 'monitoring' ⟷ 'load-active'
14
+ *
15
+ * preconditions-unmet → monitoring element + URL appear
16
+ * monitoring → load-active slot flips true (listener fires
17
+ * or external write)
18
+ * load-active → monitoring within-state cleanup resets slot
19
+ * (URL or element identity change)
20
+ * any → preconditions-unmet element or URL → undefined
21
+ *
22
+ * any state → destroying → destroyed on destroy()
23
+ * ```
24
+ */
25
+ function deriveState(presentation, mediaElement, loadActivated) {
26
+ if (!mediaElement || !presentation?.url) return "preconditions-unmet";
27
+ if (loadActivated) return "load-active";
28
+ return "monitoring";
29
+ }
30
+ /**
31
+ * Track preload-overriding events per source.
32
+ *
33
+ * Writes `state.loadActivated = true` the first time a `play` or `seeking`
34
+ * event fires on the attached media element for the current source — or
35
+ * immediately on entry if the element is already committed to loading
36
+ * (`el.autoplay`, `!el.paused`, or `el.seeking`), covering autoplay,
37
+ * native-controls, and direct-DOM-`play()` scenarios.
38
+ *
39
+ * Sticky-true *within a source identity*: subsequent play/pause/seek
40
+ * cycles don't flip back. Source identity = (mediaElement, presentation
41
+ * URL). Either changing — including direct in-place swap with no
42
+ * `undefined` intermediate — resets the slot to `false`.
43
+ *
44
+ * Multi-writer with `hls/adapter.ts:play()` (which writes `true` directly
45
+ * on programmatic play) is intentional — different domains. The adapter
46
+ * records programmatic intent; this behavior is the DOM-side observer.
47
+ * Pre-existing `true` writes are honored because `deriveState` reads the
48
+ * slot — a `true` value routes directly to `'load-active'` without
49
+ * entering `'monitoring'`.
50
+ *
51
+ * @example
52
+ * const reactor = trackLoadTriggers.setup({ state, context });
53
+ * // later:
54
+ * reactor.destroy();
55
+ */
56
+ function trackLoadTriggersSetup({ state, context }) {
57
+ const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaElement.get(), state.loadActivated.get()));
58
+ const urlSignal = computed(() => state.presentation.get()?.url);
59
+ return createMachineReactor({
60
+ initial: "preconditions-unmet",
61
+ monitor: () => derivedStateSignal.get(),
62
+ states: {
63
+ "preconditions-unmet": {},
64
+ monitoring: { effects: () => {
65
+ const el = context.mediaElement.get();
66
+ const setLoadActivated = () => state.loadActivated.set(true);
67
+ if (el.autoplay || !el.paused || el.seeking) {
68
+ setLoadActivated();
69
+ return;
70
+ }
71
+ const cleanupPlay = listen(el, "play", setLoadActivated);
72
+ const cleanupSeeking = listen(el, "seeking", setLoadActivated);
73
+ return () => {
74
+ cleanupPlay();
75
+ cleanupSeeking();
76
+ };
77
+ } },
78
+ "load-active": { effects: () => {
79
+ context.mediaElement.get();
80
+ urlSignal.get();
81
+ return () => state.loadActivated.set(false);
82
+ } }
83
+ }
84
+ });
85
+ }
86
+ const trackLoadTriggers = defineBehavior({
87
+ stateKeys: ["loadActivated", "presentation"],
88
+ contextKeys: ["mediaElement"],
89
+ setup: trackLoadTriggersSetup
90
+ });
91
+ //#endregion
92
+ export { trackLoadTriggers };
93
+
94
+ //# sourceMappingURL=track-load-triggers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-load-triggers.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-load-triggers.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal, type Signal } from '../../../core/signals/primitives';\nimport type { MaybeResolvedPresentation } from '../../../media/types';\n\n/**\n * State shape for load-trigger tracking.\n */\nexport interface LoadTriggersState {\n /**\n * Sticky-true per source. Flips to `true` the first time an event occurs\n * that should switch loading behavior from preload-based to standard load\n * (currently DOM `play` / `seeking`). Reset to `false` on source change.\n *\n * Read by `resolvePresentation` and `loadVideoSegments` / `loadAudioSegments`\n * to mirror native `HTMLMediaElement` preload semantics — only meaningful\n * when `preload !== 'auto'`, which already loads fully regardless.\n */\n loadActivated?: boolean;\n /** Current presentation — URL is used to detect source changes. */\n presentation?: MaybeResolvedPresentation;\n}\n\n/**\n * Context shape for load-trigger tracking.\n */\nexport interface LoadTriggersContext {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\ntype LoadTriggersFsmState = 'preconditions-unmet' | 'monitoring' | 'load-active';\n\n/**\n * Slot-driven FSM. The slot `loadActivated` is the canonical externally-\n * observable state; `deriveState` reads it (and the preconditions) to\n * derive the local FSM state. The slot is *both* the state and the data —\n * no separate internal bookkeeping.\n *\n * ```\n * 'preconditions-unmet' ⟷ 'monitoring' ⟷ 'load-active'\n *\n * preconditions-unmet → monitoring element + URL appear\n * monitoring → load-active slot flips true (listener fires\n * or external write)\n * load-active → monitoring within-state cleanup resets slot\n * (URL or element identity change)\n * any → preconditions-unmet element or URL → undefined\n *\n * any state → destroying → destroyed on destroy()\n * ```\n */\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaElement: HTMLMediaElement | undefined,\n loadActivated: boolean | undefined\n): LoadTriggersFsmState {\n if (!mediaElement || !presentation?.url) return 'preconditions-unmet';\n if (loadActivated) return 'load-active';\n return 'monitoring';\n}\n\n/**\n * Track preload-overriding events per source.\n *\n * Writes `state.loadActivated = true` the first time a `play` or `seeking`\n * event fires on the attached media element for the current source — or\n * immediately on entry if the element is already committed to loading\n * (`el.autoplay`, `!el.paused`, or `el.seeking`), covering autoplay,\n * native-controls, and direct-DOM-`play()` scenarios.\n *\n * Sticky-true *within a source identity*: subsequent play/pause/seek\n * cycles don't flip back. Source identity = (mediaElement, presentation\n * URL). Either changing — including direct in-place swap with no\n * `undefined` intermediate — resets the slot to `false`.\n *\n * Multi-writer with `hls/adapter.ts:play()` (which writes `true` directly\n * on programmatic play) is intentional — different domains. The adapter\n * records programmatic intent; this behavior is the DOM-side observer.\n * Pre-existing `true` writes are honored because `deriveState` reads the\n * slot — a `true` value routes directly to `'load-active'` without\n * entering `'monitoring'`.\n *\n * @example\n * const reactor = trackLoadTriggers.setup({ state, context });\n * // later:\n * reactor.destroy();\n */\nfunction trackLoadTriggersSetup({\n state,\n context,\n}: {\n state: {\n loadActivated: Signal<LoadTriggersState['loadActivated']>;\n presentation: ReadonlySignal<LoadTriggersState['presentation']>;\n };\n context: { mediaElement: ReadonlySignal<LoadTriggersContext['mediaElement']> };\n}): Reactor<LoadTriggersFsmState | 'destroying' | 'destroyed'> {\n const derivedStateSignal = computed(() =>\n deriveState(state.presentation.get(), context.mediaElement.get(), state.loadActivated.get())\n );\n // Sparse-firing source-URL signal. Emits whenever `presentation?.url`\n // resolves to a different value — covering both URL string changes\n // (url_a → url_b) and transitions where the URL becomes defined or\n // undefined (presentation set, cleared, or replaced without a URL\n // field). Does NOT emit when `state.presentation` is rewritten with\n // the same URL: manifest parsing, duration calc, track resolution,\n // and other enrichment paths re-write the presentation object\n // frequently — tracking raw `state.presentation` here would spuriously\n // reset the 'load-active' slot.\n const urlSignal = computed(() => state.presentation.get()?.url);\n\n return createMachineReactor<LoadTriggersFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n monitoring: {\n // effects (not entry) so element-identity change within this state\n // re-fires and re-attaches listeners to the new element. The monitor\n // guarantees mediaElement is truthy while we're in this state.\n effects: () => {\n const el = context.mediaElement.get()!;\n\n const setLoadActivated = () => state.loadActivated.set(true);\n if (el.autoplay || !el.paused || el.seeking) {\n setLoadActivated();\n return;\n }\n const cleanupPlay = listen(el, 'play', setLoadActivated);\n const cleanupSeeking = listen(el, 'seeking', setLoadActivated);\n return () => {\n cleanupPlay();\n cleanupSeeking();\n };\n },\n },\n\n 'load-active': {\n // effects-based cleanup (not entry) because the cleanup must fire on\n // BOTH state exit AND within-state identity change (url_a → url_b,\n // element_a → element_b — both truthy, no state transition).\n // Re-firing tracked reads → cleanup writes loadActivated=false →\n // deriveState (which reads loadActivated) returns 'monitoring' →\n // state transitions → fresh listeners attached for the new source.\n effects: () => {\n context.mediaElement.get();\n urlSignal.get();\n return () => state.loadActivated.set(false);\n },\n },\n },\n });\n}\n\nexport const trackLoadTriggers = defineBehavior({\n stateKeys: ['loadActivated', 'presentation'],\n contextKeys: ['mediaElement'],\n setup: trackLoadTriggersSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAS,YACP,cACA,cACA,eACsB;CACtB,IAAI,CAAC,gBAAgB,CAAC,cAAc,KAAK,OAAO;CAChD,IAAI,eAAe,OAAO;CAC1B,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,uBAAuB,EAC9B,OACA,WAO6D;CAC7D,MAAM,qBAAqB,eACzB,YAAY,MAAM,aAAa,IAAI,GAAG,QAAQ,aAAa,IAAI,GAAG,MAAM,cAAc,IAAI,CAAC,CAC7F;CAUA,MAAM,YAAY,eAAe,MAAM,aAAa,IAAI,CAAC,EAAE,GAAG;CAE9D,OAAO,qBAA2C;EAChD,SAAS;EACT,eAAe,mBAAmB,IAAI;EACtC,QAAQ;GACN,uBAAuB,CAAC;GAExB,YAAY,EAIV,eAAe;IACb,MAAM,KAAK,QAAQ,aAAa,IAAI;IAEpC,MAAM,yBAAyB,MAAM,cAAc,IAAI,IAAI;IAC3D,IAAI,GAAG,YAAY,CAAC,GAAG,UAAU,GAAG,SAAS;KAC3C,iBAAiB;KACjB;IACF;IACA,MAAM,cAAc,OAAO,IAAI,QAAQ,gBAAgB;IACvD,MAAM,iBAAiB,OAAO,IAAI,WAAW,gBAAgB;IAC7D,aAAa;KACX,YAAY;KACZ,eAAe;IACjB;GACF,EACF;GAEA,eAAe,EAOb,eAAe;IACb,QAAQ,aAAa,IAAI;IACzB,UAAU,IAAI;IACd,aAAa,MAAM,cAAc,IAAI,KAAK;GAC5C,EACF;EACF;CACF,CAAC;AACH;AAEA,MAAa,oBAAoB,eAAe;CAC9C,WAAW,CAAC,iBAAiB,cAAc;CAC3C,aAAa,CAAC,cAAc;CAC5B,OAAO;AACT,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
2
+ import { effect } from "../../../core/signals/effect.js";
3
+ import { listen } from "@videojs/utils/dom";
4
+ //#region src/playback/behaviors/dom/track-playback-rate.ts
5
+ /**
6
+ * Mirror `mediaElement.playbackRate` into reactive state. On each `ratechange`
7
+ * event, write the new value to `state.playbackRate`. Also syncs immediately
8
+ * when a media element becomes available so consumers don't wait for the first
9
+ * event.
10
+ *
11
+ * When no media element is attached, writes `config.defaultPlaybackRate`
12
+ * (default-default `1`, matching the HTMLMediaElement spec) so consumers
13
+ * always see the rate a freshly attached element would have. Read-only mirror;
14
+ * does not push `state.playbackRate` back to the element.
15
+ */
16
+ function trackPlaybackRateSetup({ state, context, config }) {
17
+ const defaultPlaybackRate = config?.defaultPlaybackRate ?? 1;
18
+ return effect(() => {
19
+ const mediaElement = context.mediaElement.get();
20
+ if (!mediaElement) {
21
+ state.playbackRate.set(defaultPlaybackRate);
22
+ return;
23
+ }
24
+ const sync = () => state.playbackRate.set(mediaElement.playbackRate);
25
+ sync();
26
+ return listen(mediaElement, "ratechange", sync);
27
+ });
28
+ }
29
+ const trackPlaybackRate = defineBehavior({
30
+ stateKeys: ["playbackRate"],
31
+ contextKeys: ["mediaElement"],
32
+ setup: trackPlaybackRateSetup
33
+ });
34
+ //#endregion
35
+ export { trackPlaybackRate };
36
+
37
+ //# sourceMappingURL=track-playback-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-rate.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-playback-rate.ts"],"sourcesContent":["/**\n * Mirror `mediaElement.playbackRate` into reactive state. On each `ratechange`\n * event, write the new value to `state.playbackRate`. Also syncs immediately\n * when a media element becomes available so consumers don't wait for the first\n * event.\n *\n * When no media element is attached, writes `config.defaultPlaybackRate`\n * (default-default `1`, matching the HTMLMediaElement spec) so consumers\n * always see the rate a freshly attached element would have. Read-only mirror;\n * does not push `state.playbackRate` back to the element.\n */\nimport { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport type { ReadonlySignal, Signal } from '../../../core/signals/primitives';\n\nexport interface PlaybackRateState {\n playbackRate?: number;\n}\n\nexport interface PlaybackRateContext {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\nexport interface TrackPlaybackRateConfig {\n /**\n * Value written to `state.playbackRate` when no media element is attached.\n * Defaults to `1` — the HTMLMediaElement spec default.\n */\n defaultPlaybackRate?: number;\n}\n\nfunction trackPlaybackRateSetup({\n state,\n context,\n config,\n}: {\n state: { playbackRate: Signal<PlaybackRateState['playbackRate']> };\n context: { mediaElement: ReadonlySignal<PlaybackRateContext['mediaElement']> };\n config?: TrackPlaybackRateConfig;\n}): () => void {\n const defaultPlaybackRate = config?.defaultPlaybackRate ?? 1;\n\n return effect(() => {\n const mediaElement = context.mediaElement.get();\n if (!mediaElement) {\n state.playbackRate.set(defaultPlaybackRate);\n return;\n }\n\n const sync = () => state.playbackRate.set(mediaElement.playbackRate);\n sync();\n return listen(mediaElement, 'ratechange', sync);\n });\n}\n\nexport const trackPlaybackRate = defineBehavior({\n stateKeys: ['playbackRate'],\n contextKeys: ['mediaElement'],\n setup: trackPlaybackRateSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;AAgCA,SAAS,uBAAuB,EAC9B,OACA,SACA,UAKa;CACb,MAAM,sBAAsB,QAAQ,uBAAuB;CAE3D,OAAO,aAAa;EAClB,MAAM,eAAe,QAAQ,aAAa,IAAI;EAC9C,IAAI,CAAC,cAAc;GACjB,MAAM,aAAa,IAAI,mBAAmB;GAC1C;EACF;EAEA,MAAM,aAAa,MAAM,aAAa,IAAI,aAAa,YAAY;EACnE,KAAK;EACL,OAAO,OAAO,cAAc,cAAc,IAAI;CAChD,CAAC;AACH;AAEA,MAAa,oBAAoB,eAAe;CAC9C,WAAW,CAAC,cAAc;CAC1B,aAAa,CAAC,cAAc;CAC5B,OAAO;AACT,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
2
+ import { effect } from "../../../core/signals/effect.js";
3
+ import { scaleResolution } from "../../../media/primitives/resolution.js";
4
+ import { observeElementSize, observeRenderedSize } from "@videojs/utils/dom";
5
+ import { shallowEqual } from "@videojs/utils/object";
6
+ //#region src/playback/behaviors/dom/track-player-resolution.ts
7
+ /**
8
+ * Mirror the player element's rendered pixel dimensions into reactive state, so
9
+ * a rendition cap can narrow candidates to what the element can actually show
10
+ * without reading the DOM at pick time — which would make the picker impure, and
11
+ * would never re-pick when the element resized.
12
+ *
13
+ * The player-element half of the caps in
14
+ * `internal/design/spf/features/rendition-selection-caps.md`, and the tighter
15
+ * half: a small embed on a large display is capped by its own box rather than by
16
+ * the screen behind it (`trackScreenResolution`).
17
+ *
18
+ * Reported as a width and a height in device pixels — the same units, and for the
19
+ * same reason, as `media/dom/screen`'s reading: the cap compares against real
20
+ * track dimensions, and a `"720p"`-style tier only describes a track once you
21
+ * assume its aspect ratio.
22
+ *
23
+ * `undefined` where there is nothing to measure — no element attached, or one
24
+ * that isn't being rendered (detached, `display: none`, not yet laid out) — which
25
+ * is the value the cap reads as "don't cap".
26
+ */
27
+ function trackPlayerResolutionSetup({ state, context, config = {} }) {
28
+ const { capRenditionToPlayerSize = true, useDevicePixelRatio = true } = config;
29
+ return effect(() => {
30
+ const mediaElement = context.mediaElement.get();
31
+ let current;
32
+ state.playerResolution.set(current);
33
+ if (!capRenditionToPlayerSize || !mediaElement) return;
34
+ const write = (size) => {
35
+ const next = scaleResolution(size, size.scale);
36
+ if (shallowEqual(current, next)) return;
37
+ current = next;
38
+ state.playerResolution.set(next);
39
+ };
40
+ return useDevicePixelRatio ? observeRenderedSize(mediaElement, write) : observeElementSize(mediaElement, write);
41
+ });
42
+ }
43
+ /**
44
+ * Track the player element's rendered resolution in `state.playerResolution`.
45
+ *
46
+ * @example
47
+ * const cleanup = trackPlayerResolution.setup({ state, context });
48
+ */
49
+ const trackPlayerResolution = defineBehavior({
50
+ stateKeys: ["playerResolution"],
51
+ contextKeys: ["mediaElement"],
52
+ setup: trackPlayerResolutionSetup
53
+ });
54
+ //#endregion
55
+ export { trackPlayerResolution };
56
+
57
+ //# sourceMappingURL=track-player-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-player-resolution.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-player-resolution.ts"],"sourcesContent":["/**\n * Mirror the player element's rendered pixel dimensions into reactive state, so\n * a rendition cap can narrow candidates to what the element can actually show\n * without reading the DOM at pick time — which would make the picker impure, and\n * would never re-pick when the element resized.\n *\n * The player-element half of the caps in\n * `internal/design/spf/features/rendition-selection-caps.md`, and the tighter\n * half: a small embed on a large display is capped by its own box rather than by\n * the screen behind it (`trackScreenResolution`).\n *\n * Reported as a width and a height in device pixels — the same units, and for the\n * same reason, as `media/dom/screen`'s reading: the cap compares against real\n * track dimensions, and a `\"720p\"`-style tier only describes a track once you\n * assume its aspect ratio.\n *\n * `undefined` where there is nothing to measure — no element attached, or one\n * that isn't being rendered (detached, `display: none`, not yet laid out) — which\n * is the value the cap reads as \"don't cap\".\n */\n\nimport { type ElementSize, observeElementSize, observeRenderedSize } from '@videojs/utils/dom';\nimport { shallowEqual } from '@videojs/utils/object';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport type { ReadonlySignal, Signal } from '../../../core/signals/primitives';\nimport { type Resolution, scaleResolution } from '../../../media/primitives/resolution';\n\n/** A player element's rendered pixel dimensions. */\nexport type PlayerResolution = Resolution;\n\nexport interface PlayerResolutionState {\n playerResolution?: PlayerResolution;\n}\n\nexport interface PlayerResolutionContext {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\nexport interface TrackPlayerResolutionConfig {\n /**\n * Whether to cap renditions to the player's rendered size at all. `false`\n * measures nothing, which leaves `state.playerResolution` unset and the cap\n * inert. Defaults to `true`.\n *\n * Named for the policy rather than the measurement because it is the public\n * switch for the feature — the same one hls.js spells `capLevelToPlayerSize`\n * and the Mux Video element spells `cap-rendition-to-player-size`.\n */\n capRenditionToPlayerSize?: boolean;\n /**\n * Whether the reading is scaled into device pixels. Defaults to `true` — see\n * `ScreenResolutionOptions.useDevicePixelRatio` in `media/dom/screen.ts`,\n * including its note on page zoom being folded into the ratio outside WebKit.\n */\n useDevicePixelRatio?: boolean;\n}\n\nfunction trackPlayerResolutionSetup({\n state,\n context,\n config = {},\n}: {\n state: { playerResolution: Signal<PlayerResolutionState['playerResolution']> };\n context: { mediaElement: ReadonlySignal<PlayerResolutionContext['mediaElement']> };\n config?: TrackPlayerResolutionConfig;\n}): () => void {\n const { capRenditionToPlayerSize = true, useDevicePixelRatio = true } = config;\n\n return effect(() => {\n const mediaElement = context.mediaElement.get();\n // Clear before (re-)observing so the previous element's measurement can't\n // stand while the new one's first observation is in flight.\n let current: PlayerResolution | undefined;\n state.playerResolution.set(current);\n if (!capRenditionToPlayerSize || !mediaElement) return;\n\n // Compared rather than written straight through, since the slot holds an\n // object and would otherwise notify on identity alone: a resize that rounds\n // to the same device pixels is not a change worth re-running selection for.\n // Same reason `watchScreenResolution` compares its readings.\n const write = (size: ElementSize & { scale?: number }) => {\n const next = scaleResolution(size, size.scale);\n if (shallowEqual(current, next)) return;\n\n current = next;\n state.playerResolution.set(next);\n };\n\n return useDevicePixelRatio ? observeRenderedSize(mediaElement, write) : observeElementSize(mediaElement, write);\n });\n}\n\n/**\n * Track the player element's rendered resolution in `state.playerResolution`.\n *\n * @example\n * const cleanup = trackPlayerResolution.setup({ state, context });\n */\nexport const trackPlayerResolution = defineBehavior({\n stateKeys: ['playerResolution'],\n contextKeys: ['mediaElement'],\n setup: trackPlayerResolutionSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,SAAS,2BAA2B,EAClC,OACA,SACA,SAAS,CAAC,KAKG;CACb,MAAM,EAAE,2BAA2B,MAAM,sBAAsB,SAAS;CAExE,OAAO,aAAa;EAClB,MAAM,eAAe,QAAQ,aAAa,IAAI;EAG9C,IAAI;EACJ,MAAM,iBAAiB,IAAI,OAAO;EAClC,IAAI,CAAC,4BAA4B,CAAC,cAAc;EAMhD,MAAM,SAAS,SAA2C;GACxD,MAAM,OAAO,gBAAgB,MAAM,KAAK,KAAK;GAC7C,IAAI,aAAa,SAAS,IAAI,GAAG;GAEjC,UAAU;GACV,MAAM,iBAAiB,IAAI,IAAI;EACjC;EAEA,OAAO,sBAAsB,oBAAoB,cAAc,KAAK,IAAI,mBAAmB,cAAc,KAAK;CAChH,CAAC;AACH;;;;;;;AAQA,MAAa,wBAAwB,eAAe;CAClD,WAAW,CAAC,kBAAkB;CAC9B,aAAa,CAAC,cAAc;CAC5B,OAAO;AACT,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
2
+ import { watchScreenResolution } from "../../../media/dom/screen.js";
3
+ //#region src/playback/behaviors/dom/track-screen-resolution.ts
4
+ /**
5
+ * Mirror the screen's pixel dimensions into reactive state, so a rendition cap
6
+ * can narrow candidates to what the screen can actually show without reading the
7
+ * environment at pick time — which would make the picker impure, and would never
8
+ * re-pick when the screen changed.
9
+ *
10
+ * Populated at setup rather than on the first change: `watchScreenResolution`
11
+ * reports its starting value, so nothing downstream waits on a screen that may
12
+ * never move. `undefined` where there is no screen to read, which is the value a
13
+ * cap reads as "no cap" — see `getScreenResolution` on why that beats a zero.
14
+ *
15
+ * Reads no other slot, and has no source-identity reset: the screen is
16
+ * independent of the presentation, so a new `src` doesn't invalidate the reading.
17
+ */
18
+ function trackScreenResolutionSetup({ state, config }) {
19
+ return watchScreenResolution((resolution) => state.screenResolution.set(resolution), { useDevicePixelRatio: config?.useDevicePixelRatio ?? true });
20
+ }
21
+ const trackScreenResolution = defineBehavior({
22
+ stateKeys: ["screenResolution"],
23
+ contextKeys: [],
24
+ setup: trackScreenResolutionSetup
25
+ });
26
+ //#endregion
27
+ export { trackScreenResolution };
28
+
29
+ //# sourceMappingURL=track-screen-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-screen-resolution.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-screen-resolution.ts"],"sourcesContent":["/**\n * Mirror the screen's pixel dimensions into reactive state, so a rendition cap\n * can narrow candidates to what the screen can actually show without reading the\n * environment at pick time — which would make the picker impure, and would never\n * re-pick when the screen changed.\n *\n * Populated at setup rather than on the first change: `watchScreenResolution`\n * reports its starting value, so nothing downstream waits on a screen that may\n * never move. `undefined` where there is no screen to read, which is the value a\n * cap reads as \"no cap\" — see `getScreenResolution` on why that beats a zero.\n *\n * Reads no other slot, and has no source-identity reset: the screen is\n * independent of the presentation, so a new `src` doesn't invalidate the reading.\n */\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Signal } from '../../../core/signals/primitives';\nimport { type ScreenResolution, watchScreenResolution } from '../../../media/dom/screen';\n\nexport interface ScreenResolutionState {\n screenResolution?: ScreenResolution;\n}\n\nexport interface TrackScreenResolutionConfig {\n /**\n * Whether the reading is scaled into device pixels. Defaults to `true` — see\n * `ScreenResolutionOptions.useDevicePixelRatio`, including its note on page\n * zoom being folded into the ratio outside WebKit.\n */\n useDevicePixelRatio?: boolean;\n}\n\nfunction trackScreenResolutionSetup({\n state,\n config,\n}: {\n state: { screenResolution: Signal<ScreenResolutionState['screenResolution']> };\n config?: TrackScreenResolutionConfig;\n}): () => void {\n const useDevicePixelRatio = config?.useDevicePixelRatio ?? true;\n\n // No `effect` wrapper, unlike its `track*` siblings: they re-subscribe when\n // `context.mediaElement` changes, and this behavior has no reactive dependency\n // to re-run on. The watcher's own teardown is the whole cleanup.\n return watchScreenResolution((resolution) => state.screenResolution.set(resolution), { useDevicePixelRatio });\n}\n\nexport const trackScreenResolution = defineBehavior({\n stateKeys: ['screenResolution'],\n contextKeys: [],\n setup: trackScreenResolutionSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;AA+BA,SAAS,2BAA2B,EAClC,OACA,UAIa;CAMb,OAAO,uBAAuB,eAAe,MAAM,iBAAiB,IAAI,UAAU,GAAG,EAAE,qBAL3D,QAAQ,uBAAuB,KAKgD,CAAC;AAC9G;AAEA,MAAa,wBAAwB,eAAe;CAClD,WAAW,CAAC,kBAAkB;CAC9B,aAAa,CAAC;CACd,OAAO;AACT,CAAC"}
@@ -0,0 +1,113 @@
1
+ import { computed } from "../../../core/signals/primitives.js";
2
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
3
+ import { createMachineReactor } from "../../../core/reactors/create-machine-reactor.js";
4
+ import { waitForMediaSourceOpen } from "../../../media/dom/mse/mediasource-setup.js";
5
+ import { getMaxBufferedEnd, shouldUpdateDuration, waitForSourceBuffersReady } from "../../../media/dom/mse/duration.js";
6
+ //#region src/playback/behaviors/dom/update-mediasource-duration.ts
7
+ /**
8
+ * **Propagate `presentation.duration` to `mediaSource.duration` — exactly
9
+ * once per MediaSource.**
10
+ *
11
+ * Two paths, by whether the presentation is live:
12
+ *
13
+ * - **Live** (`presentation.duration === Infinity`): write `Infinity` once the
14
+ * MediaSource is open and no SourceBuffer is mid-append. No buffered clamp is
15
+ * needed (`Infinity` ≥ any range) and — unlike the finite case — it needn't
16
+ * precede the first append: `Infinity` overrides whatever finite live-edge
17
+ * value an append may have pinned (a finite duration would otherwise stall
18
+ * the stream once the window slides past it). The wait-for-idle is required
19
+ * because the MSE spec forbids setting `duration` while a buffer is
20
+ * `updating`; both waits resolve immediately when already open / idle.
21
+ *
22
+ * - **VoD** (finite): the value is written once, after `mediaSource` is open and
23
+ * all SourceBuffers are idle, clamped to be ≥ the highest buffered range (MSE
24
+ * spec). Written only while `mediaSource.duration` is still `NaN`; once any
25
+ * non-NaN value is present (us on a prior entry, or `endOfStream` from the
26
+ * buffered end), the property is left alone — re-syncing would race
27
+ * concurrent `appendBuffer()` calls.
28
+ *
29
+ * The entry resolves three async preconditions in order before writing:
30
+ *
31
+ * 1. **MediaSource open** — `waitForMediaSourceOpen` defers until the first
32
+ * `sourceopen` event (or any readyState transition, since `'ended'` /
33
+ * `'closed'` mean we've missed the window and should bail).
34
+ * 2. **All SourceBuffers idle** — `waitForSourceBuffersReady` defers per
35
+ * the MSE-spec rule that `duration` cannot be set while any buffer has
36
+ * `updating === true`.
37
+ * 3. **Buffered-range clamp** — `getMaxBufferedEnd` ensures the written
38
+ * duration is at least the highest buffered end (MSE spec disallows
39
+ * a smaller `duration` than any buffered range).
40
+ *
41
+ * The buffer-set helpers operate across `mediaSource.sourceBuffers` (the
42
+ * canonical aggregate), so the behavior composes uniformly across
43
+ * audio-only, video-only, and mixed configurations.
44
+ *
45
+ * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'duration-writable'`):
46
+ * state derivation is purely signal-driven (presentation validity +
47
+ * mediaSource existence). MediaSource lifecycle state (`readyState`) and
48
+ * the `duration` itself are non-signal DOM properties resolved inside the
49
+ * entry's async sequence. The state-exit cleanup aborts the in-flight
50
+ * wait, so source resets and behavior destroy structurally cancel the
51
+ * pending write. A post-await re-check of `mediaSource.readyState ===
52
+ * 'open'` covers the narrow race where `endOfStream()` synchronously
53
+ * transitions readyState to `'ended'` between our `waitForMediaSourceOpen`
54
+ * resolution and the `mediaSource.duration` write.
55
+ *
56
+ * Downstream of `calculatePresentationDuration` (which writes
57
+ * `presentation.duration`); concurrent with `endOfStream` (which may later
58
+ * write `mediaSource.duration` — the "exactly once" contract keeps us out
59
+ * of that path).
60
+ */
61
+ function deriveState(presentation, mediaSource) {
62
+ return shouldUpdateDuration(presentation, mediaSource) ? "duration-writable" : "preconditions-unmet";
63
+ }
64
+ function updateMediaSourceDurationSetup({ state, context }) {
65
+ const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaSource.get()));
66
+ return createMachineReactor({
67
+ initial: "preconditions-unmet",
68
+ monitor: () => derivedStateSignal.get(),
69
+ states: {
70
+ "preconditions-unmet": {},
71
+ "duration-writable": { entry: () => {
72
+ const presentation = state.presentation.get();
73
+ const mediaSource = context.mediaSource.get();
74
+ if (presentation.duration === Number.POSITIVE_INFINITY) {
75
+ if (mediaSource.duration === Number.POSITIVE_INFINITY) return;
76
+ const controller = new AbortController();
77
+ (async () => {
78
+ await waitForMediaSourceOpen(mediaSource, controller.signal);
79
+ if (controller.signal.aborted || mediaSource.readyState !== "open") return;
80
+ await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);
81
+ if (controller.signal.aborted || mediaSource.readyState !== "open") return;
82
+ if (mediaSource.duration !== Number.POSITIVE_INFINITY) mediaSource.duration = Number.POSITIVE_INFINITY;
83
+ })();
84
+ return () => controller.abort();
85
+ }
86
+ if (!Number.isNaN(mediaSource.duration)) return;
87
+ const controller = new AbortController();
88
+ const writeWhenReady = async () => {
89
+ await waitForMediaSourceOpen(mediaSource, controller.signal);
90
+ if (controller.signal.aborted) return;
91
+ if (mediaSource.readyState !== "open") return;
92
+ await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);
93
+ if (controller.signal.aborted) return;
94
+ if (mediaSource.readyState !== "open") return;
95
+ const maxBufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);
96
+ const duration = maxBufferedEnd > presentation.duration ? maxBufferedEnd : presentation.duration;
97
+ mediaSource.duration = duration;
98
+ };
99
+ writeWhenReady();
100
+ return () => controller.abort();
101
+ } }
102
+ }
103
+ });
104
+ }
105
+ const updateMediaSourceDuration = defineBehavior({
106
+ stateKeys: ["presentation"],
107
+ contextKeys: ["mediaSource"],
108
+ setup: updateMediaSourceDurationSetup
109
+ });
110
+ //#endregion
111
+ export { updateMediaSourceDuration };
112
+
113
+ //# sourceMappingURL=update-mediasource-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-mediasource-duration.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/update-mediasource-duration.ts"],"sourcesContent":["/**\n * **Propagate `presentation.duration` to `mediaSource.duration` — exactly\n * once per MediaSource.**\n *\n * Two paths, by whether the presentation is live:\n *\n * - **Live** (`presentation.duration === Infinity`): write `Infinity` once the\n * MediaSource is open and no SourceBuffer is mid-append. No buffered clamp is\n * needed (`Infinity` ≥ any range) and — unlike the finite case — it needn't\n * precede the first append: `Infinity` overrides whatever finite live-edge\n * value an append may have pinned (a finite duration would otherwise stall\n * the stream once the window slides past it). The wait-for-idle is required\n * because the MSE spec forbids setting `duration` while a buffer is\n * `updating`; both waits resolve immediately when already open / idle.\n *\n * - **VoD** (finite): the value is written once, after `mediaSource` is open and\n * all SourceBuffers are idle, clamped to be ≥ the highest buffered range (MSE\n * spec). Written only while `mediaSource.duration` is still `NaN`; once any\n * non-NaN value is present (us on a prior entry, or `endOfStream` from the\n * buffered end), the property is left alone — re-syncing would race\n * concurrent `appendBuffer()` calls.\n *\n * The entry resolves three async preconditions in order before writing:\n *\n * 1. **MediaSource open** — `waitForMediaSourceOpen` defers until the first\n * `sourceopen` event (or any readyState transition, since `'ended'` /\n * `'closed'` mean we've missed the window and should bail).\n * 2. **All SourceBuffers idle** — `waitForSourceBuffersReady` defers per\n * the MSE-spec rule that `duration` cannot be set while any buffer has\n * `updating === true`.\n * 3. **Buffered-range clamp** — `getMaxBufferedEnd` ensures the written\n * duration is at least the highest buffered end (MSE spec disallows\n * a smaller `duration` than any buffered range).\n *\n * The buffer-set helpers operate across `mediaSource.sourceBuffers` (the\n * canonical aggregate), so the behavior composes uniformly across\n * audio-only, video-only, and mixed configurations.\n *\n * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'duration-writable'`):\n * state derivation is purely signal-driven (presentation validity +\n * mediaSource existence). MediaSource lifecycle state (`readyState`) and\n * the `duration` itself are non-signal DOM properties resolved inside the\n * entry's async sequence. The state-exit cleanup aborts the in-flight\n * wait, so source resets and behavior destroy structurally cancel the\n * pending write. A post-await re-check of `mediaSource.readyState ===\n * 'open'` covers the narrow race where `endOfStream()` synchronously\n * transitions readyState to `'ended'` between our `waitForMediaSourceOpen`\n * resolution and the `mediaSource.duration` write.\n *\n * Downstream of `calculatePresentationDuration` (which writes\n * `presentation.duration`); concurrent with `endOfStream` (which may later\n * write `mediaSource.duration` — the \"exactly once\" contract keeps us out\n * of that path).\n */\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal } from '../../../core/signals/primitives';\nimport { getMaxBufferedEnd, shouldUpdateDuration, waitForSourceBuffersReady } from '../../../media/dom/mse/duration';\nimport { waitForMediaSourceOpen } from '../../../media/dom/mse/mediasource-setup';\nimport type { MaybeResolvedPresentation } from '../../../media/types';\n\ntype DurationFsmState = 'preconditions-unmet' | 'duration-writable';\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaSource: MediaSource | undefined\n): DurationFsmState {\n return shouldUpdateDuration(presentation, mediaSource) ? 'duration-writable' : 'preconditions-unmet';\n}\n\nfunction updateMediaSourceDurationSetup({\n state,\n context,\n}: {\n state: { presentation: ReadonlySignal<MaybeResolvedPresentation | undefined> };\n context: { mediaSource: ReadonlySignal<MediaSource | undefined> };\n}): Reactor<DurationFsmState | 'destroying' | 'destroyed'> {\n // Purely signal-driven derivation: only `presentation` validity and\n // `mediaSource` existence. MediaSource lifecycle (`readyState`),\n // its attached `sourceBuffers`, and `mediaSource.duration` itself are\n // non-signal DOM properties consumed inside the entry — none of them\n // sneak into `computed(...)`.\n const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaSource.get()));\n\n return createMachineReactor<DurationFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n 'duration-writable': {\n // entry body is auto-untracked. The state machine handles source\n // resets via deriveState; this entry resolves the MediaSource\n // lifecycle preconditions in sequence and binds its cleanup\n // (abort) to state exit + destroy.\n entry: () => {\n const presentation = state.presentation.get()!;\n const mediaSource = context.mediaSource.get()!;\n\n // Live: write Infinity once the MediaSource is open and no buffer is\n // mid-append (the MSE spec forbids setting `duration` while a\n // SourceBuffer is `updating`, and racing an in-flight append throws).\n // No `NaN` guard, unlike the VoD path below — Infinity must override\n // whatever finite live-edge value an append may have pinned.\n if (presentation.duration === Number.POSITIVE_INFINITY) {\n if (mediaSource.duration === Number.POSITIVE_INFINITY) return;\n const controller = new AbortController();\n void (async () => {\n await waitForMediaSourceOpen(mediaSource, controller.signal);\n if (controller.signal.aborted || mediaSource.readyState !== 'open') return;\n await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);\n if (controller.signal.aborted || mediaSource.readyState !== 'open') return;\n if (mediaSource.duration !== Number.POSITIVE_INFINITY) {\n mediaSource.duration = Number.POSITIVE_INFINITY;\n }\n })();\n return () => controller.abort();\n }\n\n // VoD idempotency: someone (us on a prior entry, or concurrent\n // `endOfStream`) has already written. No-op and leave the state\n // alone — the next source-reset transition will produce a fresh\n // entry against the new MediaSource (whose `duration` starts at\n // `NaN` again).\n if (!Number.isNaN(mediaSource.duration)) return;\n\n const controller = new AbortController();\n\n const writeWhenReady = async () => {\n await waitForMediaSourceOpen(mediaSource, controller.signal);\n if (controller.signal.aborted) return;\n // `waitForMediaSourceOpen` resolves on any readyState transition\n // out of 'closed'; if we landed in 'ended' / 'closed' instead of\n // 'open', the write window is gone.\n if (mediaSource.readyState !== 'open') return;\n\n await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);\n if (controller.signal.aborted) return;\n\n // Narrow race: `endOfStream()` synchronously transitions\n // readyState to 'ended' AND writes `mediaSource.duration`\n // between our `waitForMediaSourceOpen` resolution and here.\n // Re-read the DOM property to catch this window.\n if (mediaSource.readyState !== 'open') return;\n\n // MSE spec: duration cannot be less than any buffered range.\n // Re-read sourceBuffers after the await so any buffer added\n // during the wait is included in the clamp.\n const maxBufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);\n const duration = maxBufferedEnd > presentation.duration! ? maxBufferedEnd : presentation.duration!;\n mediaSource.duration = duration;\n };\n\n writeWhenReady();\n\n return () => controller.abort();\n },\n },\n },\n });\n}\n\nexport const updateMediaSourceDuration = defineBehavior({\n stateKeys: ['presentation'],\n contextKeys: ['mediaSource'],\n setup: updateMediaSourceDurationSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAS,YACP,cACA,aACkB;CAClB,OAAO,qBAAqB,cAAc,WAAW,IAAI,sBAAsB;AACjF;AAEA,SAAS,+BAA+B,EACtC,OACA,WAIyD;CAMzD,MAAM,qBAAqB,eAAe,YAAY,MAAM,aAAa,IAAI,GAAG,QAAQ,YAAY,IAAI,CAAC,CAAC;CAE1G,OAAO,qBAAuC;EAC5C,SAAS;EACT,eAAe,mBAAmB,IAAI;EACtC,QAAQ;GACN,uBAAuB,CAAC;GAExB,qBAAqB,EAKnB,aAAa;IACX,MAAM,eAAe,MAAM,aAAa,IAAI;IAC5C,MAAM,cAAc,QAAQ,YAAY,IAAI;IAO5C,IAAI,aAAa,aAAa,OAAO,mBAAmB;KACtD,IAAI,YAAY,aAAa,OAAO,mBAAmB;KACvD,MAAM,aAAa,IAAI,gBAAgB;KACvC,CAAM,YAAY;MAChB,MAAM,uBAAuB,aAAa,WAAW,MAAM;MAC3D,IAAI,WAAW,OAAO,WAAW,YAAY,eAAe,QAAQ;MACpE,MAAM,0BAA0B,YAAY,eAAe,WAAW,MAAM;MAC5E,IAAI,WAAW,OAAO,WAAW,YAAY,eAAe,QAAQ;MACpE,IAAI,YAAY,aAAa,OAAO,mBAClC,YAAY,WAAW,OAAO;KAElC,EAAA,CAAG;KACH,aAAa,WAAW,MAAM;IAChC;IAOA,IAAI,CAAC,OAAO,MAAM,YAAY,QAAQ,GAAG;IAEzC,MAAM,aAAa,IAAI,gBAAgB;IAEvC,MAAM,iBAAiB,YAAY;KACjC,MAAM,uBAAuB,aAAa,WAAW,MAAM;KAC3D,IAAI,WAAW,OAAO,SAAS;KAI/B,IAAI,YAAY,eAAe,QAAQ;KAEvC,MAAM,0BAA0B,YAAY,eAAe,WAAW,MAAM;KAC5E,IAAI,WAAW,OAAO,SAAS;KAM/B,IAAI,YAAY,eAAe,QAAQ;KAKvC,MAAM,iBAAiB,kBAAkB,YAAY,aAAa;KAClE,MAAM,WAAW,iBAAiB,aAAa,WAAY,iBAAiB,aAAa;KACzF,YAAY,WAAW;IACzB;IAEA,eAAe;IAEf,aAAa,WAAW,MAAM;GAChC,EACF;EACF;CACF,CAAC;AACH;AAEA,MAAa,4BAA4B,eAAe;CACtD,WAAW,CAAC,cAAc;CAC1B,aAAa,CAAC,aAAa;CAC3B,OAAO;AACT,CAAC"}