@videojs/spf 10.0.0-beta.1 → 10.0.0-beta.11

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 (242) hide show
  1. package/README.md +16 -40
  2. package/dist/default/all.js +16 -0
  3. package/dist/default/core/abr/bandwidth-estimator.js +96 -0
  4. package/dist/default/core/abr/bandwidth-estimator.js.map +1 -0
  5. package/dist/default/core/abr/ewma.js +71 -0
  6. package/dist/default/core/abr/ewma.js.map +1 -0
  7. package/dist/default/core/abr/quality-selection.js +53 -0
  8. package/dist/default/core/abr/quality-selection.js.map +1 -0
  9. package/dist/default/core/buffer/back-buffer.js +46 -0
  10. package/dist/default/core/buffer/back-buffer.js.map +1 -0
  11. package/dist/default/core/buffer/forward-buffer.js +73 -0
  12. package/dist/default/core/buffer/forward-buffer.js.map +1 -0
  13. package/dist/default/core/events/create-event-stream.js +48 -0
  14. package/dist/default/core/events/create-event-stream.js.map +1 -0
  15. package/dist/default/core/features/calculate-presentation-duration.js +51 -0
  16. package/dist/default/core/features/calculate-presentation-duration.js.map +1 -0
  17. package/dist/default/core/features/quality-switching.js +68 -0
  18. package/dist/default/core/features/quality-switching.js.map +1 -0
  19. package/dist/default/core/features/resolve-presentation.js +99 -0
  20. package/dist/default/core/features/resolve-presentation.js.map +1 -0
  21. package/dist/default/core/features/resolve-track.js +77 -0
  22. package/dist/default/core/features/resolve-track.js.map +1 -0
  23. package/dist/default/core/features/select-tracks.js +147 -0
  24. package/dist/default/core/features/select-tracks.js.map +1 -0
  25. package/dist/default/core/hls/parse-attributes.js +134 -0
  26. package/dist/default/core/hls/parse-attributes.js.map +1 -0
  27. package/dist/default/core/hls/parse-media-playlist.js +83 -0
  28. package/dist/default/core/hls/parse-media-playlist.js.map +1 -0
  29. package/dist/default/core/hls/parse-multivariant.js +215 -0
  30. package/dist/default/core/hls/parse-multivariant.js.map +1 -0
  31. package/dist/default/core/hls/resolve-url.js +11 -0
  32. package/dist/default/core/hls/resolve-url.js.map +1 -0
  33. package/dist/default/core/reactive/combine-latest.js +72 -0
  34. package/dist/default/core/reactive/combine-latest.js.map +1 -0
  35. package/dist/default/core/state/create-state.js +143 -0
  36. package/dist/default/core/state/create-state.js.map +1 -0
  37. package/dist/default/core/task.js +122 -0
  38. package/dist/default/core/task.js.map +1 -0
  39. package/dist/default/core/types/index.js +15 -0
  40. package/dist/default/core/types/index.js.map +1 -0
  41. package/dist/default/core/utils/generate-id.js +21 -0
  42. package/dist/default/core/utils/generate-id.js.map +1 -0
  43. package/dist/default/core/utils/track-selection.js +31 -0
  44. package/dist/default/core/utils/track-selection.js.map +1 -0
  45. package/dist/default/dom/features/end-of-stream.js +169 -0
  46. package/dist/default/dom/features/end-of-stream.js.map +1 -0
  47. package/dist/default/dom/features/load-segments.js +188 -0
  48. package/dist/default/dom/features/load-segments.js.map +1 -0
  49. package/dist/default/dom/features/load-text-track-cues.js +151 -0
  50. package/dist/default/dom/features/load-text-track-cues.js.map +1 -0
  51. package/dist/default/dom/features/segment-loader-actor.js +197 -0
  52. package/dist/default/dom/features/segment-loader-actor.js.map +1 -0
  53. package/dist/default/dom/features/setup-mediasource.js +55 -0
  54. package/dist/default/dom/features/setup-mediasource.js.map +1 -0
  55. package/dist/default/dom/features/setup-sourcebuffer.js +70 -0
  56. package/dist/default/dom/features/setup-sourcebuffer.js.map +1 -0
  57. package/dist/default/dom/features/setup-text-tracks.js +88 -0
  58. package/dist/default/dom/features/setup-text-tracks.js.map +1 -0
  59. package/dist/default/dom/features/sync-selected-text-track-from-dom.js +63 -0
  60. package/dist/default/dom/features/sync-selected-text-track-from-dom.js.map +1 -0
  61. package/dist/default/dom/features/sync-text-track-modes.js +38 -0
  62. package/dist/default/dom/features/sync-text-track-modes.js.map +1 -0
  63. package/dist/default/dom/features/track-current-time.js +46 -0
  64. package/dist/default/dom/features/track-current-time.js.map +1 -0
  65. package/dist/default/dom/features/track-playback-initiated.js +55 -0
  66. package/dist/default/dom/features/track-playback-initiated.js.map +1 -0
  67. package/dist/{dom/index.js → default/dom/features/track-playback-rate.js} +2 -4
  68. package/dist/default/dom/features/track-playback-rate.js.map +1 -0
  69. package/dist/default/dom/features/update-duration.js +72 -0
  70. package/dist/default/dom/features/update-duration.js.map +1 -0
  71. package/dist/default/dom/media/append-segment.js +60 -0
  72. package/dist/default/dom/media/append-segment.js.map +1 -0
  73. package/dist/default/dom/media/buffer-flusher.js +55 -0
  74. package/dist/default/dom/media/buffer-flusher.js.map +1 -0
  75. package/dist/default/dom/media/mediasource-setup.js +154 -0
  76. package/dist/default/dom/media/mediasource-setup.js.map +1 -0
  77. package/dist/default/dom/media/source-buffer-actor.js +186 -0
  78. package/dist/default/dom/media/source-buffer-actor.js.map +1 -0
  79. package/dist/default/dom/network/chunked-stream-iterable.js +49 -0
  80. package/dist/default/dom/network/chunked-stream-iterable.js.map +1 -0
  81. package/dist/default/dom/network/fetch.js +56 -0
  82. package/dist/default/dom/network/fetch.js.map +1 -0
  83. package/dist/default/dom/playback-engine/adapter.js +95 -0
  84. package/dist/default/dom/playback-engine/adapter.js.map +1 -0
  85. package/dist/default/dom/playback-engine/engine.js +176 -0
  86. package/dist/default/dom/playback-engine/engine.js.map +1 -0
  87. package/dist/default/dom/playback-engine.js +4 -0
  88. package/dist/default/dom/text/parse-vtt-segment.js +56 -0
  89. package/dist/default/dom/text/parse-vtt-segment.js.map +1 -0
  90. package/dist/default/dom.js +11 -0
  91. package/dist/{src-DRsS_H80.js → default/index.js} +2 -2
  92. package/dist/default/index.js.map +1 -0
  93. package/dist/dev/all.js +16 -0
  94. package/dist/dev/core/abr/bandwidth-estimator.d.ts +35 -0
  95. package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +1 -0
  96. package/dist/dev/core/abr/bandwidth-estimator.js +96 -0
  97. package/dist/dev/core/abr/bandwidth-estimator.js.map +1 -0
  98. package/dist/dev/core/abr/ewma.js +71 -0
  99. package/dist/dev/core/abr/ewma.js.map +1 -0
  100. package/dist/dev/core/abr/quality-selection.js +53 -0
  101. package/dist/dev/core/abr/quality-selection.js.map +1 -0
  102. package/dist/dev/core/actor.d.ts +31 -0
  103. package/dist/dev/core/actor.d.ts.map +1 -0
  104. package/dist/dev/core/buffer/back-buffer.js +46 -0
  105. package/dist/dev/core/buffer/back-buffer.js.map +1 -0
  106. package/dist/dev/core/buffer/forward-buffer.js +73 -0
  107. package/dist/dev/core/buffer/forward-buffer.js.map +1 -0
  108. package/dist/dev/core/events/create-event-stream.d.ts +48 -0
  109. package/dist/dev/core/events/create-event-stream.d.ts.map +1 -0
  110. package/dist/dev/core/events/create-event-stream.js +48 -0
  111. package/dist/dev/core/events/create-event-stream.js.map +1 -0
  112. package/dist/dev/core/features/calculate-presentation-duration.js +51 -0
  113. package/dist/dev/core/features/calculate-presentation-duration.js.map +1 -0
  114. package/dist/dev/core/features/quality-switching.js +68 -0
  115. package/dist/dev/core/features/quality-switching.js.map +1 -0
  116. package/dist/dev/core/features/resolve-presentation.d.ts +16 -0
  117. package/dist/dev/core/features/resolve-presentation.d.ts.map +1 -0
  118. package/dist/dev/core/features/resolve-presentation.js +99 -0
  119. package/dist/dev/core/features/resolve-presentation.js.map +1 -0
  120. package/dist/dev/core/features/resolve-track.d.ts +13 -0
  121. package/dist/dev/core/features/resolve-track.d.ts.map +1 -0
  122. package/dist/dev/core/features/resolve-track.js +77 -0
  123. package/dist/dev/core/features/resolve-track.js.map +1 -0
  124. package/dist/dev/core/features/select-tracks.d.ts +11 -0
  125. package/dist/dev/core/features/select-tracks.d.ts.map +1 -0
  126. package/dist/dev/core/features/select-tracks.js +147 -0
  127. package/dist/dev/core/features/select-tracks.js.map +1 -0
  128. package/dist/dev/core/hls/parse-attributes.js +134 -0
  129. package/dist/dev/core/hls/parse-attributes.js.map +1 -0
  130. package/dist/dev/core/hls/parse-media-playlist.js +83 -0
  131. package/dist/dev/core/hls/parse-media-playlist.js.map +1 -0
  132. package/dist/dev/core/hls/parse-multivariant.js +215 -0
  133. package/dist/dev/core/hls/parse-multivariant.js.map +1 -0
  134. package/dist/dev/core/hls/resolve-url.js +11 -0
  135. package/dist/dev/core/hls/resolve-url.js.map +1 -0
  136. package/dist/dev/core/reactive/combine-latest.js +72 -0
  137. package/dist/dev/core/reactive/combine-latest.js.map +1 -0
  138. package/dist/dev/core/state/create-state.d.ts +104 -0
  139. package/dist/dev/core/state/create-state.d.ts.map +1 -0
  140. package/dist/dev/core/state/create-state.js +143 -0
  141. package/dist/dev/core/state/create-state.js.map +1 -0
  142. package/dist/dev/core/task.js +122 -0
  143. package/dist/dev/core/task.js.map +1 -0
  144. package/dist/dev/core/types/index.d.ts +173 -0
  145. package/dist/dev/core/types/index.d.ts.map +1 -0
  146. package/dist/dev/core/types/index.js +15 -0
  147. package/dist/dev/core/types/index.js.map +1 -0
  148. package/dist/dev/core/utils/generate-id.js +21 -0
  149. package/dist/dev/core/utils/generate-id.js.map +1 -0
  150. package/dist/dev/core/utils/track-selection.d.ts +16 -0
  151. package/dist/dev/core/utils/track-selection.d.ts.map +1 -0
  152. package/dist/dev/core/utils/track-selection.js +31 -0
  153. package/dist/dev/core/utils/track-selection.js.map +1 -0
  154. package/dist/dev/dom/features/end-of-stream.js +169 -0
  155. package/dist/dev/dom/features/end-of-stream.js.map +1 -0
  156. package/dist/dev/dom/features/load-segments.d.ts +72 -0
  157. package/dist/dev/dom/features/load-segments.d.ts.map +1 -0
  158. package/dist/dev/dom/features/load-segments.js +188 -0
  159. package/dist/dev/dom/features/load-segments.js.map +1 -0
  160. package/dist/dev/dom/features/load-text-track-cues.d.ts +61 -0
  161. package/dist/dev/dom/features/load-text-track-cues.d.ts.map +1 -0
  162. package/dist/dev/dom/features/load-text-track-cues.js +151 -0
  163. package/dist/dev/dom/features/load-text-track-cues.js.map +1 -0
  164. package/dist/dev/dom/features/segment-loader-actor.js +197 -0
  165. package/dist/dev/dom/features/segment-loader-actor.js.map +1 -0
  166. package/dist/dev/dom/features/setup-mediasource.js +55 -0
  167. package/dist/dev/dom/features/setup-mediasource.js.map +1 -0
  168. package/dist/dev/dom/features/setup-sourcebuffer.d.ts +9 -0
  169. package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +1 -0
  170. package/dist/dev/dom/features/setup-sourcebuffer.js +70 -0
  171. package/dist/dev/dom/features/setup-sourcebuffer.js.map +1 -0
  172. package/dist/dev/dom/features/setup-text-tracks.js +88 -0
  173. package/dist/dev/dom/features/setup-text-tracks.js.map +1 -0
  174. package/dist/dev/dom/features/sync-selected-text-track-from-dom.js +63 -0
  175. package/dist/dev/dom/features/sync-selected-text-track-from-dom.js.map +1 -0
  176. package/dist/dev/dom/features/sync-text-track-modes.js +38 -0
  177. package/dist/dev/dom/features/sync-text-track-modes.js.map +1 -0
  178. package/dist/dev/dom/features/track-current-time.d.ts +40 -0
  179. package/dist/dev/dom/features/track-current-time.d.ts.map +1 -0
  180. package/dist/dev/dom/features/track-current-time.js +46 -0
  181. package/dist/dev/dom/features/track-current-time.js.map +1 -0
  182. package/dist/dev/dom/features/track-playback-initiated.d.ts +53 -0
  183. package/dist/dev/dom/features/track-playback-initiated.d.ts.map +1 -0
  184. package/dist/dev/dom/features/track-playback-initiated.js +55 -0
  185. package/dist/dev/dom/features/track-playback-initiated.js.map +1 -0
  186. package/dist/dev/dom/features/track-playback-rate.d.ts +34 -0
  187. package/dist/dev/dom/features/track-playback-rate.d.ts.map +1 -0
  188. package/dist/dev/dom/features/track-playback-rate.js +36 -0
  189. package/dist/dev/dom/features/track-playback-rate.js.map +1 -0
  190. package/dist/dev/dom/features/update-duration.js +72 -0
  191. package/dist/dev/dom/features/update-duration.js.map +1 -0
  192. package/dist/dev/dom/media/append-segment.d.ts +24 -0
  193. package/dist/dev/dom/media/append-segment.d.ts.map +1 -0
  194. package/dist/dev/dom/media/append-segment.js +60 -0
  195. package/dist/dev/dom/media/append-segment.js.map +1 -0
  196. package/dist/dev/dom/media/buffer-flusher.d.ts +24 -0
  197. package/dist/dev/dom/media/buffer-flusher.d.ts.map +1 -0
  198. package/dist/dev/dom/media/buffer-flusher.js +55 -0
  199. package/dist/dev/dom/media/buffer-flusher.js.map +1 -0
  200. package/dist/dev/dom/media/mediasource-setup.js +154 -0
  201. package/dist/dev/dom/media/mediasource-setup.js.map +1 -0
  202. package/dist/dev/dom/media/source-buffer-actor.d.ts +56 -0
  203. package/dist/dev/dom/media/source-buffer-actor.d.ts.map +1 -0
  204. package/dist/dev/dom/media/source-buffer-actor.js +186 -0
  205. package/dist/dev/dom/media/source-buffer-actor.js.map +1 -0
  206. package/dist/dev/dom/network/chunked-stream-iterable.js +49 -0
  207. package/dist/dev/dom/network/chunked-stream-iterable.js.map +1 -0
  208. package/dist/dev/dom/network/fetch.js +56 -0
  209. package/dist/dev/dom/network/fetch.js.map +1 -0
  210. package/dist/dev/dom/playback-engine/adapter.d.ts +41 -0
  211. package/dist/dev/dom/playback-engine/adapter.d.ts.map +1 -0
  212. package/dist/dev/dom/playback-engine/adapter.js +95 -0
  213. package/dist/dev/dom/playback-engine/adapter.js.map +1 -0
  214. package/dist/dev/dom/playback-engine/engine.d.ts +135 -0
  215. package/dist/dev/dom/playback-engine/engine.d.ts.map +1 -0
  216. package/dist/dev/dom/playback-engine/engine.js +176 -0
  217. package/dist/dev/dom/playback-engine/engine.js.map +1 -0
  218. package/dist/dev/dom/playback-engine.d.ts +3 -0
  219. package/dist/dev/dom/playback-engine.js +4 -0
  220. package/dist/dev/dom/text/parse-vtt-segment.d.ts +12 -0
  221. package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +1 -0
  222. package/dist/dev/dom/text/parse-vtt-segment.js +56 -0
  223. package/dist/dev/dom/text/parse-vtt-segment.js.map +1 -0
  224. package/dist/dev/dom.d.ts +10 -0
  225. package/dist/dev/dom.js +11 -0
  226. package/dist/dev/index.d.ts.map +1 -0
  227. package/dist/dev/index.js +14 -0
  228. package/dist/dev/index.js.map +1 -0
  229. package/package.json +22 -22
  230. package/dist/adapter-CMw-rvbk.js +0 -3512
  231. package/dist/adapter-CMw-rvbk.js.map +0 -1
  232. package/dist/adapter-l8iJN_GI.d.ts +0 -689
  233. package/dist/adapter-l8iJN_GI.d.ts.map +0 -1
  234. package/dist/dom/index.d.ts +0 -243
  235. package/dist/dom/index.d.ts.map +0 -1
  236. package/dist/dom/index.js.map +0 -1
  237. package/dist/dom/playback-engine/index.d.ts +0 -2
  238. package/dist/dom/playback-engine/index.js +0 -4
  239. package/dist/index.d.ts.map +0 -1
  240. package/dist/index.js +0 -3
  241. package/dist/src-DRsS_H80.js.map +0 -1
  242. /package/dist/{index.d.ts → dev/index.d.ts} +0 -0
