@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
@@ -2,89 +2,76 @@ import { computed, peek } from "../../core/signals/primitives.js";
2
2
  import { defineBehavior } from "../../core/composition/create-composition.js";
3
3
  import { createMachineReactor } from "../../core/reactors/create-machine-reactor.js";
4
4
  import { isResolvedPresentation } from "../../media/types/index.js";
5
- import { getTracksByType } from "../../media/utils/tracks.js";
5
+ import { getCodecFamilies, getTracksByType } from "../../media/utils/tracks.js";
6
6
  import { SVTA_NO_SUPPORTED_AUDIO_TRACK, SVTA_NO_SUPPORTED_VIDEO_TRACK } from "../../media/errors.js";
7
7
  import { matchesPartialTrack, pickTextTrackFromTracks, smallestCoveringPixelArea, tracksUnderPixelArea } from "../../media/primitives/select-tracks.js";
8
- import { applyConstraints, applyRules, excludeUnplayableTracks, sameCandidateSet } from "../primitives/selection-rules.js";
9
- import { emitError } from "./collect-errors.js";
8
+ import { applyConstraints, applyRules, excludeUnplayableTracks, preferCodecFamilies, sameCandidateSet } from "../primitives/selection-rules.js";
9
+ import { DEFAULT_QUALITY_CONFIG, resolutionArea } from "../../media/abr/quality-selection.js";
10
10
  import { getCdnId } from "../../media/utils/cdn.js";
11
11
  import { DEFAULT_BANDWIDTH_CONFIG, getBandwidthEstimate } from "../../network/bandwidth-estimator.js";
12
- import { DEFAULT_QUALITY_CONFIG, resolutionArea } from "../../media/abr/quality-selection.js";
12
+ import { emitError } from "./collect-errors.js";
13
13
  //#region src/playback/behaviors/track-switching.ts
