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

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 (520) hide show
  1. package/dist/default/core/actors/create-machine-actor.js +23 -25
  2. package/dist/default/core/actors/create-machine-actor.js.map +1 -1
  3. package/dist/default/core/actors/create-transition-actor.js +7 -9
  4. package/dist/default/core/actors/create-transition-actor.js.map +1 -1
  5. package/dist/default/core/composition/create-composition.js +26 -29
  6. package/dist/default/core/composition/create-composition.js.map +1 -1
  7. package/dist/default/core/composition/share-signals.js +12 -17
  8. package/dist/default/core/composition/share-signals.js.map +1 -1
  9. package/dist/default/core/machine.js +3 -4
  10. package/dist/default/core/machine.js.map +1 -1
  11. package/dist/default/core/reactors/create-machine-reactor.js +20 -19
  12. package/dist/default/core/reactors/create-machine-reactor.js.map +1 -1
  13. package/dist/default/core/signals/effect.js +24 -4
  14. package/dist/default/core/signals/effect.js.map +1 -1
  15. package/dist/default/core/signals/primitives.js +10 -14
  16. package/dist/default/core/signals/primitives.js.map +1 -1
  17. package/dist/default/core/signals/when.js +6 -8
  18. package/dist/default/core/signals/when.js.map +1 -1
  19. package/dist/default/core/tasks/delayed-reschedule.js +7 -11
  20. package/dist/default/core/tasks/delayed-reschedule.js.map +1 -1
  21. package/dist/default/core/tasks/task.js +59 -82
  22. package/dist/default/core/tasks/task.js.map +1 -1
  23. package/dist/default/hls.js +3 -1
  24. package/dist/default/index.js +2 -3
  25. package/dist/default/index.js.map +1 -1
  26. package/dist/default/media/abr/quality-selection.js +3 -7
  27. package/dist/default/media/abr/quality-selection.js.map +1 -1
  28. package/dist/default/media/buffer/back-buffer.js +15 -19
  29. package/dist/default/media/buffer/back-buffer.js.map +1 -1
  30. package/dist/default/media/buffer/forward-buffer.js +32 -41
  31. package/dist/default/media/buffer/forward-buffer.js.map +1 -1
  32. package/dist/default/media/dom/capabilities.js +26 -37
  33. package/dist/default/media/dom/capabilities.js.map +1 -1
  34. package/dist/default/media/dom/mse/append-segment.js +3 -5
  35. package/dist/default/media/dom/mse/append-segment.js.map +1 -1
  36. package/dist/default/media/dom/mse/buffer-flusher.js +5 -5
  37. package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -1
  38. package/dist/default/media/dom/mse/duration.js +23 -31
  39. package/dist/default/media/dom/mse/duration.js.map +1 -1
  40. package/dist/default/media/dom/mse/end-of-stream.js +3 -6
  41. package/dist/default/media/dom/mse/end-of-stream.js.map +1 -1
  42. package/dist/default/media/dom/mse/mediasource-setup.js +65 -81
  43. package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -1
  44. package/dist/default/media/dom/screen.js +37 -54
  45. package/dist/default/media/dom/screen.js.map +1 -1
  46. package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -1
  47. package/dist/default/media/dom/text/text-track-slots.js +15 -23
  48. package/dist/default/media/dom/text/text-track-slots.js.map +1 -1
  49. package/dist/default/media/errors.js +25 -34
  50. package/dist/default/media/errors.js.map +1 -1
  51. package/dist/default/media/hls/parse-attributes.js +9 -26
  52. package/dist/default/media/hls/parse-attributes.js.map +1 -1
  53. package/dist/default/media/hls/parse-media-playlist.js +25 -33
  54. package/dist/default/media/hls/parse-media-playlist.js.map +1 -1
  55. package/dist/default/media/hls/parse-multivariant.js +3 -3
  56. package/dist/default/media/hls/parse-multivariant.js.map +1 -1
  57. package/dist/default/media/hls/reload-policy.js +19 -27
  58. package/dist/default/media/hls/reload-policy.js.map +1 -1
  59. package/dist/default/media/hls/resolve-url.js +1 -3
  60. package/dist/default/media/hls/resolve-url.js.map +1 -1
  61. package/dist/default/media/live-window.js +9 -13
  62. package/dist/default/media/live-window.js.map +1 -1
  63. package/dist/default/media/media-tracks/media-tracks.js +12 -17
  64. package/dist/default/media/media-tracks/media-tracks.js.map +1 -1
  65. package/dist/default/media/mp4/box.js +4 -5
  66. package/dist/default/media/mp4/box.js.map +1 -1
  67. package/dist/default/media/mp4/timestamp-origin.js +30 -42
  68. package/dist/default/media/mp4/timestamp-origin.js.map +1 -1
  69. package/dist/default/media/primitives/resolution.js +5 -8
  70. package/dist/default/media/primitives/resolution.js.map +1 -1
  71. package/dist/default/media/primitives/select-tracks.js +24 -34
  72. package/dist/default/media/primitives/select-tracks.js.map +1 -1
  73. package/dist/default/media/text/parse-vtt-timestamp-map.js +6 -8
  74. package/dist/default/media/text/parse-vtt-timestamp-map.js.map +1 -1
  75. package/dist/default/media/text/resolve-vtt-metadata.js +8 -9
  76. package/dist/default/media/text/resolve-vtt-metadata.js.map +1 -1
  77. package/dist/default/media/types/index.js +10 -18
  78. package/dist/default/media/types/index.js.map +1 -1
  79. package/dist/default/media/utils/cdn.js +10 -16
  80. package/dist/default/media/utils/cdn.js.map +1 -1
  81. package/dist/default/media/utils/preload.js +4 -6
  82. package/dist/default/media/utils/preload.js.map +1 -1
  83. package/dist/default/media/utils/track-selection.js +10 -14
  84. package/dist/default/media/utils/track-selection.js.map +1 -1
  85. package/dist/default/media/utils/tracks.js +56 -54
  86. package/dist/default/media/utils/tracks.js.map +1 -1
  87. package/dist/default/network/bandwidth-estimator.js +18 -19
  88. package/dist/default/network/bandwidth-estimator.js.map +1 -1
  89. package/dist/default/network/chunked-stream-iterable.js +4 -6
  90. package/dist/default/network/chunked-stream-iterable.js.map +1 -1
  91. package/dist/default/network/ewma.js +22 -25
  92. package/dist/default/network/ewma.js.map +1 -1
  93. package/dist/default/network/fetch.js +30 -37
  94. package/dist/default/network/fetch.js.map +1 -1
  95. package/dist/default/playback/actors/dom/segment-loader.js +28 -44
  96. package/dist/default/playback/actors/dom/segment-loader.js.map +1 -1
  97. package/dist/default/playback/actors/dom/source-buffer.js.map +1 -1
  98. package/dist/default/playback/actors/dom/text-tracks.js.map +1 -1
  99. package/dist/default/playback/actors/text-track-segment-loader.js +24 -35
  100. package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -1
  101. package/dist/default/playback/adapters/hls-audio/adapter.js +23 -31
  102. package/dist/default/playback/adapters/hls-audio/adapter.js.map +1 -1
  103. package/dist/default/playback/adapters/hls-audio/media.js.map +1 -1
  104. package/dist/default/playback/adapters/hls-background-video/adapter.js +50 -70
  105. package/dist/default/playback/adapters/hls-background-video/adapter.js.map +1 -1
  106. package/dist/default/playback/adapters/hls-background-video/host.js +12 -16
  107. package/dist/default/playback/adapters/hls-background-video/host.js.map +1 -1
  108. package/dist/default/playback/adapters/hls-background-video/media.js +5 -8
  109. package/dist/default/playback/adapters/hls-background-video/media.js.map +1 -1
  110. package/dist/default/playback/adapters/hls-video/adapter.js +43 -60
  111. package/dist/default/playback/adapters/hls-video/adapter.js.map +1 -1
  112. package/dist/default/playback/adapters/hls-video/error-surface.js +13 -18
  113. package/dist/default/playback/adapters/hls-video/error-surface.js.map +1 -1
  114. package/dist/default/playback/adapters/hls-video/media-tracks.js +4 -5
  115. package/dist/default/playback/adapters/hls-video/media-tracks.js.map +1 -1
  116. package/dist/default/playback/adapters/hls-video/media.js.map +1 -1
  117. package/dist/default/playback/adapters/mux-audio/media.js +8 -12
  118. package/dist/default/playback/adapters/mux-audio/media.js.map +1 -1
  119. package/dist/default/playback/adapters/mux-video/adapter.js +25 -33
  120. package/dist/default/playback/adapters/mux-video/adapter.js.map +1 -1
  121. package/dist/default/playback/adapters/mux-video/media.js +7 -9
  122. package/dist/default/playback/adapters/mux-video/media.js.map +1 -1
  123. package/dist/default/playback/behaviors/calculate-presentation-duration.js +5 -8
  124. package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -1
  125. package/dist/default/playback/behaviors/collect-errors.js +39 -52
  126. package/dist/default/playback/behaviors/collect-errors.js.map +1 -1
  127. package/dist/default/playback/behaviors/derive-cdn-priority.js +16 -23
  128. package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -1
  129. package/dist/default/playback/behaviors/dom/airplay.js +38 -61
  130. package/dist/default/playback/behaviors/dom/airplay.js.map +1 -1
  131. package/dist/default/playback/behaviors/dom/apply-start-position.js +23 -35
  132. package/dist/default/playback/behaviors/dom/apply-start-position.js.map +1 -1
  133. package/dist/default/playback/behaviors/dom/end-of-stream.js +11 -16
  134. package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -1
  135. package/dist/default/playback/behaviors/dom/load-segments.js +27 -39
  136. package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -1
  137. package/dist/default/playback/behaviors/dom/recover-end-stall.js +21 -26
  138. package/dist/default/playback/behaviors/dom/recover-end-stall.js.map +1 -1
  139. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js +41 -60
  140. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js.map +1 -1
  141. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +50 -74
  142. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
  143. package/dist/default/playback/behaviors/dom/setup-mediasource.js +29 -43
  144. package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -1
  145. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -1
  146. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js +3 -4
  147. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -1
  148. package/dist/default/playback/behaviors/dom/sync-text-tracks.js +30 -45
  149. package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
  150. package/dist/default/playback/behaviors/dom/track-current-time.js +9 -11
  151. package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -1
  152. package/dist/default/playback/behaviors/dom/track-load-triggers.js +24 -32
  153. package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -1
  154. package/dist/default/playback/behaviors/dom/track-playback-rate.js +6 -8
  155. package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -1
  156. package/dist/default/playback/behaviors/dom/track-player-resolution.js +10 -14
  157. package/dist/default/playback/behaviors/dom/track-player-resolution.js.map +1 -1
  158. package/dist/default/playback/behaviors/dom/track-screen-resolution.js +8 -9
  159. package/dist/default/playback/behaviors/dom/track-screen-resolution.js.map +1 -1
  160. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +28 -43
  161. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
  162. package/dist/default/playback/behaviors/establish-start-media-time.js +32 -42
  163. package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -1
  164. package/dist/default/playback/behaviors/resolve-presentation.js +13 -22
  165. package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -1
  166. package/dist/default/playback/behaviors/resolve-track.js +5 -12
  167. package/dist/default/playback/behaviors/resolve-track.js.map +1 -1
  168. package/dist/default/playback/behaviors/select-tracks.js +77 -104
  169. package/dist/default/playback/behaviors/select-tracks.js.map +1 -1
  170. package/dist/default/playback/behaviors/setup-failover-monitor.js +11 -16
  171. package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -1
  172. package/dist/default/playback/behaviors/sync-preload.js +9 -13
  173. package/dist/default/playback/behaviors/sync-preload.js.map +1 -1
  174. package/dist/default/playback/behaviors/track-switching.js +189 -173
  175. package/dist/default/playback/behaviors/track-switching.js.map +1 -1
  176. package/dist/default/playback/engines/hls/engine-audio-only.js +20 -24
  177. package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -1
  178. package/dist/default/playback/engines/hls/engine-background-video.js +22 -27
  179. package/dist/default/playback/engines/hls/engine-background-video.js.map +1 -1
  180. package/dist/default/playback/engines/hls/engine.js +22 -25
  181. package/dist/default/playback/engines/hls/engine.js.map +1 -1
  182. package/dist/default/playback/primitives/error-messages.js +16 -25
  183. package/dist/default/playback/primitives/error-messages.js.map +1 -1
  184. package/dist/default/playback/primitives/failover-fetch.js +9 -14
  185. package/dist/default/playback/primitives/failover-fetch.js.map +1 -1
  186. package/dist/default/playback/primitives/head-peek.js +7 -10
  187. package/dist/default/playback/primitives/head-peek.js.map +1 -1
  188. package/dist/default/playback/primitives/live-window.js +12 -15
  189. package/dist/default/playback/primitives/live-window.js.map +1 -1
  190. package/dist/default/playback/primitives/relocation-pipelines.js +34 -46
  191. package/dist/default/playback/primitives/relocation-pipelines.js.map +1 -1
  192. package/dist/default/playback/primitives/report-track-conditions.js +26 -38
  193. package/dist/default/playback/primitives/report-track-conditions.js.map +1 -1
  194. package/dist/default/playback/primitives/segment-load-pipeline.js +15 -15
  195. package/dist/default/playback/primitives/segment-load-pipeline.js.map +1 -1
  196. package/dist/default/playback/primitives/selection-rules.js +65 -36
  197. package/dist/default/playback/primitives/selection-rules.js.map +1 -1
  198. package/dist/default/playback/primitives/text-segment-load-pipeline.js +4 -5
  199. package/dist/default/playback/primitives/text-segment-load-pipeline.js.map +1 -1
  200. package/dist/default/playback/primitives/track-types.js +10 -18
  201. package/dist/default/playback/primitives/track-types.js.map +1 -1
  202. package/dist/dev/core/actors/actor.d.ts +4 -4
  203. package/dist/dev/core/actors/actor.d.ts.map +1 -1
  204. package/dist/dev/core/actors/create-machine-actor.d.ts +38 -47
  205. package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -1
  206. package/dist/dev/core/actors/create-machine-actor.js +23 -25
  207. package/dist/dev/core/actors/create-machine-actor.js.map +1 -1
  208. package/dist/dev/core/actors/create-transition-actor.d.ts +11 -15
  209. package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -1
  210. package/dist/dev/core/actors/create-transition-actor.js +7 -9
  211. package/dist/dev/core/actors/create-transition-actor.js.map +1 -1
  212. package/dist/dev/core/composition/create-composition.d.ts +70 -102
  213. package/dist/dev/core/composition/create-composition.d.ts.map +1 -1
  214. package/dist/dev/core/composition/create-composition.js +26 -29
  215. package/dist/dev/core/composition/create-composition.js.map +1 -1
  216. package/dist/dev/core/composition/share-signals.d.ts +17 -24
  217. package/dist/dev/core/composition/share-signals.d.ts.map +1 -1
  218. package/dist/dev/core/composition/share-signals.js +12 -17
  219. package/dist/dev/core/composition/share-signals.js.map +1 -1
  220. package/dist/dev/core/machine.d.ts +4 -4
  221. package/dist/dev/core/machine.js +3 -4
  222. package/dist/dev/core/machine.js.map +1 -1
  223. package/dist/dev/core/reactors/create-machine-reactor.d.ts +33 -39
  224. package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -1
  225. package/dist/dev/core/reactors/create-machine-reactor.js +20 -19
  226. package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -1
  227. package/dist/dev/core/signals/effect.d.ts +3 -4
  228. package/dist/dev/core/signals/effect.d.ts.map +1 -1
  229. package/dist/dev/core/signals/effect.js +24 -4
  230. package/dist/dev/core/signals/effect.js.map +1 -1
  231. package/dist/dev/core/signals/primitives.d.ts +10 -12
  232. package/dist/dev/core/signals/primitives.d.ts.map +1 -1
  233. package/dist/dev/core/signals/primitives.js +10 -14
  234. package/dist/dev/core/signals/primitives.js.map +1 -1
  235. package/dist/dev/core/signals/when.js +6 -8
  236. package/dist/dev/core/signals/when.js.map +1 -1
  237. package/dist/dev/core/tasks/delayed-reschedule.js +7 -11
  238. package/dist/dev/core/tasks/delayed-reschedule.js.map +1 -1
  239. package/dist/dev/core/tasks/task.d.ts +51 -73
  240. package/dist/dev/core/tasks/task.d.ts.map +1 -1
  241. package/dist/dev/core/tasks/task.js +59 -82
  242. package/dist/dev/core/tasks/task.js.map +1 -1
  243. package/dist/dev/hls.d.ts +3 -1
  244. package/dist/dev/hls.js +3 -1
  245. package/dist/dev/index.d.ts +2 -3
  246. package/dist/dev/index.d.ts.map +1 -1
  247. package/dist/dev/index.js +2 -3
  248. package/dist/dev/index.js.map +1 -1
  249. package/dist/dev/media/abr/quality-selection.d.ts +7 -10
  250. package/dist/dev/media/abr/quality-selection.d.ts.map +1 -1
  251. package/dist/dev/media/abr/quality-selection.js +3 -7
  252. package/dist/dev/media/abr/quality-selection.js.map +1 -1
  253. package/dist/dev/media/buffer/back-buffer.d.ts +2 -7
  254. package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -1
  255. package/dist/dev/media/buffer/back-buffer.js +15 -19
  256. package/dist/dev/media/buffer/back-buffer.js.map +1 -1
  257. package/dist/dev/media/buffer/forward-buffer.d.ts +2 -7
  258. package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -1
  259. package/dist/dev/media/buffer/forward-buffer.js +32 -41
  260. package/dist/dev/media/buffer/forward-buffer.js.map +1 -1
  261. package/dist/dev/media/dom/capabilities.js +26 -37
  262. package/dist/dev/media/dom/capabilities.js.map +1 -1
  263. package/dist/dev/media/dom/mse/append-segment.d.ts +3 -5
  264. package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -1
  265. package/dist/dev/media/dom/mse/append-segment.js +3 -5
  266. package/dist/dev/media/dom/mse/append-segment.js.map +1 -1
  267. package/dist/dev/media/dom/mse/buffer-flusher.d.ts +5 -5
  268. package/dist/dev/media/dom/mse/buffer-flusher.js +5 -5
  269. package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -1
  270. package/dist/dev/media/dom/mse/duration.js +23 -31
  271. package/dist/dev/media/dom/mse/duration.js.map +1 -1
  272. package/dist/dev/media/dom/mse/end-of-stream.js +3 -6
  273. package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -1
  274. package/dist/dev/media/dom/mse/mediasource-setup.js +65 -81
  275. package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -1
  276. package/dist/dev/media/dom/screen.d.ts.map +1 -1
  277. package/dist/dev/media/dom/screen.js +37 -54
  278. package/dist/dev/media/dom/screen.js.map +1 -1
  279. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -1
  280. package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -1
  281. package/dist/dev/media/dom/text/text-track-slots.d.ts +10 -16
  282. package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -1
  283. package/dist/dev/media/dom/text/text-track-slots.js +15 -23
  284. package/dist/dev/media/dom/text/text-track-slots.js.map +1 -1
  285. package/dist/dev/media/errors.d.ts +32 -46
  286. package/dist/dev/media/errors.d.ts.map +1 -1
  287. package/dist/dev/media/errors.js +25 -34
  288. package/dist/dev/media/errors.js.map +1 -1
  289. package/dist/dev/media/hls/parse-attributes.js +9 -26
  290. package/dist/dev/media/hls/parse-attributes.js.map +1 -1
  291. package/dist/dev/media/hls/parse-media-playlist.js +25 -33
  292. package/dist/dev/media/hls/parse-media-playlist.js.map +1 -1
  293. package/dist/dev/media/hls/parse-multivariant.js +3 -3
  294. package/dist/dev/media/hls/parse-multivariant.js.map +1 -1
  295. package/dist/dev/media/hls/reload-policy.js +19 -27
  296. package/dist/dev/media/hls/reload-policy.js.map +1 -1
  297. package/dist/dev/media/hls/resolve-url.js +1 -3
  298. package/dist/dev/media/hls/resolve-url.js.map +1 -1
  299. package/dist/dev/media/live-window.js +9 -13
  300. package/dist/dev/media/live-window.js.map +1 -1
  301. package/dist/dev/media/media-tracks/media-tracks.d.ts +8 -13
  302. package/dist/dev/media/media-tracks/media-tracks.d.ts.map +1 -1
  303. package/dist/dev/media/media-tracks/media-tracks.js +12 -17
  304. package/dist/dev/media/media-tracks/media-tracks.js.map +1 -1
  305. package/dist/dev/media/mp4/box.js +4 -5
  306. package/dist/dev/media/mp4/box.js.map +1 -1
  307. package/dist/dev/media/mp4/timestamp-origin.js +30 -42
  308. package/dist/dev/media/mp4/timestamp-origin.js.map +1 -1
  309. package/dist/dev/media/primitives/resolution.d.ts +6 -8
  310. package/dist/dev/media/primitives/resolution.d.ts.map +1 -1
  311. package/dist/dev/media/primitives/resolution.js +5 -8
  312. package/dist/dev/media/primitives/resolution.js.map +1 -1
  313. package/dist/dev/media/primitives/select-tracks.js +24 -34
  314. package/dist/dev/media/primitives/select-tracks.js.map +1 -1
  315. package/dist/dev/media/text/parse-vtt-timestamp-map.js +6 -8
  316. package/dist/dev/media/text/parse-vtt-timestamp-map.js.map +1 -1
  317. package/dist/dev/media/text/resolve-vtt-metadata.js +8 -9
  318. package/dist/dev/media/text/resolve-vtt-metadata.js.map +1 -1
  319. package/dist/dev/media/types/index.d.ts +106 -177
  320. package/dist/dev/media/types/index.d.ts.map +1 -1
  321. package/dist/dev/media/types/index.js +10 -18
  322. package/dist/dev/media/types/index.js.map +1 -1
  323. package/dist/dev/media/utils/cdn.d.ts +4 -6
  324. package/dist/dev/media/utils/cdn.d.ts.map +1 -1
  325. package/dist/dev/media/utils/cdn.js +10 -16
  326. package/dist/dev/media/utils/cdn.js.map +1 -1
  327. package/dist/dev/media/utils/preload.js +4 -6
  328. package/dist/dev/media/utils/preload.js.map +1 -1
  329. package/dist/dev/media/utils/track-selection.js +10 -14
  330. package/dist/dev/media/utils/track-selection.js.map +1 -1
  331. package/dist/dev/media/utils/tracks.js +56 -54
  332. package/dist/dev/media/utils/tracks.js.map +1 -1
  333. package/dist/dev/network/bandwidth-estimator.d.ts +7 -9
  334. package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -1
  335. package/dist/dev/network/bandwidth-estimator.js +18 -19
  336. package/dist/dev/network/bandwidth-estimator.js.map +1 -1
  337. package/dist/dev/network/chunked-stream-iterable.js +4 -6
  338. package/dist/dev/network/chunked-stream-iterable.js.map +1 -1
  339. package/dist/dev/network/ewma.js +22 -25
  340. package/dist/dev/network/ewma.js.map +1 -1
  341. package/dist/dev/network/fetch.js +30 -37
  342. package/dist/dev/network/fetch.js.map +1 -1
  343. package/dist/dev/playback/actors/dom/segment-loader.d.ts +7 -11
  344. package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -1
  345. package/dist/dev/playback/actors/dom/segment-loader.js +28 -44
  346. package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -1
  347. package/dist/dev/playback/actors/dom/source-buffer.d.ts +5 -7
  348. package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -1
  349. package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -1
  350. package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -1
  351. package/dist/dev/playback/actors/text-track-segment-loader.d.ts +10 -16
  352. package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -1
  353. package/dist/dev/playback/actors/text-track-segment-loader.js +24 -35
  354. package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -1
  355. package/dist/dev/playback/actors/text-tracks.d.ts +5 -8
  356. package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -1
  357. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts +11 -14
  358. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts.map +1 -1
  359. package/dist/dev/playback/adapters/hls-audio/adapter.js +23 -31
  360. package/dist/dev/playback/adapters/hls-audio/adapter.js.map +1 -1
  361. package/dist/dev/playback/adapters/hls-audio/media.d.ts.map +1 -1
  362. package/dist/dev/playback/adapters/hls-audio/media.js.map +1 -1
  363. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts +28 -39
  364. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts.map +1 -1
  365. package/dist/dev/playback/adapters/hls-background-video/adapter.js +50 -70
  366. package/dist/dev/playback/adapters/hls-background-video/adapter.js.map +1 -1
  367. package/dist/dev/playback/adapters/hls-background-video/host.d.ts +12 -16
  368. package/dist/dev/playback/adapters/hls-background-video/host.d.ts.map +1 -1
  369. package/dist/dev/playback/adapters/hls-background-video/host.js +12 -16
  370. package/dist/dev/playback/adapters/hls-background-video/host.js.map +1 -1
  371. package/dist/dev/playback/adapters/hls-background-video/media.d.ts +5 -8
  372. package/dist/dev/playback/adapters/hls-background-video/media.d.ts.map +1 -1
  373. package/dist/dev/playback/adapters/hls-background-video/media.js +5 -8
  374. package/dist/dev/playback/adapters/hls-background-video/media.js.map +1 -1
  375. package/dist/dev/playback/adapters/hls-video/adapter.d.ts +12 -14
  376. package/dist/dev/playback/adapters/hls-video/adapter.d.ts.map +1 -1
  377. package/dist/dev/playback/adapters/hls-video/adapter.js +43 -60
  378. package/dist/dev/playback/adapters/hls-video/adapter.js.map +1 -1
  379. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts +4 -5
  380. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts.map +1 -1
  381. package/dist/dev/playback/adapters/hls-video/error-surface.js +13 -18
  382. package/dist/dev/playback/adapters/hls-video/error-surface.js.map +1 -1
  383. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts +4 -5
  384. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts.map +1 -1
  385. package/dist/dev/playback/adapters/hls-video/media-tracks.js +4 -5
  386. package/dist/dev/playback/adapters/hls-video/media-tracks.js.map +1 -1
  387. package/dist/dev/playback/adapters/hls-video/media.d.ts.map +1 -1
  388. package/dist/dev/playback/adapters/hls-video/media.js.map +1 -1
  389. package/dist/dev/playback/adapters/mux-audio/media.d.ts +8 -12
  390. package/dist/dev/playback/adapters/mux-audio/media.d.ts.map +1 -1
  391. package/dist/dev/playback/adapters/mux-audio/media.js +8 -12
  392. package/dist/dev/playback/adapters/mux-audio/media.js.map +1 -1
  393. package/dist/dev/playback/adapters/mux-video/adapter.d.ts +9 -11
  394. package/dist/dev/playback/adapters/mux-video/adapter.d.ts.map +1 -1
  395. package/dist/dev/playback/adapters/mux-video/adapter.js +25 -33
  396. package/dist/dev/playback/adapters/mux-video/adapter.js.map +1 -1
  397. package/dist/dev/playback/adapters/mux-video/media.d.ts +7 -9
  398. package/dist/dev/playback/adapters/mux-video/media.d.ts.map +1 -1
  399. package/dist/dev/playback/adapters/mux-video/media.js +7 -9
  400. package/dist/dev/playback/adapters/mux-video/media.js.map +1 -1
  401. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +5 -8
  402. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -1
  403. package/dist/dev/playback/behaviors/calculate-presentation-duration.js +5 -8
  404. package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -1
  405. package/dist/dev/playback/behaviors/collect-errors.d.ts +2 -0
  406. package/dist/dev/playback/behaviors/collect-errors.js +39 -52
  407. package/dist/dev/playback/behaviors/collect-errors.js.map +1 -1
  408. package/dist/dev/playback/behaviors/derive-cdn-priority.js +16 -23
  409. package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -1
  410. package/dist/dev/playback/behaviors/dom/airplay.js +38 -61
  411. package/dist/dev/playback/behaviors/dom/airplay.js.map +1 -1
  412. package/dist/dev/playback/behaviors/dom/apply-start-position.js +23 -35
  413. package/dist/dev/playback/behaviors/dom/apply-start-position.js.map +1 -1
  414. package/dist/dev/playback/behaviors/dom/end-of-stream.js +11 -16
  415. package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -1
  416. package/dist/dev/playback/behaviors/dom/load-segments.d.ts +5 -8
  417. package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -1
  418. package/dist/dev/playback/behaviors/dom/load-segments.js +27 -39
  419. package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -1
  420. package/dist/dev/playback/behaviors/dom/recover-end-stall.js +21 -26
  421. package/dist/dev/playback/behaviors/dom/recover-end-stall.js.map +1 -1
  422. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js +41 -60
  423. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js.map +1 -1
  424. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +50 -74
  425. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
  426. package/dist/dev/playback/behaviors/dom/setup-mediasource.js +29 -43
  427. package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -1
  428. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +3 -3
  429. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -1
  430. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -1
  431. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js +3 -4
  432. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -1
  433. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +30 -45
  434. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
  435. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +2 -2
  436. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -1
  437. package/dist/dev/playback/behaviors/dom/track-current-time.js +9 -11
  438. package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -1
  439. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +6 -12
  440. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -1
  441. package/dist/dev/playback/behaviors/dom/track-load-triggers.js +24 -32
  442. package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -1
  443. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +2 -2
  444. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -1
  445. package/dist/dev/playback/behaviors/dom/track-playback-rate.js +6 -8
  446. package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -1
  447. package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts.map +1 -1
  448. package/dist/dev/playback/behaviors/dom/track-player-resolution.js +10 -14
  449. package/dist/dev/playback/behaviors/dom/track-player-resolution.js.map +1 -1
  450. package/dist/dev/playback/behaviors/dom/track-screen-resolution.js +8 -9
  451. package/dist/dev/playback/behaviors/dom/track-screen-resolution.js.map +1 -1
  452. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +28 -43
  453. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
  454. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +15 -19
  455. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts.map +1 -1
  456. package/dist/dev/playback/behaviors/establish-start-media-time.js +32 -42
  457. package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -1
  458. package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -1
  459. package/dist/dev/playback/behaviors/resolve-presentation.js +13 -22
  460. package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -1
  461. package/dist/dev/playback/behaviors/resolve-track.js +5 -12
  462. package/dist/dev/playback/behaviors/resolve-track.js.map +1 -1
  463. package/dist/dev/playback/behaviors/select-tracks.d.ts +28 -39
  464. package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -1
  465. package/dist/dev/playback/behaviors/select-tracks.js +77 -104
  466. package/dist/dev/playback/behaviors/select-tracks.js.map +1 -1
  467. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +1 -4
  468. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -1
  469. package/dist/dev/playback/behaviors/setup-failover-monitor.js +11 -16
  470. package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -1
  471. package/dist/dev/playback/behaviors/sync-preload.js +9 -13
  472. package/dist/dev/playback/behaviors/sync-preload.js.map +1 -1
  473. package/dist/dev/playback/behaviors/track-switching.d.ts +110 -0
  474. package/dist/dev/playback/behaviors/track-switching.d.ts.map +1 -0
  475. package/dist/dev/playback/behaviors/track-switching.js +189 -173
  476. package/dist/dev/playback/behaviors/track-switching.js.map +1 -1
  477. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +59 -72
  478. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -1
  479. package/dist/dev/playback/engines/hls/engine-audio-only.js +20 -24
  480. package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -1
  481. package/dist/dev/playback/engines/hls/engine-background-video.d.ts +51 -78
  482. package/dist/dev/playback/engines/hls/engine-background-video.d.ts.map +1 -1
  483. package/dist/dev/playback/engines/hls/engine-background-video.js +22 -27
  484. package/dist/dev/playback/engines/hls/engine-background-video.js.map +1 -1
  485. package/dist/dev/playback/engines/hls/engine.d.ts +115 -155
  486. package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -1
  487. package/dist/dev/playback/engines/hls/engine.js +22 -25
  488. package/dist/dev/playback/engines/hls/engine.js.map +1 -1
  489. package/dist/dev/playback/primitives/derive-start-media-time.d.ts +3 -3
  490. package/dist/dev/playback/primitives/derive-start-media-time.d.ts.map +1 -1
  491. package/dist/dev/playback/primitives/error-messages.js +16 -25
  492. package/dist/dev/playback/primitives/error-messages.js.map +1 -1
  493. package/dist/dev/playback/primitives/failover-fetch.js +9 -14
  494. package/dist/dev/playback/primitives/failover-fetch.js.map +1 -1
  495. package/dist/dev/playback/primitives/head-peek.js +7 -10
  496. package/dist/dev/playback/primitives/head-peek.js.map +1 -1
  497. package/dist/dev/playback/primitives/live-window.js +12 -15
  498. package/dist/dev/playback/primitives/live-window.js.map +1 -1
  499. package/dist/dev/playback/primitives/relocation-pipelines.js +34 -46
  500. package/dist/dev/playback/primitives/relocation-pipelines.js.map +1 -1
  501. package/dist/dev/playback/primitives/report-track-conditions.d.ts +1 -4
  502. package/dist/dev/playback/primitives/report-track-conditions.d.ts.map +1 -1
  503. package/dist/dev/playback/primitives/report-track-conditions.js +26 -38
  504. package/dist/dev/playback/primitives/report-track-conditions.js.map +1 -1
  505. package/dist/dev/playback/primitives/segment-load-pipeline.js +15 -15
  506. package/dist/dev/playback/primitives/segment-load-pipeline.js.map +1 -1
  507. package/dist/dev/playback/primitives/selection-rules.d.ts +45 -17
  508. package/dist/dev/playback/primitives/selection-rules.d.ts.map +1 -1
  509. package/dist/dev/playback/primitives/selection-rules.js +65 -36
  510. package/dist/dev/playback/primitives/selection-rules.js.map +1 -1
  511. package/dist/dev/playback/primitives/source-buffer-messages.d.ts +6 -9
  512. package/dist/dev/playback/primitives/source-buffer-messages.d.ts.map +1 -1
  513. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts +20 -29
  514. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts.map +1 -1
  515. package/dist/dev/playback/primitives/text-segment-load-pipeline.js +4 -5
  516. package/dist/dev/playback/primitives/text-segment-load-pipeline.js.map +1 -1
  517. package/dist/dev/playback/primitives/text-track-messages.d.ts.map +1 -1
  518. package/dist/dev/playback/primitives/track-types.js +10 -18
  519. package/dist/dev/playback/primitives/track-types.js.map +1 -1
  520. package/package.json +25 -28
