@videojs/spf 10.0.0-beta.9 → 10.0.0-rc.2

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 (556) hide show
  1. package/README.md +19 -38
  2. package/dist/default/core/actors/create-machine-actor.js +89 -0
  3. package/dist/default/core/actors/create-machine-actor.js.map +1 -0
  4. package/dist/default/core/actors/create-transition-actor.js +44 -0
  5. package/dist/default/core/actors/create-transition-actor.js.map +1 -0
  6. package/dist/default/core/composition/create-composition.js +82 -0
  7. package/dist/default/core/composition/create-composition.js.map +1 -0
  8. package/dist/default/core/composition/share-signals.js +34 -0
  9. package/dist/default/core/composition/share-signals.js.map +1 -0
  10. package/dist/default/core/machine.js +25 -0
  11. package/dist/default/core/machine.js.map +1 -0
  12. package/dist/default/core/reactors/create-machine-reactor.js +84 -0
  13. package/dist/default/core/reactors/create-machine-reactor.js.map +1 -0
  14. package/dist/default/core/signals/effect.js +61 -0
  15. package/dist/default/core/signals/effect.js.map +1 -0
  16. package/dist/default/core/signals/primitives.js +50 -0
  17. package/dist/default/core/signals/primitives.js.map +1 -0
  18. package/dist/default/core/signals/when.js +36 -0
  19. package/dist/default/core/signals/when.js.map +1 -0
  20. package/dist/default/core/tasks/delayed-reschedule.js +27 -0
  21. package/dist/default/core/tasks/delayed-reschedule.js.map +1 -0
  22. package/dist/default/core/tasks/task.js +298 -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 +10 -0
  29. package/dist/default/index.js +22 -0
  30. package/dist/default/index.js.map +1 -0
  31. package/dist/default/media/abr/quality-selection.js +17 -0
  32. package/dist/default/media/abr/quality-selection.js.map +1 -0
  33. package/dist/default/media/buffer/back-buffer.js +42 -0
  34. package/dist/default/media/buffer/back-buffer.js.map +1 -0
  35. package/dist/default/media/buffer/forward-buffer.js +110 -0
  36. package/dist/default/media/buffer/forward-buffer.js.map +1 -0
  37. package/dist/default/media/dom/capabilities.js +61 -0
  38. package/dist/default/media/dom/capabilities.js.map +1 -0
  39. package/dist/default/media/dom/mse/append-segment.js +58 -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 +87 -0
  44. package/dist/default/media/dom/mse/duration.js.map +1 -0
  45. package/dist/default/media/dom/mse/end-of-stream.js +17 -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 +227 -0
  48. package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -0
  49. package/dist/default/media/dom/screen.js +95 -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 +61 -0
  54. package/dist/default/media/dom/text/text-track-slots.js.map +1 -0
  55. package/dist/default/media/errors.js +60 -0
  56. package/dist/default/media/errors.js.map +1 -0
  57. package/dist/default/media/hls/parse-attributes.js +131 -0
  58. package/dist/default/media/hls/parse-attributes.js.map +1 -0
  59. package/dist/default/media/hls/parse-media-playlist.js +253 -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 +63 -0
  64. package/dist/default/media/hls/reload-policy.js.map +1 -0
  65. package/dist/default/media/hls/resolve-url.js +9 -0
  66. package/dist/default/media/hls/resolve-url.js.map +1 -0
  67. package/dist/default/media/live-window.js +32 -0
  68. package/dist/default/media/live-window.js.map +1 -0
  69. package/dist/default/media/media-tracks/media-tracks.js +96 -0
  70. package/dist/default/media/media-tracks/media-tracks.js.map +1 -0
  71. package/dist/default/media/mp4/box.js +57 -0
  72. package/dist/default/media/mp4/box.js.map +1 -0
  73. package/dist/default/media/mp4/timestamp-origin.js +95 -0
  74. package/dist/default/media/mp4/timestamp-origin.js.map +1 -0
  75. package/dist/default/media/primitives/resolution.js +26 -0
  76. package/dist/default/media/primitives/resolution.js.map +1 -0
  77. package/dist/default/media/primitives/select-tracks.js +97 -0
  78. package/dist/default/media/primitives/select-tracks.js.map +1 -0
  79. package/dist/default/media/text/parse-vtt-timestamp-map.js +40 -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 +22 -0
  82. package/dist/default/media/text/resolve-vtt-metadata.js.map +1 -0
  83. package/dist/default/media/types/index.js +42 -0
  84. package/dist/default/media/types/index.js.map +1 -0
  85. package/dist/default/media/utils/cdn.js +50 -0
  86. package/dist/default/media/utils/cdn.js.map +1 -0
  87. package/dist/default/media/utils/preload.js +21 -0
  88. package/dist/default/media/utils/preload.js.map +1 -0
  89. package/dist/default/media/utils/track-selection.js +43 -0
  90. package/dist/default/media/utils/track-selection.js.map +1 -0
  91. package/dist/default/media/utils/tracks.js +146 -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/network/bandwidth-estimator.js +95 -0
  95. package/dist/default/network/bandwidth-estimator.js.map +1 -0
  96. package/dist/default/network/chunked-stream-iterable.js +47 -0
  97. package/dist/default/network/chunked-stream-iterable.js.map +1 -0
  98. package/dist/default/network/ewma.js +68 -0
  99. package/dist/default/network/ewma.js.map +1 -0
  100. package/dist/default/network/fetch.js +108 -0
  101. package/dist/default/network/fetch.js.map +1 -0
  102. package/dist/default/playback/actors/dom/segment-loader.js +233 -0
  103. package/dist/default/playback/actors/dom/segment-loader.js.map +1 -0
  104. package/dist/default/playback/actors/dom/source-buffer.js +139 -0
  105. package/dist/default/playback/actors/dom/source-buffer.js.map +1 -0
  106. package/dist/default/playback/actors/dom/text-tracks.js +46 -0
  107. package/dist/default/playback/actors/dom/text-tracks.js.map +1 -0
  108. package/dist/default/playback/actors/text-track-segment-loader.js +138 -0
  109. package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -0
  110. package/dist/default/playback/adapters/hls-audio/adapter.js +8 -0
  111. package/dist/default/playback/adapters/hls-audio/adapter.js.map +1 -0
  112. package/dist/default/playback/adapters/hls-audio/mixin.js +178 -0
  113. package/dist/default/playback/adapters/hls-audio/mixin.js.map +1 -0
  114. package/dist/default/playback/adapters/hls-background-video/adapter.js +17 -0
  115. package/dist/default/playback/adapters/hls-background-video/adapter.js.map +1 -0
  116. package/dist/default/playback/adapters/hls-background-video/host.js +56 -0
  117. package/dist/default/playback/adapters/hls-background-video/host.js.map +1 -0
  118. package/dist/default/playback/adapters/hls-background-video/mixin.js +191 -0
  119. package/dist/default/playback/adapters/hls-background-video/mixin.js.map +1 -0
  120. package/dist/default/playback/adapters/hls-video/adapter.js +11 -0
  121. package/dist/default/playback/adapters/hls-video/adapter.js.map +1 -0
  122. package/dist/default/playback/adapters/hls-video/error-surface.js +46 -0
  123. package/dist/default/playback/adapters/hls-video/error-surface.js.map +1 -0
  124. package/dist/default/playback/adapters/hls-video/media-tracks.js +125 -0
  125. package/dist/default/playback/adapters/hls-video/media-tracks.js.map +1 -0
  126. package/dist/default/playback/adapters/hls-video/mixin.js +299 -0
  127. package/dist/default/playback/adapters/hls-video/mixin.js.map +1 -0
  128. package/dist/default/playback/behaviors/calculate-presentation-duration.js +33 -0
  129. package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  130. package/dist/default/playback/behaviors/collect-errors.js +98 -0
  131. package/dist/default/playback/behaviors/collect-errors.js.map +1 -0
  132. package/dist/default/playback/behaviors/derive-cdn-priority.js +61 -0
  133. package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -0
  134. package/dist/default/playback/behaviors/dom/airplay.js +188 -0
  135. package/dist/default/playback/behaviors/dom/airplay.js.map +1 -0
  136. package/dist/default/playback/behaviors/dom/apply-start-position.js +81 -0
  137. package/dist/default/playback/behaviors/dom/apply-start-position.js.map +1 -0
  138. package/dist/default/playback/behaviors/dom/end-of-stream.js +97 -0
  139. package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -0
  140. package/dist/default/playback/behaviors/dom/load-segments.js +169 -0
  141. package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -0
  142. package/dist/default/playback/behaviors/dom/recover-end-stall.js +72 -0
  143. package/dist/default/playback/behaviors/dom/recover-end-stall.js.map +1 -0
  144. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js +113 -0
  145. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
  146. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +181 -0
  147. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  148. package/dist/default/playback/behaviors/dom/setup-mediasource.js +100 -0
  149. package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  150. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +40 -0
  151. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  152. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js +25 -0
  153. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
  154. package/dist/default/playback/behaviors/dom/sync-text-tracks.js +109 -0
  155. package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  156. package/dist/default/playback/behaviors/dom/track-current-time.js +50 -0
  157. package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -0
  158. package/dist/default/playback/behaviors/dom/track-load-triggers.js +86 -0
  159. package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  160. package/dist/default/playback/behaviors/dom/track-playback-rate.js +35 -0
  161. package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  162. package/dist/default/playback/behaviors/dom/track-player-resolution.js +53 -0
  163. package/dist/default/playback/behaviors/dom/track-player-resolution.js.map +1 -0
  164. package/dist/default/playback/behaviors/dom/track-screen-resolution.js +28 -0
  165. package/dist/default/playback/behaviors/dom/track-screen-resolution.js.map +1 -0
  166. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +98 -0
  167. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  168. package/dist/default/playback/behaviors/establish-start-media-time.js +187 -0
  169. package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -0
  170. package/dist/default/playback/behaviors/resolve-presentation.js +76 -0
  171. package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -0
  172. package/dist/default/playback/behaviors/resolve-track.js +134 -0
  173. package/dist/default/playback/behaviors/resolve-track.js.map +1 -0
  174. package/dist/default/playback/behaviors/select-tracks.js +189 -0
  175. package/dist/default/playback/behaviors/select-tracks.js.map +1 -0
  176. package/dist/default/playback/behaviors/setup-failover-monitor.js +61 -0
  177. package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -0
  178. package/dist/default/playback/behaviors/sync-preload.js +59 -0
  179. package/dist/default/playback/behaviors/sync-preload.js.map +1 -0
  180. package/dist/default/playback/behaviors/track-switching.js +407 -0
  181. package/dist/default/playback/behaviors/track-switching.js.map +1 -0
  182. package/dist/default/playback/engines/hls/engine-audio-only.js +94 -0
  183. package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -0
  184. package/dist/default/playback/engines/hls/engine-background-video.js +87 -0
  185. package/dist/default/playback/engines/hls/engine-background-video.js.map +1 -0
  186. package/dist/default/playback/engines/hls/engine.js +144 -0
  187. package/dist/default/playback/engines/hls/engine.js.map +1 -0
  188. package/dist/default/playback/primitives/error-messages.js +42 -0
  189. package/dist/default/playback/primitives/error-messages.js.map +1 -0
  190. package/dist/default/playback/primitives/failover-fetch.js +37 -0
  191. package/dist/default/playback/primitives/failover-fetch.js.map +1 -0
  192. package/dist/default/playback/primitives/head-peek.js +41 -0
  193. package/dist/default/playback/primitives/head-peek.js.map +1 -0
  194. package/dist/default/playback/primitives/live-window.js +63 -0
  195. package/dist/default/playback/primitives/live-window.js.map +1 -0
  196. package/dist/default/playback/primitives/relocation-pipelines.js +191 -0
  197. package/dist/default/playback/primitives/relocation-pipelines.js.map +1 -0
  198. package/dist/default/playback/primitives/report-track-conditions.js +74 -0
  199. package/dist/default/playback/primitives/report-track-conditions.js.map +1 -0
  200. package/dist/default/playback/primitives/segment-load-pipeline.js +96 -0
  201. package/dist/default/playback/primitives/segment-load-pipeline.js.map +1 -0
  202. package/dist/default/playback/primitives/selection-rules.js +115 -0
  203. package/dist/default/playback/primitives/selection-rules.js.map +1 -0
  204. package/dist/default/playback/primitives/text-segment-load-pipeline.js +36 -0
  205. package/dist/default/playback/primitives/text-segment-load-pipeline.js.map +1 -0
  206. package/dist/default/playback/primitives/track-types.js +53 -0
  207. package/dist/default/playback/primitives/track-types.js.map +1 -0
  208. package/dist/dev/core/actors/actor.d.ts +26 -0
  209. package/dist/dev/core/actors/actor.d.ts.map +1 -0
  210. package/dist/dev/core/actors/create-machine-actor.d.ts +119 -0
  211. package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -0
  212. package/dist/dev/core/actors/create-machine-actor.js +89 -0
  213. package/dist/dev/core/actors/create-machine-actor.js.map +1 -0
  214. package/dist/dev/core/actors/create-transition-actor.d.ts +37 -0
  215. package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -0
  216. package/dist/dev/core/actors/create-transition-actor.js +44 -0
  217. package/dist/dev/core/actors/create-transition-actor.js.map +1 -0
  218. package/dist/dev/core/composition/create-composition.d.ts +241 -0
  219. package/dist/dev/core/composition/create-composition.d.ts.map +1 -0
  220. package/dist/dev/core/composition/create-composition.js +82 -0
  221. package/dist/dev/core/composition/create-composition.js.map +1 -0
  222. package/dist/dev/core/composition/share-signals.d.ts +39 -0
  223. package/dist/dev/core/composition/share-signals.d.ts.map +1 -0
  224. package/dist/dev/core/composition/share-signals.js +34 -0
  225. package/dist/dev/core/composition/share-signals.js.map +1 -0
  226. package/dist/dev/core/machine.d.ts +21 -0
  227. package/dist/dev/core/machine.d.ts.map +1 -0
  228. package/dist/dev/core/machine.js +25 -0
  229. package/dist/dev/core/machine.js.map +1 -0
  230. package/dist/dev/core/reactors/create-machine-reactor.d.ts +87 -0
  231. package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -0
  232. package/dist/dev/core/reactors/create-machine-reactor.js +84 -0
  233. package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -0
  234. package/dist/dev/core/signals/effect.d.ts +14 -0
  235. package/dist/dev/core/signals/effect.d.ts.map +1 -0
  236. package/dist/dev/core/signals/effect.js +61 -0
  237. package/dist/dev/core/signals/effect.js.map +1 -0
  238. package/dist/dev/core/signals/primitives.d.ts +44 -0
  239. package/dist/dev/core/signals/primitives.d.ts.map +1 -0
  240. package/dist/dev/core/signals/primitives.js +50 -0
  241. package/dist/dev/core/signals/primitives.js.map +1 -0
  242. package/dist/dev/core/signals/when.js +36 -0
  243. package/dist/dev/core/signals/when.js.map +1 -0
  244. package/dist/dev/core/tasks/delayed-reschedule.js +27 -0
  245. package/dist/dev/core/tasks/delayed-reschedule.js.map +1 -0
  246. package/dist/dev/core/tasks/task.d.ts +144 -0
  247. package/dist/dev/core/tasks/task.d.ts.map +1 -0
  248. package/dist/dev/core/tasks/task.js +298 -0
  249. package/dist/dev/core/tasks/task.js.map +1 -0
  250. package/dist/dev/dom.d.ts +9 -0
  251. package/dist/dev/dom.js +9 -0
  252. package/dist/dev/hls-audio.d.ts +3 -0
  253. package/dist/dev/hls-audio.js +3 -0
  254. package/dist/dev/hls-background-video.d.ts +4 -0
  255. package/dist/dev/hls-background-video.js +3 -0
  256. package/dist/dev/hls-video.d.ts +5 -0
  257. package/dist/dev/hls-video.js +4 -0
  258. package/dist/dev/hls.d.ts +11 -0
  259. package/dist/dev/hls.js +10 -0
  260. package/dist/dev/index.d.ts +23 -0
  261. package/dist/dev/index.d.ts.map +1 -0
  262. package/dist/dev/index.js +22 -0
  263. package/dist/dev/index.js.map +1 -0
  264. package/dist/dev/media/abr/quality-selection.d.ts +19 -0
  265. package/dist/dev/media/abr/quality-selection.d.ts.map +1 -0
  266. package/dist/dev/media/abr/quality-selection.js +17 -0
  267. package/dist/dev/media/abr/quality-selection.js.map +1 -0
  268. package/dist/dev/media/buffer/back-buffer.d.ts +9 -0
  269. package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -0
  270. package/dist/dev/media/buffer/back-buffer.js +42 -0
  271. package/dist/dev/media/buffer/back-buffer.js.map +1 -0
  272. package/dist/dev/media/buffer/forward-buffer.d.ts +9 -0
  273. package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -0
  274. package/dist/dev/media/buffer/forward-buffer.js +110 -0
  275. package/dist/dev/media/buffer/forward-buffer.js.map +1 -0
  276. package/dist/dev/media/dom/capabilities.js +61 -0
  277. package/dist/dev/media/dom/capabilities.js.map +1 -0
  278. package/dist/dev/media/dom/mse/append-segment.d.ts +16 -0
  279. package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -0
  280. package/dist/dev/media/dom/mse/append-segment.js +58 -0
  281. package/dist/dev/media/dom/mse/append-segment.js.map +1 -0
  282. package/dist/dev/media/dom/mse/buffer-flusher.d.ts +24 -0
  283. package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -0
  284. package/dist/dev/media/dom/mse/buffer-flusher.js +55 -0
  285. package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -0
  286. package/dist/dev/media/dom/mse/duration.js +87 -0
  287. package/dist/dev/media/dom/mse/duration.js.map +1 -0
  288. package/dist/dev/media/dom/mse/end-of-stream.js +17 -0
  289. package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -0
  290. package/dist/dev/media/dom/mse/mediasource-setup.js +227 -0
  291. package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -0
  292. package/dist/dev/media/dom/screen.d.ts +7 -0
  293. package/dist/dev/media/dom/screen.d.ts.map +1 -0
  294. package/dist/dev/media/dom/screen.js +95 -0
  295. package/dist/dev/media/dom/screen.js.map +1 -0
  296. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +6 -0
  297. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -0
  298. package/dist/dev/media/dom/text/resolve-vtt-segment.js +50 -0
  299. package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -0
  300. package/dist/dev/media/dom/text/text-track-slots.d.ts +24 -0
  301. package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -0
  302. package/dist/dev/media/dom/text/text-track-slots.js +61 -0
  303. package/dist/dev/media/dom/text/text-track-slots.js.map +1 -0
  304. package/dist/dev/media/errors.d.ts +68 -0
  305. package/dist/dev/media/errors.d.ts.map +1 -0
  306. package/dist/dev/media/errors.js +60 -0
  307. package/dist/dev/media/errors.js.map +1 -0
  308. package/dist/dev/media/hls/parse-attributes.js +131 -0
  309. package/dist/dev/media/hls/parse-attributes.js.map +1 -0
  310. package/dist/dev/media/hls/parse-media-playlist.js +253 -0
  311. package/dist/dev/media/hls/parse-media-playlist.js.map +1 -0
  312. package/dist/dev/media/hls/parse-multivariant.js +234 -0
  313. package/dist/dev/media/hls/parse-multivariant.js.map +1 -0
  314. package/dist/dev/media/hls/reload-policy.js +63 -0
  315. package/dist/dev/media/hls/reload-policy.js.map +1 -0
  316. package/dist/dev/media/hls/resolve-url.js +9 -0
  317. package/dist/dev/media/hls/resolve-url.js.map +1 -0
  318. package/dist/dev/media/live-window.js +32 -0
  319. package/dist/dev/media/live-window.js.map +1 -0
  320. package/dist/dev/media/media-tracks/media-tracks.d.ts +46 -0
  321. package/dist/dev/media/media-tracks/media-tracks.d.ts.map +1 -0
  322. package/dist/dev/media/media-tracks/media-tracks.js +96 -0
  323. package/dist/dev/media/media-tracks/media-tracks.js.map +1 -0
  324. package/dist/dev/media/mp4/box.js +57 -0
  325. package/dist/dev/media/mp4/box.js.map +1 -0
  326. package/dist/dev/media/mp4/timestamp-origin.js +95 -0
  327. package/dist/dev/media/mp4/timestamp-origin.js.map +1 -0
  328. package/dist/dev/media/primitives/resolution.d.ts +22 -0
  329. package/dist/dev/media/primitives/resolution.d.ts.map +1 -0
  330. package/dist/dev/media/primitives/resolution.js +26 -0
  331. package/dist/dev/media/primitives/resolution.js.map +1 -0
  332. package/dist/dev/media/primitives/select-tracks.js +97 -0
  333. package/dist/dev/media/primitives/select-tracks.js.map +1 -0
  334. package/dist/dev/media/text/parse-vtt-timestamp-map.js +40 -0
  335. package/dist/dev/media/text/parse-vtt-timestamp-map.js.map +1 -0
  336. package/dist/dev/media/text/resolve-vtt-metadata.js +22 -0
  337. package/dist/dev/media/text/resolve-vtt-metadata.js.map +1 -0
  338. package/dist/dev/media/types/index.d.ts +306 -0
  339. package/dist/dev/media/types/index.d.ts.map +1 -0
  340. package/dist/dev/media/types/index.js +42 -0
  341. package/dist/dev/media/types/index.js.map +1 -0
  342. package/dist/dev/media/utils/cdn.d.ts +11 -0
  343. package/dist/dev/media/utils/cdn.d.ts.map +1 -0
  344. package/dist/dev/media/utils/cdn.js +50 -0
  345. package/dist/dev/media/utils/cdn.js.map +1 -0
  346. package/dist/dev/media/utils/preload.js +21 -0
  347. package/dist/dev/media/utils/preload.js.map +1 -0
  348. package/dist/dev/media/utils/track-selection.js +43 -0
  349. package/dist/dev/media/utils/track-selection.js.map +1 -0
  350. package/dist/dev/media/utils/tracks.js +146 -0
  351. package/dist/dev/media/utils/tracks.js.map +1 -0
  352. package/dist/dev/media-tracks.d.ts +3 -0
  353. package/dist/dev/media-tracks.js +2 -0
  354. package/dist/dev/network/bandwidth-estimator.d.ts +48 -0
  355. package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -0
  356. package/dist/dev/network/bandwidth-estimator.js +95 -0
  357. package/dist/dev/network/bandwidth-estimator.js.map +1 -0
  358. package/dist/dev/network/chunked-stream-iterable.js +47 -0
  359. package/dist/dev/network/chunked-stream-iterable.js.map +1 -0
  360. package/dist/dev/network/ewma.js +68 -0
  361. package/dist/dev/network/ewma.js.map +1 -0
  362. package/dist/dev/network/fetch.js +108 -0
  363. package/dist/dev/network/fetch.js.map +1 -0
  364. package/dist/dev/playback/actors/dom/segment-loader.d.ts +44 -0
  365. package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -0
  366. package/dist/dev/playback/actors/dom/segment-loader.js +233 -0
  367. package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -0
  368. package/dist/dev/playback/actors/dom/source-buffer.d.ts +45 -0
  369. package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -0
  370. package/dist/dev/playback/actors/dom/source-buffer.js +139 -0
  371. package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -0
  372. package/dist/dev/playback/actors/dom/text-tracks.d.ts +3 -0
  373. package/dist/dev/playback/actors/dom/text-tracks.js +46 -0
  374. package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -0
  375. package/dist/dev/playback/actors/text-track-segment-loader.d.ts +48 -0
  376. package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -0
  377. package/dist/dev/playback/actors/text-track-segment-loader.js +138 -0
  378. package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -0
  379. package/dist/dev/playback/actors/text-tracks.d.ts +30 -0
  380. package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -0
  381. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts +11 -0
  382. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts.map +1 -0
  383. package/dist/dev/playback/adapters/hls-audio/adapter.js +8 -0
  384. package/dist/dev/playback/adapters/hls-audio/adapter.js.map +1 -0
  385. package/dist/dev/playback/adapters/hls-audio/mixin.d.ts +52 -0
  386. package/dist/dev/playback/adapters/hls-audio/mixin.d.ts.map +1 -0
  387. package/dist/dev/playback/adapters/hls-audio/mixin.js +178 -0
  388. package/dist/dev/playback/adapters/hls-audio/mixin.js.map +1 -0
  389. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts +19 -0
  390. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts.map +1 -0
  391. package/dist/dev/playback/adapters/hls-background-video/adapter.js +17 -0
  392. package/dist/dev/playback/adapters/hls-background-video/adapter.js.map +1 -0
  393. package/dist/dev/playback/adapters/hls-background-video/host.d.ts +34 -0
  394. package/dist/dev/playback/adapters/hls-background-video/host.d.ts.map +1 -0
  395. package/dist/dev/playback/adapters/hls-background-video/host.js +56 -0
  396. package/dist/dev/playback/adapters/hls-background-video/host.js.map +1 -0
  397. package/dist/dev/playback/adapters/hls-background-video/mixin.d.ts +69 -0
  398. package/dist/dev/playback/adapters/hls-background-video/mixin.d.ts.map +1 -0
  399. package/dist/dev/playback/adapters/hls-background-video/mixin.js +191 -0
  400. package/dist/dev/playback/adapters/hls-background-video/mixin.js.map +1 -0
  401. package/dist/dev/playback/adapters/hls-video/adapter.d.ts +11 -0
  402. package/dist/dev/playback/adapters/hls-video/adapter.d.ts.map +1 -0
  403. package/dist/dev/playback/adapters/hls-video/adapter.js +11 -0
  404. package/dist/dev/playback/adapters/hls-video/adapter.js.map +1 -0
  405. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts +17 -0
  406. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts.map +1 -0
  407. package/dist/dev/playback/adapters/hls-video/error-surface.js +46 -0
  408. package/dist/dev/playback/adapters/hls-video/error-surface.js.map +1 -0
  409. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts +21 -0
  410. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts.map +1 -0
  411. package/dist/dev/playback/adapters/hls-video/media-tracks.js +125 -0
  412. package/dist/dev/playback/adapters/hls-video/media-tracks.js.map +1 -0
  413. package/dist/dev/playback/adapters/hls-video/mixin.d.ts +62 -0
  414. package/dist/dev/playback/adapters/hls-video/mixin.d.ts.map +1 -0
  415. package/dist/dev/playback/adapters/hls-video/mixin.js +299 -0
  416. package/dist/dev/playback/adapters/hls-video/mixin.js.map +1 -0
  417. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +23 -0
  418. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -0
  419. package/dist/dev/playback/behaviors/calculate-presentation-duration.js +33 -0
  420. package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  421. package/dist/dev/playback/behaviors/collect-errors.d.ts +2 -0
  422. package/dist/dev/playback/behaviors/collect-errors.js +98 -0
  423. package/dist/dev/playback/behaviors/collect-errors.js.map +1 -0
  424. package/dist/dev/playback/behaviors/derive-cdn-priority.js +61 -0
  425. package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -0
  426. package/dist/dev/playback/behaviors/dom/airplay.js +188 -0
  427. package/dist/dev/playback/behaviors/dom/airplay.js.map +1 -0
  428. package/dist/dev/playback/behaviors/dom/apply-start-position.js +81 -0
  429. package/dist/dev/playback/behaviors/dom/apply-start-position.js.map +1 -0
  430. package/dist/dev/playback/behaviors/dom/end-of-stream.js +97 -0
  431. package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -0
  432. package/dist/dev/playback/behaviors/dom/load-segments.d.ts +69 -0
  433. package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -0
  434. package/dist/dev/playback/behaviors/dom/load-segments.js +169 -0
  435. package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -0
  436. package/dist/dev/playback/behaviors/dom/recover-end-stall.js +72 -0
  437. package/dist/dev/playback/behaviors/dom/recover-end-stall.js.map +1 -0
  438. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js +113 -0
  439. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
  440. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +181 -0
  441. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  442. package/dist/dev/playback/behaviors/dom/setup-mediasource.js +100 -0
  443. package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  444. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +28 -0
  445. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -0
  446. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +40 -0
  447. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  448. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js +25 -0
  449. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
  450. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +109 -0
  451. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  452. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +33 -0
  453. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -0
  454. package/dist/dev/playback/behaviors/dom/track-current-time.js +50 -0
  455. package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -0
  456. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +41 -0
  457. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -0
  458. package/dist/dev/playback/behaviors/dom/track-load-triggers.js +86 -0
  459. package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  460. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +33 -0
  461. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -0
  462. package/dist/dev/playback/behaviors/dom/track-playback-rate.js +35 -0
  463. package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  464. package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts +8 -0
  465. package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts.map +1 -0
  466. package/dist/dev/playback/behaviors/dom/track-player-resolution.js +53 -0
  467. package/dist/dev/playback/behaviors/dom/track-player-resolution.js.map +1 -0
  468. package/dist/dev/playback/behaviors/dom/track-screen-resolution.js +28 -0
  469. package/dist/dev/playback/behaviors/dom/track-screen-resolution.js.map +1 -0
  470. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +98 -0
  471. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  472. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +32 -0
  473. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts.map +1 -0
  474. package/dist/dev/playback/behaviors/establish-start-media-time.js +187 -0
  475. package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -0
  476. package/dist/dev/playback/behaviors/resolve-presentation.d.ts +8 -0
  477. package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -0
  478. package/dist/dev/playback/behaviors/resolve-presentation.js +76 -0
  479. package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -0
  480. package/dist/dev/playback/behaviors/resolve-track.js +134 -0
  481. package/dist/dev/playback/behaviors/resolve-track.js.map +1 -0
  482. package/dist/dev/playback/behaviors/select-tracks.d.ts +61 -0
  483. package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -0
  484. package/dist/dev/playback/behaviors/select-tracks.js +189 -0
  485. package/dist/dev/playback/behaviors/select-tracks.js.map +1 -0
  486. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +11 -0
  487. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -0
  488. package/dist/dev/playback/behaviors/setup-failover-monitor.js +61 -0
  489. package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -0
  490. package/dist/dev/playback/behaviors/sync-preload.js +59 -0
  491. package/dist/dev/playback/behaviors/sync-preload.js.map +1 -0
  492. package/dist/dev/playback/behaviors/track-switching.d.ts +110 -0
  493. package/dist/dev/playback/behaviors/track-switching.d.ts.map +1 -0
  494. package/dist/dev/playback/behaviors/track-switching.js +407 -0
  495. package/dist/dev/playback/behaviors/track-switching.js.map +1 -0
  496. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +160 -0
  497. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -0
  498. package/dist/dev/playback/engines/hls/engine-audio-only.js +94 -0
  499. package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -0
  500. package/dist/dev/playback/engines/hls/engine-background-video.d.ts +131 -0
  501. package/dist/dev/playback/engines/hls/engine-background-video.d.ts.map +1 -0
  502. package/dist/dev/playback/engines/hls/engine-background-video.js +87 -0
  503. package/dist/dev/playback/engines/hls/engine-background-video.js.map +1 -0
  504. package/dist/dev/playback/engines/hls/engine.d.ts +289 -0
  505. package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -0
  506. package/dist/dev/playback/engines/hls/engine.js +144 -0
  507. package/dist/dev/playback/engines/hls/engine.js.map +1 -0
  508. package/dist/dev/playback/primitives/derive-start-media-time.d.ts +16 -0
  509. package/dist/dev/playback/primitives/derive-start-media-time.d.ts.map +1 -0
  510. package/dist/dev/playback/primitives/error-messages.js +42 -0
  511. package/dist/dev/playback/primitives/error-messages.js.map +1 -0
  512. package/dist/dev/playback/primitives/failover-fetch.js +37 -0
  513. package/dist/dev/playback/primitives/failover-fetch.js.map +1 -0
  514. package/dist/dev/playback/primitives/head-peek.js +41 -0
  515. package/dist/dev/playback/primitives/head-peek.js.map +1 -0
  516. package/dist/dev/playback/primitives/live-window.js +63 -0
  517. package/dist/dev/playback/primitives/live-window.js.map +1 -0
  518. package/dist/dev/playback/primitives/relocation-pipelines.js +191 -0
  519. package/dist/dev/playback/primitives/relocation-pipelines.js.map +1 -0
  520. package/dist/dev/playback/primitives/report-track-conditions.d.ts +8 -0
  521. package/dist/dev/playback/primitives/report-track-conditions.d.ts.map +1 -0
  522. package/dist/dev/playback/primitives/report-track-conditions.js +74 -0
  523. package/dist/dev/playback/primitives/report-track-conditions.js.map +1 -0
  524. package/dist/dev/playback/primitives/segment-load-pipeline.d.ts +2 -0
  525. package/dist/dev/playback/primitives/segment-load-pipeline.js +96 -0
  526. package/dist/dev/playback/primitives/segment-load-pipeline.js.map +1 -0
  527. package/dist/dev/playback/primitives/selection-rules.d.ts +66 -0
  528. package/dist/dev/playback/primitives/selection-rules.d.ts.map +1 -0
  529. package/dist/dev/playback/primitives/selection-rules.js +115 -0
  530. package/dist/dev/playback/primitives/selection-rules.js.map +1 -0
  531. package/dist/dev/playback/primitives/source-buffer-messages.d.ts +40 -0
  532. package/dist/dev/playback/primitives/source-buffer-messages.d.ts.map +1 -0
  533. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts +56 -0
  534. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts.map +1 -0
  535. package/dist/dev/playback/primitives/text-segment-load-pipeline.js +36 -0
  536. package/dist/dev/playback/primitives/text-segment-load-pipeline.js.map +1 -0
  537. package/dist/dev/playback/primitives/text-track-messages.d.ts +14 -0
  538. package/dist/dev/playback/primitives/text-track-messages.d.ts.map +1 -0
  539. package/dist/dev/playback/primitives/track-types.js +53 -0
  540. package/dist/dev/playback/primitives/track-types.js.map +1 -0
  541. package/package.json +60 -41
  542. package/dist/adapter-25AtMN8s.d.ts +0 -719
  543. package/dist/adapter-25AtMN8s.d.ts.map +0 -1
  544. package/dist/adapter-CflgYzjF.js +0 -3556
  545. package/dist/adapter-CflgYzjF.js.map +0 -1
  546. package/dist/dom/index.d.ts +0 -221
  547. package/dist/dom/index.d.ts.map +0 -1
  548. package/dist/dom/index.js +0 -38
  549. package/dist/dom/index.js.map +0 -1
  550. package/dist/dom/playback-engine/index.d.ts +0 -2
  551. package/dist/dom/playback-engine/index.js +0 -4
  552. package/dist/index.d.ts +0 -13
  553. package/dist/index.d.ts.map +0 -1
  554. package/dist/index.js +0 -3
  555. package/dist/src-DRsS_H80.js +0 -14
  556. package/dist/src-DRsS_H80.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-tracks.js","names":["#abort","#renditions","#removeVideoTracks","#audioTracks","#removeAudioTracks","#selectRendition","#selectAudio","#destroyed"],"sources":["../../../../../src/playback/adapters/hls-video/media-tracks.ts"],"sourcesContent":["import type {\n AudioTrackLike,\n MediaAudioTrackCapability,\n MediaVideoRenditionCapability,\n MediaVideoTrackCapability,\n VideoRenditionLike,\n} from '@videojs/media';\nimport type { Constructor } from '@videojs/utils/types';\n\nimport type { Composition } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport { computed, untrack } from '../../../core/signals/primitives';\nimport {\n type AudioTrack,\n dedupedAudioTracks,\n dedupedVideoTracks,\n findAudioTrackById,\n findVideoTrackById,\n frameRateToNumber,\n isSameAudioTrack,\n isSameVideoTrack,\n toUserAudioTrackSelection,\n toUserVideoTrackSelection,\n type VideoTrack,\n} from '../../../media/media-tracks';\nimport type { HlsVideoEngineContext, HlsVideoEngineState } from '../../engines/hls/engine';\n\n// Translate a DOM rendition/track into the SPF dedupe-key shape\nconst toVideoKey = (rendition: VideoRenditionLike) => ({\n width: rendition.width,\n height: rendition.height,\n bandwidth: rendition.bitrate!,\n});\n\nconst toAudioKey = (track: AudioTrackLike) => ({ language: track.language, name: track.label });\n\ntype HlsVideoEngineHost = {\n readonly engine: Composition<HlsVideoEngineState, HlsVideoEngineContext>;\n destroy?(): void;\n};\n\ntype MediaTracksHost = HlsVideoEngineHost &\n MediaVideoTrackCapability &\n MediaAudioTrackCapability &\n MediaVideoRenditionCapability;\n\n/** Two track lists carry the same set when their id sequences match. */\nconst sameIds = (a: { id: string }[], b: { id: string }[]): boolean =>\n a.length === b.length && a.every((item, i) => item.id === b[i]!.id);\n\n/**\n * Projects the SPF engine's presentation onto the media element's `videoRenditions` / `audioTracks` lists, and wires\n * user selection back to the engine's `userVideoTrackSelection` / `userAudioTrackSelection` signals.\n *\n * Requires the media-tracks mixin (track-list infrastructure) earlier in the chain so the host exposes `addVideoTrack`,\n * `videoRenditions`, and friends.\n */\nexport function HlsVideoMediaTracksMixin<Base extends Constructor<MediaTracksHost>>(BaseClass: Base) {\n class HlsVideoMediaTracks extends (BaseClass as Constructor<MediaTracksHost>) {\n #abort = new AbortController();\n #destroyed = false;\n // Memoized SPF model — the last `computed` result per type — so a DOM\n // selection maps back to the engine's match criteria by id via the SPF\n // selection helpers (the DOM lists carry only DOM-shape props).\n #renditions: VideoTrack[] = [];\n #audioTracks: AudioTrack[] = [];\n\n constructor(...args: any[]) {\n super(...args);\n\n const { state } = this.engine;\n const { signal } = this.#abort;\n\n const renditionsSignal = computed(() => dedupedVideoTracks(state.presentation.get()), { equals: sameIds });\n const audioTracksSignal = computed(() => dedupedAudioTracks(state.presentation.get()), { equals: sameIds });\n\n // Rebuild video renditions when the set changes; seed `active` from the\n // current resolved selection (read untracked — `active` deltas are the\n // reflection effect's job).\n const reflectRenditions = () => {\n const renditions = renditionsSignal.get();\n\n this.#renditions = renditions;\n this.#removeVideoTracks();\n\n if (!renditions.length) return;\n\n // \"video track\" here is the Media UI Extensions concept, which maps onto\n // the SPF model differently than the name suggests:\n // MUE track -> SPF selection set\n // MUE rendition -> SPF track\n // @see https://github.com/video-dev/media-ui-extensions/blob/main/proposals/0011-renditions-list.md\n //\n // Every rendition hangs off a single \"main\" video track. Multi-track\n // video (multiple camera angles, CMAF multi-track) isn't supported yet,\n // so exactly one video track is always exposed; when it lands, this\n // becomes one track (selection set) per angle.\n const videoTrack = this.addVideoTrack('main');\n\n videoTrack.selected = true;\n\n const resolved = untrack(() => findVideoTrackById(state.presentation.get(), state.selectedVideoTrackId.get()));\n\n for (const rendition of renditions) {\n const domRendition = videoTrack.addRendition(\n '',\n rendition.width,\n rendition.height,\n rendition.codecs.join(','),\n rendition.bandwidth,\n rendition.frameRate ? frameRateToNumber(rendition.frameRate) : undefined\n );\n\n domRendition.id = rendition.id;\n domRendition.active = isSameVideoTrack(toVideoKey(domRendition), resolved);\n }\n };\n\n const reflectSelectedVideo = () => {\n const resolved = findVideoTrackById(state.presentation.get(), state.selectedVideoTrackId.get());\n\n for (const rendition of this.videoRenditions) {\n rendition.active = isSameVideoTrack(toVideoKey(rendition), resolved);\n }\n };\n\n // Rebuild audio tracks when the set changes; seed `enabled` from the\n // current resolved selection.\n const reflectAudioTracks = () => {\n const tracks = audioTracksSignal.get();\n\n this.#audioTracks = tracks;\n this.#removeAudioTracks();\n\n if (!tracks.length) return;\n\n const resolved = untrack(() => findAudioTrackById(state.presentation.get(), state.selectedAudioTrackId.get()));\n\n for (const track of tracks) {\n const domTrack = this.addAudioTrack(track.default ? 'main' : 'alternative', track.name, track.language ?? '');\n\n domTrack.id = track.id;\n domTrack.enabled = isSameAudioTrack(toAudioKey(domTrack), resolved);\n }\n };\n\n const reflectSelectedAudio = () => {\n const resolved = findAudioTrackById(state.presentation.get(), state.selectedAudioTrackId.get());\n\n for (const track of this.audioTracks) {\n track.enabled = isSameAudioTrack(toAudioKey(track), resolved);\n }\n };\n\n // A source change (new manifest URL) invalidates any pinned selection:\n // rendition identity (width/height/bandwidth) and audio identity\n // (language/name) don't reliably carry across sources, so drop both pins\n // and let the engine re-pick for the new source.\n const sourceUrl = computed(() => state.presentation.get()?.url);\n const resetSelectionOnSourceChange = () => {\n sourceUrl.get();\n state.userVideoTrackSelection.set(undefined);\n state.userAudioTrackSelection.set(undefined);\n };\n\n const effectCleanups = [\n effect(reflectRenditions),\n effect(reflectSelectedVideo),\n effect(reflectAudioTracks),\n effect(reflectSelectedAudio),\n effect(resetSelectionOnSourceChange),\n ];\n\n this.videoRenditions.addEventListener('change', this.#selectRendition, { signal });\n this.audioTracks.addEventListener('change', this.#selectAudio, { signal });\n signal.addEventListener('abort', () => effectCleanups.forEach((cleanup) => cleanup()), { once: true });\n }\n\n destroy(): void {\n if (this.#destroyed) return;\n\n this.#destroyed = true;\n this.#abort.abort();\n this.#removeVideoTracks();\n this.#removeAudioTracks();\n super.destroy?.();\n }\n\n // Manual quality pin → the chosen rendition's width/height/bandwidth as the\n // match criteria (the properties renditions were deduped on).\n // `selectedIndex === -1` (Auto) clears the pin, resuming ABR.\n #selectRendition = () => {\n const { userVideoTrackSelection } = this.engine.state;\n const index = this.videoRenditions.selectedIndex;\n const domRendition = index < 0 ? undefined : this.videoRenditions[index];\n const rendition = this.#renditions.find((candidate) => candidate.id === domRendition?.id);\n\n userVideoTrackSelection.set(toUserVideoTrackSelection(rendition));\n };\n\n #selectAudio = () => {\n const { presentation, selectedAudioTrackId, userAudioTrackSelection } = this.engine.state;\n const resolved = findAudioTrackById(presentation.get(), selectedAudioTrackId.get());\n const current = [...this.audioTracks].find((track) => isSameAudioTrack(toAudioKey(track), resolved));\n\n // `enabled` is not exclusive like video `selected`, so prefer a newly\n // enabled track over the one that is already playing.\n const enabled = [...this.audioTracks].filter((track) => track.enabled);\n const target = enabled.find((track) => track !== current) ?? enabled[0];\n if (!target) return;\n\n // Disable the rest so future change events resolve unambiguously.\n for (const track of enabled) {\n if (track !== target) track.enabled = false;\n }\n\n // Skip the write when the enabled track is already the resolved one\n // that's this projection's own reflection (or a no-op re-affirm), not a user switch.\n if (target === current) return;\n\n const audioTrack = this.#audioTracks.find((candidate) => candidate.id === target.id);\n\n userAudioTrackSelection.set(toUserAudioTrackSelection(audioTrack));\n };\n\n #removeVideoTracks() {\n for (const videoTrack of [...this.videoTracks]) this.removeVideoTrack(videoTrack);\n }\n\n #removeAudioTracks() {\n for (const audioTrack of [...this.audioTracks]) this.removeAudioTrack(audioTrack);\n }\n }\n\n return HlsVideoMediaTracks as unknown as Base;\n}\n"],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { Composition } from "../../../core/composition/create-composition.js";