14
14
  /**
15
- * **Per-type track selection as a rule chain.** While a presentation is
16
- * resolved, owns that type's `selected{Video,Audio,Text}TrackId` signal: pick a
17
- * default, react to user intent and algorithmic ranking, and clear it on src
18
- * unload.
15
+ * **Per-type track selection as a rule chain.** While a presentation is resolved, owns that type's
16
+ * `selected{Video,Audio,Text}TrackId` signal: pick a default, react to user intent and algorithmic ranking, and clear
17
+ * it on src unload.
19
18
  *
20
- * Selection runs in two stages. First a **hard-constraints pre-pass**
21
- * (`applyConstraints`) prunes the unplayable from the candidate set the
22
- * failed-CDN constraint (`excludeFailedCdns`, failover cooldown) and the
23
- * capability constraint (`excludeUnplayableTracks`, codec support). Then a small
24
- * ordered chain of rules (`applyRules`) picks among the survivors. Each constraint/rule reads the signals it needs at apply
25
- * time, so the effect subscribes to exactly what was consulted. The chain runs
26
- * most authoritative first:
19
+ * Selection runs in two stages. First a **hard-constraints pre-pass** (`applyConstraints`) prunes the unplayable from
20
+ * the candidate set — the failed-CDN constraint (`excludeFailedCdns`, failover cooldown), the capability constraint
21
+ * (`excludeUnplayableTracks`, codec support), and the codec-family sticky constraint (`stickToSelectedCodecs`, no
22
+ * `SourceBuffer.changeType()` see its note). Then a small ordered chain of rules (`applyRules`) picks among the
23
+ * survivors. Each constraint/rule reads the signals it needs at apply time, so the effect subscribes to exactly what
24
+ * was consulted. The chain runs most authoritative first:
27
25
  *
28
- * 1. **user intent** — a soft filter on `user*TrackSelection`: narrow to the
29
- * partial-track match; an empty match falls through to the full set.
30
- * 2. **active CDN** — a soft filter on `cdnPriority` (`preferActiveCdn`):
31
- * narrow to the highest-priority CDN that still has tracks; an empty match
32
- * falls through. Shared by video and audio, so every type stays on one CDN
33
- * (`deriveCdnPriority` owns the list). No-op for non-redundant sources.
34
- * 3. **player resolution** — a soft filter on `playerResolution`
35
- * (`playerResolutionCap`, video only): narrow to the smallest rendition
36
- * tier covering the player element, plus everything below it. No-op
37
- * without a measurement. Ahead of the ranker but behind the CDN scope, so
38
- * the cap chooses *within* a host rather than between hosts.
39
- * 4. **ranking** the terminal sort: `rankByBandwidth`, shared by video and
40
- * audio. Fitting tracks (within the throughput threshold) first, highest
41
- * bitrate first; over-throughput tracks after, least-over first. Hysteresis
42
- * via boosting the current track's sort weight by `upgradeMargin`.
26
+ * 1. **user intent** — a soft filter on `user*TrackSelection`: narrow to the partial-track match; an empty match falls
27
+ * through to the full set.
28
+ * 2. **codec-family preference** — a soft filter on `preferredCodecs` (`preferCodecFamilies`, default AVC/AAC): on a
29
+ * mixed-codec source, narrow which family the _initial_ pick the one the sticky constraint then holds — lands in.
30
+ * Behind user intent so an explicit initial pick may land anywhere; inert once the constraint narrows to a
31
+ * non-preferred family.
32
+ * 3. **active CDN** — a soft filter on `cdnPriority` (`preferActiveCdn`): narrow to the highest-priority CDN that still
33
+ * has tracks; an empty match falls through. Shared by video and audio, so every type stays on one CDN
34
+ * (`deriveCdnPriority` owns the list). No-op for non-redundant sources.
35
+ * 4. **player resolution** — a soft filter on `playerResolution` (`playerResolutionCap`, video only): narrow to the
36
+ * smallest rendition tier covering the player element, plus everything below it. No-op without a measurement. Ahead
37
+ * of the ranker but behind the CDN scope, so the cap chooses _within_ a host rather than between hosts.
38
+ * 5. **ranking** — the terminal sort: `rankByBandwidth`, shared by video and audio. Fitting tracks (within the throughput
39
+ * threshold) first, highest bitrate first; over-throughput tracks after, least-over first. Hysteresis via boosting
40
+ * the current track's sort weight by `upgradeMargin`.
43
41
  *
44
- * The composer's early-bail (one survivor → stop) is load-bearing: a user
45
- * selection that narrows to a single track is the pick without the ranker
46
- * running, so the bandwidth estimate is never read and the effect doesn't
47
- * re-fire on bandwidth while that choice holds.
42
+ * The composer's early-bail (one survivor → stop) is load-bearing: a user selection that narrows to a single track is
43
+ * the pick without the ranker running, so the bandwidth estimate is never read and the effect doesn't re-fire on
44
+ * bandwidth while that choice holds.
48
45
  *
49
- * Lifecycle: `'presentation-unresolved'` ↔ `'presentation-resolved'`. The
50
- * resolved state owns the signal; its entry-returned cleanup clears it on exit
51
- * (canonical cleanup-binds-to-setup per `reactors.md`).
46
+ * Lifecycle: `'presentation-unresolved'` ↔ `'presentation-resolved'`. The resolved state owns the signal; its
47
+ * entry-returned cleanup clears it on exit (canonical cleanup-binds-to-setup per `reactors.md`).
52
48
  *
53
- * The pick is the chain's result mapped to a slot value by `resolveSelection`
54
- * (default: the head, `applyRules(...)[0]`). Each variant supplies its
55
- * **constraints + rule chain (+ optional resolveSelection)** via config;
56
- * `setupTrackSwitching` owns only the lifecycle and runs what it's given. Video
57
- * and audio run constraints `[excludeFailedCdns, excludeUnplayableTracks]` then
58
- * rules `[filterByUserSelection, preferActiveCdn, rankByBandwidth]` and take the
59
- * head; video inserts `playerResolutionCap` after the active-CDN scope, and
60
- * `switchVideoTrack` also accepts ABR tuning config, `switchAudioTrack` takes
61
- * none. `switchTextTrack` differs selection is *optional* (captions are
62
- * opt-in / off-able), so it runs `[excludeFailedCdns]` + `[preferActiveCdn]` and
63
- * supplies a text terminal (`pickResolvedTextTrack`) that resolves standing user
64
- * intent (`userTextTrackSelection`, incl. `'off'`) and may yield no selection.
65
- * (The active-CDN *scope* is the sticky-pick half of multi-CDN; the failed-CDN
66
- * *constraint* is the failover half — prune the cooled-down CDN, the scope falls
67
- * to the next.)
49
+ * The pick is the chain's result mapped to a slot value by `resolveSelection` (default: the head,
50
+ * `applyRules(...)[0]`). Each variant supplies its **constraints + rule chain (+ optional resolveSelection)** via
51
+ * config; `setupTrackSwitching` owns only the lifecycle and runs what it's given. Video and audio run constraints
52
+ * `[excludeFailedCdns, excludeUnplayableTracks, stickToSelectedCodecs]` then rules `[filterByUserSelection,
53
+ * preferCodecFamilies, preferActiveCdn, rankByBandwidth]` and take the head; video inserts `playerResolutionCap` after
54
+ * the active-CDN scope, and `switchVideoTrack` also accepts ABR tuning config, `switchAudioTrack` takes none.
55
+ * `switchTextTrack` differs selection is _optional_ (captions are opt-in / off-able), so it runs
56
+ * `[excludeFailedCdns]` + `[preferActiveCdn]` and supplies a text terminal (`pickResolvedTextTrack`) that resolves
57
+ * standing user intent (`userTextTrackSelection`, incl. `'off'`) and may yield no selection. (The active-CDN _scope_ is
58
+ * the sticky-pick half of multi-CDN; the failed-CDN _constraint_ is the failover half — prune the cooled-down CDN, the
59
+ * scope falls to the next.)
68
60
  *
69
- * When the pre-pass prunes a type that *has* tracks to empty, the behavior
70
- * clears the selection (so a now-unplayable pick can't linger and stall) and
71
- * reports the type's `noSupportedTrackCode`. Which constraint emptied the set is
72
- * deliberately not consulted the behavior reads no constraint's state, so the
73
- * chain stays composable. A type with no tracks at all is left alone; that's a
74
- * legitimate source shape, not a failure. The late `createSourceBuffer` check
61
+ * When the pre-pass prunes a type that _has_ tracks to empty, the behavior clears the selection (so a now-unplayable
62
+ * pick can't linger and stall) and reports the type's `noSupportedTrackCode`. Which constraint emptied the set is
63
+ * deliberately not consulted — the behavior reads no constraint's state, so the chain stays composable. A type with no
64
+ * tracks at all is left alone; that's a legitimate source shape, not a failure. The late `createSourceBuffer` check
75
65
  * stays as the structural backstop.
76
66
  *
77
- * Deferred: audio's preferred-language / default-track selection as standing
78
- * soft-filter rules (previously the empty-slot picker, dropped in the move to
79
- * the rule chain).
67
+ * Deferred: audio's preferred-language / default-track selection as standing soft-filter rules (previously the
68
+ * empty-slot picker, dropped in the move to the rule chain).
80
69
  */