@@ -0,0 +1,40 @@
1
+ import { WritableState } from "../../core/state/create-state.js";
2
+
3
+ //#region src/dom/features/track-current-time.d.ts
4
+ /**
5
+ * State shape for current time tracking.
6
+ */
7
+ interface CurrentTimeState {
8
+ currentTime?: number;
9
+ }
10
+ /**
11
+ * Owners shape for current time tracking.
12
+ */
13
+ interface CurrentTimeOwners {
14
+ mediaElement?: HTMLMediaElement | undefined;
15
+ }
16
+ /**
17
+ * Track current playback position from the media element.
18
+ *
19
+ * Mirrors `mediaElement.currentTime` into reactive state on:
20
+ * - `timeupdate` — fires during playback (~4 Hz)
21
+ * - `seeking` — fires when a seek begins; per spec, `currentTime` is
22
+ * already at the new position when this event dispatches, so buffer
23
+ * management can react immediately rather than waiting for `timeupdate`,
24
+ * which does not fire while paused.
25
+ *
26
+ * Also syncs immediately when a media element becomes available.
27
+ *
28
+ * @example
29
+ * const cleanup = trackCurrentTime({ state, owners });
30
+ */
31
+ declare function trackCurrentTime({
32
+ state,
33
+ owners
34
+ }: {
35
+ state: WritableState<CurrentTimeState>;
36
+ owners: WritableState<CurrentTimeOwners>;
37
+ }): () => void;
38
+ //#endregion
39
+ export { CurrentTimeOwners, CurrentTimeState, trackCurrentTime };
40
+ //# sourceMappingURL=track-current-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-current-time.d.ts","names":[],"sources":["../../../../src/dom/features/track-current-time.ts"],"mappings":";;;;;AAMA;UAAiB,gBAAA;EACf,WAAA;AAAA;;AAMF;;UAAiB,iBAAA;EACf,YAAA,GAAe,gBAAA;AAAA;;;;;;;;;;;;;;;;iBA4BD,gBAAA,CAAA;EACd,KAAA;EACA;AAAA;EAEA,KAAA,EAAO,aAAA,CAAc,gBAAA;EACrB,MAAA,EAAQ,aAAA,CAAc,iBAAA;AAAA"}
@@ -0,0 +1,46 @@
1
+ import { listen } from "@videojs/utils/dom";
2
+
3
+ //#region src/dom/features/track-current-time.ts
4
+ /**
5
+ * Track current playback position from the media element.
6
+ *
7
+ * Mirrors `mediaElement.currentTime` into reactive state on:
8
+ * - `timeupdate` — fires during playback (~4 Hz)
9
+ * - `seeking` — fires when a seek begins; per spec, `currentTime` is
10
+ * already at the new position when this event dispatches, so buffer
11
+ * management can react immediately rather than waiting for `timeupdate`,
12
+ * which does not fire while paused.
13
+ *
14
+ * Also syncs immediately when a media element becomes available.
15
+ *
16
+ * @example
17
+ * const cleanup = trackCurrentTime({ state, owners });
18
+ */
19
+ function trackCurrentTime({ state, owners }) {
20
+ let lastMediaElement;
21
+ let removeListeners = null;
22
+ const unsubscribe = owners.subscribe((currentOwners) => {
23
+ const { mediaElement } = currentOwners;
24
+ if (mediaElement === lastMediaElement) return;
25
+ removeListeners?.();
26
+ removeListeners = null;
27
+ lastMediaElement = mediaElement;
28
+ if (!mediaElement) return;
29
+ state.patch({ currentTime: mediaElement.currentTime });
30
+ const sync = () => state.patch({ currentTime: mediaElement.currentTime });
31
+ const removeTimeupdate = listen(mediaElement, "timeupdate", sync);
32
+ const removeSeeking = listen(mediaElement, "seeking", sync);
33
+ removeListeners = () => {
34
+ removeTimeupdate();
35
+ removeSeeking();
36
+ };
37
+ });
38
+ return () => {
39
+ removeListeners?.();
40
+ unsubscribe();
41
+ };
42
+ }
43
+
44
+ //#endregion
45
+ export { trackCurrentTime };
46
+ //# sourceMappingURL=track-current-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-current-time.js","names":[],"sources":["../../../../src/dom/features/track-current-time.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport type { WritableState } from '../../core/state/create-state';\n\n/**\n * State shape for current time tracking.\n */\nexport interface CurrentTimeState {\n currentTime?: number;\n}\n\n/**\n * Owners shape for current time tracking.\n */\nexport interface CurrentTimeOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Check if we can track current time.\n *\n * Requires:\n * - mediaElement exists in owners\n */\nexport function canTrackCurrentTime(owners: CurrentTimeOwners): boolean {\n return !!owners.mediaElement;\n}\n\n/**\n * Track current playback position from the media element.\n *\n * Mirrors `mediaElement.currentTime` into reactive state on:\n * - `timeupdate` — fires during playback (~4 Hz)\n * - `seeking` — fires when a seek begins; per spec, `currentTime` is\n * already at the new position when this event dispatches, so buffer\n * management can react immediately rather than waiting for `timeupdate`,\n * which does not fire while paused.\n *\n * Also syncs immediately when a media element becomes available.\n *\n * @example\n * const cleanup = trackCurrentTime({ state, owners });\n */\nexport function trackCurrentTime({\n state,\n owners,\n}: {\n state: WritableState<CurrentTimeState>;\n owners: WritableState<CurrentTimeOwners>;\n}): () => void {\n let lastMediaElement: HTMLMediaElement | undefined;\n let removeListeners: (() => void) | null = null;\n\n const unsubscribe = owners.subscribe((currentOwners) => {\n const { mediaElement } = currentOwners;\n\n if (mediaElement === lastMediaElement) return;\n\n removeListeners?.();\n removeListeners = null;\n lastMediaElement = mediaElement;\n\n if (!mediaElement) return;\n\n // Sync immediately so consumers don't wait for the first event\n state.patch({ currentTime: mediaElement.currentTime });\n\n const sync = () => state.patch({ currentTime: mediaElement.currentTime });\n const removeTimeupdate = listen(mediaElement, 'timeupdate', sync);\n const removeSeeking = listen(mediaElement, 'seeking', sync);\n removeListeners = () => {\n removeTimeupdate();\n removeSeeking();\n };\n });\n\n return () => {\n removeListeners?.();\n unsubscribe();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA0CA,SAAgB,iBAAiB,EAC/B,OACA,UAIa;CACb,IAAI;CACJ,IAAI,kBAAuC;CAE3C,MAAM,cAAc,OAAO,WAAW,kBAAkB;EACtD,MAAM,EAAE,iBAAiB;AAEzB,MAAI,iBAAiB,iBAAkB;AAEvC,qBAAmB;AACnB,oBAAkB;AAClB,qBAAmB;AAEnB,MAAI,CAAC,aAAc;AAGnB,QAAM,MAAM,EAAE,aAAa,aAAa,aAAa,CAAC;EAEtD,MAAM,aAAa,MAAM,MAAM,EAAE,aAAa,aAAa,aAAa,CAAC;EACzE,MAAM,mBAAmB,OAAO,cAAc,cAAc,KAAK;EACjE,MAAM,gBAAgB,OAAO,cAAc,WAAW,KAAK;AAC3D,0BAAwB;AACtB,qBAAkB;AAClB,kBAAe;;GAEjB;AAEF,cAAa;AACX,qBAAmB;AACnB,eAAa"}
@@ -0,0 +1,53 @@
1
+ import { WritableState } from "../../core/state/create-state.js";
2
+ import { EventStream } from "../../core/events/create-event-stream.js";
3
+ import { PresentationAction } from "../../core/features/resolve-presentation.js";
4
+
5
+ //#region src/dom/features/track-playback-initiated.d.ts
6
+ /**
7
+ * State shape for playback initiation tracking.
8
+ */
9
+ interface PlaybackInitiatedState {
10
+ /** True once the user has initiated playback (play event fired). */
11
+ playbackInitiated?: boolean;
12
+ /** Current presentation — watched for URL changes to reset playbackInitiated. */
13
+ presentation?: {
14
+ url?: string;
15
+ };
16
+ }
17
+ /**
18
+ * Owners shape for playback initiation tracking.
19
+ */
20
+ interface PlaybackInitiatedOwners {
21
+ mediaElement?: HTMLMediaElement | undefined;
22
+ }
23
+ /**
24
+ * Track whether playback has been initiated by the user.
25
+ *
26
+ * Sets `state.playbackInitiated = true` when the media element fires a `play`
27
+ * event (via `element.play()`, native controls, or autoplay) and simultaneously
28
+ * dispatches `{ type: 'play' }` to the event stream so `resolvePresentation`
29
+ * can react.
30
+ *
31
+ * Resets `state.playbackInitiated = false` when `presentation.url` changes,
32
+ * so a new source with `preload="none"` won't load segments until play is
33
+ * triggered again.
34
+ *
35
+ * This flag is used by `shouldLoadSegments` to allow segment loading after
36
+ * play is initiated regardless of the initial `preload` setting — `preload`
37
+ * is a startup hint, not a runtime gate.
38
+ *
39
+ * @example
40
+ * const cleanup = trackPlaybackInitiated({ state, owners, events });
41
+ */
42
+ declare function trackPlaybackInitiated({
43
+ state,
44
+ owners,
45
+ events
46
+ }: {
47
+ state: WritableState<PlaybackInitiatedState>;
48
+ owners: WritableState<PlaybackInitiatedOwners>;
49
+ events: EventStream<PresentationAction>;
50
+ }): () => void;
51
+ //#endregion
52
+ export { PlaybackInitiatedOwners, PlaybackInitiatedState, trackPlaybackInitiated };
53
+ //# sourceMappingURL=track-playback-initiated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-initiated.d.ts","names":[],"sources":["../../../../src/dom/features/track-playback-initiated.ts"],"mappings":";;;;;;;AAQA;UAAiB,sBAAA;;EAEf,iBAAA;EAAA;EAEA,YAAA;IAAiB,GAAA;EAAA;AAAA;AAMnB;;;AAAA,UAAiB,uBAAA;EACf,YAAA,GAAe,gBAAA;AAAA;;;;;;;;;;;;;;;;;;;;iBAsBD,sBAAA,CAAA;EACd,KAAA;EACA,MAAA;EACA;AAAA;EAEA,KAAA,EAAO,aAAA,CAAc,sBAAA;EACrB,MAAA,EAAQ,aAAA,CAAc,uBAAA;EACtB,MAAA,EAAQ,WAAA,CAAY,kBAAA;AAAA"}
@@ -0,0 +1,55 @@
1
+ import { listen } from "@videojs/utils/dom";
2
+
3
+ //#region src/dom/features/track-playback-initiated.ts
4
+ /**
5
+ * Track whether playback has been initiated by the user.
6
+ *
7
+ * Sets `state.playbackInitiated = true` when the media element fires a `play`
8
+ * event (via `element.play()`, native controls, or autoplay) and simultaneously
9
+ * dispatches `{ type: 'play' }` to the event stream so `resolvePresentation`
10
+ * can react.
11
+ *
12
+ * Resets `state.playbackInitiated = false` when `presentation.url` changes,
13
+ * so a new source with `preload="none"` won't load segments until play is
14
+ * triggered again.
15
+ *
16
+ * This flag is used by `shouldLoadSegments` to allow segment loading after
17
+ * play is initiated regardless of the initial `preload` setting — `preload`
18
+ * is a startup hint, not a runtime gate.
19
+ *
20
+ * @example
21
+ * const cleanup = trackPlaybackInitiated({ state, owners, events });
22
+ */
23
+ function trackPlaybackInitiated({ state, owners, events }) {
24
+ let lastMediaElement;
25
+ let removeListener = null;
26
+ let lastPresentationUrl;
27
+ const unsubscribeState = state.subscribe((currentState) => {
28
+ const url = currentState.presentation?.url;
29
+ if (url !== lastPresentationUrl) {
30
+ if (lastPresentationUrl !== void 0) state.patch({ playbackInitiated: false });
31
+ lastPresentationUrl = url;
32
+ }
33
+ });
34
+ const unsubscribeOwners = owners.subscribe((currentOwners) => {
35
+ const { mediaElement } = currentOwners;
36
+ if (mediaElement === lastMediaElement) return;
37
+ removeListener?.();
38
+ removeListener = null;
39
+ lastMediaElement = mediaElement;
40
+ if (!mediaElement) return;
41
+ removeListener = listen(mediaElement, "play", () => {
42
+ state.patch({ playbackInitiated: true });
43
+ events.dispatch({ type: "play" });
44
+ });
45
+ });
46
+ return () => {
47
+ removeListener?.();
48
+ unsubscribeState();
49
+ unsubscribeOwners();
50
+ };
51
+ }
52
+
53
+ //#endregion
54
+ export { trackPlaybackInitiated };
55
+ //# sourceMappingURL=track-playback-initiated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-initiated.js","names":[],"sources":["../../../../src/dom/features/track-playback-initiated.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport type { EventStream } from '../../core/events/create-event-stream';\nimport type { PresentationAction } from '../../core/features/resolve-presentation';\nimport type { WritableState } from '../../core/state/create-state';\n\n/**\n * State shape for playback initiation tracking.\n */\nexport interface PlaybackInitiatedState {\n /** True once the user has initiated playback (play event fired). */\n playbackInitiated?: boolean;\n /** Current presentation — watched for URL changes to reset playbackInitiated. */\n presentation?: { url?: string };\n}\n\n/**\n * Owners shape for playback initiation tracking.\n */\nexport interface PlaybackInitiatedOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Track whether playback has been initiated by the user.\n *\n * Sets `state.playbackInitiated = true` when the media element fires a `play`\n * event (via `element.play()`, native controls, or autoplay) and simultaneously\n * dispatches `{ type: 'play' }` to the event stream so `resolvePresentation`\n * can react.\n *\n * Resets `state.playbackInitiated = false` when `presentation.url` changes,\n * so a new source with `preload=\"none\"` won't load segments until play is\n * triggered again.\n *\n * This flag is used by `shouldLoadSegments` to allow segment loading after\n * play is initiated regardless of the initial `preload` setting — `preload`\n * is a startup hint, not a runtime gate.\n *\n * @example\n * const cleanup = trackPlaybackInitiated({ state, owners, events });\n */\nexport function trackPlaybackInitiated({\n state,\n owners,\n events,\n}: {\n state: WritableState<PlaybackInitiatedState>;\n owners: WritableState<PlaybackInitiatedOwners>;\n events: EventStream<PresentationAction>;\n}): () => void {\n let lastMediaElement: HTMLMediaElement | undefined;\n let removeListener: (() => void) | null = null;\n let lastPresentationUrl: string | undefined;\n\n // Watch for presentation URL changes to reset playbackInitiated\n const unsubscribeState = state.subscribe((currentState) => {\n const url = currentState.presentation?.url;\n if (url !== lastPresentationUrl) {\n if (lastPresentationUrl !== undefined) {\n // URL changed to a new value — reset so new source requires play again\n state.patch({ playbackInitiated: false });\n }\n lastPresentationUrl = url;\n }\n });\n\n // Bridge media element play event → state flag + event stream\n const unsubscribeOwners = owners.subscribe((currentOwners) => {\n const { mediaElement } = currentOwners;\n\n if (mediaElement === lastMediaElement) return;\n\n removeListener?.();\n removeListener = null;\n lastMediaElement = mediaElement;\n\n if (!mediaElement) return;\n\n removeListener = listen(mediaElement, 'play', () => {\n state.patch({ playbackInitiated: true });\n events.dispatch({ type: 'play' });\n });\n });\n\n return () => {\n removeListener?.();\n unsubscribeState();\n unsubscribeOwners();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,uBAAuB,EACrC,OACA,QACA,UAKa;CACb,IAAI;CACJ,IAAI,iBAAsC;CAC1C,IAAI;CAGJ,MAAM,mBAAmB,MAAM,WAAW,iBAAiB;EACzD,MAAM,MAAM,aAAa,cAAc;AACvC,MAAI,QAAQ,qBAAqB;AAC/B,OAAI,wBAAwB,OAE1B,OAAM,MAAM,EAAE,mBAAmB,OAAO,CAAC;AAE3C,yBAAsB;;GAExB;CAGF,MAAM,oBAAoB,OAAO,WAAW,kBAAkB;EAC5D,MAAM,EAAE,iBAAiB;AAEzB,MAAI,iBAAiB,iBAAkB;AAEvC,oBAAkB;AAClB,mBAAiB;AACjB,qBAAmB;AAEnB,MAAI,CAAC,aAAc;AAEnB,mBAAiB,OAAO,cAAc,cAAc;AAClD,SAAM,MAAM,EAAE,mBAAmB,MAAM,CAAC;AACxC,UAAO,SAAS,EAAE,MAAM,QAAQ,CAAC;IACjC;GACF;AAEF,cAAa;AACX,oBAAkB;AAClB,oBAAkB;AAClB,qBAAmB"}
@@ -0,0 +1,34 @@
1
+ import { WritableState } from "../../core/state/create-state.js";
2
+
3
+ //#region src/dom/features/track-playback-rate.d.ts
4
+ /**
5
+ * State shape for playback rate tracking.
6
+ */
7
+ interface PlaybackRateState {
8
+ playbackRate?: number;
9
+ }
10
+ /**
11
+ * Owners shape for playback rate tracking.
12
+ */
13
+ interface PlaybackRateOwners {
14
+ mediaElement?: HTMLMediaElement | undefined;
15
+ }
16
+ /**
17
+ * Track playback rate from the media element.
18
+ *
19
+ * Mirrors `mediaElement.playbackRate` into reactive state on each `ratechange`
20
+ * event. Also syncs immediately when a media element becomes available.
21
+ *
22
+ * @example
23
+ * const cleanup = trackPlaybackRate({ state, owners });
24
+ */
25
+ declare function trackPlaybackRate({
26
+ state,
27
+ owners
28
+ }: {
29
+ state: WritableState<PlaybackRateState>;
30
+ owners: WritableState<PlaybackRateOwners>;
31
+ }): () => void;
32
+ //#endregion
33
+ export { PlaybackRateOwners, PlaybackRateState, trackPlaybackRate };
34
+ //# sourceMappingURL=track-playback-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-rate.d.ts","names":[],"sources":["../../../../src/dom/features/track-playback-rate.ts"],"mappings":";;;;;AAMA;UAAiB,iBAAA;EACf,YAAA;AAAA;;AAMF;;UAAiB,kBAAA;EACf,YAAA,GAAe,gBAAA;AAAA;;;;;;;;;;iBAsBD,iBAAA,CAAA;EACd,KAAA;EACA;AAAA;EAEA,KAAA,EAAO,aAAA,CAAc,iBAAA;EACrB,MAAA,EAAQ,aAAA,CAAc,kBAAA;AAAA"}
@@ -0,0 +1,36 @@
1
+ import { listen } from "@videojs/utils/dom";
2
+
3
+ //#region src/dom/features/track-playback-rate.ts
4
+ /**
5
+ * Track playback rate from the media element.
6
+ *
7
+ * Mirrors `mediaElement.playbackRate` into reactive state on each `ratechange`
8
+ * event. Also syncs immediately when a media element becomes available.
9
+ *
10
+ * @example
11
+ * const cleanup = trackPlaybackRate({ state, owners });
12
+ */
13
+ function trackPlaybackRate({ state, owners }) {
14
+ let lastMediaElement;
15
+ let removeListener = null;
16
+ const unsubscribe = owners.subscribe((currentOwners) => {
17
+ const { mediaElement } = currentOwners;
18
+ if (mediaElement === lastMediaElement) return;
19
+ removeListener?.();
20
+ removeListener = null;
21
+ lastMediaElement = mediaElement;
22
+ if (!mediaElement) return;
23
+ state.patch({ playbackRate: mediaElement.playbackRate });
24
+ removeListener = listen(mediaElement, "ratechange", () => {
25
+ state.patch({ playbackRate: mediaElement.playbackRate });
26
+ });
27
+ });
28
+ return () => {
29
+ removeListener?.();
30
+ unsubscribe();
31
+ };
32
+ }
33
+
34
+ //#endregion
35
+ export { trackPlaybackRate };
36
+ //# sourceMappingURL=track-playback-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-rate.js","names":[],"sources":["../../../../src/dom/features/track-playback-rate.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport type { WritableState } from '../../core/state/create-state';\n\n/**\n * State shape for playback rate tracking.\n */\nexport interface PlaybackRateState {\n playbackRate?: number;\n}\n\n/**\n * Owners shape for playback rate tracking.\n */\nexport interface PlaybackRateOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Check if we can track playback rate.\n *\n * Requires:\n * - mediaElement exists in owners\n */\nexport function canTrackPlaybackRate(owners: PlaybackRateOwners): boolean {\n return !!owners.mediaElement;\n}\n\n/**\n * Track playback rate from the media element.\n *\n * Mirrors `mediaElement.playbackRate` into reactive state on each `ratechange`\n * event. Also syncs immediately when a media element becomes available.\n *\n * @example\n * const cleanup = trackPlaybackRate({ state, owners });\n */\nexport function trackPlaybackRate({\n state,\n owners,\n}: {\n state: WritableState<PlaybackRateState>;\n owners: WritableState<PlaybackRateOwners>;\n}): () => void {\n let lastMediaElement: HTMLMediaElement | undefined;\n let removeListener: (() => void) | null = null;\n\n const unsubscribe = owners.subscribe((currentOwners) => {\n const { mediaElement } = currentOwners;\n\n if (mediaElement === lastMediaElement) return;\n\n removeListener?.();\n removeListener = null;\n lastMediaElement = mediaElement;\n\n if (!mediaElement) return;\n\n state.patch({ playbackRate: mediaElement.playbackRate });\n\n removeListener = listen(mediaElement, 'ratechange', () => {\n state.patch({ playbackRate: mediaElement.playbackRate });\n });\n });\n\n return () => {\n removeListener?.();\n unsubscribe();\n };\n}\n"],"mappings":";;;;;;;;;;;;AAoCA,SAAgB,kBAAkB,EAChC,OACA,UAIa;CACb,IAAI;CACJ,IAAI,iBAAsC;CAE1C,MAAM,cAAc,OAAO,WAAW,kBAAkB;EACtD,MAAM,EAAE,iBAAiB;AAEzB,MAAI,iBAAiB,iBAAkB;AAEvC,oBAAkB;AAClB,mBAAiB;AACjB,qBAAmB;AAEnB,MAAI,CAAC,aAAc;AAEnB,QAAM,MAAM,EAAE,cAAc,aAAa,cAAc,CAAC;AAExD,mBAAiB,OAAO,cAAc,oBAAoB;AACxD,SAAM,MAAM,EAAE,cAAc,aAAa,cAAc,CAAC;IACxD;GACF;AAEF,cAAa;AACX,oBAAkB;AAClB,eAAa"}
@@ -0,0 +1,72 @@
1
+ import { hasPresentationDuration } from "../../core/types/index.js";
2
+ import { combineLatest } from "../../core/reactive/combine-latest.js";
3
+
4
+ //#region src/dom/features/update-duration.ts
5
+ /**
6
+ * Check if we can update MediaSource duration (have required data).
7
+ */
8
+ function canUpdateDuration(state, owners) {
9
+ return !!(owners.mediaSource && state.presentation && hasPresentationDuration(state.presentation));
10
+ }
11
+ /**
12
+ * Get the maximum buffered end time across all SourceBuffers.
13
+ */
14
+ function getMaxBufferedEnd(owners) {
15
+ let maxEnd = 0;
16
+ const buffers = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter((buf) => buf !== void 0);
17
+ for (const buffer of buffers) {
18
+ const { buffered } = buffer;
19
+ if (buffered.length > 0) {
20
+ const end = buffered.end(buffered.length - 1);
21
+ if (end > maxEnd) maxEnd = end;
22
+ }
23
+ }
24
+ return maxEnd;
25
+ }
26
+ /**
27
+ * Check if we should update MediaSource duration (conditions met).
28
+ */
29
+ function shouldUpdateDuration(state, owners) {
30
+ if (!canUpdateDuration(state, owners)) return false;
31
+ const { mediaSource } = owners;
32
+ const { presentation } = state;
33
+ if (mediaSource.readyState !== "open") return false;
34
+ const duration = presentation.duration;
35
+ if (!Number.isFinite(duration) || Number.isNaN(duration) || duration <= 0) return false;
36
+ return Number.isNaN(mediaSource.duration);
37
+ }
38
+ /**
39
+ * Wait for all currently-updating SourceBuffers to finish.
40
+ *
41
+ * The MSE spec forbids setting MediaSource.duration while any attached
42
+ * SourceBuffer has updating === true. This defers until all are idle.
43
+ */
44
+ function waitForSourceBuffersReady(owners) {
45
+ const updating = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter((buf) => buf?.updating === true);
46
+ if (updating.length === 0) return Promise.resolve();
47
+ return Promise.all(updating.map((buf) => new Promise((resolve) => buf.addEventListener("updateend", () => resolve(), { once: true })))).then(() => void 0);
48
+ }
49
+ /**
50
+ * Update MediaSource duration when presentation duration becomes available.
51
+ */
52
+ function updateDuration({ state, owners }) {
53
+ let destroyed = false;
54
+ const unsubscribe = combineLatest([state, owners]).subscribe(async ([currentState, currentOwners]) => {
55
+ if (!shouldUpdateDuration(currentState, currentOwners)) return;
56
+ const { mediaSource } = currentOwners;
57
+ await waitForSourceBuffersReady(currentOwners);
58
+ if (destroyed || mediaSource.readyState !== "open") return;
59
+ let duration = currentState.presentation.duration;
60
+ const maxBufferedEnd = getMaxBufferedEnd(currentOwners);
61
+ if (maxBufferedEnd > duration) duration = maxBufferedEnd;
62
+ mediaSource.duration = duration;
63
+ });
64
+ return () => {
65
+ destroyed = true;
66
+ unsubscribe();
67
+ };
68
+ }
69
+
70
+ //#endregion
71
+ export { updateDuration };
72
+ //# sourceMappingURL=update-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-duration.js","names":[],"sources":["../../../../src/dom/features/update-duration.ts"],"sourcesContent":["import { combineLatest } from '../../core/reactive/combine-latest';\nimport type { WritableState } from '../../core/state/create-state';\nimport type { Presentation } from '../../core/types';\nimport { hasPresentationDuration } from '../../core/types';\n\nexport interface DurationUpdateState {\n presentation?: Presentation;\n}\n\nexport interface DurationUpdateOwners {\n mediaSource?: MediaSource;\n videoSourceBuffer?: SourceBuffer;\n audioSourceBuffer?: SourceBuffer;\n}\n\n/**\n * Check if we can update MediaSource duration (have required data).\n */\nexport function canUpdateDuration(state: DurationUpdateState, owners: DurationUpdateOwners): boolean {\n return !!(owners.mediaSource && state.presentation && hasPresentationDuration(state.presentation));\n}\n\n/**\n * Get the maximum buffered end time across all SourceBuffers.\n */\nexport function getMaxBufferedEnd(owners: DurationUpdateOwners): number {\n let maxEnd = 0;\n\n const buffers = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter(\n (buf): buf is SourceBuffer => buf !== undefined\n );\n\n for (const buffer of buffers) {\n const { buffered } = buffer;\n if (buffered.length > 0) {\n const end = buffered.end(buffered.length - 1);\n if (end > maxEnd) {\n maxEnd = end;\n }\n }\n }\n\n return maxEnd;\n}\n\n/**\n * Check if we should update MediaSource duration (conditions met).\n */\nexport function shouldUpdateDuration(state: DurationUpdateState, owners: DurationUpdateOwners): boolean {\n if (!canUpdateDuration(state, owners)) return false;\n\n const { mediaSource } = owners;\n const { presentation } = state;\n\n // MediaSource must be open\n if (mediaSource!.readyState !== 'open') return false;\n\n const duration = presentation!.duration!;\n\n // Validate duration: finite, positive, not NaN\n if (!Number.isFinite(duration) || Number.isNaN(duration) || duration <= 0) return false;\n\n // Only set duration on initial MediaSource setup, when it hasn't been set yet.\n // A freshly opened MediaSource has duration === NaN. Once set (either by this\n // task or by endOfStreamTask from buffered.end()), we leave it alone — attempting\n // to re-sync a slight drift between mediaSource.duration and presentation.duration\n // races with concurrent appendBuffer() calls from loadSegmentsTask.\n return Number.isNaN(mediaSource!.duration);\n}\n\n/**\n * Wait for all currently-updating SourceBuffers to finish.\n *\n * The MSE spec forbids setting MediaSource.duration while any attached\n * SourceBuffer has updating === true. This defers until all are idle.\n */\nfunction waitForSourceBuffersReady(owners: DurationUpdateOwners): Promise<void> {\n const updating = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter(\n (buf): buf is SourceBuffer => buf?.updating === true\n );\n\n if (updating.length === 0) return Promise.resolve();\n\n return Promise.all(\n updating.map(\n (buf) => new Promise<void>((resolve) => buf.addEventListener('updateend', () => resolve(), { once: true }))\n )\n ).then(() => undefined);\n}\n\n/**\n * Update MediaSource duration when presentation duration becomes available.\n */\nexport function updateDuration({\n state,\n owners,\n}: {\n state: WritableState<DurationUpdateState>;\n owners: WritableState<DurationUpdateOwners>;\n}): () => void {\n let destroyed = false;\n\n const unsubscribe = combineLatest([state, owners]).subscribe(\n async ([currentState, currentOwners]: [DurationUpdateState, DurationUpdateOwners]) => {\n if (!shouldUpdateDuration(currentState, currentOwners)) return;\n\n const { mediaSource } = currentOwners;\n\n // MSE spec: duration cannot be set while any SourceBuffer is updating\n await waitForSourceBuffersReady(currentOwners);\n\n // Re-check after async wait: destroyed, or readyState changed (e.g. endOfStream\n // already called endOfStream(), transitioning 'open' → 'ended').\n if (destroyed || mediaSource!.readyState !== 'open') return;\n\n let duration = currentState.presentation!.duration!;\n\n // Get max buffered end time across all SourceBuffers\n const maxBufferedEnd = getMaxBufferedEnd(currentOwners);\n\n // MSE spec: duration cannot be less than any buffered range\n // If buffered ranges exceed calculated duration, extend to match\n if (maxBufferedEnd > duration) {\n duration = maxBufferedEnd;\n }\n\n // Set duration on MediaSource\n mediaSource!.duration = duration;\n }\n );\n\n return () => {\n destroyed = true;\n unsubscribe();\n };\n}\n"],"mappings":";;;;;;;AAkBA,SAAgB,kBAAkB,OAA4B,QAAuC;AACnG,QAAO,CAAC,EAAE,OAAO,eAAe,MAAM,gBAAgB,wBAAwB,MAAM,aAAa;;;;;AAMnG,SAAgB,kBAAkB,QAAsC;CACtE,IAAI,SAAS;CAEb,MAAM,UAAU,CAAC,OAAO,mBAAmB,OAAO,kBAAkB,CAAC,QAClE,QAA6B,QAAQ,OACvC;AAED,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,EAAE,aAAa;AACrB,MAAI,SAAS,SAAS,GAAG;GACvB,MAAM,MAAM,SAAS,IAAI,SAAS,SAAS,EAAE;AAC7C,OAAI,MAAM,OACR,UAAS;;;AAKf,QAAO;;;;;AAMT,SAAgB,qBAAqB,OAA4B,QAAuC;AACtG,KAAI,CAAC,kBAAkB,OAAO,OAAO,CAAE,QAAO;CAE9C,MAAM,EAAE,gBAAgB;CACxB,MAAM,EAAE,iBAAiB;AAGzB,KAAI,YAAa,eAAe,OAAQ,QAAO;CAE/C,MAAM,WAAW,aAAc;AAG/B,KAAI,CAAC,OAAO,SAAS,SAAS,IAAI,OAAO,MAAM,SAAS,IAAI,YAAY,EAAG,QAAO;AAOlF,QAAO,OAAO,MAAM,YAAa,SAAS;;;;;;;;AAS5C,SAAS,0BAA0B,QAA6C;CAC9E,MAAM,WAAW,CAAC,OAAO,mBAAmB,OAAO,kBAAkB,CAAC,QACnE,QAA6B,KAAK,aAAa,KACjD;AAED,KAAI,SAAS,WAAW,EAAG,QAAO,QAAQ,SAAS;AAEnD,QAAO,QAAQ,IACb,SAAS,KACN,QAAQ,IAAI,SAAe,YAAY,IAAI,iBAAiB,mBAAmB,SAAS,EAAE,EAAE,MAAM,MAAM,CAAC,CAAC,CAC5G,CACF,CAAC,WAAW,OAAU;;;;;AAMzB,SAAgB,eAAe,EAC7B,OACA,UAIa;CACb,IAAI,YAAY;CAEhB,MAAM,cAAc,cAAc,CAAC,OAAO,OAAO,CAAC,CAAC,UACjD,OAAO,CAAC,cAAc,mBAAgE;AACpF,MAAI,CAAC,qBAAqB,cAAc,cAAc,CAAE;EAExD,MAAM,EAAE,gBAAgB;AAGxB,QAAM,0BAA0B,cAAc;AAI9C,MAAI,aAAa,YAAa,eAAe,OAAQ;EAErD,IAAI,WAAW,aAAa,aAAc;EAG1C,MAAM,iBAAiB,kBAAkB,cAAc;AAIvD,MAAI,iBAAiB,SACnB,YAAW;AAIb,cAAa,WAAW;GAE3B;AAED,cAAa;AACX,cAAY;AACZ,eAAa"}
@@ -0,0 +1,24 @@
1
+ //#region src/dom/media/append-segment.d.ts
2
+ /**
3
+ * Segment appender helper.
4
+ *
5
+ * Appends media data (ArrayBuffer or AsyncIterable<Uint8Array> stream) to a
6
+ * SourceBuffer, waiting for `updateend` between calls so the browser can
7
+ * process each append before the next one arrives.
8
+ */
9
+ /** Data accepted by appendSegment — a full buffer or an async chunk stream. */
10
+ type AppendData = ArrayBuffer | AsyncIterable<Uint8Array>;
11
+ /**
12
+ * Append media data to a SourceBuffer.
13
+ *
14
+ * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of
15
+ * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`
16
+ * between each call so appends are serialized correctly.
17
+ *
18
+ * Errors from the SourceBuffer (`error` event) or from the iterable are
19
+ * propagated as rejections.
20
+ */
21
+ declare function appendSegment(sourceBuffer: SourceBuffer, data: AppendData, signal?: AbortSignal): Promise<void>;
22
+ //#endregion
23
+ export { AppendData, appendSegment };
24
+ //# sourceMappingURL=append-segment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-segment.d.ts","names":[],"sources":["../../../../src/dom/media/append-segment.ts"],"mappings":";;AASA;;;;;;;KAAY,UAAA,GAAa,WAAA,GAAc,aAAA,CAAc,UAAA;;;;;;AAYrD;;;;;iBAAsB,aAAA,CAAc,YAAA,EAAc,YAAA,EAAc,IAAA,EAAM,UAAA,EAAY,MAAA,GAAS,WAAA,GAAc,OAAA"}
@@ -0,0 +1,60 @@
1
+ //#region src/dom/media/append-segment.ts
2
+ /**
3
+ * Append media data to a SourceBuffer.
4
+ *
5
+ * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of
6
+ * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`
7
+ * between each call so appends are serialized correctly.
8
+ *
9
+ * Errors from the SourceBuffer (`error` event) or from the iterable are
10
+ * propagated as rejections.
11
+ */
12
+ async function appendSegment(sourceBuffer, data, signal) {
13
+ if (data instanceof ArrayBuffer) await appendChunk(sourceBuffer, data);
14
+ else try {
15
+ for await (const chunk of data) {
16
+ if (signal?.aborted) throw signal.reason ?? new DOMException("Aborted", "AbortError");
17
+ await appendChunk(sourceBuffer, chunk);
18
+ }
19
+ } catch (e) {
20
+ if (e instanceof DOMException && e.name === "AbortError" && !sourceBuffer.updating) try {
21
+ sourceBuffer.abort();
22
+ } catch {}
23
+ throw e;
24
+ }
25
+ }
26
+ async function appendChunk(sourceBuffer, data) {
27
+ if (sourceBuffer.updating) await new Promise((resolve) => {
28
+ const onUpdateEnd = () => {
29
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
30
+ resolve();
31
+ };
32
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
33
+ });
34
+ return new Promise((resolve, reject) => {
35
+ const onUpdateEnd = () => {
36
+ cleanup();
37
+ resolve();
38
+ };
39
+ const onError = (event) => {
40
+ cleanup();
41
+ reject(/* @__PURE__ */ new Error(`SourceBuffer append error: ${event.type}`));
42
+ };
43
+ const cleanup = () => {
44
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
45
+ sourceBuffer.removeEventListener("error", onError);
46
+ };
47
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
48
+ sourceBuffer.addEventListener("error", onError);
49
+ try {
50
+ sourceBuffer.appendBuffer(data);
51
+ } catch (error) {
52
+ cleanup();
53
+ reject(error);
54
+ }
55
+ });
56
+ }
57
+
58
+ //#endregion
59
+ export { appendSegment };
60
+ //# sourceMappingURL=append-segment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-segment.js","names":[],"sources":["../../../../src/dom/media/append-segment.ts"],"sourcesContent":["/**\n * Segment appender helper.\n *\n * Appends media data (ArrayBuffer or AsyncIterable<Uint8Array> stream) to a\n * SourceBuffer, waiting for `updateend` between calls so the browser can\n * process each append before the next one arrives.\n */\n\n/** Data accepted by appendSegment — a full buffer or an async chunk stream. */\nexport type AppendData = ArrayBuffer | AsyncIterable<Uint8Array>;\n\n/**\n * Append media data to a SourceBuffer.\n *\n * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of\n * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`\n * between each call so appends are serialized correctly.\n *\n * Errors from the SourceBuffer (`error` event) or from the iterable are\n * propagated as rejections.\n */\nexport async function appendSegment(sourceBuffer: SourceBuffer, data: AppendData, signal?: AbortSignal): Promise<void> {\n if (data instanceof ArrayBuffer) {\n await appendChunk(sourceBuffer, data);\n } else {\n try {\n for await (const chunk of data) {\n // Check between chunks so an abort can stop streaming before the next\n // appendBuffer call. The current chunk (if any) has already landed in the\n // SourceBuffer; the partial: true flag in the actor model reflects this.\n if (signal?.aborted) throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n await appendChunk(sourceBuffer, chunk);\n }\n } catch (e) {\n // Reset the MSE segment parser on any abort to discard partial fMP4 box\n // data from the SourceBuffer's internal byte buffer. Two paths reach here:\n //\n // 1. Explicit abort check above (signal.aborted between chunks) — the\n // last appended chunk may have left the parser mid-fragment.\n //\n // 2. The fetch was cancelled (signal fired), causing the underlying\n // ReadableStream to error and the for-await loop to throw directly\n // without ever passing through the signal check above. Same cleanup\n // is needed: partial box data must be cleared before the next append.\n //\n // Without this, the next appendBuffer call sees stale partial data and\n // Chrome throws CHUNK_DEMUXER_ERROR_APPEND_FAILED.\n if (e instanceof DOMException && e.name === 'AbortError' && !sourceBuffer.updating) {\n try {\n sourceBuffer.abort();\n } catch {\n // Thrown if the MediaSource is not \"open\" (e.g. during teardown).\n }\n }\n throw e;\n }\n }\n}\n\nasync function appendChunk(sourceBuffer: SourceBuffer, data: ArrayBuffer | Uint8Array<ArrayBufferLike>): Promise<void> {\n if (sourceBuffer.updating) {\n await new Promise<void>((resolve) => {\n const onUpdateEnd = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n resolve();\n };\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n });\n }\n\n return new Promise<void>((resolve, reject) => {\n const onUpdateEnd = () => {\n cleanup();\n resolve();\n };\n\n const onError = (event: Event) => {\n cleanup();\n reject(new Error(`SourceBuffer append error: ${event.type}`));\n };\n\n const cleanup = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n sourceBuffer.removeEventListener('error', onError);\n };\n\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n sourceBuffer.addEventListener('error', onError);\n\n try {\n sourceBuffer.appendBuffer(data as ArrayBuffer);\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;AAqBA,eAAsB,cAAc,cAA4B,MAAkB,QAAqC;AACrH,KAAI,gBAAgB,YAClB,OAAM,YAAY,cAAc,KAAK;KAErC,KAAI;AACF,aAAW,MAAM,SAAS,MAAM;AAI9B,OAAI,QAAQ,QAAS,OAAM,OAAO,UAAU,IAAI,aAAa,WAAW,aAAa;AACrF,SAAM,YAAY,cAAc,MAAM;;UAEjC,GAAG;AAcV,MAAI,aAAa,gBAAgB,EAAE,SAAS,gBAAgB,CAAC,aAAa,SACxE,KAAI;AACF,gBAAa,OAAO;UACd;AAIV,QAAM;;;AAKZ,eAAe,YAAY,cAA4B,MAAgE;AACrH,KAAI,aAAa,SACf,OAAM,IAAI,SAAe,YAAY;EACnC,MAAM,oBAAoB;AACxB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,YAAS;;AAEX,eAAa,iBAAiB,aAAa,YAAY;GACvD;AAGJ,QAAO,IAAI,SAAe,SAAS,WAAW;EAC5C,MAAM,oBAAoB;AACxB,YAAS;AACT,YAAS;;EAGX,MAAM,WAAW,UAAiB;AAChC,YAAS;AACT,0BAAO,IAAI,MAAM,8BAA8B,MAAM,OAAO,CAAC;;EAG/D,MAAM,gBAAgB;AACpB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,gBAAa,oBAAoB,SAAS,QAAQ;;AAGpD,eAAa,iBAAiB,aAAa,YAAY;AACvD,eAAa,iBAAiB,SAAS,QAAQ;AAE/C,MAAI;AACF,gBAAa,aAAa,KAAoB;WACvC,OAAO;AACd,YAAS;AACT,UAAO,MAAM;;GAEf"}
@@ -0,0 +1,24 @@
1
+ //#region src/dom/media/buffer-flusher.d.ts
2
+ /**
3
+ * Buffer flusher helper (P12)
4
+ *
5
+ * Removes a time range from a SourceBuffer to manage memory.
6
+ */
7
+ /**
8
+ * Remove a time range from a SourceBuffer.
9
+ *
10
+ * Waits for the SourceBuffer to be ready (not updating), then removes
11
+ * the specified range. Returns a promise that resolves when removal completes.
12
+ *
13
+ * @param sourceBuffer - The SourceBuffer to remove data from
14
+ * @param start - Start of the time range to remove (seconds)
15
+ * @param end - End of the time range to remove (seconds)
16
+ * @returns Promise that resolves when removal completes
17
+ *
18
+ * @example
19
+ * await flushBuffer(videoSourceBuffer, 0, 30);
20
+ */
21
+ declare function flushBuffer(sourceBuffer: SourceBuffer, start: number, end: number): Promise<void>;
22
+ //#endregion
23
+ export { flushBuffer };
24
+ //# sourceMappingURL=buffer-flusher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer-flusher.d.ts","names":[],"sources":["../../../../src/dom/media/buffer-flusher.ts"],"mappings":";;AAoBA;;;;;;;;;;;;;;;;;;iBAAsB,WAAA,CAAY,YAAA,EAAc,YAAA,EAAc,KAAA,UAAe,GAAA,WAAc,OAAA"}
@@ -0,0 +1,55 @@
1
+ //#region src/dom/media/buffer-flusher.ts
2
+ /**
3
+ * Buffer flusher helper (P12)
4
+ *
5
+ * Removes a time range from a SourceBuffer to manage memory.
6
+ */
7
+ /**
8
+ * Remove a time range from a SourceBuffer.
9
+ *
10
+ * Waits for the SourceBuffer to be ready (not updating), then removes
11
+ * the specified range. Returns a promise that resolves when removal completes.
12
+ *
13
+ * @param sourceBuffer - The SourceBuffer to remove data from
14
+ * @param start - Start of the time range to remove (seconds)
15
+ * @param end - End of the time range to remove (seconds)
16
+ * @returns Promise that resolves when removal completes
17
+ *
18
+ * @example
19
+ * await flushBuffer(videoSourceBuffer, 0, 30);
20
+ */
21
+ async function flushBuffer(sourceBuffer, start, end) {
22
+ if (sourceBuffer.updating) await new Promise((resolve) => {
23
+ const onUpdateEnd = () => {
24
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
25
+ resolve();
26
+ };
27
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
28
+ });
29
+ return new Promise((resolve, reject) => {
30
+ const onUpdateEnd = () => {
31
+ cleanup();
32
+ resolve();
33
+ };
34
+ const onError = (event) => {
35
+ cleanup();
36
+ reject(/* @__PURE__ */ new Error(`SourceBuffer remove error: ${event.type}`));
37
+ };
38
+ const cleanup = () => {
39
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
40
+ sourceBuffer.removeEventListener("error", onError);
41
+ };
42
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
43
+ sourceBuffer.addEventListener("error", onError);
44
+ try {
45
+ sourceBuffer.remove(start, end);
46
+ } catch (error) {
47
+ cleanup();
48
+ reject(error);
49
+ }
50
+ });
51
+ }
52
+
53
+ //#endregion
54
+ export { flushBuffer };
55
+ //# sourceMappingURL=buffer-flusher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer-flusher.js","names":[],"sources":["../../../../src/dom/media/buffer-flusher.ts"],"sourcesContent":["/**\n * Buffer flusher helper (P12)\n *\n * Removes a time range from a SourceBuffer to manage memory.\n */\n\n/**\n * Remove a time range from a SourceBuffer.\n *\n * Waits for the SourceBuffer to be ready (not updating), then removes\n * the specified range. Returns a promise that resolves when removal completes.\n *\n * @param sourceBuffer - The SourceBuffer to remove data from\n * @param start - Start of the time range to remove (seconds)\n * @param end - End of the time range to remove (seconds)\n * @returns Promise that resolves when removal completes\n *\n * @example\n * await flushBuffer(videoSourceBuffer, 0, 30);\n */\nexport async function flushBuffer(sourceBuffer: SourceBuffer, start: number, end: number): Promise<void> {\n // Wait for SourceBuffer to be ready (not currently updating)\n if (sourceBuffer.updating) {\n await new Promise<void>((resolve) => {\n const onUpdateEnd = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n resolve();\n };\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n });\n }\n\n return new Promise<void>((resolve, reject) => {\n const onUpdateEnd = () => {\n cleanup();\n resolve();\n };\n\n const onError = (event: Event) => {\n cleanup();\n reject(new Error(`SourceBuffer remove error: ${event.type}`));\n };\n\n const cleanup = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n sourceBuffer.removeEventListener('error', onError);\n };\n\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n sourceBuffer.addEventListener('error', onError);\n\n try {\n sourceBuffer.remove(start, end);\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,eAAsB,YAAY,cAA4B,OAAe,KAA4B;AAEvG,KAAI,aAAa,SACf,OAAM,IAAI,SAAe,YAAY;EACnC,MAAM,oBAAoB;AACxB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,YAAS;;AAEX,eAAa,iBAAiB,aAAa,YAAY;GACvD;AAGJ,QAAO,IAAI,SAAe,SAAS,WAAW;EAC5C,MAAM,oBAAoB;AACxB,YAAS;AACT,YAAS;;EAGX,MAAM,WAAW,UAAiB;AAChC,YAAS;AACT,0BAAO,IAAI,MAAM,8BAA8B,MAAM,OAAO,CAAC;;EAG/D,MAAM,gBAAgB;AACpB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,gBAAa,oBAAoB,SAAS,QAAQ;;AAGpD,eAAa,iBAAiB,aAAa,YAAY;AACvD,eAAa,iBAAiB,SAAS,QAAQ;AAE/C,MAAI;AACF,gBAAa,OAAO,OAAO,IAAI;WACxB,OAAO;AACd,YAAS;AACT,UAAO,MAAM;;GAEf"}