@@ -1,20 +1,16 @@
1
1
  import { isCaptionOrSubtitleTrack } from "@videojs/utils/dom";
2
2
  //#region src/media/dom/text/text-track-slots.ts
3
3
  /**
4
- * SPF-owned `<track>` selector. Each slot created by
5
- * `addSubtitlesTracksToMedia` carries this attribute so reads and removals can
6
- * filter SPF-owned tracks from host-page-owned ones.
4
+ * SPF-owned `<track>` selector. Each slot created by `addSubtitlesTracksToMedia` carries this attribute so reads and
5
+ * removals can filter SPF-owned tracks from host-page-owned ones.
7
6
  */
8
7
  const SPF_TRACK_SELECTOR = "track[data-src-track]";
9
8
  /**
10
- * Allocate text-track slots on `mediaElement` for each model track by creating
11
- * and appending `<track>` children. Marks each element with `data-src-track`
12
- * so it can be distinguished from `<track>` children the host page added
13
- * directly used by `getShowingSubtitlesTrackFromMedia` and
14
- * `removeAllSubtitlesTracksFromMedia` to scope their reads/removals to
15
- * SPF-owned slots. The spec has no `removeTextTrack` API, so creating
16
- * `<track>` elements is the only mechanism for adding *and* removing entries
17
- * to `mediaElement.textTracks`.
9
+ * Allocate text-track slots on `mediaElement` for each model track by creating and appending `<track>` children. Marks
10
+ * each element with `data-src-track` so it can be distinguished from `<track>` children the host page added directly —
11
+ * used by `getShowingSubtitlesTrackFromMedia` and `removeAllSubtitlesTracksFromMedia` to scope their reads/removals to
12
+ * SPF-owned slots. The spec has no `removeTextTrack` API, so creating `<track>` elements is the only mechanism for
13
+ * adding _and_ removing entries to `mediaElement.textTracks`.
18
14
  */