81
70
  /** Default initial-bandwidth value before bandwidth measurements arrive. */
82
71
  const DEFAULT_INITIAL_BANDWIDTH = 5e6;
83
72
  /**
84
- * User intent — a soft filter. Narrows to tracks matching the partial-track
85
- * selection in `user*TrackSelection`; an empty match falls through (the
86
- * composer skips it) to the unfiltered set — e.g. a stale id from a previous
87
- * source.
73
+ * User intent — a soft filter. Narrows to tracks matching the partial-track selection in `user*TrackSelection`; an
74
+ * empty match falls through (the composer skips it) to the unfiltered set — e.g. a stale id from a previous source.
88
75
  */
89
76
  function filterByUserSelection(tracks, { state, config }) {
90
77
  const key = config.userSelectionKey;
@@ -93,50 +80,42 @@ function filterByUserSelection(tracks, { state, config }) {
93
80
  return filter ? tracks.filter((track) => matchesPartialTrack(track, filter)) : tracks;
94
81
  }
95
82
  /**
96
- * Player-resolution cap — a soft filter, video only. Narrows to the renditions
97
- * worth delivering at the player element's rendered size, so a small embed
98
- * doesn't pull segments nobody can perceive. The tighter sibling of
83
+ * Player-resolution cap — a soft filter, video only. Narrows to the renditions worth delivering at the player element's
84
+ * rendered size, so a small embed doesn't pull segments nobody can perceive. The tighter sibling of
99
85
  * `screenResolutionCap`: the element's box, not the screen behind it.
100
86
  *
101
- * The cap is the *smallest tier that still covers the player*, and everything at
102
- * or below it survives — not "everything at or below the player's area," which
103
- * under-serves a player falling between two tiers. Take an 800×450 player
104
- * against a 360p/720p/1080p ladder: only 360p is below it, so capping at the
105
- * player's area would hold an 800-px-wide box to a 640-px-wide picture. The
106
- * honest answer is the tier above, 720p, with 360p left in for the ranker.
107
- * `smallestCoveringPixelArea` picks that cap; `tracksUnderPixelArea` — the same
108
- * filter `screenResolutionCap` narrows with — applies it.
87
+ * The cap is the _smallest tier that still covers the player_, and everything at or below it survives — not "everything
88
+ * at or below the player's area," which under-serves a player falling between two tiers. Take an 800×450 player against
89
+ * a 360p/720p/1080p ladder: only 360p is below it, so capping at the player's area would hold an 800-px-wide box to a
90
+ * 640-px-wide picture. The honest answer is the tier above, 720p, with 360p left in for the ranker.
91
+ * `smallestCoveringPixelArea` picks that cap; `tracksUnderPixelArea` the same filter `screenResolutionCap` narrows
92
+ * with applies it.
109
93
  *
110
- * Renditions declaring no width or height compare as area `0` and are never capped
111
- * out — they can't be judged against the player, and dropping them could strand
112
- * a source whose renditions all omit it.
94
+ * Renditions declaring no width or height compare as area `0` and are never capped out — they can't be judged against
95
+ * the player, and dropping them could strand a source whose renditions all omit it.
113
96
  *
114
- * Runs *after* `preferActiveCdn`, so it narrows within the host already chosen.
115
- * Ahead of it, a cap that pruned every rendition of the preferred CDN would leave
116
- * the scope to fall to the next one with survivors a size preference silently
117
- * moving playback to another host. Redundant streams normally mirror the same
118
- * ladder, which makes that a nonstandard-but-legal mismatch across CDNs rather
119
- * than an everyday case; the ordering costs nothing either way.
97
+ * Runs _after_ `preferActiveCdn`, so it narrows within the host already chosen. Ahead of it, a cap that pruned every
98
+ * rendition of the preferred CDN would leave the scope to fall to the next one with survivors — a size preference
99
+ * silently moving playback to another host. Redundant streams normally mirror the same ladder, which makes that a
100
+ * nonstandard-but-legal mismatch across CDNs rather than an everyday case; the ordering costs nothing either way.
120
101
  *
121
- * Reading `state.playerResolution` through its signal is what subscribes the
122
- * chain to resizes; `undefined` no signal composed, or nothing to measure
123
- * means "don't cap" rather than a cap of zero, so the chain proceeds unnarrowed.
102
+ * Reading `state.playerResolution` through its signal is what subscribes the chain to resizes; `undefined` — no signal
103
+ * composed, or nothing to measuremeans "don't cap" rather than a cap of zero, so the chain proceeds unnarrowed.
124
104
  */
125
105
  function playerResolutionCap(tracks, { state }) {
126
106
  const playerResolution = state.playerResolution?.get();
127
107
  if (!playerResolution) return tracks;
128
- return tracksUnderPixelArea(tracks, smallestCoveringPixelArea(tracks, playerResolution.width * playerResolution.height));
108
+ const cap = smallestCoveringPixelArea(tracks, playerResolution.width * playerResolution.height);
109
+ return tracksUnderPixelArea(tracks, cap);
129
110
  }
130
111
  /**
131
- * Failed-CDN constraint — a *hard* filter (constraints pre-pass), shared by
132
- * video and audio. Removes tracks served from a CDN currently in failover
133
- * cooldown (`failedCdns`, written by the failover monitor). Removed tracks are never
134
- * attempted; the scope then narrows to the next surviving CDN in `cdnPriority`,
135
- * and snaps back to the primary once it leaves cooldown.
112
+ * Failed-CDN constraint — a _hard_ filter (constraints pre-pass), shared by video and audio. Removes tracks served from
113
+ * a CDN currently in failover cooldown (`failedCdns`, written by the failover monitor). Removed tracks are never
114
+ * attempted; the scope then narrows to the next surviving CDN in `cdnPriority`, and snaps back to the primary once it
115
+ * leaves cooldown.
136
116
  *
137
- * Passes everything through when there's no `failedCdns` signal/value. When it
138
- * prunes *every* track (all CDNs cooled down), the empty result is preserved
139
- * (per `applyConstraints`) — "nothing playable," which clears the selection (no
117
+ * Passes everything through when there's no `failedCdns` signal/value. When it prunes _every_ track (all CDNs cooled
118
+ * down), the empty result is preserved (per `applyConstraints`) "nothing playable," which clears the selection (no
140
119
  * pick); a later CDN recovery refills the candidate set and re-picks.
141
120
  */
142
121
  function excludeFailedCdns(tracks, { state, config }) {
@@ -147,25 +126,21 @@ function excludeFailedCdns(tracks, { state, config }) {
147
126
  return tracks.filter((track) => !failedSet.has(getCdnId$1(track.url)));
148
127
  }
149
128
  /**
150
- * Active-CDN scope — a soft filter, shared by video and audio. Narrows to the
151
- * highest-priority CDN in `cdnPriority` (owned by `deriveCdnPriority`) that
152
- * still has tracks, so every track type stays on one CDN. A redundant-streams
153
- * source lists the same renditions on multiple hosts; this keeps the pick on one
154
- * host rather than letting the ranker drift across them.
129
+ * Active-CDN scope — a soft filter, shared by video and audio. Narrows to the highest-priority CDN in `cdnPriority`
130
+ * (owned by `deriveCdnPriority`) that still has tracks, so every track type stays on one CDN. A redundant-streams
131
+ * source lists the same renditions on multiple hosts; this keeps the pick on one host rather than letting the ranker
132
+ * drift across them.
155
133
  *
156
- * "Active" is derived, not stored: constraints run before the rule chain, so a
157
- * failed CDN's tracks are already pruned by the time this runs — "first CDN with
158
- * survivors" *is* the active CDN, and it falls through to the next on failover
159
- * (and snaps back to the primary when it recovers). Content steering reorders
160
- * `cdnPriority`; this rule just honors the order.
134
+ * "Active" is derived, not stored: constraints run before the rule chain, so a failed CDN's tracks are already pruned
135
+ * by the time this runs — "first CDN with survivors" _is_ the active CDN, and it falls through to the next on failover
136
+ * (and snaps back to the primary when it recovers). Content steering reorders `cdnPriority`; this rule just honors the
137
+ * order.
161
138
  *
162
- * Soft-filter semantics: passes through when there's no `cdnPriority` signal/value
163
- * (no preference) or when nothing matches (`applyRules` skips an empty result).
164
- * Non-redundant sources have one CDN, so the narrow is a no-op.
139
+ * Soft-filter semantics: passes through when there's no `cdnPriority` signal/value (no preference) or when nothing
140
+ * matches (`applyRules` skips an empty result). Non-redundant sources have one CDN, so the narrow is a no-op.
165
141
  *
166
- * The CDN-id derivation defaults to origin-based `getCdnId`, overridable via the
167
- * `getCdnId` config it must match the one `deriveCdnPriority` used to build
168
- * `cdnPriority`, or no track's CDN would ever equal an entry.
142
+ * The CDN-id derivation defaults to origin-based `getCdnId`, overridable via the `getCdnId` config — it must match the
143
+ * one `deriveCdnPriority` used to build `cdnPriority`, or no track's CDN would ever equal an entry.
169
144
  */
170
145
  function preferActiveCdn(tracks, { state, config }) {
171
146
  const cdnPriority = state.cdnPriority?.get();
@@ -178,24 +153,63 @@ function preferActiveCdn(tracks, { state, config }) {
178
153
  return tracks;
179
154
  }
180
155
  /**
181
- * Bandwidth ranking — the terminal sort, shared by video and audio. Orders by
182
- * the throughput estimate: tracks within the bandwidth threshold first
183
- * (fitting), highest bitrate first; then over-threshold tracks, least-over
184
- * first. The head is the best-quality track that fits, falling back to the
185
- * smallest over-throughput track when nothing fits.
156
+ * Codec-family sticky constraint a _hard_ filter for the constraints pre-pass, shared by video and audio, and the
157
+ * reason the re-evaluating variants can run ABR over a mixed-codec source at all: SPF implements no
158
+ * `SourceBuffer.changeType()`, so once segments of the selected track's codec families are what a buffer was created
159
+ * for, a pick outside them could never play — constraint semantics ("can't play here"), not preference. Removes the
160
+ * candidates whose codec-family set doesn't _equal_ the selected track's (set-equality so a muxed `hvc1,mp4a` rendition
161
+ * can't pass as a match for `avc1,mp4a` on its audio half). Purely relational: the lock _is_ the current selection's
162
+ * families, no state of its own. Pruned pre-pass, a cross-family user selection mid-stream never reaches the user
163
+ * filter, which falls through unhonored instead of killing playback. Before any selection exists (the initial pick) it
164
+ * removes nothing, which is what leaves the initial family choice to the rule chain (user intent, then
165
+ * `preferCodecFamilies`).
186
166
  *
187
- * Hysteresis without temporal state: the current track's effective bitrate is
188
- * boosted by `upgradeMargin` in the fitting sort, so a higher track only
189
- * outranks it once it clears `current.bitrate * upgradeMargin` (no flapping on
190
- * marginal bandwidth gains). Downgrades fall out for freea current track
191
- * over the threshold isn't in the fitting set to be boosted, so the best fit (a
192
- * downgrade) wins immediately. Equal-bitrate tracks break by resolution (higher
193
- * `width × height` first), so an equal-bitrate ladder never picks a lower-
194
- * quality rendition by manifest order; audio tracks carry no dimensions, so
195
- * they area-compare equal and a stable sort keeps their candidate order (e.g.
196
- * same-bitrate language variants). Early-bail skips this rule when a prior one
197
- * narrowed to a single track, so the estimate is neither read nor subscribed
198
- * while that holds.
167
+ * The selected track's families come from the _presentation's_ track list, not the already-pruned candidates: a CDN
168
+ * entering cooldown may prune the current track itself while same-family renditions survive on another host, and the
169
+ * lock must carry over to them. Removes nothing when: no selection yet, a selection the presentation no longer carries,
170
+ * or a selected track without codecs (then no candidate's compatibility is decidable same inertness as
171
+ * `preferCodecFamilies` on a codec-less ladder).
172
+ *
173
+ * When the selected family genuinely vanishes from the playable set, the pre-pass empties: the behavior reports the
174
+ * type's no-supported-track code and clears the selection an explicable stop instead of a cross-family append
175
+ * surfacing as an opaque decode error. Clearing also releases the lock (it keys on the live selection), so a following
176
+ * pick may land in the surviving family on freshly-created buffer actors; whether the append layer survives that
177
+ * rebuild is the changeType gap, out of this constraint's hands. If `changeType` support ever lands, this constraint is
178
+ * what relaxes.
179
+ *
180
+ * Reading the selection here — a slot the picking effect itself writes, from inside the candidate-set computed — is
181
+ * safe only because the effect scheduler revalidates an effect's sources after each run; see `core/signals/effect.ts`
182
+ * (`revalidateSources`) for the lost-wakeup it prevents.
183
+ *
184
+ * Composed only into the re-evaluating `switch*` variants: the pinned `selectVideoTrack` (background compositions)
185
+ * evaluates once and never re-picks, so it's immune by construction.
186
+ */
187
+ function stickToSelectedCodecs(tracks, { state, config }) {
188
+ const currentId = state[config.selectionKey].get();
189
+ if (!currentId) return tracks;
190
+ const presentation = state.presentation.get();
191
+ if (!isResolvedPresentation(presentation)) return tracks;
192
+ const current = config.getTracks(presentation).find((track) => track.id === currentId);
193
+ const families = current && getCodecFamilies(current);
194
+ if (!families) return tracks;
195
+ return tracks.filter((track) => {
196
+ const candidateFamilies = getCodecFamilies(track);
197
+ return !!candidateFamilies && candidateFamilies.length === families.length && candidateFamilies.every((family) => families.includes(family));
198
+ });
199
+ }
200
+ /**
201
+ * Bandwidth ranking — the terminal sort, shared by video and audio. Orders by the throughput estimate: tracks within
202
+ * the bandwidth threshold first (fitting), highest bitrate first; then over-threshold tracks, least-over first. The
203
+ * head is the best-quality track that fits, falling back to the smallest over-throughput track when nothing fits.
204
+ *
205
+ * Hysteresis without temporal state: the current track's effective bitrate is boosted by `upgradeMargin` in the fitting
206
+ * sort, so a higher track only outranks it once it clears `current.bitrate * upgradeMargin` (no flapping on marginal
207
+ * bandwidth gains). Downgrades fall out for free — a current track over the threshold isn't in the fitting set to be
208
+ * boosted, so the best fit (a downgrade) wins immediately. Equal-bitrate tracks break by resolution (higher `width ×
209
+ * height` first), so an equal-bitrate ladder never picks a lower- quality rendition by manifest order; audio tracks
210
+ * carry no dimensions, so they area-compare equal and a stable sort keeps their candidate order (e.g. same-bitrate
211
+ * language variants). Early-bail skips this rule when a prior one narrowed to a single track, so the estimate is
212
+ * neither read nor subscribed while that holds.
199
213
  */
200
214
  function rankByBandwidth(tracks, { state, config }) {
201
215
  const safetyMargin = config.quality?.safetyMargin ?? DEFAULT_QUALITY_CONFIG.safetyMargin;
@@ -215,28 +229,25 @@ function rankByBandwidth(tracks, { state, config }) {
215
229
  return [...fitting, ...over];
216
230
  }
217
231
  /**
218
- * Default final pick: the chain head. `applyRules` never narrows to nothing and
219
- * early-bails to a single survivor, so video and audio always converge to a
220
- * track and the head is the pick.
232
+ * Default final pick: the chain head. `applyRules` never narrows to nothing and early-bails to a single survivor, so
233
+ * video and audio always converge to a track and the head is the pick.
221
234
  */
222
235
  function selectChainHead(candidates) {
223
236
  return candidates[0].id;
224
237
  }
225
238
  /**
226
- * Terminal pick for text — the `resolveSelection` the text variant supplies.
227
- * Resolves the standing `userTextTrackSelection` intent against the chain's
228
- * survivors (already CDN-failover-pruned and active-CDN-scoped):
239
+ * Terminal pick for text — the `resolveSelection` the text variant supplies. Resolves the standing
240
+ * `userTextTrackSelection` intent against the chain's survivors (already CDN-failover-pruned and active-CDN-scoped):
229
241
  *
230
- * - `'off'` → no selection (clear the slot). Sticky through re-evaluation, so a
231
- * live refresh or failover re-run can't re-assert a default.
232
- * - explicit `Partial<TextTrack>` → narrow to the match (language-based). A
233
- * stale pick whose match is gone (e.g. the language dropped on a source
234
- * change) falls through to the default policy.
235
- * - auto (`undefined`) → the opt-in default policy (`preferredSubtitleLanguage`
236
- * → `DEFAULT=YES + AUTOSELECT=YES` → none), via `pickTextTrackFromTracks`.
242
+ * - `'off'` → no selection (clear the slot). Sticky through re-evaluation, so a live refresh or failover re-run can't
243
+ * re-assert a default.
244
+ * - Explicit `Partial<TextTrack>` → narrow to the match (language-based). A stale pick whose match is gone (e.g. the
245
+ * language dropped on a source change) falls through to the default policy.
246
+ * - Auto (`undefined`) the opt-in default policy (`preferredSubtitleLanguage` → `DEFAULT=YES + AUTOSELECT=YES` → none),
247
+ * via `pickTextTrackFromTracks`.
237
248
  *
238
- * Returning `undefined` is a real outcome (captions are opt-in), which is why the
239
- * text variant relies on `setupTrackSwitching`'s no-selection seam.
249
+ * Returning `undefined` is a real outcome (captions are opt-in), which is why the text variant relies on
250
+ * `setupTrackSwitching`'s no-selection seam.
240
251
  */
241
252
  function pickResolvedTextTrack(candidates, { state, config }) {
242
253
  const intent = state.userTextTrackSelection?.get();
@@ -288,13 +299,12 @@ function setupTrackSwitching(deps) {
288
299
  });
289
300
  }
290
301
  /**
291
- * Manage `selectedVideoTrackId`: pick a default on src load, dynamically
292
- * adjust based on bandwidth, clear on src unload. Honors
293
- * `userVideoTrackSelection` as a partial-track constraint on candidates;
294
- * short-circuits ABR when the constraint narrows to a single track.
302
+ * Manage `selectedVideoTrackId`: pick a default on src load, dynamically adjust based on bandwidth, clear on src
303
+ * unload. Honors `userVideoTrackSelection` as a partial-track constraint on candidates; short-circuits ABR when the
304
+ * constraint narrows to a single track.
295
305
  *
296
306
  * @example
297
- * const reactor = switchVideoTrack.setup({ state });
307
+ * const reactor = switchVideoTrack.setup({ state });
298
308
  */
299
309
  const switchVideoTrack = defineBehavior({
300
310
  stateKeys: ["presentation", "selectedVideoTrackId"],
@@ -307,9 +317,14 @@ const switchVideoTrack = defineBehavior({
307
317
  selectionKey: "selectedVideoTrackId",
308
318
  userSelectionKey: "userVideoTrackSelection",
309
319
  getTracks: (presentation) => getTracksByType(presentation, "video"),
310
- constraints: [excludeFailedCdns, excludeUnplayableTracks],
320
+ constraints: [
321
+ excludeFailedCdns,
322
+ excludeUnplayableTracks,
323
+ stickToSelectedCodecs
324
+ ],
311
325
  rules: [
312
326
  filterByUserSelection,
327
+ preferCodecFamilies,
313
328
  preferActiveCdn,
314
329
  playerResolutionCap,
315
330
  rankByBandwidth
@@ -319,17 +334,15 @@ const switchVideoTrack = defineBehavior({
319
334
  })
320
335
  });
321
336
  /**
322
- * Manage `selectedAudioTrackId`: pick a default on src load, narrow by
323
- * `userAudioTrackSelection` filter, re-pick on filter change, clear on
324
- * src unload.
337
+ * Manage `selectedAudioTrackId`: pick a default on src load, narrow by `userAudioTrackSelection` filter, re-pick on
338
+ * filter change, clear on src unload.
325
339
  *
326
- * Mid-stream flush on language switch is handled by the segment-loader's
327
- * `planTasks` (see `playback/actors/dom/segment-loader.ts`) — not this
328
- * behavior. Same split as the video pipeline: slot owner writes; loader
329
- * orchestrates segment + flush plans.
340
+ * Mid-stream flush on language switch is handled by the segment-loader's `planTasks` (see
341
+ * `playback/actors/dom/segment-loader.ts`) — not this behavior. Same split as the video pipeline: slot owner writes;
342
+ * loader orchestrates segment + flush plans.
330
343
  *
331
344
  * @example
332
- * const reactor = switchAudioTrack.setup({ state });
345
+ * const reactor = switchAudioTrack.setup({ state });
333
346
  */
334
347
  const switchAudioTrack = defineBehavior({
335
348
  stateKeys: ["presentation", "selectedAudioTrackId"],
@@ -342,9 +355,14 @@ const switchAudioTrack = defineBehavior({
342
355
  selectionKey: "selectedAudioTrackId",
343
356
  userSelectionKey: "userAudioTrackSelection",
344
357
  getTracks: (presentation) => getTracksByType(presentation, "audio"),
345
- constraints: [excludeFailedCdns, excludeUnplayableTracks],
358
+ constraints: [
359
+ excludeFailedCdns,
360
+ excludeUnplayableTracks,
361
+ stickToSelectedCodecs
362
+ ],
346
363
  rules: [
347
364
  filterByUserSelection,
365
+ preferCodecFamilies,
348
366
  preferActiveCdn,
349
367
  rankByBandwidth
350
368
  ],
@@ -353,21 +371,19 @@ const switchAudioTrack = defineBehavior({
353
371
  })
354
372
  });
355
373
  /**
356
- * Manage `selectedTextTrackId` as the single-writer **output** of standing user
357
- * intent (`userTextTrackSelection`) resolved against the playable, CDN-scoped
358
- * text renditions: clear on src unload; re-resolve when a CDN fails or recovers.
374
+ * Manage `selectedTextTrackId` as the single-writer **output** of standing user intent (`userTextTrackSelection`)
375
+ * resolved against the playable, CDN-scoped text renditions: clear on src unload; re-resolve when a CDN fails or
376
+ * recovers.
359
377
  *
360
- * Unlike video/audio, the selection is *optional* — captions are opt-in and the
361
- * user can turn them off — so the chain skips the bandwidth ranker and the shared
362
- * user-selection filter, and supplies a text-specific terminal
363
- * (`pickResolvedTextTrack`) that may resolve to no-selection via
364
- * `setupTrackSwitching`'s `resolveSelection` seam. Constraints are failed-CDN only
365
- * (`excludeUnplayableTracks`/`canPlayTrack` is MSE-based — the wrong probe for
366
- * text, whose playability is SPF-parser support); the active-CDN scope co-locates
367
- * captions with the surviving CDN on failover.
378
+ * Unlike video/audio, the selection is _optional_ — captions are opt-in and the user can turn them off — so the chain
379
+ * skips the bandwidth ranker and the shared user-selection filter, and supplies a text-specific terminal
380
+ * (`pickResolvedTextTrack`) that may resolve to no-selection via `setupTrackSwitching`'s `resolveSelection` seam.
381
+ * Constraints are failed-CDN only (`excludeUnplayableTracks`/`canPlayTrack` is MSE-based the wrong probe for text,
382
+ * whose playability is SPF-parser support); the active-CDN scope co-locates captions with the surviving CDN on
383
+ * failover.
368
384
  *
369
385
  * @example
370
- * const reactor = switchTextTrack.setup({ state, config: { preferredSubtitleLanguage: 'en' } });
386
+ * const reactor = switchTextTrack.setup({ state, config: { preferredSubtitleLanguage: 'en' } });
371
387
  */
372
388
  const switchTextTrack = defineBehavior({
373
389
  stateKeys: ["presentation", "selectedTextTrackId"],
@@ -386,6 +402,6 @@ const switchTextTrack = defineBehavior({
386
402
  })
387
403
  });
388
404
  //#endregion
389
- export { DEFAULT_INITIAL_BANDWIDTH, setupTrackSwitching, switchAudioTrack, switchTextTrack, switchVideoTrack };
405
+ export { DEFAULT_INITIAL_BANDWIDTH, setupTrackSwitching, stickToSelectedCodecs, switchAudioTrack, switchTextTrack, switchVideoTrack };
390
406
 
391
407
  //# sourceMappingURL=track-switching.js.map