2
+ import { HlsVideoMediaError } from "./error-surface.js";
3
+ import { HlsVideoEngineContext, HlsVideoEngineState } from "../../engines/hls/engine.js";
4
+ import { MediaStreamType } from "@videojs/media";
5
+ import { Constructor, MixinReturn } from "@videojs/utils/types";
6
+ //#region src/playback/adapters/hls-video/mixin.d.ts
7
+ /**
8
+ * The media-level stream type: the engine's detected stream type (`'live'` / `'on-demand'`) plus `'unknown'` for "no
9
+ * playlist parsed yet." `@videojs/media`'s {@link MediaStreamType} itself, kept under its own name because that is what
10
+ * this adapter's `streamType` property is documented against.
11
+ */
12
+ type HlsVideoMediaStreamType = MediaStreamType;
13
+ interface HlsVideoAdapterProps {
14
+ src: string;
15
+ preload: '' | 'none' | 'metadata' | 'auto';
16
+ disableRemotePlayback: boolean;
17
+ streamType: HlsVideoMediaStreamType;
18
+ }
19
+ interface HlsVideoAdapterAPI extends HlsVideoAdapterProps {
20
+ readonly engine: Composition<HlsVideoEngineState, HlsVideoEngineContext>;
21
+ readonly error: HlsVideoMediaError | null;
22
+ readonly liveEdgeStart: number;
23
+ readonly targetLiveWindow: number;
24
+ attach(mediaElement: HTMLMediaElement): void;
25
+ detach(): void;
26
+ destroy(): void;
27
+ play(): Promise<void>;
28
+ }
29
+ /**
30
+ * Mixin that adds SPF playback engine behavior to any base class.
31
+ *
32
+ * Implements the src/play() contract per the WHATWG HTML spec so that SPF can be used anywhere a media element API is
33
+ * expected.
34
+ *
35
+ * A single engine instance is created at construction and recycled across src changes.
36
+ *
37
+ * @example
38
+ * class HlsVideoAdapter extends HlsVideoMixin(HTMLVideoAdapter) {}
39
+ *
40
+ * const media = new HlsVideoAdapter();
41
+ * media.attach(document.querySelector('video'));
42
+ * media.src = 'https://stream.mux.com/abc123.m3u8';
43
+ *
44
+ * @fires streamtypechange - Fired when the detected stream type changes. Read `streamType` for the new value.
45
+ * @fires targetlivewindowchange - Fired when the target live window changes. Read `targetLiveWindow` for the new value.
46
+ * @fires error - Fired when a fatal condition is reported. Read `error` for it.
47
+ */
48
+ declare function HlsVideoMixin<Base extends Constructor<any>>(BaseClass: Base): MixinReturn<Base, HlsVideoAdapterAPI> & {
49
+ readonly alternativeMediaSuggestion: string | undefined;
50
+ readonly defaultProps: HlsVideoAdapterProps;
51
+ };
52
+ declare const HlsVideoAdapterCore_base: Constructor<{} & HlsVideoAdapterAPI, any[]> & Omit<{
53
+ new (): {};
54
+ }, "prototype"> & {
55
+ readonly alternativeMediaSuggestion: string | undefined;
56
+ readonly defaultProps: HlsVideoAdapterProps;
57
+ };
58
+ /** Standalone SPF media adapter with no base class. */
59
+ declare class HlsVideoAdapterCore extends HlsVideoAdapterCore_base {}
60
+ //#endregion
61
+ export { HlsVideoAdapterAPI, HlsVideoAdapterCore, HlsVideoAdapterProps, type HlsVideoMediaError, HlsVideoMediaStreamType, HlsVideoMixin };
62
+ //# sourceMappingURL=mixin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mixin.d.ts","names":[],"sources":["../../../../../src/playback/adapters/hls-video/mixin.ts"],"mappings":";;;;;;;;;;;KA6CY,0BAA0B;UAIrB;EACf;EACA;EACA;EACA,YAAY;;UAGG,2BAA2B;WACjC,QAAQ,YAAY,qBAAqB;WACzC,OAAO;WACP;WACA;EACT,OAAO,cAAc;EACrB;EACA;EACA,QAAQ;;;;;;;;;;;;;;;;;;;;;iBA8DM,cAAc,aAAa,kBAAkB,WAAW,OAkYpC,YAAY,MAAM;WACzC;WACA,cAAc;;;;;WADc;WACd,cAAA;;;cAKd,4BAA4B"}
@@ -0,0 +1,299 @@
1
+ import { effect } from "../../../core/signals/effect.js";
2
+ import { deriveStreamType, getMediaPlaylistMetadata, isResolvedPresentation, isResolvedTrack } from "../../../media/types/index.js";
3
+ import { findTrackById } from "../../../media/utils/tracks.js";
4
+ import { SVTA_NO_SUPPORTED_AUDIO_TRACK, SVTA_NO_SUPPORTED_VIDEO_TRACK, SVTA_UNSUPPORTED_PLAYBACK_FEATURE } from "../../../media/errors.js";
5
+ import { resolveLiveLatency } from "../../../media/hls/reload-policy.js";
6
+ import { getLiveEdge, liveTrackId } from "../../primitives/live-window.js";
7
+ import { createHlsVideoEngine } from "../../engines/hls/engine.js";
8
+ import { DVR_EXPERIMENTAL_MESSAGE, LOW_LATENCY_UNSUPPORTED_MESSAGE, UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE } from "../../primitives/error-messages.js";
9
+ import { firstFatal, hasUnsupportedFeatureCause, withAlternativeMediaSuggestion } from "./error-surface.js";
10
+ import { MediaStreamTypes } from "@videojs/media";
11
+ //#region src/playback/adapters/hls-video/mixin.ts
12
+ /**
13
+ * `targetLiveWindow` per the media-ui-extensions live-edge proposal: `NaN` for on-demand (or nothing resolved yet), `0`
14
+ * for standard sliding-window live, `Infinity` for DVR (`#EXT-X-PLAYLIST-TYPE:EVENT` — the window grows from the
15
+ * start). Read from the timeline-bearing track's playlist metadata.
16
+ */
17
+ function deriveTargetLiveWindow(presentation, trackId) {
18
+ if (!isResolvedPresentation(presentation) || !trackId) return NaN;
19
+ const track = findTrackById(presentation, trackId);
20
+ if (!track || !isResolvedTrack(track)) return NaN;
21
+ const metadata = getMediaPlaylistMetadata(track);
22
+ if (!metadata) return NaN;
23
+ if (metadata.playlistType === "EVENT") return Number.POSITIVE_INFINITY;
24
+ return deriveStreamType(metadata) === "live" ? 0 : NaN;
25
+ }
26
+ /**
27
+ * Which reported conditions this composition treats as **fatal** — the ones that reach `error` and fire `'error'`.
28
+ * Severity isn't part of an SVTA code (§Approach: "impact varies with player implementation"), and here it also varies
29
+ * by composition, so it's decided at this boundary rather than by the reporter.
30
+ *
31
+ * An allow-list, deliberately: only _verdicts_ are here. The per-rendition causes `resolve-track` reports (unsupported
32
+ * format, unsupported DRM) stay in the sequence as context — one unplayable rendition doesn't fail the source, and
33
+ * promoting a cause would put a dialog over a mixed source that goes on to play.
34
+ */
35
+ const FATAL_SVTA_CODES = /* @__PURE__ */ new Set([SVTA_NO_SUPPORTED_VIDEO_TRACK, SVTA_NO_SUPPORTED_AUDIO_TRACK]);
36
+ /**
37
+ * Mixin that adds SPF playback engine behavior to any base class.
38
+ *
39
+ * Implements the src/play() contract per the WHATWG HTML spec so that SPF can be used anywhere a media element API is
40
+ * expected.
41
+ *
42
+ * A single engine instance is created at construction and recycled across src changes.
43
+ *
44
+ * @example
45
+ * class HlsVideoAdapter extends HlsVideoMixin(HTMLVideoAdapter) {}
46
+ *
47
+ * const media = new HlsVideoAdapter();
48
+ * media.attach(document.querySelector('video'));
49
+ * media.src = 'https://stream.mux.com/abc123.m3u8';
50
+ *
51
+ * @fires streamtypechange - Fired when the detected stream type changes. Read `streamType` for the new value.
52
+ * @fires targetlivewindowchange - Fired when the target live window changes. Read `targetLiveWindow` for the new value.
53
+ * @fires error - Fired when a fatal condition is reported. Read `error` for it.
54
+ */
55
+ function HlsVideoMixin(BaseClass) {
56
+ class HlsVideoImpl extends BaseClass {
57
+ static defaultProps = {
58
+ src: "",
59
+ preload: "",
60
+ disableRemotePlayback: false,
61
+ streamType: MediaStreamTypes.UNKNOWN
62
+ };
63
+ /**
64
+ * A complete sentence naming the Media to reach for when this one can't play a source — `Try the hls.js-backed Mux
65
+ * media instead: import the hls-js flavor in place of the spf one.` Appended to the copy this adapter surfaces, and
66
+ * to the notices it logs. Name the flavor, not an import path: a Media is reached through several packages, each
67
+ * with its own counterpart.
68
+ *
69
+ * Empty here: `hls-video` has no better-equipped sibling to point at. A Media that does (a Mux Video built on this
70
+ * engine, whose hls.js-backed counterpart plays MPEG-TS and DRM) overrides this static, and its copy gains the
71
+ * second sentence with no other change.
72
+ */
73
+ static get alternativeMediaSuggestion() {}
74
+ #engine;
75
+ #config;
76
+ #signals;
77
+ #preload = HlsVideoImpl.defaultProps.preload;
78
+ #disableRemotePlayback = HlsVideoImpl.defaultProps.disableRemotePlayback;
79
+ #streamType = HlsVideoImpl.defaultProps.streamType;
80
+ #isUserStreamType = false;
81
+ #targetLiveWindow = NaN;
82
+ #error = null;
83
+ /**
84
+ * The _reported_ condition currently surfaced, which is what the re-fire latch keys on. Not `#error.code`: that's
85
+ * the code this adapter chose to surface, and a later cause can change the choice for a condition already
86
+ * announced.
87
+ */
88
+ #reportedCode = null;
89
+ /** Notices already logged for the current source; cleared when it unloads. */
90
+ #noticed = /* @__PURE__ */ new Set();
91
+ #stopLiveSync;
92
+ #stopErrorSync;
93
+ /** Pending loadstart listener from a deferred play() retry, if any. */
94
+ #loadstartListener = null;
95
+ constructor(...args) {
96
+ super(...args);
97
+ const { config } = args?.[0] ?? {};
98
+ this.#config = config;
99
+ this.#engine = this.#createEngine();
100
+ this.#stopLiveSync = effect(() => {
101
+ const presentation = this.#signals.state.presentation.get();
102
+ this.#setDetectedStreamType(presentation?.streamType ?? MediaStreamTypes.UNKNOWN);
103
+ this.#setTargetLiveWindow(deriveTargetLiveWindow(presentation, liveTrackId(this.#signals.state)));
104
+ this.#reportDeliveryNotices(presentation);
105
+ });
106
+ this.#stopErrorSync = effect(() => {
107
+ const errors = this.#signals.state.errors.get();
108
+ this.#setError(firstFatal(errors, FATAL_SVTA_CODES), errors);
109
+ });
110
+ }
111
+ /**
112
+ * Underlying playback engine — the low-level SPF reactive composition that drives playback. An advanced escape
113
+ * hatch for direct engine access; normal playback is driven through this element's own properties and methods.
114
+ */
115
+ get engine() {
116
+ return this.#engine;
117
+ }
118
+ /**
119
+ * The current fatal error, or `null`. Only _fatal_ conditions appear here — the engine reports non-fatal ones too
120
+ * (they stay in `engine.state.errors`), and promoting them would tell a consumer playback had failed when it
121
+ * hadn't. Resets per source. Fires `'error'` when set.
122
+ */
123
+ get error() {
124
+ return this.#error;
125
+ }
126
+ /**
127
+ * The source's stream type — `'live'`, `'on-demand'`, or `'unknown'` until a media playlist has been parsed.
128
+ * Setting a non-`'unknown'` value pins a user override (detection stops updating it); setting `'unknown'` reverts
129
+ * to the engine's detected value.
130
+ */
131
+ get streamType() {
132
+ return this.#streamType;
133
+ }
134
+ set streamType(value) {
135
+ if (value === MediaStreamTypes.UNKNOWN) {
136
+ this.#isUserStreamType = false;
137
+ this.#updateStreamType(this.#signals.state.presentation.get()?.streamType ?? MediaStreamTypes.UNKNOWN);
138
+ return;
139
+ }
140
+ this.#isUserStreamType = true;
141
+ this.#updateStreamType(value);
142
+ }
143
+ /**
144
+ * Presentation time marking the start of the live-edge window — playback at `currentTime >= liveEdgeStart` counts
145
+ * as "at the live edge" (the same target the engine's `seekToLiveEdge` seeks to: window end − HOLD-BACK). `NaN`
146
+ * when the stream isn't live or nothing is resolved yet. Derived at read time from the engine's live window — no
147
+ * change event; re-read on `timeupdate`/`progress` (as the store's live feature does).
148
+ */
149
+ get liveEdgeStart() {
150
+ return getLiveEdge({
151
+ state: this.#signals.state,
152
+ config: { resolveLiveLatency }
153
+ })?.liveEdgeStart ?? NaN;
154
+ }
155
+ /**
156
+ * The target live window: `NaN` for on-demand (or unknown), `0` for standard sliding-window live, `Infinity` for
157
+ * DVR (`#EXT-X-PLAYLIST-TYPE:EVENT`). Fires `targetlivewindowchange` on change.
158
+ */
159
+ get targetLiveWindow() {
160
+ return this.#targetLiveWindow;
161
+ }
162
+ #setDetectedStreamType(value) {
163
+ if (this.#isUserStreamType) return;
164
+ this.#updateStreamType(value);
165
+ }
166
+ #updateStreamType(value) {
167
+ if (this.#streamType === value) return;
168
+ this.#streamType = value;
169
+ this.dispatchEvent?.(new Event("streamtypechange"));
170
+ }
171
+ #setTargetLiveWindow(value) {
172
+ if (Object.is(this.#targetLiveWindow, value)) return;
173
+ this.#targetLiveWindow = value;
174
+ this.dispatchEvent?.(new Event("targetlivewindowchange"));
175
+ }
176
+ #setError(reported, errors) {
177
+ if (!reported) {
178
+ this.#error = null;
179
+ this.#reportedCode = null;
180
+ return;
181
+ }
182
+ if (this.#reportedCode === reported.code) return;
183
+ this.#reportedCode = reported.code;
184
+ const unsupported = hasUnsupportedFeatureCause(errors);
185
+ if (unsupported) console.error(this.#withSuggestion(UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE), { conditions: errors });
186
+ this.#error = {
187
+ code: unsupported ? SVTA_UNSUPPORTED_PLAYBACK_FEATURE : reported.code,
188
+ message: reported.message ?? "",
189
+ ...reported.data === void 0 ? {} : { data: reported.data }
190
+ };
191
+ this.dispatchEvent?.(new Event("error"));
192
+ }
193
+ attach(mediaElement) {
194
+ super.attach?.(mediaElement);
195
+ this.#signals.context.mediaElement.set(mediaElement);
196
+ }
197
+ detach() {
198
+ this.#cancelPendingPlay();
199
+ this.#signals.context.mediaElement.set(void 0);
200
+ super.detach?.();
201
+ }
202
+ destroy() {
203
+ this.#cancelPendingPlay();
204
+ this.#stopLiveSync();
205
+ this.#stopErrorSync();
206
+ this.#engine.destroy();
207
+ }
208
+ /** Preload type (`'none'` / `'metadata'` / `'auto'`). */
209
+ get preload() {
210
+ return this.#preload;
211
+ }
212
+ set preload(value) {
213
+ this.#preload = value;
214
+ if (value) this.#signals.state.preload.set(value);
215
+ }
216
+ get disableRemotePlayback() {
217
+ return this.#disableRemotePlayback;
218
+ }
219
+ set disableRemotePlayback(value) {
220
+ this.#disableRemotePlayback = value;
221
+ this.#signals.state.disableRemotePlayback.set(value);
222
+ }
223
+ get src() {
224
+ return this.#signals.state.presentation.get()?.url ?? "";
225
+ }
226
+ set src(value) {
227
+ if (value === this.src) return;
228
+ this.#cancelPendingPlay();
229
+ this.#signals.state.presentation.set(value ? { url: value } : void 0);
230
+ }
231
+ play() {
232
+ const mediaElement = this.#signals.context.mediaElement.get();
233
+ if (!mediaElement) return Promise.reject(/* @__PURE__ */ new Error("HlsVideoAdapterCore: no media element attached"));
234
+ this.#signals.state.loadActivated.set(true);
235
+ return mediaElement.play().catch((err) => {
236
+ if (this.src) return new Promise((resolve, reject) => {
237
+ const listener = () => {
238
+ this.#loadstartListener = null;
239
+ mediaElement.play().then(resolve, reject);
240
+ };
241
+ this.#loadstartListener = listener;
242
+ mediaElement.addEventListener("loadstart", listener, { once: true });
243
+ });
244
+ throw err;
245
+ });
246
+ }
247
+ /** `message`, plus the alternative-Media sentence when this class names one. */
248
+ #withSuggestion(message) {
249
+ return withAlternativeMediaSuggestion(message, this);
250
+ }
251
+ /**
252
+ * Log what this engine is delivering differently from what the playlist asked for. Neither condition stops
253
+ * playback, so neither is an error — they go to the console rather than the error surface.
254
+ *
255
+ * Once per source, not per parse: a live playlist reloads every target duration, and the timeline track re-parses
256
+ * on each one. Keyed on the notice rather than latched with a boolean so the two are independent, and cleared when
257
+ * the presentation unresolves so the next source starts quiet.
258
+ */
259
+ #reportDeliveryNotices(presentation) {
260
+ if (!isResolvedPresentation(presentation)) {
261
+ this.#noticed.clear();
262
+ return;
263
+ }
264
+ const trackId = liveTrackId(this.#signals.state);
265
+ const track = trackId ? findTrackById(presentation, trackId) : void 0;
266
+ if (!track || !isResolvedTrack(track)) return;
267
+ const metadata = getMediaPlaylistMetadata(track);
268
+ if (!metadata) return;
269
+ if (metadata.lowLatency && !this.#noticed.has("lowLatency")) {
270
+ this.#noticed.add("lowLatency");
271
+ console.warn(this.#withSuggestion(LOW_LATENCY_UNSUPPORTED_MESSAGE));
272
+ }
273
+ if (metadata.playlistType === "EVENT" && !this.#noticed.has("dvr")) {
274
+ this.#noticed.add("dvr");
275
+ console.warn(this.#withSuggestion(DVR_EXPERIMENTAL_MESSAGE));
276
+ }
277
+ }
278
+ #createEngine() {
279
+ return createHlsVideoEngine({
280
+ ...this.#config,
281
+ onSignalsReady: (signals) => {
282
+ this.#signals = signals;
283
+ }
284
+ });
285
+ }
286
+ #cancelPendingPlay() {
287
+ if (!this.#loadstartListener) return;
288
+ this.#signals.context.mediaElement.get()?.removeEventListener("loadstart", this.#loadstartListener);
289
+ this.#loadstartListener = null;
290
+ }
291
+ }
292
+ return HlsVideoImpl;
293
+ }
294
+ /** Standalone SPF media adapter with no base class. */
295
+ var HlsVideoAdapterCore = class extends HlsVideoMixin(class {}) {};
296
+ //#endregion
297
+ export { HlsVideoAdapterCore, HlsVideoMixin };
298
+
299
+ //# sourceMappingURL=mixin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mixin.js","names":["#engine","#config","#createEngine","#stopLiveSync","#signals","#setDetectedStreamType","#setTargetLiveWindow","#reportDeliveryNotices","#stopErrorSync","#setError","#error","#streamType","#isUserStreamType","#updateStreamType","#targetLiveWindow","#reportedCode","#withSuggestion","#cancelPendingPlay","#preload","#disableRemotePlayback","#loadstartListener","#noticed"],"sources":["../../../../../src/playback/adapters/hls-video/mixin.ts"],"sourcesContent":["import { type MediaStreamType, MediaStreamTypes } from '@videojs/media';\nimport type { Constructor, MixinReturn } from '@videojs/utils/types';\n\nimport type { Composition } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport {\n SVTA_NO_SUPPORTED_AUDIO_TRACK,\n SVTA_NO_SUPPORTED_VIDEO_TRACK,\n SVTA_UNSUPPORTED_PLAYBACK_FEATURE,\n type SvtaError,\n} from '../../../media/errors';\nimport { resolveLiveLatency } from '../../../media/hls/reload-policy';\nimport {\n deriveStreamType,\n getMediaPlaylistMetadata,\n isResolvedPresentation,\n isResolvedTrack,\n type MaybeResolvedPresentation,\n} from '../../../media/types';\nimport { findTrackById } from '../../../media/utils/tracks';\nimport {\n createHlsVideoEngine,\n type HlsVideoEngineConfig,\n type HlsVideoEngineContext,\n type HlsVideoEngineSignals,\n type HlsVideoEngineState,\n} from '../../engines/hls/engine';\nimport {\n DVR_EXPERIMENTAL_MESSAGE,\n LOW_LATENCY_UNSUPPORTED_MESSAGE,\n UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE,\n} from '../../primitives/error-messages';\nimport { getLiveEdge, type LiveWindowState, liveTrackId } from '../../primitives/live-window';\nimport {\n firstFatal,\n type HlsVideoMediaError,\n hasUnsupportedFeatureCause,\n withAlternativeMediaSuggestion,\n} from './error-surface';\n\n/**\n * The media-level stream type: the engine's detected stream type (`'live'` / `'on-demand'`) plus `'unknown'` for \"no\n * playlist parsed yet.\" `@videojs/media`'s {@link MediaStreamType} itself, kept under its own name because that is what\n * this adapter's `streamType` property is documented against.\n */\nexport type HlsVideoMediaStreamType = MediaStreamType;\n\nexport type { HlsVideoMediaError } from './error-surface';\n\nexport interface HlsVideoAdapterProps {\n src: string;\n preload: '' | 'none' | 'metadata' | 'auto';\n disableRemotePlayback: boolean;\n streamType: HlsVideoMediaStreamType;\n}\n\nexport interface HlsVideoAdapterAPI extends HlsVideoAdapterProps {\n readonly engine: Composition<HlsVideoEngineState, HlsVideoEngineContext>;\n readonly error: HlsVideoMediaError | null;\n readonly liveEdgeStart: number;\n readonly targetLiveWindow: number;\n attach(mediaElement: HTMLMediaElement): void;\n detach(): void;\n destroy(): void;\n play(): Promise<void>;\n}\n\n/**\n * `targetLiveWindow` per the media-ui-extensions live-edge proposal: `NaN` for on-demand (or nothing resolved yet), `0`\n * for standard sliding-window live, `Infinity` for DVR (`#EXT-X-PLAYLIST-TYPE:EVENT` — the window grows from the\n * start). Read from the timeline-bearing track's playlist metadata.\n */\nfunction deriveTargetLiveWindow(\n presentation: MaybeResolvedPresentation | undefined,\n trackId: string | undefined\n): number {\n if (!isResolvedPresentation(presentation) || !trackId) return Number.NaN;\n\n const track = findTrackById(presentation, trackId);\n if (!track || !isResolvedTrack(track)) return Number.NaN;\n\n const metadata = getMediaPlaylistMetadata(track);\n if (!metadata) return Number.NaN;\n\n if (metadata.playlistType === 'EVENT') return Number.POSITIVE_INFINITY;\n\n return deriveStreamType(metadata) === 'live' ? 0 : Number.NaN;\n}\n\n// ============================================================================\n// Error surface\n// ============================================================================\n\n/**\n * Which reported conditions this composition treats as **fatal** — the ones that reach `error` and fire `'error'`.\n * Severity isn't part of an SVTA code (§Approach: \"impact varies with player implementation\"), and here it also varies\n * by composition, so it's decided at this boundary rather than by the reporter.\n *\n * An allow-list, deliberately: only _verdicts_ are here. The per-rendition causes `resolve-track` reports (unsupported\n * format, unsupported DRM) stay in the sequence as context — one unplayable rendition doesn't fail the source, and\n * promoting a cause would put a dialog over a mixed source that goes on to play.\n */\nconst FATAL_SVTA_CODES: ReadonlySet<number> = new Set<number>([\n SVTA_NO_SUPPORTED_VIDEO_TRACK,\n SVTA_NO_SUPPORTED_AUDIO_TRACK,\n]);\n\n/**\n * Mixin that adds SPF playback engine behavior to any base class.\n *\n * Implements the src/play() contract per the WHATWG HTML spec so that SPF can be used anywhere a media element API is\n * expected.\n *\n * A single engine instance is created at construction and recycled across src changes.\n *\n * @example\n * class HlsVideoAdapter extends HlsVideoMixin(HTMLVideoAdapter) {}\n *\n * const media = new HlsVideoAdapter();\n * media.attach(document.querySelector('video'));\n * media.src = 'https://stream.mux.com/abc123.m3u8';\n *\n * @fires streamtypechange - Fired when the detected stream type changes. Read `streamType` for the new value.\n * @fires targetlivewindowchange - Fired when the target live window changes. Read `targetLiveWindow` for the new value.\n * @fires error - Fired when a fatal condition is reported. Read `error` for it.\n */\nexport function HlsVideoMixin<Base extends Constructor<any>>(BaseClass: Base) {\n class HlsVideoImpl extends BaseClass {\n static readonly defaultProps: HlsVideoAdapterProps = {\n src: '',\n preload: '',\n disableRemotePlayback: false,\n streamType: MediaStreamTypes.UNKNOWN,\n };\n\n /**\n * A complete sentence naming the Media to reach for when this one can't play a source — `Try the hls.js-backed Mux\n * media instead: import the hls-js flavor in place of the spf one.` Appended to the copy this adapter surfaces, and\n * to the notices it logs. Name the flavor, not an import path: a Media is reached through several packages, each\n * with its own counterpart.\n *\n * Empty here: `hls-video` has no better-equipped sibling to point at. A Media that does (a Mux Video built on this\n * engine, whose hls.js-backed counterpart plays MPEG-TS and DRM) overrides this static, and its copy gains the\n * second sentence with no other change.\n */\n static get alternativeMediaSuggestion(): string | undefined {\n return undefined;\n }\n\n readonly #engine: Composition<HlsVideoEngineState, HlsVideoEngineContext>;\n #config: HlsVideoEngineConfig;\n #signals!: HlsVideoEngineSignals;\n #preload: '' | 'none' | 'metadata' | 'auto' = HlsVideoImpl.defaultProps.preload;\n #disableRemotePlayback: boolean = HlsVideoImpl.defaultProps.disableRemotePlayback;\n #streamType: HlsVideoMediaStreamType = HlsVideoImpl.defaultProps.streamType;\n #isUserStreamType = false;\n #targetLiveWindow = Number.NaN;\n #error: HlsVideoMediaError | null = null;\n /**\n * The _reported_ condition currently surfaced, which is what the re-fire latch keys on. Not `#error.code`: that's\n * the code this adapter chose to surface, and a later cause can change the choice for a condition already\n * announced.\n */\n #reportedCode: number | null = null;\n /** Notices already logged for the current source; cleared when it unloads. */\n #noticed = new Set<string>();\n #stopLiveSync: () => void;\n #stopErrorSync: () => void;\n\n /** Pending loadstart listener from a deferred play() retry, if any. */\n #loadstartListener: (() => void) | null = null;\n\n constructor(...args: any[]) {\n super(...args);\n\n const { config } = args?.[0] ?? {};\n\n this.#config = config;\n this.#engine = this.#createEngine();\n\n // Mirror the engine's live/stream-type detection onto the media surface,\n // firing the change events the store features listen for. One effect over\n // the presentation + selection signals; `liveEdgeStart` is deliberately\n // NOT cached here — it's derived at read time (the store re-reads it on\n // `timeupdate`/`progress`), so a sliding window needs no event churn.\n this.#stopLiveSync = effect(() => {\n const presentation = this.#signals.state.presentation.get();\n\n this.#setDetectedStreamType(presentation?.streamType ?? MediaStreamTypes.UNKNOWN);\n this.#setTargetLiveWindow(deriveTargetLiveWindow(presentation, liveTrackId(this.#signals.state)));\n this.#reportDeliveryNotices(presentation);\n });\n\n // Promote the first fatal condition out of the engine's reported sequence\n // onto the media surface. Clearing rides the same signal: `collectErrors`\n // resets the slot per source, so a new source starts with no error without\n // this needing its own source-change hook.\n this.#stopErrorSync = effect(() => {\n const errors = this.#signals.state.errors.get();\n\n this.#setError(firstFatal(errors, FATAL_SVTA_CODES), errors);\n });\n }\n\n /**\n * Underlying playback engine — the low-level SPF reactive composition that drives playback. An advanced escape\n * hatch for direct engine access; normal playback is driven through this element's own properties and methods.\n */\n get engine(): Composition<HlsVideoEngineState, HlsVideoEngineContext> {\n return this.#engine;\n }\n\n // -------------------------------------------------------------------------\n // Live surface — streamType / targetLiveWindow / liveEdgeStart\n // (the MediaStreamTypeCapability + MediaLiveCapability contract the player\n // store's stream-type and live features consume)\n // -------------------------------------------------------------------------\n\n /**\n * The current fatal error, or `null`. Only _fatal_ conditions appear here — the engine reports non-fatal ones too\n * (they stay in `engine.state.errors`), and promoting them would tell a consumer playback had failed when it\n * hadn't. Resets per source. Fires `'error'` when set.\n */\n get error(): HlsVideoMediaError | null {\n return this.#error;\n }\n\n /**\n * The source's stream type — `'live'`, `'on-demand'`, or `'unknown'` until a media playlist has been parsed.\n * Setting a non-`'unknown'` value pins a user override (detection stops updating it); setting `'unknown'` reverts\n * to the engine's detected value.\n */\n get streamType(): HlsVideoMediaStreamType {\n return this.#streamType;\n }\n\n set streamType(value: HlsVideoMediaStreamType) {\n if (value === MediaStreamTypes.UNKNOWN) {\n this.#isUserStreamType = false;\n this.#updateStreamType(this.#signals.state.presentation.get()?.streamType ?? MediaStreamTypes.UNKNOWN);\n return;\n }\n\n this.#isUserStreamType = true;\n this.#updateStreamType(value);\n }\n\n /**\n * Presentation time marking the start of the live-edge window — playback at `currentTime >= liveEdgeStart` counts\n * as \"at the live edge\" (the same target the engine's `seekToLiveEdge` seeks to: window end − HOLD-BACK). `NaN`\n * when the stream isn't live or nothing is resolved yet. Derived at read time from the engine's live window — no\n * change event; re-read on `timeupdate`/`progress` (as the store's live feature does).\n */\n get liveEdgeStart(): number {\n const edge = getLiveEdge({\n state: this.#signals.state as LiveWindowState,\n config: { resolveLiveLatency },\n });\n\n return edge?.liveEdgeStart ?? Number.NaN;\n }\n\n /**\n * The target live window: `NaN` for on-demand (or unknown), `0` for standard sliding-window live, `Infinity` for\n * DVR (`#EXT-X-PLAYLIST-TYPE:EVENT`). Fires `targetlivewindowchange` on change.\n */\n get targetLiveWindow(): number {\n return this.#targetLiveWindow;\n }\n\n #setDetectedStreamType(value: HlsVideoMediaStreamType): void {\n if (this.#isUserStreamType) return;\n\n this.#updateStreamType(value);\n }\n\n #updateStreamType(value: HlsVideoMediaStreamType): void {\n if (this.#streamType === value) return;\n\n this.#streamType = value;\n // Optional-chained: with an EventTarget-less base (`HlsVideoAdapterCore`\n // standalone) there's nowhere to dispatch; hosts forward it to listeners.\n this.dispatchEvent?.(new Event('streamtypechange'));\n }\n\n #setTargetLiveWindow(value: number): void {\n if (Object.is(this.#targetLiveWindow, value)) return;\n\n this.#targetLiveWindow = value;\n this.dispatchEvent?.(new Event('targetlivewindowchange'));\n }\n\n #setError(reported: SvtaError | undefined, errors: readonly SvtaError[] | undefined): void {\n if (!reported) {\n // Cleared (new source). No event: `'error'` announces a failure, and\n // consumers reset their own copy on source change.\n this.#error = null;\n this.#reportedCode = null;\n return;\n }\n\n // Keyed on the code, not the object: a later append re-runs this effect\n // with an equal-but-new array, and re-firing `'error'` for a condition\n // already surfaced would look like a second failure.\n if (this.#reportedCode === reported.code) return;\n\n this.#reportedCode = reported.code;\n\n // A verdict says a type emptied; the causes say whether anything could\n // have played it. When they include something this engine simply doesn't\n // implement, that's the more useful thing to tell a consumer, so it\n // replaces the verdict's code on the surface.\n const unsupported = hasUnsupportedFeatureCause(errors);\n\n if (unsupported) {\n // The only place this engine explains itself in prose, and it's a\n // console: the viewer-facing sentence is the consumer's to localize from\n // the code. Conditions ride along structured so the specifics stay\n // inspectable.\n console.error(this.#withSuggestion(UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE), { conditions: errors });\n }\n\n this.#error = {\n code: unsupported ? SVTA_UNSUPPORTED_PLAYBACK_FEATURE : reported.code,\n message: reported.message ?? '',\n ...(reported.data === undefined ? {} : { data: reported.data }),\n };\n this.dispatchEvent?.(new Event('error'));\n }\n\n // -------------------------------------------------------------------------\n // Media element lifecycle\n // -------------------------------------------------------------------------\n\n attach(mediaElement: HTMLMediaElement): void {\n super.attach?.(mediaElement);\n this.#signals.context.mediaElement.set(mediaElement);\n }\n\n detach(): void {\n this.#cancelPendingPlay();\n this.#signals.context.mediaElement.set(undefined);\n super.detach?.();\n }\n\n destroy(): void {\n this.#cancelPendingPlay();\n this.#stopLiveSync();\n this.#stopErrorSync();\n this.#engine.destroy();\n }\n\n // -------------------------------------------------------------------------\n // preload — synchronous IDL attribute (WHATWG §4.8.11.2)\n // -------------------------------------------------------------------------\n\n /** Preload type (`'none'` / `'metadata'` / `'auto'`). */\n get preload(): '' | 'none' | 'metadata' | 'auto' {\n return this.#preload;\n }\n\n set preload(value: '' | 'none' | 'metadata' | 'auto') {\n this.#preload = value;\n\n if (value) {\n this.#signals.state.preload.set(value);\n }\n // value = '' resets the IDL mirror (so `get preload` reflects '') but does\n // not patch state — the engine keeps its current preload until an explicit\n // W3C value replaces it.\n }\n\n // -------------------------------------------------------------------------\n // disableRemotePlayback — synchronous IDL attribute (WHATWG Remote Playback)\n // Author intent for whether the AirPlay/remote picker is offered. Mirrors\n // the DOM attribute name; the value flows to `state.disableRemotePlayback`,\n // which `setupAirPlay` reads to honor an explicit opt-out. The underlying\n // <video>'s own `disableRemotePlayback` stays programmatically managed\n // (ManagedMediaSource needs it `true` to open; AirPlay flips it `false` once\n // the source is open), so author intent and the effective flag stay distinct.\n // -------------------------------------------------------------------------\n\n get disableRemotePlayback(): boolean {\n return this.#disableRemotePlayback;\n }\n\n set disableRemotePlayback(value: boolean) {\n this.#disableRemotePlayback = value;\n this.#signals.state.disableRemotePlayback.set(value);\n }\n\n // -------------------------------------------------------------------------\n // src — synchronous IDL attribute (WHATWG §4.8.11.2)\n // Each assignment overwrites the engine's presentation state in place. The\n // resolver FSM routes back through teardown → rebuild on the same engine,\n // mirroring how the browser's load algorithm resets media state on src change\n // — without recreating the engine or re-capturing its signals. Setting an\n // empty src un-resolves the presentation, tearing the current source down to\n // the engine's fresh-but-attached \"no source\" state.\n // -------------------------------------------------------------------------\n\n get src(): string {\n return this.#signals.state.presentation.get()?.url ?? '';\n }\n\n set src(value: string) {\n // Assigning the URL already playing is not a request to reload it. The\n // presentation is set from a fresh object every time, so re-resolving an\n // unchanged URL restarts playback — which is what a caller changing only\n // the parts of a structured source that describe images, rather than the\n // stream, would get. The hls.js Media draws the same line: it announces\n // every source change and loads only when the URL or engine config moves.\n if (value === this.src) return;\n\n this.#cancelPendingPlay();\n this.#signals.state.presentation.set(value ? { url: value } : undefined);\n }\n\n // -------------------------------------------------------------------------\n // play() — WHATWG §4.8.11.8\n // Delegates to the attached media element's native play().\n // -------------------------------------------------------------------------\n\n play(): Promise<void> {\n const mediaElement = this.#signals.context.mediaElement.get();\n if (!mediaElement) return Promise.reject(new Error('HlsVideoAdapterCore: no media element attached'));\n\n // Signal play intent — enables loading even with preload=\"none\"\n this.#signals.state.loadActivated.set(true);\n\n return mediaElement.play().catch((err: unknown) => {\n // If we have a pending HLS source, the rejection may be because MSE\n // hasn't attached a blob URL yet. Wait for loadstart (src assigned\n // by MSE setup) and retry once.\n if (this.src) {\n return new Promise<void>((resolve, reject) => {\n const listener = () => {\n this.#loadstartListener = null;\n mediaElement.play().then(resolve, reject);\n };\n\n this.#loadstartListener = listener;\n mediaElement.addEventListener('loadstart', listener, { once: true });\n });\n }\n\n throw err;\n });\n }\n\n // -------------------------------------------------------------------------\n // Private\n // -------------------------------------------------------------------------\n\n /** `message`, plus the alternative-Media sentence when this class names one. */\n #withSuggestion(message: string): string {\n return withAlternativeMediaSuggestion(message, this);\n }\n\n /**\n * Log what this engine is delivering differently from what the playlist asked for. Neither condition stops\n * playback, so neither is an error — they go to the console rather than the error surface.\n *\n * Once per source, not per parse: a live playlist reloads every target duration, and the timeline track re-parses\n * on each one. Keyed on the notice rather than latched with a boolean so the two are independent, and cleared when\n * the presentation unresolves so the next source starts quiet.\n */\n #reportDeliveryNotices(presentation: MaybeResolvedPresentation | undefined): void {\n if (!isResolvedPresentation(presentation)) {\n this.#noticed.clear();\n return;\n }\n\n const trackId = liveTrackId(this.#signals.state);\n const track = trackId ? findTrackById(presentation, trackId) : undefined;\n if (!track || !isResolvedTrack(track)) return;\n\n const metadata = getMediaPlaylistMetadata(track);\n if (!metadata) return;\n\n if (metadata.lowLatency && !this.#noticed.has('lowLatency')) {\n this.#noticed.add('lowLatency');\n console.warn(this.#withSuggestion(LOW_LATENCY_UNSUPPORTED_MESSAGE));\n }\n\n if (metadata.playlistType === 'EVENT' && !this.#noticed.has('dvr')) {\n this.#noticed.add('dvr');\n console.warn(this.#withSuggestion(DVR_EXPERIMENTAL_MESSAGE));\n }\n }\n\n #createEngine(): Composition<HlsVideoEngineState, HlsVideoEngineContext> {\n return createHlsVideoEngine({\n ...this.#config,\n onSignalsReady: (signals) => {\n this.#signals = signals;\n },\n });\n }\n\n #cancelPendingPlay(): void {\n if (!this.#loadstartListener) return;\n\n const mediaElement = this.#signals.context.mediaElement.get();\n\n mediaElement?.removeEventListener('loadstart', this.#loadstartListener);\n this.#loadstartListener = null;\n }\n }\n\n // `MixinReturn` sources statics from `Base`, so the adapter's own static needs\n // adding back to the type or callers can't read it.\n return HlsVideoImpl as unknown as MixinReturn<Base, HlsVideoAdapterAPI> & {\n readonly alternativeMediaSuggestion: string | undefined;\n readonly defaultProps: HlsVideoAdapterProps;\n };\n}\n\n/** Standalone SPF media adapter with no base class. */\nexport class HlsVideoAdapterCore extends HlsVideoMixin(class {}) {}\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { MaybeResolvedPresentation } from "../../media/types/index.js";
2
+ import "../../core/signals/primitives.js";
3
+ import "../../core/composition/create-composition.js";
4
+ //#region src/playback/behaviors/calculate-presentation-duration.d.ts
5
+ /**
6
+ * Input shape passed to the duration resolver. Represents the union of track-selection slots the default
7
+ * `getResolvedSelectedTrackDuration` resolver inspects to pick a representative resolved track. Variants that compose
8
+ * neither audio nor video selection still satisfy this shape — the missing fields read as `undefined` and the resolver
9
+ * falls through.
10
+ */
11
+ interface PresentationDurationState {
12
+ presentation?: MaybeResolvedPresentation;
13
+ selectedVideoTrackId?: string;
14
+ selectedAudioTrackId?: string;
15
+ }
16
+ /**
17
+ * Resolver supplied via `config.resolveDuration`. Returns the duration to write to `presentation.duration`, or
18
+ * `undefined` when it isn't yet derivable. Positive `Infinity` is the canonical live value.
19
+ */
20
+ type PresentationDurationResolver = (state: PresentationDurationState) => number | undefined;
21
+ //#endregion
22
+ export { PresentationDurationResolver, PresentationDurationState };
23
+ //# sourceMappingURL=calculate-presentation-duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calculate-presentation-duration.d.ts","names":[],"sources":["../../../../src/playback/behaviors/calculate-presentation-duration.ts"],"mappings":";;;;;;;;;;UAmCiB;EACf,eAAe;EACf;EACA;;;;;;KAOU,gCAAgC,OAAO"}
@@ -0,0 +1,33 @@
1
+ import { untrack, update } from "../../core/signals/primitives.js";
2
+ import { effect } from "../../core/signals/effect.js";
3
+ //#region src/playback/behaviors/calculate-presentation-duration.ts
4
+ function calculatePresentationDurationSetup({ state, config }) {
5
+ return effect(() => {
6
+ const presentation = state.presentation.get();
7
+ if (!presentation || presentation.duration !== void 0) return;
8
+ const resolverInput = untrack(() => ({
9
+ presentation,
10
+ selectedVideoTrackId: state.selectedVideoTrackId?.get(),
11
+ selectedAudioTrackId: state.selectedAudioTrackId?.get()
12
+ }));
13
+ const duration = config.resolveDuration(resolverInput);
14
+ if (duration === void 0 || Number.isNaN(duration) || duration <= 0) return;
15
+ update(state.presentation, { duration });
16
+ });
17
+ }
18
+ /**
19
+ * `calculatePresentationDuration` uses a manual `Behavior<>` literal (rather than `defineBehavior`) so it can declare
20
+ * just `presentation` in its stateKeys while the typed setup-param shape includes the optional `selectedVideoTrackId` /
21
+ * `selectedAudioTrackId` reads used at runtime. Mirrors the pattern in `endOfStream` for the same reason: the behavior
22
+ * is uniform-across-tracks and reads slots contributed by other behaviors, so it shouldn't leak those slot declarations
23
+ * into variants that don't compose the contributors.
24
+ */
25
+ const calculatePresentationDuration = {
26
+ stateKeys: ["presentation"],
27
+ contextKeys: [],
28
+ setup: calculatePresentationDurationSetup
29
+ };
30
+ //#endregion
31
+ export { calculatePresentationDuration };
32
+
33
+ //# sourceMappingURL=calculate-presentation-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calculate-presentation-duration.js","names":[],"sources":["../../../../src/playback/behaviors/calculate-presentation-duration.ts"],"sourcesContent":["/**\n * **Populate presentation duration via a config-supplied resolver.**\n *\n * Once a presentation is in place and its `duration` is still undefined, calls `config.resolveDuration(state)` whenever\n * a tracked slot changes. The resolver decides what counts as \"duration is now derivable\" for the variant in play; the\n * behavior itself stays variant-agnostic:\n *\n * - **VoD** — derive from the first resolved selected track's `duration` (video preferred, audio fallback). The HLS\n * engine wires `getResolvedSelectedTrackDuration` from `media/utils/track-selection.ts` as the default. Audio-only\n * falls out of the same resolver naturally (no video selected → audio is the first resolved track).\n * - **Live** — return `Number.POSITIVE_INFINITY` once the presentation is established as live. This is the MSE-spec value\n * for `mediaSource.duration` under live playback; downstream `updateMediaSourceDuration` propagates it through.\n *\n * Validation: writes whatever the resolver returns as long as it's a positive number, including `Infinity`. `undefined`\n * / `NaN` / `<= 0` are skipped.\n *\n * Fires at most once per presentation — an already-set `duration` is never overwritten, and the next reset arrives\n * structurally when a new (unresolved) presentation replaces the current one. The resolver may return `undefined` while\n * duration is still indeterminate; subsequent tracked-slot changes re-run the effect until the resolver commits a\n * value.\n *\n * Downstream of `resolveVideoTrack` / `resolveAudioTrack`; upstream of `updateMediaSourceDuration` (which writes the\n * value through to `mediaSource.duration`).\n */\nimport type { Behavior } from '../../core/composition/create-composition';\nimport { effect } from '../../core/signals/effect';\nimport { type ReadonlySignal, type Signal, untrack, update } from '../../core/signals/primitives';\nimport type { MaybeResolvedPresentation } from '../../media/types';\n\n/**\n * Input shape passed to the duration resolver. Represents the union of track-selection slots the default\n * `getResolvedSelectedTrackDuration` resolver inspects to pick a representative resolved track. Variants that compose\n * neither audio nor video selection still satisfy this shape — the missing fields read as `undefined` and the resolver\n * falls through.\n */\nexport interface PresentationDurationState {\n presentation?: MaybeResolvedPresentation;\n selectedVideoTrackId?: string;\n selectedAudioTrackId?: string;\n}\n\n/**\n * Resolver supplied via `config.resolveDuration`. Returns the duration to write to `presentation.duration`, or\n * `undefined` when it isn't yet derivable. Positive `Infinity` is the canonical live value.\n */\nexport type PresentationDurationResolver = (state: PresentationDurationState) => number | undefined;\n\nexport interface PresentationDurationConfig {\n resolveDuration: PresentationDurationResolver;\n}\n\nfunction calculatePresentationDurationSetup({\n state,\n config,\n}: {\n state: {\n presentation: Signal<PresentationDurationState['presentation']>;\n // selectedVideoTrackId / selectedAudioTrackId are read defensively —\n // they are *not* declared in this behavior's stateKeys. The slots are\n // contributed by other behaviors (`switchVideoTrack` writes the\n // video slot in the default engine; `selectAudioTrack` writes the\n // audio slot) which compose conditionally per engine variant. Treating\n // each signal as optional lets calculatePresentationDuration stay\n // variant-agnostic — it feeds whatever's present to the resolver and\n // lets the resolver decide.\n selectedVideoTrackId?: ReadonlySignal<PresentationDurationState['selectedVideoTrackId']>;\n selectedAudioTrackId?: ReadonlySignal<PresentationDurationState['selectedAudioTrackId']>;\n };\n config: PresentationDurationConfig;\n}): () => void {\n return effect(() => {\n const presentation = state.presentation.get();\n if (!presentation || presentation.duration !== undefined) return;\n\n // presentation drives the effect; selection-only changes can't yield a\n // writable duration on their own (resolver returns undefined until a\n // track resolves, at which point resolve-track writes back through\n // state.presentation anyway), so we read the rest untracked.\n const resolverInput: PresentationDurationState = untrack(() => ({\n presentation,\n selectedVideoTrackId: state.selectedVideoTrackId?.get(),\n selectedAudioTrackId: state.selectedAudioTrackId?.get(),\n }));\n const duration = config.resolveDuration(resolverInput);\n if (duration === undefined || Number.isNaN(duration) || duration <= 0) return;\n\n // Patch-object form requires `T extends object`; `state.presentation`'s\n // T is `MaybeResolvedPresentation | undefined`, which doesn't satisfy\n // the constraint. The guard at the top of the effect already\n // established that the slot holds a defined value, so cast to narrow\n // the signal type and use the cleaner merge form.\n update(state.presentation as Signal<MaybeResolvedPresentation>, { duration });\n });\n}\n\n/**\n * `calculatePresentationDuration` uses a manual `Behavior<>` literal (rather than `defineBehavior`) so it can declare\n * just `presentation` in its stateKeys while the typed setup-param shape includes the optional `selectedVideoTrackId` /\n * `selectedAudioTrackId` reads used at runtime. Mirrors the pattern in `endOfStream` for the same reason: the behavior\n * is uniform-across-tracks and reads slots contributed by other behaviors, so it shouldn't leak those slot declarations\n * into variants that don't compose the contributors.\n */\nexport const calculatePresentationDuration: Behavior<\n { presentation: Signal<PresentationDurationState['presentation']> },\n Record<string, never>,\n PresentationDurationConfig\n> = {\n stateKeys: ['presentation'],\n contextKeys: [],\n setup: calculatePresentationDurationSetup,\n};\n"],"mappings":";;;AAmDA,SAAS,mCAAmC,EAC1C,OACA,UAgBa;CACb,OAAO,aAAa;EAClB,MAAM,eAAe,MAAM,aAAa,IAAI;EAC5C,IAAI,CAAC,gBAAgB,aAAa,aAAa,KAAA,GAAW;EAM1D,MAAM,gBAA2C,eAAe;GAC9D;GACA,sBAAsB,MAAM,sBAAsB,IAAI;GACtD,sBAAsB,MAAM,sBAAsB,IAAI;EACxD,EAAE;EACF,MAAM,WAAW,OAAO,gBAAgB,aAAa;EACrD,IAAI,aAAa,KAAA,KAAa,OAAO,MAAM,QAAQ,KAAK,YAAY,GAAG;EAOvE,OAAO,MAAM,cAAmD,EAAE,SAAS,CAAC;CAC9E,CAAC;AACH;;;;;;;;AASA,MAAa,gCAIT;CACF,WAAW,CAAC,cAAc;CAC1B,aAAa,CAAC;CACd,OAAO;AACT"}
@@ -0,0 +1,2 @@
1
+ import "../../core/signals/primitives.js";
2
+ import "../../index.js";
@@ -0,0 +1,98 @@
1
+ import { computed, peek, update } 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 { isResolvedPresentation } from "../../media/types/index.js";
5
+ //#region src/playback/behaviors/collect-errors.ts
6
+ /**
7
+ * **Owns the engine's error sequence.** Reporters append through {@link emitError}; this behavior owns the slot and its
8
+ * per-source lifecycle, clearing it on exit so a new source starts clean and the sequence can't grow unbounded across a
9
+ * session.
10
+ *
11
+ * Same split as `setupFailoverMonitor` and `failedCdns`: writes come from wherever the condition is detected, one
12
+ * behavior owns the slot. Deliberately has no `effects` — it holds no policy and derives nothing. Severity is decided
13
+ * at the adapter, not here (see `internal/design/spf/features/errors.md`), which is why this is a lifecycle owner
14
+ * rather than an error _handler_.
15
+ *
16
+ * Clearing binds to _exit_ of `presentation-resolved`, mirroring the sibling mixins' clear-on-teardown (`emptied` /
17
+ * `MEDIA_DETACHED`). A live reload swaps the presentation object without leaving the resolved state, so it doesn't
18
+ * clear — only an actual source change or destroy does. Known gap: a resolved→resolved source swap that never passes
19
+ * through unresolved carries the prior source's errors forward; `resolve-track` guards the same transition with a
20
+ * commit-time id check, and doing likewise here is a follow-up.
21
+ *
22
+ * The vocabulary itself ({@link SvtaError} and the codes) is DOM- and signal-free in `media/errors`; only the write
23
+ * seam lives here, with the slot it writes.
24
+ */
25
+ /**
26
+ * Append `error` to the engine's error sequence. No-op when no owner is composed. Replaces the array rather than
27
+ * mutating it, so signal consumers notify; duplicates are kept, since a repeated condition is a real observation.
28
+ * Writes go through `update` so concurrent reporters can't lose each other's appends.
29
+ *
30
+ * Every emission is logged, deliberately _before_ the owner check. A condition emitted with no `collectErrors` composed
31
+ * is dropped on the floor — that's the case where a log is the only evidence it happened at all, so gating the log on
32
+ * the same check would hide exactly what's worth seeing. Emissions that _are_ collected still get logged, because
33
+ * reaching `state.errors` is no guarantee of reaching a person: only _verdicts_ are promoted to the media surface, so
34
+ * every cause (and any non-fatal notice) is otherwise invisible outside a debugger.
35
+ *
36
+ * Ungated rather than `__DEV__`-only, matching the other reporting paths in this package (`resolve-presentation`,
37
+ * `track-switching`, the segment actors).
38
+ */
39
+ function emitError(state, error) {
40
+ console.error("[spf] reported condition", error);
41
+ if (!state.errors) return;
42
+ update(state.errors, (errors) => [...errors ?? [], error]);
43
+ }
44
+ /**
45
+ * A "constraint" that reports a type the source carries **no** renditions of, for a composition that can't play without
46
+ * it.
47
+ *
48
+ * Strange on purpose, and the strangeness is the point: it never constrains anything, always returning its input
49
+ * untouched. It is shaped as a rule so a composition opts in by adding it to `constraints` — nothing to thread through
50
+ * config, and no cost at all to a composition that leaves it out.
51
+ *
52
+ * **Belongs last in the chain.** A constraint sees the list as it stands at its own position, so only at the tail does
53
+ * an empty input mean "nothing playable here" — none of this type offered, or the constraints ahead pruned them all.
54
+ *
55
+ * These are the failures no per-rendition cause can report: causes come from `reportUnsupportedTrackConditions` as each
56
+ * media playlist resolves, and a rendition pruned before selection never resolves. Container and encryption aren't
57
+ * knowable until one does; CODECS is, so an undecodable ladder isn't.
58
+ *
59
+ * Idempotent because the constraint chain runs inside a `computed` that re-derives on every `presentation` write —
60
+ * segment appends and live reloads included — and the sequence deliberately keeps duplicates. `peek` is what keeps that
61
+ * computed from subscribing to the slot this writes.
62
+ *
63
+ * @example
64
+ * // engine-background-video.ts — video-only, so a source with none can't play
65
+ * constraints: [excludeUnplayableTracks, reportAbsentTrackType(SVTA_NO_SUPPORTED_VIDEO_TRACK)];
66
+ */
67
+ function reportAbsentTrackType(code) {
68
+ return (tracks, { state }) => {
69
+ const reported = state.errors && peek(state.errors);
70
+ if (!tracks.length && !reported?.some((error) => error.code === code)) emitError(state, { code });
71
+ return tracks;
72
+ };
73
+ }
74
+ /**
75
+ * Own `errors` for the resolved source's lifetime.
76
+ *
77
+ * @example
78
+ * const reactor = collectErrors.setup({ state });
79
+ */
80
+ const collectErrors = defineBehavior({
81
+ stateKeys: ["presentation", "errors"],
82
+ contextKeys: [],
83
+ setup: ({ state }) => {
84
+ const derivedStateSignal = computed(() => isResolvedPresentation(state.presentation.get()) ? "presentation-resolved" : "presentation-unresolved");
85
+ return createMachineReactor({
86
+ initial: "presentation-unresolved",
87
+ monitor: () => derivedStateSignal.get(),
88
+ states: {
89
+ "presentation-unresolved": {},
90
+ "presentation-resolved": { entry: () => () => state.errors.set(void 0) }
91
+ }
92
+ });
93
+ }
94
+ });
95
+ //#endregion
96
+ export { collectErrors, emitError, reportAbsentTrackType };
97
+
98
+ //# sourceMappingURL=collect-errors.js.map