19
15
  function addSubtitlesTracksToMedia(mediaElement, modelTextTracks) {
20
16
  for (const modelTrack of modelTextTracks) {
@@ -28,11 +24,9 @@ function addSubtitlesTracksToMedia(mediaElement, modelTextTracks) {
28
24
  }
29
25
  }
30
26
  /**
31
- * Return the SPF-owned subtitle/caption `TextTrack` currently in `'showing'`
32
- * mode, or `undefined` if none. Restricts the search to slots created by
33
- * `addSubtitlesTracksToMedia` (via the `data-src-track` selector) so a showing
34
- * track that the host page added directly is ignored — SPF selection only
35
- * mirrors tracks it owns.
27
+ * Return the SPF-owned subtitle/caption `TextTrack` currently in `'showing'` mode, or `undefined` if none. Restricts
28
+ * the search to slots created by `addSubtitlesTracksToMedia` (via the `data-src-track` selector) so a showing track
29
+ * that the host page added directly is ignored — SPF selection only mirrors tracks it owns.
36
30
  */
37
31
  function getShowingSubtitlesTrackFromMedia(mediaElement) {
38
32
  const elements = mediaElement.querySelectorAll(SPF_TRACK_SELECTOR);
@@ -42,19 +36,17 @@ function getShowingSubtitlesTrackFromMedia(mediaElement) {
42
36
  }
43
37
  }
44
38
  /**
45
- * Remove every SPF-owned `<track>` child from `mediaElement` (those tagged
46
- * with `data-src-track` by `addSubtitlesTracksToMedia`). `<track>` elements
47
- * the host page added directly are left in place.
39
+ * Remove every SPF-owned `<track>` child from `mediaElement` (those tagged with `data-src-track` by
40
+ * `addSubtitlesTracksToMedia`). `<track>` elements the host page added directly are left in place.
48
41
  */
49
42
  function removeAllSubtitlesTracksFromMedia(mediaElement) {
50
43
  const elements = mediaElement.querySelectorAll(SPF_TRACK_SELECTOR);
51
44
  for (const el of elements) el.remove();
52
45
  }
53
46
  /**
54
- * Apply a selection to a `TextTrackList` by setting each subtitle/caption
55
- * track's `mode` to `'showing'` if its `id` matches `selectedId` and
56
- * `'disabled'` otherwise. Tracks of other kinds (chapters, metadata,
57
- * descriptions) are left untouched — they may be owned by the host page.
47
+ * Apply a selection to a `TextTrackList` by setting each subtitle/caption track's `mode` to `'showing'` if its `id`
48
+ * matches `selectedId` and `'disabled'` otherwise. Tracks of other kinds (chapters, metadata, descriptions) are left
49
+ * untouched they may be owned by the host page.
58
50
  */
59
51
  function syncTextTrackModes(textTracks, selectedId) {
60
52
  for (let i = 0; i < textTracks.length; i++) {
@@ -1 +1 @@
1
- {"version":3,"file":"text-track-slots.js","names":[],"sources":["../../../../../src/media/dom/text/text-track-slots.ts"],"sourcesContent":["import { isCaptionOrSubtitleTrack } from '@videojs/utils/dom';\n\nimport type { PartiallyResolvedTextTrack, TextTrack } from '../../types';\n\n/**\n * SPF-owned `<track>` selector. Each slot created by\n * `addSubtitlesTracksToMedia` carries this attribute so reads and removals can\n * filter SPF-owned tracks from host-page-owned ones.\n */\nconst SPF_TRACK_SELECTOR = 'track[data-src-track]';\n\n/**\n * Allocate text-track slots on `mediaElement` for each model track by creating\n * and appending `<track>` children. Marks each element with `data-src-track`\n * so it can be distinguished from `<track>` children the host page added\n * directly — used by `getShowingSubtitlesTrackFromMedia` and\n * `removeAllSubtitlesTracksFromMedia` to scope their reads/removals to\n * SPF-owned slots. The spec has no `removeTextTrack` API, so creating\n * `<track>` elements is the only mechanism for adding *and* removing entries\n * to `mediaElement.textTracks`.\n */\nexport function addSubtitlesTracksToMedia(\n mediaElement: HTMLMediaElement,\n modelTextTracks: readonly (PartiallyResolvedTextTrack | TextTrack)[]\n): void {\n for (const modelTrack of modelTextTracks) {\n const el = document.createElement('track');\n el.id = modelTrack.id;\n el.kind = modelTrack.kind;\n el.label = modelTrack.label;\n el.toggleAttribute('data-src-track', true);\n if (modelTrack.language) el.srclang = modelTrack.language;\n // Deliberately NOT propagating `modelTrack.default` to the `default`\n // attribute: that makes the browser auto-activate the slot on insertion,\n // which fires a `change` that `syncTextTracks` records as user intent —\n // auto-enabling captions past SPF's opt-in policy (`enableDefaultTrack`\n // governs DEFAULT=YES handling in `switchTextTrack`, not the browser). SPF\n // owns selection; these slots are containers, so they carry no selection hint.\n mediaElement.appendChild(el);\n }\n}\n\n/**\n * Return the SPF-owned subtitle/caption `TextTrack` currently in `'showing'`\n * mode, or `undefined` if none. Restricts the search to slots created by\n * `addSubtitlesTracksToMedia` (via the `data-src-track` selector) so a showing\n * track that the host page added directly is ignored — SPF selection only\n * mirrors tracks it owns.\n */\nexport function getShowingSubtitlesTrackFromMedia(mediaElement: HTMLMediaElement): globalThis.TextTrack | undefined {\n const elements = mediaElement.querySelectorAll<HTMLTrackElement>(SPF_TRACK_SELECTOR);\n for (const el of elements) {\n const track = el.track;\n if (track.mode === 'showing' && isCaptionOrSubtitleTrack(track)) {\n return track;\n }\n }\n return undefined;\n}\n\n/**\n * Remove every SPF-owned `<track>` child from `mediaElement` (those tagged\n * with `data-src-track` by `addSubtitlesTracksToMedia`). `<track>` elements\n * the host page added directly are left in place.\n */\nexport function removeAllSubtitlesTracksFromMedia(mediaElement: HTMLMediaElement): void {\n const elements = mediaElement.querySelectorAll<HTMLTrackElement>(SPF_TRACK_SELECTOR);\n for (const el of elements) {\n el.remove();\n }\n}\n\n/**\n * Apply a selection to a `TextTrackList` by setting each subtitle/caption\n * track's `mode` to `'showing'` if its `id` matches `selectedId` and\n * `'disabled'` otherwise. Tracks of other kinds (chapters, metadata,\n * descriptions) are left untouched — they may be owned by the host page.\n */\nexport function syncTextTrackModes(textTracks: TextTrackList, selectedId: string | undefined): void {\n for (let i = 0; i < textTracks.length; i++) {\n const track = textTracks[i]!;\n if (!isCaptionOrSubtitleTrack(track)) continue;\n track.mode = track.id === selectedId ? 'showing' : 'disabled';\n }\n}\n"],"mappings":";;;;;;;AASA,MAAM,qBAAqB;;;;;;;;;;;AAY3B,SAAgB,0BACd,cACA,iBACM;CACN,KAAK,MAAM,cAAc,iBAAiB;EACxC,MAAM,KAAK,SAAS,cAAc,OAAO;EACzC,GAAG,KAAK,WAAW;EACnB,GAAG,OAAO,WAAW;EACrB,GAAG,QAAQ,WAAW;EACtB,GAAG,gBAAgB,kBAAkB,IAAI;EACzC,IAAI,WAAW,UAAU,GAAG,UAAU,WAAW;EAOjD,aAAa,YAAY,EAAE;CAC7B;AACF;;;;;;;;AASA,SAAgB,kCAAkC,cAAkE;CAClH,MAAM,WAAW,aAAa,iBAAmC,kBAAkB;CACnF,KAAK,MAAM,MAAM,UAAU;EACzB,MAAM,QAAQ,GAAG;EACjB,IAAI,MAAM,SAAS,aAAa,yBAAyB,KAAK,GAC5D,OAAO;CAEX;AAEF;;;;;;AAOA,SAAgB,kCAAkC,cAAsC;CACtF,MAAM,WAAW,aAAa,iBAAmC,kBAAkB;CACnF,KAAK,MAAM,MAAM,UACf,GAAG,OAAO;AAEd;;;;;;;AAQA,SAAgB,mBAAmB,YAA2B,YAAsC;CAClG,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;EAC1C,MAAM,QAAQ,WAAW;EACzB,IAAI,CAAC,yBAAyB,KAAK,GAAG;EACtC,MAAM,OAAO,MAAM,OAAO,aAAa,YAAY;CACrD;AACF"}
1
+ {"version":3,"file":"text-track-slots.js","names":[],"sources":["../../../../../src/media/dom/text/text-track-slots.ts"],"sourcesContent":["import { isCaptionOrSubtitleTrack } from '@videojs/utils/dom';\n\nimport type { PartiallyResolvedTextTrack, TextTrack } from '../../types';\n\n/**\n * SPF-owned `<track>` selector. Each slot created by `addSubtitlesTracksToMedia` carries this attribute so reads and\n * removals can filter SPF-owned tracks from host-page-owned ones.\n */\nconst SPF_TRACK_SELECTOR = 'track[data-src-track]';\n\n/**\n * Allocate text-track slots on `mediaElement` for each model track by creating and appending `<track>` children. Marks\n * each element with `data-src-track` so it can be distinguished from `<track>` children the host page added directly —\n * used by `getShowingSubtitlesTrackFromMedia` and `removeAllSubtitlesTracksFromMedia` to scope their reads/removals to\n * SPF-owned slots. The spec has no `removeTextTrack` API, so creating `<track>` elements is the only mechanism for\n * adding _and_ removing entries to `mediaElement.textTracks`.\n */\nexport function addSubtitlesTracksToMedia(\n mediaElement: HTMLMediaElement,\n modelTextTracks: readonly (PartiallyResolvedTextTrack | TextTrack)[]\n): void {\n for (const modelTrack of modelTextTracks) {\n const el = document.createElement('track');\n\n el.id = modelTrack.id;\n el.kind = modelTrack.kind;\n el.label = modelTrack.label;\n el.toggleAttribute('data-src-track', true);\n\n if (modelTrack.language) el.srclang = modelTrack.language;\n\n // Deliberately NOT propagating `modelTrack.default` to the `default`\n // attribute: that makes the browser auto-activate the slot on insertion,\n // which fires a `change` that `syncTextTracks` records as user intent —\n // auto-enabling captions past SPF's opt-in policy (`enableDefaultTrack`\n // governs DEFAULT=YES handling in `switchTextTrack`, not the browser). SPF\n // owns selection; these slots are containers, so they carry no selection hint.\n mediaElement.appendChild(el);\n }\n}\n\n/**\n * Return the SPF-owned subtitle/caption `TextTrack` currently in `'showing'` mode, or `undefined` if none. Restricts\n * the search to slots created by `addSubtitlesTracksToMedia` (via the `data-src-track` selector) so a showing track\n * that the host page added directly is ignored — SPF selection only mirrors tracks it owns.\n */\nexport function getShowingSubtitlesTrackFromMedia(mediaElement: HTMLMediaElement): globalThis.TextTrack | undefined {\n const elements = mediaElement.querySelectorAll<HTMLTrackElement>(SPF_TRACK_SELECTOR);\n\n for (const el of elements) {\n const track = el.track;\n if (track.mode === 'showing' && isCaptionOrSubtitleTrack(track)) return track;\n }\n\n return undefined;\n}\n\n/**\n * Remove every SPF-owned `<track>` child from `mediaElement` (those tagged with `data-src-track` by\n * `addSubtitlesTracksToMedia`). `<track>` elements the host page added directly are left in place.\n */\nexport function removeAllSubtitlesTracksFromMedia(mediaElement: HTMLMediaElement): void {\n const elements = mediaElement.querySelectorAll<HTMLTrackElement>(SPF_TRACK_SELECTOR);\n\n for (const el of elements) {\n el.remove();\n }\n}\n\n/**\n * Apply a selection to a `TextTrackList` by setting each subtitle/caption track's `mode` to `'showing'` if its `id`\n * matches `selectedId` and `'disabled'` otherwise. Tracks of other kinds (chapters, metadata, descriptions) are left\n * untouched — they may be owned by the host page.\n */\nexport function syncTextTrackModes(textTracks: TextTrackList, selectedId: string | undefined): void {\n for (let i = 0; i < textTracks.length; i++) {\n const track = textTracks[i]!;\n if (!isCaptionOrSubtitleTrack(track)) continue;\n\n track.mode = track.id === selectedId ? 'showing' : 'disabled';\n }\n}\n"],"mappings":";;;;;;AAQA,MAAM,qBAAqB;;;;;;;;AAS3B,SAAgB,0BACd,cACA,iBACM;CACN,KAAK,MAAM,cAAc,iBAAiB;EACxC,MAAM,KAAK,SAAS,cAAc,OAAO;EAEzC,GAAG,KAAK,WAAW;EACnB,GAAG,OAAO,WAAW;EACrB,GAAG,QAAQ,WAAW;EACtB,GAAG,gBAAgB,kBAAkB,IAAI;EAEzC,IAAI,WAAW,UAAU,GAAG,UAAU,WAAW;EAQjD,aAAa,YAAY,EAAE;CAC7B;AACF;;;;;;AAOA,SAAgB,kCAAkC,cAAkE;CAClH,MAAM,WAAW,aAAa,iBAAmC,kBAAkB;CAEnF,KAAK,MAAM,MAAM,UAAU;EACzB,MAAM,QAAQ,GAAG;EACjB,IAAI,MAAM,SAAS,aAAa,yBAAyB,KAAK,GAAG,OAAO;CAC1E;AAGF;;;;;AAMA,SAAgB,kCAAkC,cAAsC;CACtF,MAAM,WAAW,aAAa,iBAAmC,kBAAkB;CAEnF,KAAK,MAAM,MAAM,UACf,GAAG,OAAO;AAEd;;;;;;AAOA,SAAgB,mBAAmB,YAA2B,YAAsC;CAClG,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;EAC1C,MAAM,QAAQ,WAAW;EACzB,IAAI,CAAC,yBAAyB,KAAK,GAAG;EAEtC,MAAM,OAAO,MAAM,OAAO,aAAa,YAAY;CACrD;AACF"}
@@ -4,61 +4,52 @@ const SVTA_UNSUPPORTED_VIDEO_FORMAT = 1004;
4
4
  /** SVTA 1 [Media Content] 005 — the audio counterpart of {@link SVTA_UNSUPPORTED_VIDEO_FORMAT}. */
5
5
  const SVTA_UNSUPPORTED_AUDIO_FORMAT = 1005;
6
6
  /**
7
- * SVTA 4 [Content Protection] 008 — unsupported or unavailable DRM system. Used
8
- * for "this source is encrypted and we have no decryption pipeline," which is
9
- * detection, not a license failure.
7
+ * SVTA 4 [Content Protection] 008 — unsupported or unavailable DRM system. Used for "this source is encrypted and we
8
+ * have no decryption pipeline," which is detection, not a license failure.
10
9
  */
11
10
  const SVTA_UNSUPPORTED_DRM_SYSTEM = 4008;
12
11
  /**
13
12
  * SVTA 2 [Playback] 011 — no video track the environment can play.
14
13
  *
15
- * Covers both ways a composition can end up with nothing to select: renditions
16
- * that existed and were all excluded as unplayable, and where the composition
17
- * composes `reportAbsentTrackType` to say it needs the type a source carrying
18
- * none to begin with. Deliberately one code for both, because they are the same
19
- * answer to a viewer, and because the alternative is a code the SVTA spec doesn't
20
- * define. Whether an absent type is a failure is the composition's to state,
21
- * which is why it opts in rather than being read off the source.
14
+ * Covers both ways a composition can end up with nothing to select: renditions that existed and were all excluded as
15
+ * unplayable, and where the composition composes `reportAbsentTrackType` to say it needs the type — a source carrying
16
+ * none to begin with. Deliberately one code for both, because they are the same answer to a viewer, and because the
17
+ * alternative is a code the SVTA spec doesn't define. Whether an absent type is a failure is the composition's to
18
+ * state, which is why it opts in rather than being read off the source.
22
19
  */
23
20
  const SVTA_NO_SUPPORTED_VIDEO_TRACK = 2011;
24
21
  /** SVTA 2 [Playback] 012 — the audio counterpart of {@link SVTA_NO_SUPPORTED_VIDEO_TRACK}. */
25
22
  const SVTA_NO_SUPPORTED_AUDIO_TRACK = 2012;
26
23
  /**
27
- * SVTA 99 [Custom] 001 — this engine has no pipeline for something the source
28
- * requires, so the source is unplayable *here* rather than broken.
24
+ * SVTA 99 [Custom] 001 — this engine has no pipeline for something the source requires, so the source is unplayable
25
+ * _here_ rather than broken.
29
26
  *
30
- * Custom rather than standard because the standard codes available describe
31
- * either narrower or wider things. The causes (1004/1005 unsupported format,
32
- * 4008 unsupported DRM) say what one rendition hit; the verdicts (2011/2012 no
33
- * supported track) say a type emptied without saying why it's unfixable. And
34
- * 2039 "Manifest feature unsupported" covers features that are unsupported but
35
- * still *playable* — LL-HLS degrading to standard live is a 2039 — so
36
- * overloading it for a fatal condition would make it useless for the notices it
37
- * belongs on.
27
+ * Custom rather than standard because the standard codes available describe either narrower or wider things. The causes
28
+ * (1004/1005 unsupported format, 4008 unsupported DRM) say what one rendition hit; the verdicts (2011/2012 no supported
29
+ * track) say a type emptied without saying why it's unfixable. And 2039 "Manifest feature unsupported" covers features
30
+ * that are unsupported but still _playable_ — LL-HLS degrading to standard live is a 2039 so overloading it for a
31
+ * fatal condition would make it useless for the notices it belongs on.
38
32
  *
39
- * Index `001`: the spec defines only `99000` (Unknown) for the custom category
40
- * and leaves the rest to the publisher, so this is the first code we define.
33
+ * Index `001`: the spec defines only `99000` (Unknown) for the custom category and leaves the rest to the publisher, so
34
+ * this is the first code we define.
41
35
  *
42
- * Five digits, and deliberately not special-cased anywhere: {@link svtaCategory}
43
- * and {@link svtaIndex} decompose it correctly by arithmetic alone, because
44
- * every standard category is below `8000` and custom starts at `99000`.
36
+ * Five digits, and deliberately not special-cased anywhere: {@link svtaCategory} and {@link svtaIndex} decompose it
37
+ * correctly by arithmetic alone, because every standard category is below `8000` and custom starts at `99000`.
45
38
  */
46
39
  const SVTA_UNSUPPORTED_PLAYBACK_FEATURE = 99001;
47
40
  /**
48
- * The error's domain — `code / 1000`, per the spec's "divide by one thousand to
49
- * obtain the error category". Works uniformly across the four-digit native form
50
- * and the five-digit form embedding an external standard: `"03404"` is
51
- * numerically 3404, which decomposes identically. That also makes a numeric code
52
- * immune to the spec's inconsistent zero-padding (§Approach writes a
53
- * category-unknown network error as `"0300"` where the error index implies
54
- * category 3 / index 000).
41
+ * The error's domain — `code / 1000`, per the spec's "divide by one thousand to obtain the error category". Works
42
+ * uniformly across the four-digit native form and the five-digit form embedding an external standard: `"03404"` is
43
+ * numerically 3404, which decomposes identically. That also makes a numeric code immune to the spec's inconsistent
44
+ * zero-padding (§Approach writes a category-unknown network error as `"0300"` where the error index implies category 3
45
+ * / index 000).
55
46
  */
56
47
  function svtaCategory(code) {
57
48
  return Math.floor(code / 1e3);
58
49
  }
59
50
  /**
60
- * The specific error within its category — `code % 1000`. For a five-digit code
61
- * this is the embedded external value (an HTTP status, a VAST code).
51
+ * The specific error within its category — `code % 1000`. For a five-digit code this is the embedded external value (an
52
+ * HTTP status, a VAST code).
62
53
  */
63
54
  function svtaIndex(code) {
64
55
  return code % 1e3;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../../../src/media/errors.ts"],"sourcesContent":["/**\n * SVTA 2070 (Standardized Error Codes) — the vocabulary for identifying a\n * playback failure or notice, independent of how it's transported or who\n * decides what to do about it.\n *\n * A code is a single integer: the leading digit(s) are the **category** (the\n * error's domain) and the trailing three are the **index** (the specific error\n * within it). Four digits for natively-defined errors, five when an external\n * standard is embedded — `\"03404\"` is an HTTP 404 under the network category.\n * Categories are `0` unknown, `1` media content, `2` playback, `3` network,\n * `4` content protection, `5` accessibility, `6` remote play, `7` advertising,\n * `99` custom.\n *\n * Two properties of the spec shape the types here:\n *\n * - **Severity is deliberately not part of a code.** Per §Approach, \"impact\n * varies with player implementation, breaking the consistency of a specific\n * error mapping to single code.\" So an error carries no fatal flag — whether a\n * condition is fatal depends on the composition observing it, and is decided\n * downstream.\n * - **Reporting is partial and stacked** (Principles 5–6). Errors are reported\n * as encountered, most of them non-fatal, and the *sequence* carries causation\n * a single value can't.\n *\n * See `internal/design/spf/features/errors.md`.\n */\n\n/**\n * A reported condition, identified by its SVTA code.\n *\n * Named for the spec rather than the engine because the vocabulary is\n * format- and player-neutral. Not `MediaError` that name belongs to the\n * `@videojs/media` DOM-facing class this eventually maps *onto*, and the mapping\n * is the point at which severity and user-facing text get decided.\n */\nexport interface SvtaError {\n /** The SVTA code — see {@link svtaCategory} / {@link svtaIndex}. */\n code: number;\n /** Engineer-facing detail. Optional; the code is the identity. */\n message?: string;\n /** Reporter-specific context (track type, url, the constraint that fired). */\n data?: unknown;\n}\n\n/** SVTA 1 [Media Content] 004 — the video is in a format we can't play (e.g. an MPEG-TS container). */\nexport const SVTA_UNSUPPORTED_VIDEO_FORMAT = 1004;\n\n/** SVTA 1 [Media Content] 005 — the audio counterpart of {@link SVTA_UNSUPPORTED_VIDEO_FORMAT}. */\nexport const SVTA_UNSUPPORTED_AUDIO_FORMAT = 1005;\n\n/**\n * SVTA 4 [Content Protection] 008 — unsupported or unavailable DRM system. Used\n * for \"this source is encrypted and we have no decryption pipeline,\" which is\n * detection, not a license failure.\n */\nexport const SVTA_UNSUPPORTED_DRM_SYSTEM = 4008;\n\n/**\n * SVTA 2 [Playback] 011 — no video track the environment can play.\n *\n * Covers both ways a composition can end up with nothing to select: renditions\n * that existed and were all excluded as unplayable, and — where the composition\n * composes `reportAbsentTrackType` to say it needs the type — a source carrying\n * none to begin with. Deliberately one code for both, because they are the same\n * answer to a viewer, and because the alternative is a code the SVTA spec doesn't\n * define. Whether an absent type is a failure is the composition's to state,\n * which is why it opts in rather than being read off the source.\n */\nexport const SVTA_NO_SUPPORTED_VIDEO_TRACK = 2011;\n\n/** SVTA 2 [Playback] 012 — the audio counterpart of {@link SVTA_NO_SUPPORTED_VIDEO_TRACK}. */\nexport const SVTA_NO_SUPPORTED_AUDIO_TRACK = 2012;\n\n/**\n * SVTA 99 [Custom] 001 — this engine has no pipeline for something the source\n * requires, so the source is unplayable *here* rather than broken.\n *\n * Custom rather than standard because the standard codes available describe\n * either narrower or wider things. The causes (1004/1005 unsupported format,\n * 4008 unsupported DRM) say what one rendition hit; the verdicts (2011/2012 no\n * supported track) say a type emptied without saying why it's unfixable. And\n * 2039 \"Manifest feature unsupported\" covers features that are unsupported but\n * still *playable* — LL-HLS degrading to standard live is a 2039 — so\n * overloading it for a fatal condition would make it useless for the notices it\n * belongs on.\n *\n * Index `001`: the spec defines only `99000` (Unknown) for the custom category\n * and leaves the rest to the publisher, so this is the first code we define.\n *\n * Five digits, and deliberately not special-cased anywhere: {@link svtaCategory}\n * and {@link svtaIndex} decompose it correctly by arithmetic alone, because\n * every standard category is below `8000` and custom starts at `99000`.\n */\nexport const SVTA_UNSUPPORTED_PLAYBACK_FEATURE = 99001;\n\n/**\n * The error's domain — `code / 1000`, per the spec's \"divide by one thousand to\n * obtain the error category\". Works uniformly across the four-digit native form\n * and the five-digit form embedding an external standard: `\"03404\"` is\n * numerically 3404, which decomposes identically. That also makes a numeric code\n * immune to the spec's inconsistent zero-padding (§Approach writes a\n * category-unknown network error as `\"0300\"` where the error index implies\n * category 3 / index 000).\n */\nexport function svtaCategory(code: number): number {\n return Math.floor(code / 1000);\n}\n\n/**\n * The specific error within its category — `code % 1000`. For a five-digit code\n * this is the embedded external value (an HTTP status, a VAST code).\n */\nexport function svtaIndex(code: number): number {\n return code % 1000;\n}\n"],"mappings":";;AA6CA,MAAa,gCAAgC;;AAG7C,MAAa,gCAAgC;;;;;;AAO7C,MAAa,8BAA8B;;;;;;;;;;;;AAa3C,MAAa,gCAAgC;;AAG7C,MAAa,gCAAgC;;;;;;;;;;;;;;;;;;;;;AAsB7C,MAAa,oCAAoC;;;;;;;;;;AAWjD,SAAgB,aAAa,MAAsB;CACjD,OAAO,KAAK,MAAM,OAAO,GAAI;AAC/B;;;;;AAMA,SAAgB,UAAU,MAAsB;CAC9C,OAAO,OAAO;AAChB"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../../../src/media/errors.ts"],"sourcesContent":["/**\n * SVTA 2070 (Standardized Error Codes) — the vocabulary for identifying a playback failure or notice, independent of\n * how it's transported or who decides what to do about it.\n *\n * A code is a single integer: the leading digit(s) are the **category** (the error's domain) and the trailing three are\n * the **index** (the specific error within it). Four digits for natively-defined errors, five when an external standard\n * is embedded — `\"03404\"` is an HTTP 404 under the network category. Categories are `0` unknown, `1` media content, `2`\n * playback, `3` network, `4` content protection, `5` accessibility, `6` remote play, `7` advertising, `99` custom.\n *\n * Two properties of the spec shape the types here:\n *\n * - **Severity is deliberately not part of a code.** Per §Approach, \"impact varies with player implementation, breaking\n * the consistency of a specific error mapping to single code.\" So an error carries no fatal flag — whether a\n * condition is fatal depends on the composition observing it, and is decided downstream.\n * - **Reporting is partial and stacked** (Principles 5–6). Errors are reported as encountered, most of them non-fatal,\n * and the _sequence_ carries causation a single value can't.\n *\n * See `internal/design/spf/features/errors.md`.\n */\n\n/**\n * A reported condition, identified by its SVTA code.\n *\n * Named for the spec rather than the engine because the vocabulary is format- and player-neutral. Not `MediaError` —\n * that name belongs to the `@videojs/media` DOM-facing class this eventually maps _onto_, and the mapping is the point\n * at which severity and user-facing text get decided.\n */\nexport interface SvtaError {\n /** The SVTA code — see {@link svtaCategory} / {@link svtaIndex}. */\n code: number;\n /** Engineer-facing detail. Optional; the code is the identity. */\n message?: string;\n /** Reporter-specific context (track type, url, the constraint that fired). */\n data?: unknown;\n}\n\n/** SVTA 1 [Media Content] 004 — the video is in a format we can't play (e.g. an MPEG-TS container). */\nexport const SVTA_UNSUPPORTED_VIDEO_FORMAT = 1004;\n\n/** SVTA 1 [Media Content] 005 — the audio counterpart of {@link SVTA_UNSUPPORTED_VIDEO_FORMAT}. */\nexport const SVTA_UNSUPPORTED_AUDIO_FORMAT = 1005;\n\n/**\n * SVTA 4 [Content Protection] 008 — unsupported or unavailable DRM system. Used for \"this source is encrypted and we\n * have no decryption pipeline,\" which is detection, not a license failure.\n */\nexport const SVTA_UNSUPPORTED_DRM_SYSTEM = 4008;\n\n/**\n * SVTA 2 [Playback] 011 — no video track the environment can play.\n *\n * Covers both ways a composition can end up with nothing to select: renditions that existed and were all excluded as\n * unplayable, and — where the composition composes `reportAbsentTrackType` to say it needs the type — a source carrying\n * none to begin with. Deliberately one code for both, because they are the same answer to a viewer, and because the\n * alternative is a code the SVTA spec doesn't define. Whether an absent type is a failure is the composition's to\n * state, which is why it opts in rather than being read off the source.\n */\nexport const SVTA_NO_SUPPORTED_VIDEO_TRACK = 2011;\n\n/** SVTA 2 [Playback] 012 — the audio counterpart of {@link SVTA_NO_SUPPORTED_VIDEO_TRACK}. */\nexport const SVTA_NO_SUPPORTED_AUDIO_TRACK = 2012;\n\n/**\n * SVTA 99 [Custom] 001 — this engine has no pipeline for something the source requires, so the source is unplayable\n * _here_ rather than broken.\n *\n * Custom rather than standard because the standard codes available describe either narrower or wider things. The causes\n * (1004/1005 unsupported format, 4008 unsupported DRM) say what one rendition hit; the verdicts (2011/2012 no supported\n * track) say a type emptied without saying why it's unfixable. And 2039 \"Manifest feature unsupported\" covers features\n * that are unsupported but still _playable_ — LL-HLS degrading to standard live is a 2039 — so overloading it for a\n * fatal condition would make it useless for the notices it belongs on.\n *\n * Index `001`: the spec defines only `99000` (Unknown) for the custom category and leaves the rest to the publisher, so\n * this is the first code we define.\n *\n * Five digits, and deliberately not special-cased anywhere: {@link svtaCategory} and {@link svtaIndex} decompose it\n * correctly by arithmetic alone, because every standard category is below `8000` and custom starts at `99000`.\n */\nexport const SVTA_UNSUPPORTED_PLAYBACK_FEATURE = 99001;\n\n/**\n * The error's domain — `code / 1000`, per the spec's \"divide by one thousand to obtain the error category\". Works\n * uniformly across the four-digit native form and the five-digit form embedding an external standard: `\"03404\"` is\n * numerically 3404, which decomposes identically. That also makes a numeric code immune to the spec's inconsistent\n * zero-padding (§Approach writes a category-unknown network error as `\"0300\"` where the error index implies category 3\n * / index 000).\n */\nexport function svtaCategory(code: number): number {\n return Math.floor(code / 1000);\n}\n\n/**\n * The specific error within its category — `code % 1000`. For a five-digit code this is the embedded external value (an\n * HTTP status, a VAST code).\n */\nexport function svtaIndex(code: number): number {\n return code % 1000;\n}\n"],"mappings":";;AAqCA,MAAa,gCAAgC;;AAG7C,MAAa,gCAAgC;;;;;AAM7C,MAAa,8BAA8B;;;;;;;;;;AAW3C,MAAa,gCAAgC;;AAG7C,MAAa,gCAAgC;;;;;;;;;;;;;;;;;AAkB7C,MAAa,oCAAoC;;;;;;;;AASjD,SAAgB,aAAa,MAAsB;CACjD,OAAO,KAAK,MAAM,OAAO,GAAI;AAC/B;;;;;AAMA,SAAgB,UAAU,MAAsB;CAC9C,OAAO,OAAO;AAChB"}
@@ -1,8 +1,5 @@
1
1
  //#region src/media/hls/parse-attributes.ts
2
- /**
3
- * Parse HLS attribute list from a tag line.
4
- * Handles both quoted and unquoted values.
5
- */
2
+ /** Parse HLS attribute list from a tag line. Handles both quoted and unquoted values. */
6
3
  function parseAttributeList(line) {
7
4
  const attributes = /* @__PURE__ */ new Map();
8
5
  for (const match of line.matchAll(/([A-Z0-9-]+)=(?:"([^"]*)"|([^,]*))/g)) {
@@ -12,9 +9,7 @@ function parseAttributeList(line) {
12
9
  }
13
10
  return attributes;
14
11
  }
15
- /**
16
- * Parse RESOLUTION attribute value (WIDTHxHEIGHT).
17
- */
12
+ /** Parse RESOLUTION attribute value (WIDTHxHEIGHT). */
18
13
  function parseResolution(value) {
19
14
  const match = /^(\d+)x(\d+)$/.exec(value);
20
15
  if (!match) return null;
@@ -23,9 +18,7 @@ function parseResolution(value) {
23
18
  height: Number.parseInt(match[2], 10)
24
19
  };
25
20
  }
26
- /**
27
- * Parse FRAME-RATE attribute to rational frame rate.
28
- */
21
+ /** Parse FRAME-RATE attribute to rational frame rate. */
29
22
  function parseFrameRate(value) {
30
23
  const fps = Number.parseFloat(value);
31
24
  if (Number.isNaN(fps) || fps <= 0) return void 0;
@@ -55,9 +48,7 @@ const AUDIO_CODEC_PREFIXES = [
55
48
  "alac",
56
49
  "vorbis"
57
50
  ];
58
- /**
59
- * Parse CODECS attribute into separate video and audio codecs.
60
- */
51
+ /** Parse CODECS attribute into separate video and audio codecs. */
61
52
  function parseCodecs(codecs) {
62
53
  const parts = codecs.split(",").map((s) => s.trim());
63
54
  const result = {};
@@ -68,18 +59,15 @@ function parseCodecs(codecs) {
68
59
  }
69
60
  return result;
70
61
  }
71
- /**
72
- * Parse #EXTINF duration value.
73
- */
62
+ /** Parse #EXTINF duration value. */
74
63
  function parseExtInfDuration(value) {
75
64
  const durationPart = value.split(",")[0] ?? value;
76
65
  const duration = Number.parseFloat(durationPart);
77
66
  return Number.isNaN(duration) ? 0 : duration;
78
67
  }
79
68
  /**
80
- * Parse BYTERANGE attribute value.
81
- * Format: "length[@offset]"
82
- * If offset is omitted, it continues from the previous byte range end.
69
+ * Parse BYTERANGE attribute value. Format: "length[@offset]" If offset is omitted, it continues from the previous byte
70
+ * range end.
83
71
  */
84
72
  function parseByteRange(value, previousEnd) {
85
73
  const match = /^(\d+)(?:@(\d+))?$/.exec(value);
@@ -97,9 +85,7 @@ function parseByteRange(value, previousEnd) {
97
85
  end: start + length - 1
98
86
  };
99
87
  }
100
- /**
101
- * Create AttributeList from raw attribute string.
102
- */
88
+ /** Create AttributeList from raw attribute string. */
103
89
  function createAttributeList(line) {
104
90
  const map = parseAttributeList(line);
105
91
  return {
@@ -133,10 +119,7 @@ function createAttributeList(line) {
133
119
  }
134
120
  };
135
121
  }
136
- /**
137
- * Match a tag and extract its attributes.
138
- * Returns null if the line doesn't match the tag.
139
- */
122
+ /** Match a tag and extract its attributes. Returns null if the line doesn't match the tag. */
140
123
  function matchTag(line, tag) {
141
124
  const prefix = `#${tag}:`;
142
125
  if (!line.startsWith(prefix)) return null;
@@ -1 +1 @@
1
- {"version":3,"file":"parse-attributes.js","names":[],"sources":["../../../../src/media/hls/parse-attributes.ts"],"sourcesContent":["import type { FrameRate } from '../types';\n\n/**\n * Parse HLS attribute list from a tag line.\n * Handles both quoted and unquoted values.\n */\nexport function parseAttributeList(line: string): Map<string, string> {\n const attributes = new Map<string, string>();\n const regex = /([A-Z0-9-]+)=(?:\"([^\"]*)\"|([^,]*))/g;\n\n for (const match of line.matchAll(regex)) {\n const key = match[1];\n const value = match[2] ?? match[3] ?? '';\n if (key) {\n attributes.set(key, value);\n }\n }\n\n return attributes;\n}\n\n/**\n * Parse RESOLUTION attribute value (WIDTHxHEIGHT).\n */\nexport function parseResolution(value: string): { width: number; height: number } | null {\n const match = /^(\\d+)x(\\d+)$/.exec(value);\n if (!match) return null;\n\n const width = Number.parseInt(match[1]!, 10);\n const height = Number.parseInt(match[2]!, 10);\n\n return { width, height };\n}\n\n/**\n * Parse FRAME-RATE attribute to rational frame rate.\n */\nexport function parseFrameRate(value: string): FrameRate | undefined {\n const fps = Number.parseFloat(value);\n if (Number.isNaN(fps) || fps <= 0) return undefined;\n\n // Common frame rates with tolerance for floating point precision\n if (Math.abs(fps - 23.976) < 0.01) {\n return { frameRateNumerator: 24000, frameRateDenominator: 1001 };\n }\n if (Math.abs(fps - 29.97) < 0.01) {\n return { frameRateNumerator: 30000, frameRateDenominator: 1001 };\n }\n if (Math.abs(fps - 59.94) < 0.01) {\n return { frameRateNumerator: 60000, frameRateDenominator: 1001 };\n }\n\n // Integer frame rates\n if (fps % 1 === 0) {\n return { frameRateNumerator: Math.round(fps) };\n }\n\n // Default: use rounded value\n return { frameRateNumerator: Math.round(fps) };\n}\n\n// Audio codec identifiers, matched case-insensitively against each CODECS\n// entry's prefix. Beyond AAC (`mp4a.*`): Dolby (`ac-3`, `ec-3`, `ac-4`), Opus,\n// FLAC (`fLaC`), DTS (`dts*`), ALAC, and Vorbis. Recognizing these is what lets\n// capability probing filter undecodable audio renditions (e.g. an AC-3 5.1\n// track on a browser without AC-3) — an unrecognized codec parses empty and is\n// treated as unprobeable, so it would never be pruned.\nconst AUDIO_CODEC_PREFIXES = ['mp4a.', 'ac-3', 'ec-3', 'ac-4', 'opus', 'flac', 'dts', 'alac', 'vorbis'];\n\n/**\n * Parse CODECS attribute into separate video and audio codecs.\n */\nexport function parseCodecs(codecs: string): { video?: string; audio?: string } {\n const parts = codecs.split(',').map((s) => s.trim());\n const result: { video?: string; audio?: string } = {};\n\n for (const codec of parts) {\n const lower = codec.toLowerCase();\n if (codec.startsWith('avc1.') || codec.startsWith('hvc1.') || codec.startsWith('hev1.')) {\n result.video = codec;\n } else if (AUDIO_CODEC_PREFIXES.some((prefix) => lower.startsWith(prefix))) {\n result.audio = codec;\n }\n }\n\n return result;\n}\n\n/**\n * Parse #EXTINF duration value.\n */\nexport function parseExtInfDuration(value: string): number {\n const durationPart = value.split(',')[0] ?? value;\n const duration = Number.parseFloat(durationPart);\n return Number.isNaN(duration) ? 0 : duration;\n}\n\n/**\n * Parse BYTERANGE attribute value.\n * Format: \"length[@offset]\"\n * If offset is omitted, it continues from the previous byte range end.\n */\nexport function parseByteRange(value: string, previousEnd?: number): { start: number; end: number } | null {\n const match = /^(\\d+)(?:@(\\d+))?$/.exec(value);\n if (!match) return null;\n\n const length = Number.parseInt(match[1]!, 10);\n if (Number.isNaN(length)) return null;\n\n let start: number;\n if (match[2] !== undefined) {\n start = Number.parseInt(match[2], 10);\n if (Number.isNaN(start)) return null;\n } else if (previousEnd !== undefined) {\n start = previousEnd;\n } else {\n return null;\n }\n\n return { start, end: start + length - 1 };\n}\n\n/**\n * AttributeList - Typed attribute access wrapper.\n */\nexport interface AttributeList {\n get: (key: string) => string | undefined;\n getInt: (key: string, defaultValue?: number) => number | undefined;\n getFloat: (key: string, defaultValue?: number) => number | undefined;\n getBool: (key: string) => boolean;\n getResolution: (key: string) => { width: number; height: number } | undefined;\n getFrameRate: (key: string) => FrameRate | undefined;\n}\n\n/**\n * Create AttributeList from raw attribute string.\n */\nexport function createAttributeList(line: string): AttributeList {\n const map = parseAttributeList(line);\n\n return {\n get(key: string): string | undefined {\n return map.get(key);\n },\n\n getInt(key: string, defaultValue?: number): number | undefined {\n const value = map.get(key);\n if (value === undefined) return defaultValue;\n const parsed = Number.parseInt(value, 10);\n return Number.isNaN(parsed) ? defaultValue : parsed;\n },\n\n getFloat(key: string, defaultValue?: number): number | undefined {\n const value = map.get(key);\n if (value === undefined) return defaultValue;\n const parsed = Number.parseFloat(value);\n return Number.isNaN(parsed) ? defaultValue : parsed;\n },\n\n getBool(key: string): boolean {\n return map.get(key) === 'YES';\n },\n\n getResolution(key: string): { width: number; height: number } | undefined {\n const value = map.get(key);\n if (!value) return undefined;\n return parseResolution(value) ?? undefined;\n },\n\n getFrameRate(key: string): FrameRate | undefined {\n const value = map.get(key);\n if (!value) return undefined;\n return parseFrameRate(value);\n },\n };\n}\n\n/**\n * Match a tag and extract its attributes.\n * Returns null if the line doesn't match the tag.\n */\nexport function matchTag(line: string, tag: string): AttributeList | null {\n const prefix = `#${tag}:`;\n if (!line.startsWith(prefix)) return null;\n return createAttributeList(line.slice(prefix.length));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parse-attributes.js","names":[],"sources":["../../../../src/media/hls/parse-attributes.ts"],"sourcesContent":["import type { FrameRate } from '../types';\n\n/** Parse HLS attribute list from a tag line. Handles both quoted and unquoted values. */\nexport function parseAttributeList(line: string): Map<string, string> {\n const attributes = new Map<string, string>();\n const regex = /([A-Z0-9-]+)=(?:\"([^\"]*)\"|([^,]*))/g;\n\n for (const match of line.matchAll(regex)) {\n const key = match[1];\n const value = match[2] ?? match[3] ?? '';\n\n if (key) {\n attributes.set(key, value);\n }\n }\n\n return attributes;\n}\n\n/** Parse RESOLUTION attribute value (WIDTHxHEIGHT). */\nexport function parseResolution(value: string): { width: number; height: number } | null {\n const match = /^(\\d+)x(\\d+)$/.exec(value);\n if (!match) return null;\n\n const width = Number.parseInt(match[1]!, 10);\n const height = Number.parseInt(match[2]!, 10);\n\n return { width, height };\n}\n\n/** Parse FRAME-RATE attribute to rational frame rate. */\nexport function parseFrameRate(value: string): FrameRate | undefined {\n const fps = Number.parseFloat(value);\n if (Number.isNaN(fps) || fps <= 0) return undefined;\n\n // Common frame rates with tolerance for floating point precision\n if (Math.abs(fps - 23.976) < 0.01) {\n return { frameRateNumerator: 24000, frameRateDenominator: 1001 };\n }\n\n if (Math.abs(fps - 29.97) < 0.01) {\n return { frameRateNumerator: 30000, frameRateDenominator: 1001 };\n }\n\n if (Math.abs(fps - 59.94) < 0.01) {\n return { frameRateNumerator: 60000, frameRateDenominator: 1001 };\n }\n\n // Integer frame rates\n if (fps % 1 === 0) {\n return { frameRateNumerator: Math.round(fps) };\n }\n\n // Default: use rounded value\n return { frameRateNumerator: Math.round(fps) };\n}\n\n// Audio codec identifiers, matched case-insensitively against each CODECS\n// entry's prefix. Beyond AAC (`mp4a.*`): Dolby (`ac-3`, `ec-3`, `ac-4`), Opus,\n// FLAC (`fLaC`), DTS (`dts*`), ALAC, and Vorbis. Recognizing these is what lets\n// capability probing filter undecodable audio renditions (e.g. an AC-3 5.1\n// track on a browser without AC-3) — an unrecognized codec parses empty and is\n// treated as unprobeable, so it would never be pruned.\nconst AUDIO_CODEC_PREFIXES = ['mp4a.', 'ac-3', 'ec-3', 'ac-4', 'opus', 'flac', 'dts', 'alac', 'vorbis'];\n\n/** Parse CODECS attribute into separate video and audio codecs. */\nexport function parseCodecs(codecs: string): { video?: string; audio?: string } {\n const parts = codecs.split(',').map((s) => s.trim());\n const result: { video?: string; audio?: string } = {};\n\n for (const codec of parts) {\n const lower = codec.toLowerCase();\n\n if (codec.startsWith('avc1.') || codec.startsWith('hvc1.') || codec.startsWith('hev1.')) {\n result.video = codec;\n } else if (AUDIO_CODEC_PREFIXES.some((prefix) => lower.startsWith(prefix))) {\n result.audio = codec;\n }\n }\n\n return result;\n}\n\n/** Parse #EXTINF duration value. */\nexport function parseExtInfDuration(value: string): number {\n const durationPart = value.split(',')[0] ?? value;\n const duration = Number.parseFloat(durationPart);\n\n return Number.isNaN(duration) ? 0 : duration;\n}\n\n/**\n * Parse BYTERANGE attribute value. Format: \"length[@offset]\" If offset is omitted, it continues from the previous byte\n * range end.\n */\nexport function parseByteRange(value: string, previousEnd?: number): { start: number; end: number } | null {\n const match = /^(\\d+)(?:@(\\d+))?$/.exec(value);\n if (!match) return null;\n\n const length = Number.parseInt(match[1]!, 10);\n if (Number.isNaN(length)) return null;\n\n let start: number;\n\n if (match[2] !== undefined) {\n start = Number.parseInt(match[2], 10);\n\n if (Number.isNaN(start)) return null;\n } else if (previousEnd !== undefined) {\n start = previousEnd;\n } else {\n return null;\n }\n\n return { start, end: start + length - 1 };\n}\n\n/** AttributeList - Typed attribute access wrapper. */\nexport interface AttributeList {\n get: (key: string) => string | undefined;\n getInt: (key: string, defaultValue?: number) => number | undefined;\n getFloat: (key: string, defaultValue?: number) => number | undefined;\n getBool: (key: string) => boolean;\n getResolution: (key: string) => { width: number; height: number } | undefined;\n getFrameRate: (key: string) => FrameRate | undefined;\n}\n\n/** Create AttributeList from raw attribute string. */\nexport function createAttributeList(line: string): AttributeList {\n const map = parseAttributeList(line);\n\n return {\n get(key: string): string | undefined {\n return map.get(key);\n },\n\n getInt(key: string, defaultValue?: number): number | undefined {\n const value = map.get(key);\n if (value === undefined) return defaultValue;\n\n const parsed = Number.parseInt(value, 10);\n\n return Number.isNaN(parsed) ? defaultValue : parsed;\n },\n\n getFloat(key: string, defaultValue?: number): number | undefined {\n const value = map.get(key);\n if (value === undefined) return defaultValue;\n\n const parsed = Number.parseFloat(value);\n\n return Number.isNaN(parsed) ? defaultValue : parsed;\n },\n\n getBool(key: string): boolean {\n return map.get(key) === 'YES';\n },\n\n getResolution(key: string): { width: number; height: number } | undefined {\n const value = map.get(key);\n if (!value) return undefined;\n\n return parseResolution(value) ?? undefined;\n },\n\n getFrameRate(key: string): FrameRate | undefined {\n const value = map.get(key);\n if (!value) return undefined;\n\n return parseFrameRate(value);\n },\n };\n}\n\n/** Match a tag and extract its attributes. Returns null if the line doesn't match the tag. */\nexport function matchTag(line: string, tag: string): AttributeList | null {\n const prefix = `#${tag}:`;\n if (!line.startsWith(prefix)) return null;\n\n return createAttributeList(line.slice(prefix.length));\n}\n"],"mappings":";;AAGA,SAAgB,mBAAmB,MAAmC;CACpE,MAAM,6BAAa,IAAI,IAAoB;CAG3C,KAAK,MAAM,SAAS,KAAK,SAAS,qCAAK,GAAG;EACxC,MAAM,MAAM,MAAM;EAClB,MAAM,QAAQ,MAAM,MAAM,MAAM,MAAM;EAEtC,IAAI,KACF,WAAW,IAAI,KAAK,KAAK;CAE7B;CAEA,OAAO;AACT;;AAGA,SAAgB,gBAAgB,OAAyD;CACvF,MAAM,QAAQ,gBAAgB,KAAK,KAAK;CACxC,IAAI,CAAC,OAAO,OAAO;CAKnB,OAAO;EAAE,OAHK,OAAO,SAAS,MAAM,IAAK,EAG5B;EAAG,QAFD,OAAO,SAAS,MAAM,IAAK,EAErB;CAAE;AACzB;;AAGA,SAAgB,eAAe,OAAsC;CACnE,MAAM,MAAM,OAAO,WAAW,KAAK;CACnC,IAAI,OAAO,MAAM,GAAG,KAAK,OAAO,GAAG,OAAO,KAAA;CAG1C,IAAI,KAAK,IAAI,MAAM,MAAM,IAAI,KAC3B,OAAO;EAAE,oBAAoB;EAAO,sBAAsB;CAAK;CAGjE,IAAI,KAAK,IAAI,MAAM,KAAK,IAAI,KAC1B,OAAO;EAAE,oBAAoB;EAAO,sBAAsB;CAAK;CAGjE,IAAI,KAAK,IAAI,MAAM,KAAK,IAAI,KAC1B,OAAO;EAAE,oBAAoB;EAAO,sBAAsB;CAAK;CAIjE,IAAI,MAAM,MAAM,GACd,OAAO,EAAE,oBAAoB,KAAK,MAAM,GAAG,EAAE;CAI/C,OAAO,EAAE,oBAAoB,KAAK,MAAM,GAAG,EAAE;AAC/C;AAQA,MAAM,uBAAuB;CAAC;CAAS;CAAQ;CAAQ;CAAQ;CAAQ;CAAQ;CAAO;CAAQ;AAAQ;;AAGtG,SAAgB,YAAY,QAAoD;CAC9E,MAAM,QAAQ,OAAO,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC;CACnD,MAAM,SAA6C,CAAC;CAEpD,KAAK,MAAM,SAAS,OAAO;EACzB,MAAM,QAAQ,MAAM,YAAY;EAEhC,IAAI,MAAM,WAAW,OAAO,KAAK,MAAM,WAAW,OAAO,KAAK,MAAM,WAAW,OAAO,GACpF,OAAO,QAAQ;OACV,IAAI,qBAAqB,MAAM,WAAW,MAAM,WAAW,MAAM,CAAC,GACvE,OAAO,QAAQ;CAEnB;CAEA,OAAO;AACT;;AAGA,SAAgB,oBAAoB,OAAuB;CACzD,MAAM,eAAe,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM;CAC5C,MAAM,WAAW,OAAO,WAAW,YAAY;CAE/C,OAAO,OAAO,MAAM,QAAQ,IAAI,IAAI;AACtC;;;;;AAMA,SAAgB,eAAe,OAAe,aAA6D;CACzG,MAAM,QAAQ,qBAAqB,KAAK,KAAK;CAC7C,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,SAAS,OAAO,SAAS,MAAM,IAAK,EAAE;CAC5C,IAAI,OAAO,MAAM,MAAM,GAAG,OAAO;CAEjC,IAAI;CAEJ,IAAI,MAAM,OAAO,KAAA,GAAW;EAC1B,QAAQ,OAAO,SAAS,MAAM,IAAI,EAAE;EAEpC,IAAI,OAAO,MAAM,KAAK,GAAG,OAAO;CAClC,OAAO,IAAI,gBAAgB,KAAA,GACzB,QAAQ;MAER,OAAO;CAGT,OAAO;EAAE;EAAO,KAAK,QAAQ,SAAS;CAAE;AAC1C;;AAaA,SAAgB,oBAAoB,MAA6B;CAC/D,MAAM,MAAM,mBAAmB,IAAI;CAEnC,OAAO;EACL,IAAI,KAAiC;GACnC,OAAO,IAAI,IAAI,GAAG;EACpB;EAEA,OAAO,KAAa,cAA2C;GAC7D,MAAM,QAAQ,IAAI,IAAI,GAAG;GACzB,IAAI,UAAU,KAAA,GAAW,OAAO;GAEhC,MAAM,SAAS,OAAO,SAAS,OAAO,EAAE;GAExC,OAAO,OAAO,MAAM,MAAM,IAAI,eAAe;EAC/C;EAEA,SAAS,KAAa,cAA2C;GAC/D,MAAM,QAAQ,IAAI,IAAI,GAAG;GACzB,IAAI,UAAU,KAAA,GAAW,OAAO;GAEhC,MAAM,SAAS,OAAO,WAAW,KAAK;GAEtC,OAAO,OAAO,MAAM,MAAM,IAAI,eAAe;EAC/C;EAEA,QAAQ,KAAsB;GAC5B,OAAO,IAAI,IAAI,GAAG,MAAM;EAC1B;EAEA,cAAc,KAA4D;GACxE,MAAM,QAAQ,IAAI,IAAI,GAAG;GACzB,IAAI,CAAC,OAAO,OAAO,KAAA;GAEnB,OAAO,gBAAgB,KAAK,KAAK,KAAA;EACnC;EAEA,aAAa,KAAoC;GAC/C,MAAM,QAAQ,IAAI,IAAI,GAAG;GACzB,IAAI,CAAC,OAAO,OAAO,KAAA;GAEnB,OAAO,eAAe,KAAK;EAC7B;CACF;AACF;;AAGA,SAAgB,SAAS,MAAc,KAAmC;CACxE,MAAM,SAAS,IAAI,IAAI;CACvB,IAAI,CAAC,KAAK,WAAW,MAAM,GAAG,OAAO;CAErC,OAAO,oBAAoB,KAAK,MAAM,OAAO,MAAM,CAAC;AACtD"}
@@ -3,7 +3,10 @@ import { matchTag, parseByteRange, parseExtInfDuration } from "./parse-attribute
3
3
  import { resolveUrl } from "./resolve-url.js";
4
4
  import { isUndefined } from "@videojs/utils/predicate";
5
5
  //#region src/media/hls/parse-media-playlist.ts
6
- /** MPEG-2 Transport Stream (IANA `video/MP2T`, lowercased for `isTypeSupported`). Video + audio TS — there is no `audio/mp2t`. */
6
+ /**
7
+ * MPEG-2 Transport Stream (IANA `video/MP2T`, lowercased for `isTypeSupported`). Video + audio TS — there is no
8
+ * `audio/mp2t`.
9
+ */
7
10
  const MPEG_TS_MIME = "video/mp2t";
8
11
  /** Raw ADTS AAC packed-audio (HLS `.aac` segments; IANA `audio/aac`). */
9
12
  const RAW_AAC_MIME = "audio/aac";
@@ -14,8 +17,8 @@ const CONTAINER_MIME_BY_EXTENSION = {
14
17
  /** The non-fMP4 container MIMEs the parser detects — all currently treated as unplayable. */
15
18
  const NON_FMP4_CONTAINER_MIMES = new Set(Object.values(CONTAINER_MIME_BY_EXTENSION));
16
19
  /**
17
- * Non-fMP4 container MIME for a (resolved, absolute) segment URL, by file
18
- * extension, ignoring the query string. `undefined` for fMP4 / unrecognized.
20
+ * Non-fMP4 container MIME for a (resolved, absolute) segment URL, by file extension, ignoring the query string.
21
+ * `undefined` for fMP4 / unrecognized.
19
22
  */
20
23
  function containerMimeFromSegment(url) {
21
24
  if (!url) return void 0;
@@ -29,23 +32,18 @@ function containerMimeFromSegment(url) {
29
32
  return dot === -1 ? void 0 : CONTAINER_MIME_BY_EXTENSION[path.slice(dot)];
30
33
  }
31
34
  /**
32
- * Position a freshly-parsed window (whose segment `startTime`s are snapshot-
33
- * local, i.e. from 0) onto the timeline established by the previous resolved
34
- * snapshot. Carries the timeline forward using the media-sequence overlap and
35
- * the previous window's *actual* segment durations — see
36
- * `internal/design/spf/live-presentation-timeline-model.md`.
35
+ * Position a freshly-parsed window (whose segment `startTime`s are snapshot- local, i.e. from 0) onto the timeline
36
+ * established by the previous resolved snapshot. Carries the timeline forward using the media-sequence overlap and the
37
+ * previous window's _actual_ segment durations see `internal/design/spf/live-presentation-timeline-model.md`.
37
38
  *
38
- * - **Overlap** (`0 <= offset < previous.segments.length`): the new window's
39
- * first segment is the same segment as `previous.segments[offset]`; anchor to
40
- * its start (URLs checked — a mismatch warns).
39
+ * - **Overlap** (`0 <= offset < previous.segments.length`): the new window's first segment is the same segment as
40
+ * `previous.segments[offset]`; anchor to its start (URLs checked — a mismatch warns).
41
41
  * - **Sequence went backwards** (non-conformant): reset to the local base.
42
- * - **Full turnover** (no overlap): estimate forward from the previous window's
43
- * end across the unseen gap. This is the *only* place EXT-X-TARGETDURATION is
44
- * used for timing — an upper-bound estimate, since the actual rolled-off
42
+ * - **Full turnover** (no overlap): estimate forward from the previous window's end across the unseen gap. This is the
43
+ * _only_ place EXT-X-TARGETDURATION is used for timing — an upper-bound estimate, since the actual rolled-off
45
44
  * durations are gone (exact recovery is the deferred PDT decision).
46
45
  *
47
- * Returns the rebased segments (the window edge is `segments[0].startTime`,
48
- * derived — never stored on the track).
46
+ * Returns the rebased segments (the window edge is `segments[0].startTime`, derived — never stored on the track).
49
47
  */
50
48
  function placeOnPreviousTimeline(previous, segments, mediaSequence, targetDuration) {
51
49
  const prevSegments = previous.segments;
@@ -76,17 +74,13 @@ function placeOnPreviousTimeline(previous, segments, mediaSequence, targetDurati
76
74
  }));
77
75
  }
78
76
  /**
79
- * Place a window on the frozen wall-clock anchor — the track's `startDate`
80
- * (wall clock at media-time 0). The anchoring PDT-bearing segment lands at
81
- * `segment.startDate anchor`, so the window sits on the shared presentation
82
- * timeline rather than a local-from-zero one (the `startDate` recomputed
83
- * afterwards then reads back as the anchor idempotent). This is the **main
84
- * live path on every parse**: first resolves place against the pre-stamped
85
- * shell anchor, and reloads re-place from PDT so drift can't accumulate and a
86
- * long-stall turnover re-places with no overlap bridging (the HLS authoring
87
- * spec's §8.1 EXTINF-accuracy rule bounds the per-reload correction to ~one
88
- * video frame). Falls back to the local base when no segment carries PDT —
89
- * there's nothing to anchor against (callers guard this for reloads, where the
77
+ * Place a window on the frozen wall-clock anchor — the track's `startDate` (wall clock at media-time 0). The anchoring
78
+ * PDT-bearing segment lands at `segment.startDate anchor`, so the window sits on the shared presentation timeline
79
+ * rather than a local-from-zero one (the `startDate` recomputed afterwards then reads back as the anchor — idempotent).
80
+ * This is the **main live path on every parse**: first resolves place against the pre-stamped shell anchor, and reloads
81
+ * re-place from PDT so drift can't accumulate and a long-stall turnover re-places with no overlap bridging (the HLS
82
+ * authoring spec's §8.1 EXTINF-accuracy rule bounds the per-reload correction to ~one video frame). Falls back to the
83
+ * local base when no segment carries PDT there's nothing to anchor against (callers guard this for reloads, where the
90
84
  * local base would reset the timeline).
91
85
  */
92
86
  function placeOnAnchor(segments, anchor) {
@@ -102,12 +96,10 @@ function placeOnAnchor(segments, anchor) {
102
96
  /**
103
97
  * Parse HLS media playlist and resolve track with segments.
104
98
  *
105
- * `previous` is what was known about this track before this parse: the
106
- * partially-resolved track from the multivariant playlist on the first resolve,
107
- * or the previously-resolved snapshot on a live reload. Its metadata is carried
108
- * onto the result either way; when it's already resolved (has segments), its
109
- * timeline is carried forward so the new window lands on a stable, advancing
110
- * timeline (see {@link placeOnPreviousTimeline}).
99
+ * `previous` is what was known about this track before this parse: the partially-resolved track from the multivariant
100
+ * playlist on the first resolve, or the previously-resolved snapshot on a live reload. Its metadata is carried onto the
101
+ * result either way; when it's already resolved (has segments), its timeline is carried forward so the new window lands
102
+ * on a stable, advancing timeline (see {@link placeOnPreviousTimeline}).
111
103
  *
112
104
  * @param text - Media playlist text content
113
105
  * @param previous - Prior track state (unresolved shell, or previous resolved snapshot)
@@ -1 +1 @@
1
- {"version":3,"file":"parse-media-playlist.js","names":[],"sources":["../../../../src/media/hls/parse-media-playlist.ts"],"sourcesContent":["import { isUndefined } from '@videojs/utils/predicate';\nimport {\n type AudioTrack,\n getMediaPlaylistMetadata,\n isResolvedTrack,\n MEDIA_PLAYLIST_METADATA_KEY,\n type MediaPlaylistMetadata,\n type PartiallyResolvedAudioTrack,\n type PartiallyResolvedTextTrack,\n type PartiallyResolvedTrack,\n type PartiallyResolvedVideoTrack,\n type ResolvedTrack,\n type Segment,\n type TextTrack,\n type VideoTrack,\n} from '../types';\nimport { matchTag, parseByteRange, parseExtInfDuration } from './parse-attributes';\nimport { resolveUrl } from './resolve-url';\n\n/** MPEG-2 Transport Stream (IANA `video/MP2T`, lowercased for `isTypeSupported`). Video + audio TS — there is no `audio/mp2t`. */\nexport const MPEG_TS_MIME = 'video/mp2t';\n/** Raw ADTS AAC packed-audio (HLS `.aac` segments; IANA `audio/aac`). */\nexport const RAW_AAC_MIME = 'audio/aac';\n\n// Non-fMP4 container MIMEs keyed by segment file extension. fMP4 (the MSE\n// default) always carries an EXT-X-MAP init segment, so a media playlist with\n// no init segment and one of these extensions is a non-fMP4 rendition,\n// relabeled from the fMP4 default. Extend with `.mp3` → 'audio/mpeg' etc.\nconst CONTAINER_MIME_BY_EXTENSION: Record<string, string> = {\n '.ts': MPEG_TS_MIME,\n '.aac': RAW_AAC_MIME,\n};\n\n/** The non-fMP4 container MIMEs the parser detects — all currently treated as unplayable. */\nexport const NON_FMP4_CONTAINER_MIMES = new Set(Object.values(CONTAINER_MIME_BY_EXTENSION));\n\n/**\n * Non-fMP4 container MIME for a (resolved, absolute) segment URL, by file\n * extension, ignoring the query string. `undefined` for fMP4 / unrecognized.\n */\nfunction containerMimeFromSegment(url: string | undefined): string | undefined {\n if (!url) return undefined;\n let path: string;\n try {\n path = new URL(url).pathname.toLowerCase();\n } catch {\n path = url.toLowerCase().split('?')[0] ?? '';\n }\n const dot = path.lastIndexOf('.');\n return dot === -1 ? undefined : CONTAINER_MIME_BY_EXTENSION[path.slice(dot)];\n}\n\n/**\n * Resolve unresolved track type to its resolved equivalent.\n */\ntype ResolveTrack<T> = T extends PartiallyResolvedVideoTrack\n ? VideoTrack\n : T extends PartiallyResolvedAudioTrack\n ? AudioTrack\n : T extends PartiallyResolvedTextTrack\n ? TextTrack\n : never;\n\n/**\n * Position a freshly-parsed window (whose segment `startTime`s are snapshot-\n * local, i.e. from 0) onto the timeline established by the previous resolved\n * snapshot. Carries the timeline forward using the media-sequence overlap and\n * the previous window's *actual* segment durations — see\n * `internal/design/spf/live-presentation-timeline-model.md`.\n *\n * - **Overlap** (`0 <= offset < previous.segments.length`): the new window's\n * first segment is the same segment as `previous.segments[offset]`; anchor to\n * its start (URLs checked — a mismatch warns).\n * - **Sequence went backwards** (non-conformant): reset to the local base.\n * - **Full turnover** (no overlap): estimate forward from the previous window's\n * end across the unseen gap. This is the *only* place EXT-X-TARGETDURATION is\n * used for timing — an upper-bound estimate, since the actual rolled-off\n * durations are gone (exact recovery is the deferred PDT decision).\n *\n * Returns the rebased segments (the window edge is `segments[0].startTime`,\n * derived — never stored on the track).\n */\nfunction placeOnPreviousTimeline(\n previous: ResolvedTrack,\n segments: Segment[],\n mediaSequence: number,\n targetDuration: number\n): Segment[] {\n const prevSegments = previous.segments;\n const localBase = segments[0]?.startTime ?? 0;\n\n if (prevSegments.length === 0 || segments.length === 0) {\n return segments;\n }\n\n const prevMediaSequence = getMediaPlaylistMetadata(previous)?.mediaSequence ?? 0;\n const offset = mediaSequence - prevMediaSequence;\n\n let anchor: number;\n if (offset >= 0 && offset < prevSegments.length) {\n const overlap = prevSegments[offset]!;\n if (overlap.url !== segments[0]!.url) {\n console.warn(\n `[parseMediaPlaylist] media-sequence aligns previous[${offset}] with the new window's first segment, ` +\n `but URLs differ (${overlap.url} vs ${segments[0]!.url}); sequence numbers may be unreliable.`\n );\n }\n anchor = overlap.startTime;\n } else if (offset < 0) {\n console.warn(`[parseMediaPlaylist] media-sequence went backwards (offset ${offset}); resetting timeline.`);\n anchor = localBase;\n } else {\n const last = prevSegments[prevSegments.length - 1]!;\n const newFirst = segments[0]!;\n if (last.startDate !== undefined && newFirst.startDate !== undefined) {\n // PDT bridges the gap exactly — the spec-consistent cross-reload reference,\n // immune to the target-duration over-estimate when actual segment durations\n // differ from the declared ceiling. (`startDate` is intrinsic PDT, unaffected\n // by the new window's local positioning.)\n anchor = last.startTime + (newFirst.startDate - last.startDate);\n } else {\n anchor = last.startTime + last.duration + (offset - prevSegments.length) * targetDuration;\n console.warn(\n `[parseMediaPlaylist] full window turnover (offset ${offset} >= ${prevSegments.length}); ` +\n 'no PDT to bridge — estimating from previous end.'\n );\n }\n }\n\n const shift = anchor - localBase;\n return shift === 0 ? segments : segments.map((segment) => ({ ...segment, startTime: segment.startTime + shift }));\n}\n\n/**\n * Place a window on the frozen wall-clock anchor — the track's `startDate`\n * (wall clock at media-time 0). The anchoring PDT-bearing segment lands at\n * `segment.startDate − anchor`, so the window sits on the shared presentation\n * timeline rather than a local-from-zero one (the `startDate` recomputed\n * afterwards then reads back as the anchor — idempotent). This is the **main\n * live path on every parse**: first resolves place against the pre-stamped\n * shell anchor, and reloads re-place from PDT so drift can't accumulate and a\n * long-stall turnover re-places with no overlap bridging (the HLS authoring\n * spec's §8.1 EXTINF-accuracy rule bounds the per-reload correction to ~one\n * video frame). Falls back to the local base when no segment carries PDT —\n * there's nothing to anchor against (callers guard this for reloads, where the\n * local base would reset the timeline).\n */\nfunction placeOnAnchor(segments: Segment[], anchor: number): Segment[] {\n const anchorSegment = segments.find((segment) => !isUndefined(segment.startDate));\n if (!anchorSegment || isUndefined(anchorSegment.startDate)) {\n return segments;\n }\n\n const shift = anchorSegment.startDate - anchor - anchorSegment.startTime;\n if (shift === 0) {\n return segments;\n }\n\n return segments.map((segment) => ({ ...segment, startTime: segment.startTime + shift }));\n}\n\n/**\n * Parse HLS media playlist and resolve track with segments.\n *\n * `previous` is what was known about this track before this parse: the\n * partially-resolved track from the multivariant playlist on the first resolve,\n * or the previously-resolved snapshot on a live reload. Its metadata is carried\n * onto the result either way; when it's already resolved (has segments), its\n * timeline is carried forward so the new window lands on a stable, advancing\n * timeline (see {@link placeOnPreviousTimeline}).\n *\n * @param text - Media playlist text content\n * @param previous - Prior track state (unresolved shell, or previous resolved snapshot)\n * @returns Resolved track with segments (type inferred from input)\n */\nexport function parseMediaPlaylist<T extends PartiallyResolvedTrack>(\n text: string,\n previous: T | ResolveTrack<T>\n): ResolveTrack<T> {\n const lines = text.split(/\\r?\\n/);\n\n // Segments and resources resolve relative to media playlist URL (per HLS spec)\n const baseUrl = previous.url;\n\n // Parse playlist\n const segments: Segment[] = [];\n let initSegmentUrl: string | undefined;\n let initSegmentByteRange: { start: number; end: number } | undefined;\n\n let currentDuration = 0;\n let currentByteRange: { start: number; end: number } | undefined;\n let currentTime = 0;\n let segmentIndex = 0;\n let previousByteRangeEnd: number | undefined;\n // Absolute wall-clock of the next segment's first sample, in epoch seconds.\n // Seeded by an explicit `#EXT-X-PROGRAM-DATE-TIME` (which re-anchors, e.g.\n // across a discontinuity) and advanced by each segment's duration so segments\n // without their own tag are interpolated forward (per RFC 8216).\n let currentStartDate: number | undefined;\n\n // Playlist-level metadata (surfaced for live reload pacing / merge / termination).\n let targetDuration = 0;\n let mediaSequence = 0;\n let playlistType: 'VOD' | 'EVENT' | undefined;\n let endList = false;\n let holdBack: number | undefined;\n // Low-Latency HLS delivery. Any of the partial-segment tags is enough: the\n // parser ignores parts and the loader fetches whole segments, so this records\n // that the publisher configured LL-HLS, not that we honour it.\n let lowLatency = false;\n // Any EXT-X-KEY with a real METHOD makes the rendition encrypted. Sticky: a\n // clear lead (METHOD=NONE first, a real key later) still counts, since we can\n // only report whether decryption is needed at all.\n let encrypted = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n if (!trimmed || (trimmed.startsWith('#') && !trimmed.startsWith('#EXT'))) {\n continue;\n }\n\n if (trimmed.startsWith('#EXT-X-TARGETDURATION:')) {\n targetDuration = Number.parseInt(trimmed.slice('#EXT-X-TARGETDURATION:'.length), 10) || 0;\n continue;\n }\n\n if (trimmed.startsWith('#EXT-X-MEDIA-SEQUENCE:')) {\n mediaSequence = Number.parseInt(trimmed.slice('#EXT-X-MEDIA-SEQUENCE:'.length), 10) || 0;\n continue;\n }\n\n if (trimmed.startsWith('#EXT-X-PROGRAM-DATE-TIME:')) {\n const parsed = Date.parse(trimmed.slice('#EXT-X-PROGRAM-DATE-TIME:'.length).trim());\n currentStartDate = Number.isNaN(parsed) ? currentStartDate : parsed / 1000;\n continue;\n }\n\n if (trimmed.startsWith('#EXT-X-PLAYLIST-TYPE:')) {\n const value = trimmed.slice('#EXT-X-PLAYLIST-TYPE:'.length).trim();\n playlistType = value === 'VOD' || value === 'EVENT' ? value : undefined;\n continue;\n }\n\n const key = matchTag(trimmed, 'EXT-X-KEY');\n if (key) {\n const method = key.get('METHOD');\n if (method !== undefined && method !== 'NONE') encrypted = true;\n continue;\n }\n\n // #EXT-X-SERVER-CONTROL — HOLD-BACK supplies the latency target. PART-HOLD-BACK\n // is read for its presence only, as an LL-HLS signal; see MediaPlaylistMetadata\n // on why its value stays out of `holdBack` until LL-HLS support lands.\n const serverControl = matchTag(trimmed, 'EXT-X-SERVER-CONTROL');\n if (serverControl) {\n const value = serverControl.get('HOLD-BACK');\n if (value !== undefined) {\n const parsed = Number.parseFloat(value);\n if (Number.isFinite(parsed) && parsed > 0) holdBack = parsed;\n }\n // Advertised for clients playing partial segments — its presence is an\n // LL-HLS signal even though the value stays deliberately unread.\n if (serverControl.get('PART-HOLD-BACK') !== undefined) lowLatency = true;\n continue;\n }\n\n // Partial segments: noted, then skipped. `#EXT-X-PART` lines would otherwise\n // fall through as unrecognized `#EXT` tags, so LL-HLS delivery went unnoticed.\n if (trimmed.startsWith('#EXT-X-PART-INF') || trimmed.startsWith('#EXT-X-PART:')) {\n lowLatency = true;\n continue;\n }\n\n if (\n trimmed === '#EXTM3U' ||\n trimmed.startsWith('#EXT-X-VERSION:') ||\n trimmed.startsWith('#EXT-X-INDEPENDENT-SEGMENTS')\n ) {\n continue;\n }\n\n // #EXT-X-MAP - Init segment\n const mapAttrs = matchTag(trimmed, 'EXT-X-MAP');\n if (mapAttrs) {\n const uri = mapAttrs.get('URI');\n if (uri) {\n initSegmentUrl = resolveUrl(uri, baseUrl);\n const byteRangeStr = mapAttrs.get('BYTERANGE');\n if (byteRangeStr) {\n initSegmentByteRange = parseByteRange(byteRangeStr, 0) ?? undefined;\n }\n }\n continue;\n }\n\n // #EXTINF - Segment duration\n if (trimmed.startsWith('#EXTINF:')) {\n currentDuration = parseExtInfDuration(trimmed.slice(8));\n continue;\n }\n\n // #EXT-X-BYTERANGE - Segment byte range\n if (trimmed.startsWith('#EXT-X-BYTERANGE:')) {\n currentByteRange = parseByteRange(trimmed.slice(17), previousByteRangeEnd) ?? undefined;\n continue;\n }\n\n if (trimmed === '#EXT-X-ENDLIST') {\n endList = true;\n continue;\n }\n\n // Segment URI\n if (!trimmed.startsWith('#') && currentDuration > 0) {\n const segment: Segment = {\n id: `segment-${mediaSequence + segmentIndex}`,\n url: resolveUrl(trimmed, baseUrl),\n duration: currentDuration,\n startTime: currentTime,\n };\n\n if (!isUndefined(currentStartDate)) {\n segment.startDate = currentStartDate;\n // Interpolate forward: the next segment without an explicit tag inherits\n // this anchor plus this segment's duration.\n currentStartDate += currentDuration;\n }\n\n if (currentByteRange) {\n segment.byteRange = currentByteRange;\n previousByteRangeEnd = currentByteRange.end + 1;\n } else {\n previousByteRangeEnd = undefined;\n }\n\n segments.push(segment);\n currentTime += currentDuration;\n segmentIndex++;\n\n currentDuration = 0;\n currentByteRange = undefined;\n }\n }\n\n const complete = endList || playlistType === 'VOD';\n\n // Position this window on the timeline. PDT is primary: whenever the track\n // carries a wall-clock anchor (`startDate` — pre-stamped on the shell for a\n // first resolve, recomputed-and-stable for a reload) and the new window has\n // PDT to place with, place from PDT against that frozen anchor\n // (`placeOnAnchor`) — self-correcting across reloads, history-free across\n // turnovers. Media-sequence/EXTINF carry-forward is the fallback for\n // PDT-less windows; a first resolve with neither anchors at the local base 0.\n const anchor = previous.startDate;\n const hasPdt = segments.some((segment) => !isUndefined(segment.startDate));\n const placed =\n !isUndefined(anchor) && hasPdt\n ? placeOnAnchor(segments, anchor)\n : isResolvedTrack(previous)\n ? placeOnPreviousTimeline(previous, segments, mediaSequence, targetDuration)\n : segments;\n\n // `duration` is the track's duration: Infinity while the playlist can still\n // grow (unended live), finite once complete (VOD / ENDLIST). Paired with\n // `startTime: 0` below, it spans the presentation origin to the last placed\n // segment's end — so it's measured off `placed`, not the local EXTINF sum:\n // for a window that has slid, the sum is the window's length, not its span.\n // Never the target duration.\n const lastPlaced = placed[placed.length - 1];\n const placedEnd = lastPlaced ? lastPlaced.startTime + lastPlaced.duration : 0;\n const trackDuration = complete ? placedEnd : Number.POSITIVE_INFINITY;\n\n // Wall-clock anchor: `startDate − startTime` for the first PDT-bearing\n // segment (constant along a linear timeline). Maps this track's origin to\n // wall clock; recomputed each parse, so it stays stable as the window slides\n // and is comparable across tracks for A/V alignment.\n const anchorSegment = placed.find((segment) => !isUndefined(segment.startDate));\n const startDate =\n anchorSegment && !isUndefined(anchorSegment.startDate)\n ? anchorSegment.startDate - anchorSegment.startTime\n : undefined;\n\n // Build initialization (VTT may not have init segment)\n const initialization =\n previous.type === 'text' && !initSegmentUrl\n ? undefined\n : initSegmentUrl\n ? { url: initSegmentUrl, ...(initSegmentByteRange ? { byteRange: initSegmentByteRange } : {}) }\n : { url: '' };\n\n // Container detection: fMP4 always carries an EXT-X-MAP init segment, so its\n // absence plus a recognized non-fMP4 segment extension (`.ts` → MPEG-TS,\n // `.aac` → raw ADTS AAC) marks a non-fMP4 rendition (high-precision — never\n // trips on fMP4, which mandates the map). Relabel from the fMP4 default\n // `video/mp4` / `audio/mp4` to the container MIME so capability probing prunes\n // it (these containers are currently treated as unplayable; see `canPlayTrack`).\n const detectedContainer = initSegmentUrl ? undefined : containerMimeFromSegment(placed[0]?.url);\n const mimeType = previous.type !== 'text' && detectedContainer ? detectedContainer : previous.mimeType;\n\n // Generic resolution: All type-specific fields already on `previous` track\n // Just add parsed properties (startTime, duration, segments, initialization, metadata)\n return {\n ...previous,\n mimeType,\n startTime: 0,\n startDate,\n duration: trackDuration,\n segments: placed,\n initialization,\n metadata: {\n ...previous.metadata,\n [MEDIA_PLAYLIST_METADATA_KEY]: {\n targetDuration,\n mediaSequence,\n playlistType,\n lowLatency,\n endList,\n holdBack,\n encrypted,\n } satisfies MediaPlaylistMetadata,\n },\n } as unknown as ResolveTrack<T>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parse-media-playlist.js","names":[],"sources":["../../../../src/media/hls/parse-media-playlist.ts"],"sourcesContent":["import { isUndefined } from '@videojs/utils/predicate';\n\nimport {\n type AudioTrack,\n getMediaPlaylistMetadata,\n isResolvedTrack,\n MEDIA_PLAYLIST_METADATA_KEY,\n type MediaPlaylistMetadata,\n type PartiallyResolvedAudioTrack,\n type PartiallyResolvedTextTrack,\n type PartiallyResolvedTrack,\n type PartiallyResolvedVideoTrack,\n type ResolvedTrack,\n type Segment,\n type TextTrack,\n type VideoTrack,\n} from '../types';\nimport { matchTag, parseByteRange, parseExtInfDuration } from './parse-attributes';\nimport { resolveUrl } from './resolve-url';\n\n/**\n * MPEG-2 Transport Stream (IANA `video/MP2T`, lowercased for `isTypeSupported`). Video + audio TS — there is no\n * `audio/mp2t`.\n */\nexport const MPEG_TS_MIME = 'video/mp2t';\n/** Raw ADTS AAC packed-audio (HLS `.aac` segments; IANA `audio/aac`). */\nexport const RAW_AAC_MIME = 'audio/aac';\n\n// Non-fMP4 container MIMEs keyed by segment file extension. fMP4 (the MSE\n// default) always carries an EXT-X-MAP init segment, so a media playlist with\n// no init segment and one of these extensions is a non-fMP4 rendition,\n// relabeled from the fMP4 default. Extend with `.mp3` → 'audio/mpeg' etc.\nconst CONTAINER_MIME_BY_EXTENSION: Record<string, string> = {\n '.ts': MPEG_TS_MIME,\n '.aac': RAW_AAC_MIME,\n};\n\n/** The non-fMP4 container MIMEs the parser detects — all currently treated as unplayable. */\nexport const NON_FMP4_CONTAINER_MIMES = new Set(Object.values(CONTAINER_MIME_BY_EXTENSION));\n\n/**\n * Non-fMP4 container MIME for a (resolved, absolute) segment URL, by file extension, ignoring the query string.\n * `undefined` for fMP4 / unrecognized.\n */\nfunction containerMimeFromSegment(url: string | undefined): string | undefined {\n if (!url) return undefined;\n\n let path: string;\n\n try {\n path = new URL(url).pathname.toLowerCase();\n } catch {\n path = url.toLowerCase().split('?')[0] ?? '';\n }\n\n const dot = path.lastIndexOf('.');\n\n return dot === -1 ? undefined : CONTAINER_MIME_BY_EXTENSION[path.slice(dot)];\n}\n\n/** Resolve unresolved track type to its resolved equivalent. */\ntype ResolveTrack<T> = T extends PartiallyResolvedVideoTrack\n ? VideoTrack\n : T extends PartiallyResolvedAudioTrack\n ? AudioTrack\n : T extends PartiallyResolvedTextTrack\n ? TextTrack\n : never;\n\n/**\n * Position a freshly-parsed window (whose segment `startTime`s are snapshot- local, i.e. from 0) onto the timeline\n * established by the previous resolved snapshot. Carries the timeline forward using the media-sequence overlap and the\n * previous window's _actual_ segment durations — see `internal/design/spf/live-presentation-timeline-model.md`.\n *\n * - **Overlap** (`0 <= offset < previous.segments.length`): the new window's first segment is the same segment as\n * `previous.segments[offset]`; anchor to its start (URLs checked — a mismatch warns).\n * - **Sequence went backwards** (non-conformant): reset to the local base.\n * - **Full turnover** (no overlap): estimate forward from the previous window's end across the unseen gap. This is the\n * _only_ place EXT-X-TARGETDURATION is used for timing — an upper-bound estimate, since the actual rolled-off\n * durations are gone (exact recovery is the deferred PDT decision).\n *\n * Returns the rebased segments (the window edge is `segments[0].startTime`, derived — never stored on the track).\n */\nfunction placeOnPreviousTimeline(\n previous: ResolvedTrack,\n segments: Segment[],\n mediaSequence: number,\n targetDuration: number\n): Segment[] {\n const prevSegments = previous.segments;\n const localBase = segments[0]?.startTime ?? 0;\n\n if (prevSegments.length === 0 || segments.length === 0) {\n return segments;\n }\n\n const prevMediaSequence = getMediaPlaylistMetadata(previous)?.mediaSequence ?? 0;\n const offset = mediaSequence - prevMediaSequence;\n\n let anchor: number;\n\n if (offset >= 0 && offset < prevSegments.length) {\n const overlap = prevSegments[offset]!;\n\n if (overlap.url !== segments[0]!.url) {\n console.warn(\n `[parseMediaPlaylist] media-sequence aligns previous[${offset}] with the new window's first segment, ` +\n `but URLs differ (${overlap.url} vs ${segments[0]!.url}); sequence numbers may be unreliable.`\n );\n }\n\n anchor = overlap.startTime;\n } else if (offset < 0) {\n console.warn(`[parseMediaPlaylist] media-sequence went backwards (offset ${offset}); resetting timeline.`);\n anchor = localBase;\n } else {\n const last = prevSegments[prevSegments.length - 1]!;\n const newFirst = segments[0]!;\n\n if (last.startDate !== undefined && newFirst.startDate !== undefined) {\n // PDT bridges the gap exactly — the spec-consistent cross-reload reference,\n // immune to the target-duration over-estimate when actual segment durations\n // differ from the declared ceiling. (`startDate` is intrinsic PDT, unaffected\n // by the new window's local positioning.)\n anchor = last.startTime + (newFirst.startDate - last.startDate);\n } else {\n anchor = last.startTime + last.duration + (offset - prevSegments.length) * targetDuration;\n console.warn(\n `[parseMediaPlaylist] full window turnover (offset ${offset} >= ${prevSegments.length}); ` +\n 'no PDT to bridge — estimating from previous end.'\n );\n }\n }\n\n const shift = anchor - localBase;\n\n return shift === 0 ? segments : segments.map((segment) => ({ ...segment, startTime: segment.startTime + shift }));\n}\n\n/**\n * Place a window on the frozen wall-clock anchor — the track's `startDate` (wall clock at media-time 0). The anchoring\n * PDT-bearing segment lands at `segment.startDate − anchor`, so the window sits on the shared presentation timeline\n * rather than a local-from-zero one (the `startDate` recomputed afterwards then reads back as the anchor — idempotent).\n * This is the **main live path on every parse**: first resolves place against the pre-stamped shell anchor, and reloads\n * re-place from PDT so drift can't accumulate and a long-stall turnover re-places with no overlap bridging (the HLS\n * authoring spec's §8.1 EXTINF-accuracy rule bounds the per-reload correction to ~one video frame). Falls back to the\n * local base when no segment carries PDT — there's nothing to anchor against (callers guard this for reloads, where the\n * local base would reset the timeline).\n */\nfunction placeOnAnchor(segments: Segment[], anchor: number): Segment[] {\n const anchorSegment = segments.find((segment) => !isUndefined(segment.startDate));\n if (!anchorSegment || isUndefined(anchorSegment.startDate)) return segments;\n\n const shift = anchorSegment.startDate - anchor - anchorSegment.startTime;\n if (shift === 0) return segments;\n\n return segments.map((segment) => ({ ...segment, startTime: segment.startTime + shift }));\n}\n\n/**\n * Parse HLS media playlist and resolve track with segments.\n *\n * `previous` is what was known about this track before this parse: the partially-resolved track from the multivariant\n * playlist on the first resolve, or the previously-resolved snapshot on a live reload. Its metadata is carried onto the\n * result either way; when it's already resolved (has segments), its timeline is carried forward so the new window lands\n * on a stable, advancing timeline (see {@link placeOnPreviousTimeline}).\n *\n * @param text - Media playlist text content\n * @param previous - Prior track state (unresolved shell, or previous resolved snapshot)\n * @returns Resolved track with segments (type inferred from input)\n */\nexport function parseMediaPlaylist<T extends PartiallyResolvedTrack>(\n text: string,\n previous: T | ResolveTrack<T>\n): ResolveTrack<T> {\n const lines = text.split(/\\r?\\n/);\n\n // Segments and resources resolve relative to media playlist URL (per HLS spec)\n const baseUrl = previous.url;\n\n // Parse playlist\n const segments: Segment[] = [];\n let initSegmentUrl: string | undefined;\n let initSegmentByteRange: { start: number; end: number } | undefined;\n\n let currentDuration = 0;\n let currentByteRange: { start: number; end: number } | undefined;\n let currentTime = 0;\n let segmentIndex = 0;\n let previousByteRangeEnd: number | undefined;\n // Absolute wall-clock of the next segment's first sample, in epoch seconds.\n // Seeded by an explicit `#EXT-X-PROGRAM-DATE-TIME` (which re-anchors, e.g.\n // across a discontinuity) and advanced by each segment's duration so segments\n // without their own tag are interpolated forward (per RFC 8216).\n let currentStartDate: number | undefined;\n\n // Playlist-level metadata (surfaced for live reload pacing / merge / termination).\n let targetDuration = 0;\n let mediaSequence = 0;\n let playlistType: 'VOD' | 'EVENT' | undefined;\n let endList = false;\n let holdBack: number | undefined;\n // Low-Latency HLS delivery. Any of the partial-segment tags is enough: the\n // parser ignores parts and the loader fetches whole segments, so this records\n // that the publisher configured LL-HLS, not that we honour it.\n let lowLatency = false;\n // Any EXT-X-KEY with a real METHOD makes the rendition encrypted. Sticky: a\n // clear lead (METHOD=NONE first, a real key later) still counts, since we can\n // only report whether decryption is needed at all.\n let encrypted = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || (trimmed.startsWith('#') && !trimmed.startsWith('#EXT'))) continue;\n\n if (trimmed.startsWith('#EXT-X-TARGETDURATION:')) {\n targetDuration = Number.parseInt(trimmed.slice('#EXT-X-TARGETDURATION:'.length), 10) || 0;\n continue;\n }\n\n if (trimmed.startsWith('#EXT-X-MEDIA-SEQUENCE:')) {\n mediaSequence = Number.parseInt(trimmed.slice('#EXT-X-MEDIA-SEQUENCE:'.length), 10) || 0;\n continue;\n }\n\n if (trimmed.startsWith('#EXT-X-PROGRAM-DATE-TIME:')) {\n const parsed = Date.parse(trimmed.slice('#EXT-X-PROGRAM-DATE-TIME:'.length).trim());\n\n currentStartDate = Number.isNaN(parsed) ? currentStartDate : parsed / 1000;\n continue;\n }\n\n if (trimmed.startsWith('#EXT-X-PLAYLIST-TYPE:')) {\n const value = trimmed.slice('#EXT-X-PLAYLIST-TYPE:'.length).trim();\n\n playlistType = value === 'VOD' || value === 'EVENT' ? value : undefined;\n continue;\n }\n\n const key = matchTag(trimmed, 'EXT-X-KEY');\n\n if (key) {\n const method = key.get('METHOD');\n\n if (method !== undefined && method !== 'NONE') encrypted = true;\n\n continue;\n }\n\n // #EXT-X-SERVER-CONTROL — HOLD-BACK supplies the latency target. PART-HOLD-BACK\n // is read for its presence only, as an LL-HLS signal; see MediaPlaylistMetadata\n // on why its value stays out of `holdBack` until LL-HLS support lands.\n const serverControl = matchTag(trimmed, 'EXT-X-SERVER-CONTROL');\n\n if (serverControl) {\n const value = serverControl.get('HOLD-BACK');\n\n if (value !== undefined) {\n const parsed = Number.parseFloat(value);\n\n if (Number.isFinite(parsed) && parsed > 0) holdBack = parsed;\n }\n\n // Advertised for clients playing partial segments — its presence is an\n // LL-HLS signal even though the value stays deliberately unread.\n if (serverControl.get('PART-HOLD-BACK') !== undefined) lowLatency = true;\n\n continue;\n }\n\n // Partial segments: noted, then skipped. `#EXT-X-PART` lines would otherwise\n // fall through as unrecognized `#EXT` tags, so LL-HLS delivery went unnoticed.\n if (trimmed.startsWith('#EXT-X-PART-INF') || trimmed.startsWith('#EXT-X-PART:')) {\n lowLatency = true;\n continue;\n }\n\n if (\n trimmed === '#EXTM3U' ||\n trimmed.startsWith('#EXT-X-VERSION:') ||\n trimmed.startsWith('#EXT-X-INDEPENDENT-SEGMENTS')\n ) {\n continue;\n }\n\n // #EXT-X-MAP - Init segment\n const mapAttrs = matchTag(trimmed, 'EXT-X-MAP');\n\n if (mapAttrs) {\n const uri = mapAttrs.get('URI');\n\n if (uri) {\n initSegmentUrl = resolveUrl(uri, baseUrl);\n const byteRangeStr = mapAttrs.get('BYTERANGE');\n\n if (byteRangeStr) {\n initSegmentByteRange = parseByteRange(byteRangeStr, 0) ?? undefined;\n }\n }\n\n continue;\n }\n\n // #EXTINF - Segment duration\n if (trimmed.startsWith('#EXTINF:')) {\n currentDuration = parseExtInfDuration(trimmed.slice(8));\n continue;\n }\n\n // #EXT-X-BYTERANGE - Segment byte range\n if (trimmed.startsWith('#EXT-X-BYTERANGE:')) {\n currentByteRange = parseByteRange(trimmed.slice(17), previousByteRangeEnd) ?? undefined;\n continue;\n }\n\n if (trimmed === '#EXT-X-ENDLIST') {\n endList = true;\n continue;\n }\n\n // Segment URI\n if (!trimmed.startsWith('#') && currentDuration > 0) {\n const segment: Segment = {\n id: `segment-${mediaSequence + segmentIndex}`,\n url: resolveUrl(trimmed, baseUrl),\n duration: currentDuration,\n startTime: currentTime,\n };\n\n if (!isUndefined(currentStartDate)) {\n segment.startDate = currentStartDate;\n // Interpolate forward: the next segment without an explicit tag inherits\n // this anchor plus this segment's duration.\n currentStartDate += currentDuration;\n }\n\n if (currentByteRange) {\n segment.byteRange = currentByteRange;\n previousByteRangeEnd = currentByteRange.end + 1;\n } else {\n previousByteRangeEnd = undefined;\n }\n\n segments.push(segment);\n currentTime += currentDuration;\n segmentIndex++;\n\n currentDuration = 0;\n currentByteRange = undefined;\n }\n }\n\n const complete = endList || playlistType === 'VOD';\n\n // Position this window on the timeline. PDT is primary: whenever the track\n // carries a wall-clock anchor (`startDate` — pre-stamped on the shell for a\n // first resolve, recomputed-and-stable for a reload) and the new window has\n // PDT to place with, place from PDT against that frozen anchor\n // (`placeOnAnchor`) — self-correcting across reloads, history-free across\n // turnovers. Media-sequence/EXTINF carry-forward is the fallback for\n // PDT-less windows; a first resolve with neither anchors at the local base 0.\n const anchor = previous.startDate;\n const hasPdt = segments.some((segment) => !isUndefined(segment.startDate));\n const placed =\n !isUndefined(anchor) && hasPdt\n ? placeOnAnchor(segments, anchor)\n : isResolvedTrack(previous)\n ? placeOnPreviousTimeline(previous, segments, mediaSequence, targetDuration)\n : segments;\n\n // `duration` is the track's duration: Infinity while the playlist can still\n // grow (unended live), finite once complete (VOD / ENDLIST). Paired with\n // `startTime: 0` below, it spans the presentation origin to the last placed\n // segment's end — so it's measured off `placed`, not the local EXTINF sum:\n // for a window that has slid, the sum is the window's length, not its span.\n // Never the target duration.\n const lastPlaced = placed[placed.length - 1];\n const placedEnd = lastPlaced ? lastPlaced.startTime + lastPlaced.duration : 0;\n const trackDuration = complete ? placedEnd : Number.POSITIVE_INFINITY;\n\n // Wall-clock anchor: `startDate − startTime` for the first PDT-bearing\n // segment (constant along a linear timeline). Maps this track's origin to\n // wall clock; recomputed each parse, so it stays stable as the window slides\n // and is comparable across tracks for A/V alignment.\n const anchorSegment = placed.find((segment) => !isUndefined(segment.startDate));\n const startDate =\n anchorSegment && !isUndefined(anchorSegment.startDate)\n ? anchorSegment.startDate - anchorSegment.startTime\n : undefined;\n\n // Build initialization (VTT may not have init segment)\n const initialization =\n previous.type === 'text' && !initSegmentUrl\n ? undefined\n : initSegmentUrl\n ? { url: initSegmentUrl, ...(initSegmentByteRange ? { byteRange: initSegmentByteRange } : {}) }\n : { url: '' };\n\n // Container detection: fMP4 always carries an EXT-X-MAP init segment, so its\n // absence plus a recognized non-fMP4 segment extension (`.ts` → MPEG-TS,\n // `.aac` → raw ADTS AAC) marks a non-fMP4 rendition (high-precision — never\n // trips on fMP4, which mandates the map). Relabel from the fMP4 default\n // `video/mp4` / `audio/mp4` to the container MIME so capability probing prunes\n // it (these containers are currently treated as unplayable; see `canPlayTrack`).\n const detectedContainer = initSegmentUrl ? undefined : containerMimeFromSegment(placed[0]?.url);\n const mimeType = previous.type !== 'text' && detectedContainer ? detectedContainer : previous.mimeType;\n\n // Generic resolution: All type-specific fields already on `previous` track\n // Just add parsed properties (startTime, duration, segments, initialization, metadata)\n return {\n ...previous,\n mimeType,\n startTime: 0,\n startDate,\n duration: trackDuration,\n segments: placed,\n initialization,\n metadata: {\n ...previous.metadata,\n [MEDIA_PLAYLIST_METADATA_KEY]: {\n targetDuration,\n mediaSequence,\n playlistType,\n lowLatency,\n endList,\n holdBack,\n encrypted,\n } satisfies MediaPlaylistMetadata,\n },\n } as unknown as ResolveTrack<T>;\n}\n"],"mappings":"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"}
@@ -5,9 +5,9 @@ import { generateId } from "@videojs/utils/string";
5
5
  /**
6
6
  * Parse HLS multivariant playlist into a Presentation.
7
7
  *
8
- * Returns Presentation with partially resolved tracks (no segment information).
9
- * Tracks contain metadata from multivariant playlist (bandwidth, resolution, codecs)
10
- * but segment information is added when media playlists are fetched.
8
+ * Returns Presentation with partially resolved tracks (no segment information). Tracks contain metadata from
9
+ * multivariant playlist (bandwidth, resolution, codecs) but segment information is added when media playlists are
10
+ * fetched.
11
11
  *
12
12
  * @param text - Raw playlist text content
13
13
  * @param unresolved - Unresolved presentation (contains URL for base URL resolution)