@videojs/spf 10.0.0-beta.3 → 10.0.0-beta.31
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.
- package/README.md +19 -38
- package/dist/default/core/actors/create-machine-actor.js +91 -0
- package/dist/default/core/actors/create-machine-actor.js.map +1 -0
- package/dist/default/core/actors/create-transition-actor.js +46 -0
- package/dist/default/core/actors/create-transition-actor.js.map +1 -0
- package/dist/default/core/composition/create-composition.js +85 -0
- package/dist/default/core/composition/create-composition.js.map +1 -0
- package/dist/default/core/composition/share-signals.js +39 -0
- package/dist/default/core/composition/share-signals.js.map +1 -0
- package/dist/default/core/machine.js +26 -0
- package/dist/default/core/machine.js.map +1 -0
- package/dist/default/core/reactors/create-machine-reactor.js +83 -0
- package/dist/default/core/reactors/create-machine-reactor.js.map +1 -0
- package/dist/default/core/signals/effect.js +41 -0
- package/dist/default/core/signals/effect.js.map +1 -0
- package/dist/default/core/signals/primitives.js +54 -0
- package/dist/default/core/signals/primitives.js.map +1 -0
- package/dist/default/core/signals/when.js +38 -0
- package/dist/default/core/signals/when.js.map +1 -0
- package/dist/default/core/tasks/delayed-reschedule.js +31 -0
- package/dist/default/core/tasks/delayed-reschedule.js.map +1 -0
- package/dist/default/core/tasks/task.js +321 -0
- package/dist/default/core/tasks/task.js.map +1 -0
- package/dist/default/dom.js +9 -0
- package/dist/default/hls-audio.js +3 -0
- package/dist/default/hls-background-video.js +3 -0
- package/dist/default/hls-video.js +4 -0
- package/dist/default/hls.js +8 -0
- package/dist/default/index.js +23 -0
- package/dist/default/index.js.map +1 -0
- package/dist/default/media/abr/quality-selection.js +21 -0
- package/dist/default/media/abr/quality-selection.js.map +1 -0
- package/dist/default/media/buffer/back-buffer.js +46 -0
- package/dist/default/media/buffer/back-buffer.js.map +1 -0
- package/dist/default/media/buffer/forward-buffer.js +119 -0
- package/dist/default/media/buffer/forward-buffer.js.map +1 -0
- package/dist/default/media/dom/capabilities.js +72 -0
- package/dist/default/media/dom/capabilities.js.map +1 -0
- package/dist/default/media/dom/mse/append-segment.js +60 -0
- package/dist/default/media/dom/mse/append-segment.js.map +1 -0
- package/dist/default/media/dom/mse/buffer-flusher.js +55 -0
- package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -0
- package/dist/default/media/dom/mse/duration.js +95 -0
- package/dist/default/media/dom/mse/duration.js.map +1 -0
- package/dist/default/media/dom/mse/end-of-stream.js +20 -0
- package/dist/default/media/dom/mse/end-of-stream.js.map +1 -0
- package/dist/default/media/dom/mse/mediasource-setup.js +242 -0
- package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -0
- package/dist/default/media/dom/screen.js +112 -0
- package/dist/default/media/dom/screen.js.map +1 -0
- package/dist/default/media/dom/text/resolve-vtt-segment.js +50 -0
- package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -0
- package/dist/default/media/dom/text/text-track-slots.js +69 -0
- package/dist/default/media/dom/text/text-track-slots.js.map +1 -0
- package/dist/default/media/errors.js +69 -0
- package/dist/default/media/errors.js.map +1 -0
- package/dist/default/media/hls/parse-attributes.js +148 -0
- package/dist/default/media/hls/parse-attributes.js.map +1 -0
- package/dist/default/media/hls/parse-media-playlist.js +261 -0
- package/dist/default/media/hls/parse-media-playlist.js.map +1 -0
- package/dist/default/media/hls/parse-multivariant.js +234 -0
- package/dist/default/media/hls/parse-multivariant.js.map +1 -0
- package/dist/default/media/hls/reload-policy.js +71 -0
- package/dist/default/media/hls/reload-policy.js.map +1 -0
- package/dist/default/media/hls/resolve-url.js +11 -0
- package/dist/default/media/hls/resolve-url.js.map +1 -0
- package/dist/default/media/live-window.js +36 -0
- package/dist/default/media/live-window.js.map +1 -0
- package/dist/default/media/media-tracks/media-tracks.js +101 -0
- package/dist/default/media/media-tracks/media-tracks.js.map +1 -0
- package/dist/default/media/mp4/box.js +58 -0
- package/dist/default/media/mp4/box.js.map +1 -0
- package/dist/default/media/mp4/timestamp-origin.js +107 -0
- package/dist/default/media/mp4/timestamp-origin.js.map +1 -0
- package/dist/default/media/primitives/resolution.js +29 -0
- package/dist/default/media/primitives/resolution.js.map +1 -0
- package/dist/default/media/primitives/select-tracks.js +107 -0
- package/dist/default/media/primitives/select-tracks.js.map +1 -0
- package/dist/default/media/text/parse-vtt-timestamp-map.js +42 -0
- package/dist/default/media/text/parse-vtt-timestamp-map.js.map +1 -0
- package/dist/default/media/text/resolve-vtt-metadata.js +23 -0
- package/dist/default/media/text/resolve-vtt-metadata.js.map +1 -0
- package/dist/default/media/types/index.js +50 -0
- package/dist/default/media/types/index.js.map +1 -0
- package/dist/default/media/utils/cdn.js +56 -0
- package/dist/default/media/utils/cdn.js.map +1 -0
- package/dist/default/media/utils/preload.js +22 -0
- package/dist/default/media/utils/preload.js.map +1 -0
- package/dist/default/media/utils/track-selection.js +47 -0
- package/dist/default/media/utils/track-selection.js.map +1 -0
- package/dist/default/media/utils/tracks.js +144 -0
- package/dist/default/media/utils/tracks.js.map +1 -0
- package/dist/default/media-tracks.js +2 -0
- package/dist/default/mux-audio.js +3 -0
- package/dist/default/mux-background-video.js +3 -0
- package/dist/default/mux-video.js +3 -0
- package/dist/default/network/bandwidth-estimator.js +96 -0
- package/dist/default/network/bandwidth-estimator.js.map +1 -0
- package/dist/default/network/chunked-stream-iterable.js +49 -0
- package/dist/default/network/chunked-stream-iterable.js.map +1 -0
- package/dist/default/network/ewma.js +71 -0
- package/dist/default/network/ewma.js.map +1 -0
- package/dist/default/network/fetch.js +115 -0
- package/dist/default/network/fetch.js.map +1 -0
- package/dist/default/playback/actors/dom/segment-loader.js +249 -0
- package/dist/default/playback/actors/dom/segment-loader.js.map +1 -0
- package/dist/default/playback/actors/dom/source-buffer.js +139 -0
- package/dist/default/playback/actors/dom/source-buffer.js.map +1 -0
- package/dist/default/playback/actors/dom/text-tracks.js +46 -0
- package/dist/default/playback/actors/dom/text-tracks.js.map +1 -0
- package/dist/default/playback/actors/text-track-segment-loader.js +149 -0
- package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -0
- package/dist/default/playback/adapters/hls-audio/adapter.js +186 -0
- package/dist/default/playback/adapters/hls-audio/adapter.js.map +1 -0
- package/dist/default/playback/adapters/hls-audio/media.js +9 -0
- package/dist/default/playback/adapters/hls-audio/media.js.map +1 -0
- package/dist/default/playback/adapters/hls-background-video/adapter.js +211 -0
- package/dist/default/playback/adapters/hls-background-video/adapter.js.map +1 -0
- package/dist/default/playback/adapters/hls-background-video/host.js +60 -0
- package/dist/default/playback/adapters/hls-background-video/host.js.map +1 -0
- package/dist/default/playback/adapters/hls-background-video/media.js +21 -0
- package/dist/default/playback/adapters/hls-background-video/media.js.map +1 -0
- package/dist/default/playback/adapters/hls-video/adapter.js +316 -0
- package/dist/default/playback/adapters/hls-video/adapter.js.map +1 -0
- package/dist/default/playback/adapters/hls-video/error-surface.js +51 -0
- package/dist/default/playback/adapters/hls-video/error-surface.js.map +1 -0
- package/dist/default/playback/adapters/hls-video/media-tracks.js +126 -0
- package/dist/default/playback/adapters/hls-video/media-tracks.js.map +1 -0
- package/dist/default/playback/adapters/hls-video/media.js +11 -0
- package/dist/default/playback/adapters/hls-video/media.js.map +1 -0
- package/dist/default/playback/adapters/mux-audio/media.js +26 -0
- package/dist/default/playback/adapters/mux-audio/media.js.map +1 -0
- package/dist/default/playback/adapters/mux-video/adapter.js +105 -0
- package/dist/default/playback/adapters/mux-video/adapter.js.map +1 -0
- package/dist/default/playback/adapters/mux-video/media.js +23 -0
- package/dist/default/playback/adapters/mux-video/media.js.map +1 -0
- package/dist/default/playback/behaviors/calculate-presentation-duration.js +36 -0
- package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -0
- package/dist/default/playback/behaviors/collect-errors.js +111 -0
- package/dist/default/playback/behaviors/collect-errors.js.map +1 -0
- package/dist/default/playback/behaviors/derive-cdn-priority.js +68 -0
- package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -0
- package/dist/default/playback/behaviors/dom/airplay.js +211 -0
- package/dist/default/playback/behaviors/dom/airplay.js.map +1 -0
- package/dist/default/playback/behaviors/dom/apply-start-position.js +93 -0
- package/dist/default/playback/behaviors/dom/apply-start-position.js.map +1 -0
- package/dist/default/playback/behaviors/dom/end-of-stream.js +102 -0
- package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -0
- package/dist/default/playback/behaviors/dom/load-segments.js +181 -0
- package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -0
- package/dist/default/playback/behaviors/dom/recover-end-stall.js +77 -0
- package/dist/default/playback/behaviors/dom/recover-end-stall.js.map +1 -0
- package/dist/default/playback/behaviors/dom/seek-to-live-edge.js +132 -0
- package/dist/default/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +205 -0
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-mediasource.js +114 -0
- package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +40 -0
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
- package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js +26 -0
- package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js +124 -0
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-current-time.js +52 -0
- package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-load-triggers.js +94 -0
- package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-playback-rate.js +37 -0
- package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-player-resolution.js +57 -0
- package/dist/default/playback/behaviors/dom/track-player-resolution.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-screen-resolution.js +29 -0
- package/dist/default/playback/behaviors/dom/track-screen-resolution.js.map +1 -0
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +113 -0
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
- package/dist/default/playback/behaviors/establish-start-media-time.js +197 -0
- package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -0
- package/dist/default/playback/behaviors/resolve-presentation.js +85 -0
- package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -0
- package/dist/default/playback/behaviors/resolve-track.js +141 -0
- package/dist/default/playback/behaviors/resolve-track.js.map +1 -0
- package/dist/default/playback/behaviors/select-tracks.js +216 -0
- package/dist/default/playback/behaviors/select-tracks.js.map +1 -0
- package/dist/default/playback/behaviors/setup-failover-monitor.js +66 -0
- package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -0
- package/dist/default/playback/behaviors/sync-preload.js +59 -0
- package/dist/default/playback/behaviors/sync-preload.js.map +1 -0
- package/dist/default/playback/behaviors/track-switching.js +391 -0
- package/dist/default/playback/behaviors/track-switching.js.map +1 -0
- package/dist/default/playback/engines/hls/engine-audio-only.js +98 -0
- package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -0
- package/dist/default/playback/engines/hls/engine-background-video.js +92 -0
- package/dist/default/playback/engines/hls/engine-background-video.js.map +1 -0
- package/dist/default/playback/engines/hls/engine.js +147 -0
- package/dist/default/playback/engines/hls/engine.js.map +1 -0
- package/dist/default/playback/primitives/error-messages.js +51 -0
- package/dist/default/playback/primitives/error-messages.js.map +1 -0
- package/dist/default/playback/primitives/failover-fetch.js +42 -0
- package/dist/default/playback/primitives/failover-fetch.js.map +1 -0
- package/dist/default/playback/primitives/head-peek.js +44 -0
- package/dist/default/playback/primitives/head-peek.js.map +1 -0
- package/dist/default/playback/primitives/live-window.js +66 -0
- package/dist/default/playback/primitives/live-window.js.map +1 -0
- package/dist/default/playback/primitives/relocation-pipelines.js +203 -0
- package/dist/default/playback/primitives/relocation-pipelines.js.map +1 -0
- package/dist/default/playback/primitives/report-track-conditions.js +86 -0
- package/dist/default/playback/primitives/report-track-conditions.js.map +1 -0
- package/dist/default/playback/primitives/segment-load-pipeline.js +96 -0
- package/dist/default/playback/primitives/segment-load-pipeline.js.map +1 -0
- package/dist/default/playback/primitives/selection-rules.js +86 -0
- package/dist/default/playback/primitives/selection-rules.js.map +1 -0
- package/dist/default/playback/primitives/text-segment-load-pipeline.js +37 -0
- package/dist/default/playback/primitives/text-segment-load-pipeline.js.map +1 -0
- package/dist/default/playback/primitives/track-types.js +61 -0
- package/dist/default/playback/primitives/track-types.js.map +1 -0
- package/dist/dev/core/actors/actor.d.ts +26 -0
- package/dist/dev/core/actors/actor.d.ts.map +1 -0
- package/dist/dev/core/actors/create-machine-actor.d.ts +128 -0
- package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -0
- package/dist/dev/core/actors/create-machine-actor.js +91 -0
- package/dist/dev/core/actors/create-machine-actor.js.map +1 -0
- package/dist/dev/core/actors/create-transition-actor.d.ts +41 -0
- package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -0
- package/dist/dev/core/actors/create-transition-actor.js +46 -0
- package/dist/dev/core/actors/create-transition-actor.js.map +1 -0
- package/dist/dev/core/composition/create-composition.d.ts +273 -0
- package/dist/dev/core/composition/create-composition.d.ts.map +1 -0
- package/dist/dev/core/composition/create-composition.js +85 -0
- package/dist/dev/core/composition/create-composition.js.map +1 -0
- package/dist/dev/core/composition/share-signals.d.ts +46 -0
- package/dist/dev/core/composition/share-signals.d.ts.map +1 -0
- package/dist/dev/core/composition/share-signals.js +39 -0
- package/dist/dev/core/composition/share-signals.js.map +1 -0
- package/dist/dev/core/machine.d.ts +21 -0
- package/dist/dev/core/machine.d.ts.map +1 -0
- package/dist/dev/core/machine.js +26 -0
- package/dist/dev/core/machine.js.map +1 -0
- package/dist/dev/core/reactors/create-machine-reactor.d.ts +93 -0
- package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -0
- package/dist/dev/core/reactors/create-machine-reactor.js +83 -0
- package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -0
- package/dist/dev/core/signals/effect.d.ts +15 -0
- package/dist/dev/core/signals/effect.d.ts.map +1 -0
- package/dist/dev/core/signals/effect.js +41 -0
- package/dist/dev/core/signals/effect.js.map +1 -0
- package/dist/dev/core/signals/primitives.d.ts +46 -0
- package/dist/dev/core/signals/primitives.d.ts.map +1 -0
- package/dist/dev/core/signals/primitives.js +54 -0
- package/dist/dev/core/signals/primitives.js.map +1 -0
- package/dist/dev/core/signals/when.js +38 -0
- package/dist/dev/core/signals/when.js.map +1 -0
- package/dist/dev/core/tasks/delayed-reschedule.js +31 -0
- package/dist/dev/core/tasks/delayed-reschedule.js.map +1 -0
- package/dist/dev/core/tasks/task.d.ts +166 -0
- package/dist/dev/core/tasks/task.d.ts.map +1 -0
- package/dist/dev/core/tasks/task.js +321 -0
- package/dist/dev/core/tasks/task.js.map +1 -0
- package/dist/dev/dom.d.ts +9 -0
- package/dist/dev/dom.js +9 -0
- package/dist/dev/hls-audio.d.ts +3 -0
- package/dist/dev/hls-audio.js +3 -0
- package/dist/dev/hls-background-video.d.ts +4 -0
- package/dist/dev/hls-background-video.js +3 -0
- package/dist/dev/hls-video.d.ts +5 -0
- package/dist/dev/hls-video.js +4 -0
- package/dist/dev/hls.d.ts +9 -0
- package/dist/dev/hls.js +8 -0
- package/dist/dev/index.d.ts +24 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +23 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/media/abr/quality-selection.d.ts +22 -0
- package/dist/dev/media/abr/quality-selection.d.ts.map +1 -0
- package/dist/dev/media/abr/quality-selection.js +21 -0
- package/dist/dev/media/abr/quality-selection.js.map +1 -0
- package/dist/dev/media/buffer/back-buffer.d.ts +14 -0
- package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -0
- package/dist/dev/media/buffer/back-buffer.js +46 -0
- package/dist/dev/media/buffer/back-buffer.js.map +1 -0
- package/dist/dev/media/buffer/forward-buffer.d.ts +14 -0
- package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -0
- package/dist/dev/media/buffer/forward-buffer.js +119 -0
- package/dist/dev/media/buffer/forward-buffer.js.map +1 -0
- package/dist/dev/media/dom/capabilities.js +72 -0
- package/dist/dev/media/dom/capabilities.js.map +1 -0
- package/dist/dev/media/dom/mse/append-segment.d.ts +18 -0
- package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -0
- package/dist/dev/media/dom/mse/append-segment.js +60 -0
- package/dist/dev/media/dom/mse/append-segment.js.map +1 -0
- package/dist/dev/media/dom/mse/buffer-flusher.d.ts +24 -0
- package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -0
- package/dist/dev/media/dom/mse/buffer-flusher.js +55 -0
- package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -0
- package/dist/dev/media/dom/mse/duration.js +95 -0
- package/dist/dev/media/dom/mse/duration.js.map +1 -0
- package/dist/dev/media/dom/mse/end-of-stream.js +20 -0
- package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -0
- package/dist/dev/media/dom/mse/mediasource-setup.js +242 -0
- package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -0
- package/dist/dev/media/dom/screen.d.ts +7 -0
- package/dist/dev/media/dom/screen.d.ts.map +1 -0
- package/dist/dev/media/dom/screen.js +112 -0
- package/dist/dev/media/dom/screen.js.map +1 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +6 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.js +50 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -0
- package/dist/dev/media/dom/text/text-track-slots.d.ts +30 -0
- package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -0
- package/dist/dev/media/dom/text/text-track-slots.js +69 -0
- package/dist/dev/media/dom/text/text-track-slots.js.map +1 -0
- package/dist/dev/media/errors.d.ts +82 -0
- package/dist/dev/media/errors.d.ts.map +1 -0
- package/dist/dev/media/errors.js +69 -0
- package/dist/dev/media/errors.js.map +1 -0
- package/dist/dev/media/hls/parse-attributes.js +148 -0
- package/dist/dev/media/hls/parse-attributes.js.map +1 -0
- package/dist/dev/media/hls/parse-media-playlist.js +261 -0
- package/dist/dev/media/hls/parse-media-playlist.js.map +1 -0
- package/dist/dev/media/hls/parse-multivariant.js +234 -0
- package/dist/dev/media/hls/parse-multivariant.js.map +1 -0
- package/dist/dev/media/hls/reload-policy.js +71 -0
- package/dist/dev/media/hls/reload-policy.js.map +1 -0
- package/dist/dev/media/hls/resolve-url.js +11 -0
- package/dist/dev/media/hls/resolve-url.js.map +1 -0
- package/dist/dev/media/live-window.js +36 -0
- package/dist/dev/media/live-window.js.map +1 -0
- package/dist/dev/media/media-tracks/media-tracks.d.ts +51 -0
- package/dist/dev/media/media-tracks/media-tracks.d.ts.map +1 -0
- package/dist/dev/media/media-tracks/media-tracks.js +101 -0
- package/dist/dev/media/media-tracks/media-tracks.js.map +1 -0
- package/dist/dev/media/mp4/box.js +58 -0
- package/dist/dev/media/mp4/box.js.map +1 -0
- package/dist/dev/media/mp4/timestamp-origin.js +107 -0
- package/dist/dev/media/mp4/timestamp-origin.js.map +1 -0
- package/dist/dev/media/primitives/resolution.d.ts +24 -0
- package/dist/dev/media/primitives/resolution.d.ts.map +1 -0
- package/dist/dev/media/primitives/resolution.js +29 -0
- package/dist/dev/media/primitives/resolution.js.map +1 -0
- package/dist/dev/media/primitives/select-tracks.js +107 -0
- package/dist/dev/media/primitives/select-tracks.js.map +1 -0
- package/dist/dev/media/text/parse-vtt-timestamp-map.js +42 -0
- package/dist/dev/media/text/parse-vtt-timestamp-map.js.map +1 -0
- package/dist/dev/media/text/resolve-vtt-metadata.js +23 -0
- package/dist/dev/media/text/resolve-vtt-metadata.js.map +1 -0
- package/dist/dev/media/types/index.d.ts +377 -0
- package/dist/dev/media/types/index.d.ts.map +1 -0
- package/dist/dev/media/types/index.js +50 -0
- package/dist/dev/media/types/index.js.map +1 -0
- package/dist/dev/media/utils/cdn.d.ts +13 -0
- package/dist/dev/media/utils/cdn.d.ts.map +1 -0
- package/dist/dev/media/utils/cdn.js +56 -0
- package/dist/dev/media/utils/cdn.js.map +1 -0
- package/dist/dev/media/utils/preload.js +22 -0
- package/dist/dev/media/utils/preload.js.map +1 -0
- package/dist/dev/media/utils/track-selection.js +47 -0
- package/dist/dev/media/utils/track-selection.js.map +1 -0
- package/dist/dev/media/utils/tracks.js +144 -0
- package/dist/dev/media/utils/tracks.js.map +1 -0
- package/dist/dev/media-tracks.d.ts +3 -0
- package/dist/dev/media-tracks.js +2 -0
- package/dist/dev/mux-audio.d.ts +4 -0
- package/dist/dev/mux-audio.js +3 -0
- package/dist/dev/mux-background-video.d.ts +4 -0
- package/dist/dev/mux-background-video.js +3 -0
- package/dist/dev/mux-video.d.ts +4 -0
- package/dist/dev/mux-video.js +3 -0
- package/dist/dev/network/bandwidth-estimator.d.ts +50 -0
- package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -0
- package/dist/dev/network/bandwidth-estimator.js +96 -0
- package/dist/dev/network/bandwidth-estimator.js.map +1 -0
- package/dist/dev/network/chunked-stream-iterable.js +49 -0
- package/dist/dev/network/chunked-stream-iterable.js.map +1 -0
- package/dist/dev/network/ewma.js +71 -0
- package/dist/dev/network/ewma.js.map +1 -0
- package/dist/dev/network/fetch.js +115 -0
- package/dist/dev/network/fetch.js.map +1 -0
- package/dist/dev/playback/actors/dom/segment-loader.d.ts +48 -0
- package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -0
- package/dist/dev/playback/actors/dom/segment-loader.js +249 -0
- package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -0
- package/dist/dev/playback/actors/dom/source-buffer.d.ts +47 -0
- package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -0
- package/dist/dev/playback/actors/dom/source-buffer.js +139 -0
- package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -0
- package/dist/dev/playback/actors/dom/text-tracks.d.ts +3 -0
- package/dist/dev/playback/actors/dom/text-tracks.js +46 -0
- package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -0
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts +54 -0
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -0
- package/dist/dev/playback/actors/text-track-segment-loader.js +149 -0
- package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -0
- package/dist/dev/playback/actors/text-tracks.d.ts +33 -0
- package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-audio/adapter.d.ts +54 -0
- package/dist/dev/playback/adapters/hls-audio/adapter.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-audio/adapter.js +186 -0
- package/dist/dev/playback/adapters/hls-audio/adapter.js.map +1 -0
- package/dist/dev/playback/adapters/hls-audio/media.d.ts +10 -0
- package/dist/dev/playback/adapters/hls-audio/media.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-audio/media.js +9 -0
- package/dist/dev/playback/adapters/hls-audio/media.js.map +1 -0
- package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts +77 -0
- package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-background-video/adapter.js +211 -0
- package/dist/dev/playback/adapters/hls-background-video/adapter.js.map +1 -0
- package/dist/dev/playback/adapters/hls-background-video/host.d.ts +38 -0
- package/dist/dev/playback/adapters/hls-background-video/host.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-background-video/host.js +60 -0
- package/dist/dev/playback/adapters/hls-background-video/host.js.map +1 -0
- package/dist/dev/playback/adapters/hls-background-video/media.d.ts +20 -0
- package/dist/dev/playback/adapters/hls-background-video/media.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-background-video/media.js +21 -0
- package/dist/dev/playback/adapters/hls-background-video/media.js.map +1 -0
- package/dist/dev/playback/adapters/hls-video/adapter.d.ts +63 -0
- package/dist/dev/playback/adapters/hls-video/adapter.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-video/adapter.js +316 -0
- package/dist/dev/playback/adapters/hls-video/adapter.js.map +1 -0
- package/dist/dev/playback/adapters/hls-video/error-surface.d.ts +18 -0
- package/dist/dev/playback/adapters/hls-video/error-surface.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-video/error-surface.js +51 -0
- package/dist/dev/playback/adapters/hls-video/error-surface.js.map +1 -0
- package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts +22 -0
- package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-video/media-tracks.js +126 -0
- package/dist/dev/playback/adapters/hls-video/media-tracks.js.map +1 -0
- package/dist/dev/playback/adapters/hls-video/media.d.ts +10 -0
- package/dist/dev/playback/adapters/hls-video/media.d.ts.map +1 -0
- package/dist/dev/playback/adapters/hls-video/media.js +11 -0
- package/dist/dev/playback/adapters/hls-video/media.js.map +1 -0
- package/dist/dev/playback/adapters/mux-audio/media.d.ts +28 -0
- package/dist/dev/playback/adapters/mux-audio/media.d.ts.map +1 -0
- package/dist/dev/playback/adapters/mux-audio/media.js +26 -0
- package/dist/dev/playback/adapters/mux-audio/media.js.map +1 -0
- package/dist/dev/playback/adapters/mux-video/adapter.d.ts +34 -0
- package/dist/dev/playback/adapters/mux-video/adapter.d.ts.map +1 -0
- package/dist/dev/playback/adapters/mux-video/adapter.js +105 -0
- package/dist/dev/playback/adapters/mux-video/adapter.js.map +1 -0
- package/dist/dev/playback/adapters/mux-video/media.d.ts +24 -0
- package/dist/dev/playback/adapters/mux-video/media.d.ts.map +1 -0
- package/dist/dev/playback/adapters/mux-video/media.js +23 -0
- package/dist/dev/playback/adapters/mux-video/media.js.map +1 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +26 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js +36 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -0
- package/dist/dev/playback/behaviors/collect-errors.js +111 -0
- package/dist/dev/playback/behaviors/collect-errors.js.map +1 -0
- package/dist/dev/playback/behaviors/derive-cdn-priority.js +68 -0
- package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/airplay.js +211 -0
- package/dist/dev/playback/behaviors/dom/airplay.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/apply-start-position.js +93 -0
- package/dist/dev/playback/behaviors/dom/apply-start-position.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/end-of-stream.js +102 -0
- package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts +72 -0
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/load-segments.js +181 -0
- package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/recover-end-stall.js +77 -0
- package/dist/dev/playback/behaviors/dom/recover-end-stall.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js +132 -0
- package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +205 -0
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js +114 -0
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +28 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +40 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js +26 -0
- package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +124 -0
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +33 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.js +52 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +47 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js +94 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +33 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js +37 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts +8 -0
- package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-player-resolution.js +57 -0
- package/dist/dev/playback/behaviors/dom/track-player-resolution.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-screen-resolution.js +29 -0
- package/dist/dev/playback/behaviors/dom/track-screen-resolution.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +113 -0
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
- package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +36 -0
- package/dist/dev/playback/behaviors/establish-start-media-time.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/establish-start-media-time.js +197 -0
- package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -0
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts +8 -0
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/resolve-presentation.js +85 -0
- package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -0
- package/dist/dev/playback/behaviors/resolve-track.js +141 -0
- package/dist/dev/playback/behaviors/resolve-track.js.map +1 -0
- package/dist/dev/playback/behaviors/select-tracks.d.ts +72 -0
- package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/select-tracks.js +216 -0
- package/dist/dev/playback/behaviors/select-tracks.js.map +1 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +14 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.js +66 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -0
- package/dist/dev/playback/behaviors/sync-preload.js +59 -0
- package/dist/dev/playback/behaviors/sync-preload.js.map +1 -0
- package/dist/dev/playback/behaviors/track-switching.js +391 -0
- package/dist/dev/playback/behaviors/track-switching.js.map +1 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +173 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.js +98 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -0
- package/dist/dev/playback/engines/hls/engine-background-video.d.ts +158 -0
- package/dist/dev/playback/engines/hls/engine-background-video.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/engine-background-video.js +92 -0
- package/dist/dev/playback/engines/hls/engine-background-video.js.map +1 -0
- package/dist/dev/playback/engines/hls/engine.d.ts +329 -0
- package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/engine.js +147 -0
- package/dist/dev/playback/engines/hls/engine.js.map +1 -0
- package/dist/dev/playback/primitives/derive-start-media-time.d.ts +16 -0
- package/dist/dev/playback/primitives/derive-start-media-time.d.ts.map +1 -0
- package/dist/dev/playback/primitives/error-messages.js +51 -0
- package/dist/dev/playback/primitives/error-messages.js.map +1 -0
- package/dist/dev/playback/primitives/failover-fetch.js +42 -0
- package/dist/dev/playback/primitives/failover-fetch.js.map +1 -0
- package/dist/dev/playback/primitives/head-peek.js +44 -0
- package/dist/dev/playback/primitives/head-peek.js.map +1 -0
- package/dist/dev/playback/primitives/live-window.js +66 -0
- package/dist/dev/playback/primitives/live-window.js.map +1 -0
- package/dist/dev/playback/primitives/relocation-pipelines.js +203 -0
- package/dist/dev/playback/primitives/relocation-pipelines.js.map +1 -0
- package/dist/dev/playback/primitives/report-track-conditions.d.ts +11 -0
- package/dist/dev/playback/primitives/report-track-conditions.d.ts.map +1 -0
- package/dist/dev/playback/primitives/report-track-conditions.js +86 -0
- package/dist/dev/playback/primitives/report-track-conditions.js.map +1 -0
- package/dist/dev/playback/primitives/segment-load-pipeline.d.ts +2 -0
- package/dist/dev/playback/primitives/segment-load-pipeline.js +96 -0
- package/dist/dev/playback/primitives/segment-load-pipeline.js.map +1 -0
- package/dist/dev/playback/primitives/selection-rules.d.ts +38 -0
- package/dist/dev/playback/primitives/selection-rules.d.ts.map +1 -0
- package/dist/dev/playback/primitives/selection-rules.js +86 -0
- package/dist/dev/playback/primitives/selection-rules.js.map +1 -0
- package/dist/dev/playback/primitives/source-buffer-messages.d.ts +43 -0
- package/dist/dev/playback/primitives/source-buffer-messages.d.ts.map +1 -0
- package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts +65 -0
- package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts.map +1 -0
- package/dist/dev/playback/primitives/text-segment-load-pipeline.js +37 -0
- package/dist/dev/playback/primitives/text-segment-load-pipeline.js.map +1 -0
- package/dist/dev/playback/primitives/text-track-messages.d.ts +14 -0
- package/dist/dev/playback/primitives/text-track-messages.d.ts.map +1 -0
- package/dist/dev/playback/primitives/track-types.js +61 -0
- package/dist/dev/playback/primitives/track-types.js.map +1 -0
- package/package.json +63 -26
- package/dist/adapter-CMw-rvbk.js +0 -3512
- package/dist/adapter-CMw-rvbk.js.map +0 -1
- package/dist/adapter-l8iJN_GI.d.ts +0 -689
- package/dist/adapter-l8iJN_GI.d.ts.map +0 -1
- package/dist/dom/index.d.ts +0 -243
- package/dist/dom/index.d.ts.map +0 -1
- package/dist/dom/index.js +0 -38
- package/dist/dom/index.js.map +0 -1
- package/dist/dom/playback-engine/index.d.ts +0 -2
- package/dist/dom/playback-engine/index.js +0 -4
- package/dist/index.d.ts +0 -13
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- package/dist/src-DRsS_H80.js +0 -14
- package/dist/src-DRsS_H80.js.map +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { computed } from "../../../core/signals/primitives.js";
|
|
2
|
+
import { defineBehavior } from "../../../core/composition/create-composition.js";
|
|
3
|
+
import { createMachineReactor } from "../../../core/reactors/create-machine-reactor.js";
|
|
4
|
+
import { waitForMediaSourceOpen } from "../../../media/dom/mse/mediasource-setup.js";
|
|
5
|
+
import { getMaxBufferedEnd, shouldUpdateDuration, waitForSourceBuffersReady } from "../../../media/dom/mse/duration.js";
|
|
6
|
+
//#region src/playback/behaviors/dom/update-mediasource-duration.ts
|
|
7
|
+
/**
|
|
8
|
+
* **Propagate `presentation.duration` to `mediaSource.duration` — exactly
|
|
9
|
+
* once per MediaSource.**
|
|
10
|
+
*
|
|
11
|
+
* Two paths, by whether the presentation is live:
|
|
12
|
+
*
|
|
13
|
+
* - **Live** (`presentation.duration === Infinity`): write `Infinity` once the
|
|
14
|
+
* MediaSource is open and no SourceBuffer is mid-append. No buffered clamp is
|
|
15
|
+
* needed (`Infinity` ≥ any range) and — unlike the finite case — it needn't
|
|
16
|
+
* precede the first append: `Infinity` overrides whatever finite live-edge
|
|
17
|
+
* value an append may have pinned (a finite duration would otherwise stall
|
|
18
|
+
* the stream once the window slides past it). The wait-for-idle is required
|
|
19
|
+
* because the MSE spec forbids setting `duration` while a buffer is
|
|
20
|
+
* `updating`; both waits resolve immediately when already open / idle.
|
|
21
|
+
*
|
|
22
|
+
* - **VoD** (finite): the value is written once, after `mediaSource` is open and
|
|
23
|
+
* all SourceBuffers are idle, clamped to be ≥ the highest buffered range (MSE
|
|
24
|
+
* spec). Written only while `mediaSource.duration` is still `NaN`; once any
|
|
25
|
+
* non-NaN value is present (us on a prior entry, or `endOfStream` from the
|
|
26
|
+
* buffered end), the property is left alone — re-syncing would race
|
|
27
|
+
* concurrent `appendBuffer()` calls.
|
|
28
|
+
*
|
|
29
|
+
* The entry resolves three async preconditions in order before writing:
|
|
30
|
+
*
|
|
31
|
+
* 1. **MediaSource open** — `waitForMediaSourceOpen` defers until the first
|
|
32
|
+
* `sourceopen` event (or any readyState transition, since `'ended'` /
|
|
33
|
+
* `'closed'` mean we've missed the window and should bail).
|
|
34
|
+
* 2. **All SourceBuffers idle** — `waitForSourceBuffersReady` defers per
|
|
35
|
+
* the MSE-spec rule that `duration` cannot be set while any buffer has
|
|
36
|
+
* `updating === true`.
|
|
37
|
+
* 3. **Buffered-range clamp** — `getMaxBufferedEnd` ensures the written
|
|
38
|
+
* duration is at least the highest buffered end (MSE spec disallows
|
|
39
|
+
* a smaller `duration` than any buffered range).
|
|
40
|
+
*
|
|
41
|
+
* The buffer-set helpers operate across `mediaSource.sourceBuffers` (the
|
|
42
|
+
* canonical aggregate), so the behavior composes uniformly across
|
|
43
|
+
* audio-only, video-only, and mixed configurations.
|
|
44
|
+
*
|
|
45
|
+
* Single-positive-state reactor (`'preconditions-unmet'` ↔ `'duration-writable'`):
|
|
46
|
+
* state derivation is purely signal-driven (presentation validity +
|
|
47
|
+
* mediaSource existence). MediaSource lifecycle state (`readyState`) and
|
|
48
|
+
* the `duration` itself are non-signal DOM properties resolved inside the
|
|
49
|
+
* entry's async sequence. The state-exit cleanup aborts the in-flight
|
|
50
|
+
* wait, so source resets and behavior destroy structurally cancel the
|
|
51
|
+
* pending write. A post-await re-check of `mediaSource.readyState ===
|
|
52
|
+
* 'open'` covers the narrow race where `endOfStream()` synchronously
|
|
53
|
+
* transitions readyState to `'ended'` between our `waitForMediaSourceOpen`
|
|
54
|
+
* resolution and the `mediaSource.duration` write.
|
|
55
|
+
*
|
|
56
|
+
* Downstream of `calculatePresentationDuration` (which writes
|
|
57
|
+
* `presentation.duration`); concurrent with `endOfStream` (which may later
|
|
58
|
+
* write `mediaSource.duration` — the "exactly once" contract keeps us out
|
|
59
|
+
* of that path).
|
|
60
|
+
*/
|
|
61
|
+
function deriveState(presentation, mediaSource) {
|
|
62
|
+
return shouldUpdateDuration(presentation, mediaSource) ? "duration-writable" : "preconditions-unmet";
|
|
63
|
+
}
|
|
64
|
+
function updateMediaSourceDurationSetup({ state, context }) {
|
|
65
|
+
const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaSource.get()));
|
|
66
|
+
return createMachineReactor({
|
|
67
|
+
initial: "preconditions-unmet",
|
|
68
|
+
monitor: () => derivedStateSignal.get(),
|
|
69
|
+
states: {
|
|
70
|
+
"preconditions-unmet": {},
|
|
71
|
+
"duration-writable": { entry: () => {
|
|
72
|
+
const presentation = state.presentation.get();
|
|
73
|
+
const mediaSource = context.mediaSource.get();
|
|
74
|
+
if (presentation.duration === Number.POSITIVE_INFINITY) {
|
|
75
|
+
if (mediaSource.duration === Number.POSITIVE_INFINITY) return;
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
(async () => {
|
|
78
|
+
await waitForMediaSourceOpen(mediaSource, controller.signal);
|
|
79
|
+
if (controller.signal.aborted || mediaSource.readyState !== "open") return;
|
|
80
|
+
await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);
|
|
81
|
+
if (controller.signal.aborted || mediaSource.readyState !== "open") return;
|
|
82
|
+
if (mediaSource.duration !== Number.POSITIVE_INFINITY) mediaSource.duration = Number.POSITIVE_INFINITY;
|
|
83
|
+
})();
|
|
84
|
+
return () => controller.abort();
|
|
85
|
+
}
|
|
86
|
+
if (!Number.isNaN(mediaSource.duration)) return;
|
|
87
|
+
const controller = new AbortController();
|
|
88
|
+
const writeWhenReady = async () => {
|
|
89
|
+
await waitForMediaSourceOpen(mediaSource, controller.signal);
|
|
90
|
+
if (controller.signal.aborted) return;
|
|
91
|
+
if (mediaSource.readyState !== "open") return;
|
|
92
|
+
await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);
|
|
93
|
+
if (controller.signal.aborted) return;
|
|
94
|
+
if (mediaSource.readyState !== "open") return;
|
|
95
|
+
const maxBufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);
|
|
96
|
+
const duration = maxBufferedEnd > presentation.duration ? maxBufferedEnd : presentation.duration;
|
|
97
|
+
mediaSource.duration = duration;
|
|
98
|
+
};
|
|
99
|
+
writeWhenReady();
|
|
100
|
+
return () => controller.abort();
|
|
101
|
+
} }
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const updateMediaSourceDuration = defineBehavior({
|
|
106
|
+
stateKeys: ["presentation"],
|
|
107
|
+
contextKeys: ["mediaSource"],
|
|
108
|
+
setup: updateMediaSourceDurationSetup
|
|
109
|
+
});
|
|
110
|
+
//#endregion
|
|
111
|
+
export { updateMediaSourceDuration };
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=update-mediasource-duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-mediasource-duration.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/update-mediasource-duration.ts"],"sourcesContent":["/**\n * **Propagate `presentation.duration` to `mediaSource.duration` — exactly\n * once per MediaSource.**\n *\n * Two paths, by whether the presentation is live:\n *\n * - **Live** (`presentation.duration === Infinity`): write `Infinity` once the\n * MediaSource is open and no SourceBuffer is mid-append. No buffered clamp is\n * needed (`Infinity` ≥ any range) and — unlike the finite case — it needn't\n * precede the first append: `Infinity` overrides whatever finite live-edge\n * value an append may have pinned (a finite duration would otherwise stall\n * the stream once the window slides past it). The wait-for-idle is required\n * because the MSE spec forbids setting `duration` while a buffer is\n * `updating`; both waits resolve immediately when already open / idle.\n *\n * - **VoD** (finite): the value is written once, after `mediaSource` is open and\n * all SourceBuffers are idle, clamped to be ≥ the highest buffered range (MSE\n * spec). Written only while `mediaSource.duration` is still `NaN`; once any\n * non-NaN value is present (us on a prior entry, or `endOfStream` from the\n * buffered end), the property is left alone — re-syncing would race\n * concurrent `appendBuffer()` calls.\n *\n * The entry resolves three async preconditions in order before writing:\n *\n * 1. **MediaSource open** — `waitForMediaSourceOpen` defers until the first\n * `sourceopen` event (or any readyState transition, since `'ended'` /\n * `'closed'` mean we've missed the window and should bail).\n * 2. **All SourceBuffers idle** — `waitForSourceBuffersReady` defers per\n * the MSE-spec rule that `duration` cannot be set while any buffer has\n * `updating === true`.\n * 3. **Buffered-range clamp** — `getMaxBufferedEnd` ensures the written\n * duration is at least the highest buffered end (MSE spec disallows\n * a smaller `duration` than any buffered range).\n *\n * The buffer-set helpers operate across `mediaSource.sourceBuffers` (the\n * canonical aggregate), so the behavior composes uniformly across\n * audio-only, video-only, and mixed configurations.\n *\n * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'duration-writable'`):\n * state derivation is purely signal-driven (presentation validity +\n * mediaSource existence). MediaSource lifecycle state (`readyState`) and\n * the `duration` itself are non-signal DOM properties resolved inside the\n * entry's async sequence. The state-exit cleanup aborts the in-flight\n * wait, so source resets and behavior destroy structurally cancel the\n * pending write. A post-await re-check of `mediaSource.readyState ===\n * 'open'` covers the narrow race where `endOfStream()` synchronously\n * transitions readyState to `'ended'` between our `waitForMediaSourceOpen`\n * resolution and the `mediaSource.duration` write.\n *\n * Downstream of `calculatePresentationDuration` (which writes\n * `presentation.duration`); concurrent with `endOfStream` (which may later\n * write `mediaSource.duration` — the \"exactly once\" contract keeps us out\n * of that path).\n */\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal } from '../../../core/signals/primitives';\nimport { getMaxBufferedEnd, shouldUpdateDuration, waitForSourceBuffersReady } from '../../../media/dom/mse/duration';\nimport { waitForMediaSourceOpen } from '../../../media/dom/mse/mediasource-setup';\nimport type { MaybeResolvedPresentation } from '../../../media/types';\n\ntype DurationFsmState = 'preconditions-unmet' | 'duration-writable';\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaSource: MediaSource | undefined\n): DurationFsmState {\n return shouldUpdateDuration(presentation, mediaSource) ? 'duration-writable' : 'preconditions-unmet';\n}\n\nfunction updateMediaSourceDurationSetup({\n state,\n context,\n}: {\n state: { presentation: ReadonlySignal<MaybeResolvedPresentation | undefined> };\n context: { mediaSource: ReadonlySignal<MediaSource | undefined> };\n}): Reactor<DurationFsmState | 'destroying' | 'destroyed'> {\n // Purely signal-driven derivation: only `presentation` validity and\n // `mediaSource` existence. MediaSource lifecycle (`readyState`),\n // its attached `sourceBuffers`, and `mediaSource.duration` itself are\n // non-signal DOM properties consumed inside the entry — none of them\n // sneak into `computed(...)`.\n const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaSource.get()));\n\n return createMachineReactor<DurationFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n 'duration-writable': {\n // entry body is auto-untracked. The state machine handles source\n // resets via deriveState; this entry resolves the MediaSource\n // lifecycle preconditions in sequence and binds its cleanup\n // (abort) to state exit + destroy.\n entry: () => {\n const presentation = state.presentation.get()!;\n const mediaSource = context.mediaSource.get()!;\n\n // Live: write Infinity once the MediaSource is open and no buffer is\n // mid-append (the MSE spec forbids setting `duration` while a\n // SourceBuffer is `updating`, and racing an in-flight append throws).\n // No `NaN` guard, unlike the VoD path below — Infinity must override\n // whatever finite live-edge value an append may have pinned.\n if (presentation.duration === Number.POSITIVE_INFINITY) {\n if (mediaSource.duration === Number.POSITIVE_INFINITY) return;\n const controller = new AbortController();\n void (async () => {\n await waitForMediaSourceOpen(mediaSource, controller.signal);\n if (controller.signal.aborted || mediaSource.readyState !== 'open') return;\n await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);\n if (controller.signal.aborted || mediaSource.readyState !== 'open') return;\n if (mediaSource.duration !== Number.POSITIVE_INFINITY) {\n mediaSource.duration = Number.POSITIVE_INFINITY;\n }\n })();\n return () => controller.abort();\n }\n\n // VoD idempotency: someone (us on a prior entry, or concurrent\n // `endOfStream`) has already written. No-op and leave the state\n // alone — the next source-reset transition will produce a fresh\n // entry against the new MediaSource (whose `duration` starts at\n // `NaN` again).\n if (!Number.isNaN(mediaSource.duration)) return;\n\n const controller = new AbortController();\n\n const writeWhenReady = async () => {\n await waitForMediaSourceOpen(mediaSource, controller.signal);\n if (controller.signal.aborted) return;\n // `waitForMediaSourceOpen` resolves on any readyState transition\n // out of 'closed'; if we landed in 'ended' / 'closed' instead of\n // 'open', the write window is gone.\n if (mediaSource.readyState !== 'open') return;\n\n await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);\n if (controller.signal.aborted) return;\n\n // Narrow race: `endOfStream()` synchronously transitions\n // readyState to 'ended' AND writes `mediaSource.duration`\n // between our `waitForMediaSourceOpen` resolution and here.\n // Re-read the DOM property to catch this window.\n if (mediaSource.readyState !== 'open') return;\n\n // MSE spec: duration cannot be less than any buffered range.\n // Re-read sourceBuffers after the await so any buffer added\n // during the wait is included in the clamp.\n const maxBufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);\n const duration = maxBufferedEnd > presentation.duration! ? maxBufferedEnd : presentation.duration!;\n mediaSource.duration = duration;\n };\n\n writeWhenReady();\n\n return () => controller.abort();\n },\n },\n },\n });\n}\n\nexport const updateMediaSourceDuration = defineBehavior({\n stateKeys: ['presentation'],\n contextKeys: ['mediaSource'],\n setup: updateMediaSourceDurationSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAS,YACP,cACA,aACkB;CAClB,OAAO,qBAAqB,cAAc,WAAW,IAAI,sBAAsB;AACjF;AAEA,SAAS,+BAA+B,EACtC,OACA,WAIyD;CAMzD,MAAM,qBAAqB,eAAe,YAAY,MAAM,aAAa,IAAI,GAAG,QAAQ,YAAY,IAAI,CAAC,CAAC;CAE1G,OAAO,qBAAuC;EAC5C,SAAS;EACT,eAAe,mBAAmB,IAAI;EACtC,QAAQ;GACN,uBAAuB,CAAC;GAExB,qBAAqB,EAKnB,aAAa;IACX,MAAM,eAAe,MAAM,aAAa,IAAI;IAC5C,MAAM,cAAc,QAAQ,YAAY,IAAI;IAO5C,IAAI,aAAa,aAAa,OAAO,mBAAmB;KACtD,IAAI,YAAY,aAAa,OAAO,mBAAmB;KACvD,MAAM,aAAa,IAAI,gBAAgB;KACvC,CAAM,YAAY;MAChB,MAAM,uBAAuB,aAAa,WAAW,MAAM;MAC3D,IAAI,WAAW,OAAO,WAAW,YAAY,eAAe,QAAQ;MACpE,MAAM,0BAA0B,YAAY,eAAe,WAAW,MAAM;MAC5E,IAAI,WAAW,OAAO,WAAW,YAAY,eAAe,QAAQ;MACpE,IAAI,YAAY,aAAa,OAAO,mBAClC,YAAY,WAAW,OAAO;KAElC,EAAA,CAAG;KACH,aAAa,WAAW,MAAM;IAChC;IAOA,IAAI,CAAC,OAAO,MAAM,YAAY,QAAQ,GAAG;IAEzC,MAAM,aAAa,IAAI,gBAAgB;IAEvC,MAAM,iBAAiB,YAAY;KACjC,MAAM,uBAAuB,aAAa,WAAW,MAAM;KAC3D,IAAI,WAAW,OAAO,SAAS;KAI/B,IAAI,YAAY,eAAe,QAAQ;KAEvC,MAAM,0BAA0B,YAAY,eAAe,WAAW,MAAM;KAC5E,IAAI,WAAW,OAAO,SAAS;KAM/B,IAAI,YAAY,eAAe,QAAQ;KAKvC,MAAM,iBAAiB,kBAAkB,YAAY,aAAa;KAClE,MAAM,WAAW,iBAAiB,aAAa,WAAY,iBAAiB,aAAa;KACzF,YAAY,WAAW;IACzB;IAEA,eAAe;IAEf,aAAa,WAAW,MAAM;GAChC,EACF;EACF;CACF,CAAC;AACH;AAEA,MAAa,4BAA4B,eAAe;CACtD,WAAW,CAAC,cAAc;CAC1B,aAAa,CAAC,aAAa;CAC3B,OAAO;AACT,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import "../../core/signals/primitives.js";
|
|
2
|
+
import "../../core/composition/create-composition.js";
|
|
3
|
+
import { DeriveStartMediaTime, DeriveStartMediaTimeContext } from "../primitives/derive-start-media-time.js";
|
|
4
|
+
//#region src/playback/behaviors/establish-start-media-time.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The **default** — relocate the whole presentation by one shared origin: the `min`
|
|
7
|
+
* across the *selected* A/V tracks' own origins, denormalized onto every type. This
|
|
8
|
+
* single reduce subsumes the "per-type" and "shared" tiers:
|
|
9
|
+
* - **aligned A/V** — `min` equals each origin (they're equal), so it matches per-type;
|
|
10
|
+
* - **skewed A/V** (e.g. Apple's 44ms audio-lead) — `min` keeps every track's earliest
|
|
11
|
+
* DTS ≥ 0 (relocating by ≤ each own origin never drives one negative) *and* preserves
|
|
12
|
+
* the real skew (per-type would flatten it, desyncing A/V);
|
|
13
|
+
* - **single type / muxed** — `min` of the one origin is that origin.
|
|
14
|
+
*
|
|
15
|
+
* Returns `undefined` for every type until all *selected* types have a complete origin
|
|
16
|
+
* (the shared-`min` barrier). Which types must contribute is read from `ctx` (the
|
|
17
|
+
* selected v/a ids); with no selection context it coordinates across whatever types
|
|
18
|
+
* have data. A shared origin below {@link NEAR_ZERO_ORIGIN_THRESHOLD} is returned as `0`
|
|
19
|
+
* (native — ordinary ~0-PTS VOD isn't relocated).
|
|
20
|
+
*/
|
|
21
|
+
declare const deriveSharedMinStartMediaTime: DeriveStartMediaTime;
|
|
22
|
+
/**
|
|
23
|
+
* Coordination-axis *off* — each type relocates by its own origin, independently. Not
|
|
24
|
+
* the default: it flattens real A/V skew (see {@link deriveSharedMinStartMediaTime}).
|
|
25
|
+
* Kept as an opt-in for compositions that know their A/V is aligned and want to skip
|
|
26
|
+
* the shared-`min` barrier (each type stamps as soon as its own origin is discovered).
|
|
27
|
+
*
|
|
28
|
+
* Caution: {@link NEAR_ZERO_ORIGIN_THRESHOLD} applies independently per type here — if
|
|
29
|
+
* two types' origins straddle it, one snaps to `0` and the other doesn't, injecting up
|
|
30
|
+
* to a threshold's worth of A/V offset. Safe for the intended aligned-A/V opt-in; see
|
|
31
|
+
* `internal/design/spf/live-presentation-timeline-model.md` (collision 5).
|
|
32
|
+
*/
|
|
33
|
+
declare const derivePerTypeStartMediaTime: DeriveStartMediaTime;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { type DeriveStartMediaTime, derivePerTypeStartMediaTime, deriveSharedMinStartMediaTime };
|
|
36
|
+
//# sourceMappingURL=establish-start-media-time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"establish-start-media-time.d.ts","names":[],"sources":["../../../../src/playback/behaviors/establish-start-media-time.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;cA+Ga,+BAA+B;;;;;;;;;;;;cA2B/B,6BAA6B"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { update } from "../../core/signals/primitives.js";
|
|
2
|
+
import { createMachineReactor } from "../../core/reactors/create-machine-reactor.js";
|
|
3
|
+
import { isResolvedPresentation, isResolvedTrack } from "../../media/types/index.js";
|
|
4
|
+
import { findTrackById } from "../../media/utils/tracks.js";
|
|
5
|
+
//#region src/playback/behaviors/establish-start-media-time.ts
|
|
6
|
+
/**
|
|
7
|
+
* A single type's own media-timeline origin:
|
|
8
|
+
* `baseMediaDecodeTime/timescale − segmentStartTime` (the `segmentStartTime` term
|
|
9
|
+
* makes it the stream origin even when the first loaded segment isn't the 0th).
|
|
10
|
+
* `undefined` until timescale + baseMediaDecodeTime + segmentStartTime are all present.
|
|
11
|
+
*/
|
|
12
|
+
function ownOrigin(data) {
|
|
13
|
+
const { timescale, baseMediaDecodeTime, segmentStartTime } = data ?? {};
|
|
14
|
+
return timescale != null && baseMediaDecodeTime != null && segmentStartTime != null ? baseMediaDecodeTime / timescale - segmentStartTime : void 0;
|
|
15
|
+
}
|
|
16
|
+
/** Snap a below-threshold (incl. negative) origin to `0` so it isn't relocated. */
|
|
17
|
+
function thresholdOrigin(origin) {
|
|
18
|
+
return origin < 1 ? 0 : origin;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The **default** — relocate the whole presentation by one shared origin: the `min`
|
|
22
|
+
* across the *selected* A/V tracks' own origins, denormalized onto every type. This
|
|
23
|
+
* single reduce subsumes the "per-type" and "shared" tiers:
|
|
24
|
+
* - **aligned A/V** — `min` equals each origin (they're equal), so it matches per-type;
|
|
25
|
+
* - **skewed A/V** (e.g. Apple's 44ms audio-lead) — `min` keeps every track's earliest
|
|
26
|
+
* DTS ≥ 0 (relocating by ≤ each own origin never drives one negative) *and* preserves
|
|
27
|
+
* the real skew (per-type would flatten it, desyncing A/V);
|
|
28
|
+
* - **single type / muxed** — `min` of the one origin is that origin.
|
|
29
|
+
*
|
|
30
|
+
* Returns `undefined` for every type until all *selected* types have a complete origin
|
|
31
|
+
* (the shared-`min` barrier). Which types must contribute is read from `ctx` (the
|
|
32
|
+
* selected v/a ids); with no selection context it coordinates across whatever types
|
|
33
|
+
* have data. A shared origin below {@link NEAR_ZERO_ORIGIN_THRESHOLD} is returned as `0`
|
|
34
|
+
* (native — ordinary ~0-PTS VOD isn't relocated).
|
|
35
|
+
*/
|
|
36
|
+
const deriveSharedMinStartMediaTime = (containerData, ctx) => {
|
|
37
|
+
const contributingTypes = [];
|
|
38
|
+
if (ctx.selectedVideoTrackId != null) contributingTypes.push("video");
|
|
39
|
+
if (ctx.selectedAudioTrackId != null) contributingTypes.push("audio");
|
|
40
|
+
const origins = (contributingTypes.length > 0 ? contributingTypes : Object.keys(containerData)).map((type) => ownOrigin(containerData[type]));
|
|
41
|
+
if (origins.length === 0 || origins.some((origin) => origin === void 0)) return {};
|
|
42
|
+
const shared = thresholdOrigin(Math.min(...origins));
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const type of Object.keys(containerData)) out[type] = shared;
|
|
45
|
+
return out;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Coordination-axis *off* — each type relocates by its own origin, independently. Not
|
|
49
|
+
* the default: it flattens real A/V skew (see {@link deriveSharedMinStartMediaTime}).
|
|
50
|
+
* Kept as an opt-in for compositions that know their A/V is aligned and want to skip
|
|
51
|
+
* the shared-`min` barrier (each type stamps as soon as its own origin is discovered).
|
|
52
|
+
*
|
|
53
|
+
* Caution: {@link NEAR_ZERO_ORIGIN_THRESHOLD} applies independently per type here — if
|
|
54
|
+
* two types' origins straddle it, one snaps to `0` and the other doesn't, injecting up
|
|
55
|
+
* to a threshold's worth of A/V offset. Safe for the intended aligned-A/V opt-in; see
|
|
56
|
+
* `internal/design/spf/live-presentation-timeline-model.md` (collision 5).
|
|
57
|
+
*/
|
|
58
|
+
const derivePerTypeStartMediaTime = (containerData) => {
|
|
59
|
+
const out = {};
|
|
60
|
+
for (const [type, data] of Object.entries(containerData)) {
|
|
61
|
+
const origin = ownOrigin(data);
|
|
62
|
+
out[type] = origin === void 0 ? void 0 : thresholdOrigin(origin);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The anchor-source track: the selected video track, falling back to audio
|
|
68
|
+
* (audio-only). Deterministic — never "first resolved", which is race-dependent
|
|
69
|
+
* under concurrent resolves.
|
|
70
|
+
*/
|
|
71
|
+
function referenceTrackId(ctx) {
|
|
72
|
+
return ctx.selectedVideoTrackId ?? ctx.selectedAudioTrackId;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The live-anchor first-parse gate (the "parses align" step of the establishment
|
|
76
|
+
* order). Open for the reference track itself — its local-from-0 placement *is*
|
|
77
|
+
* the presentation timeline (presentation-0 = the join point). Every other track
|
|
78
|
+
* holds until the reference's first parse settles the anchor question: no PDT →
|
|
79
|
+
* local placement is already correct, proceed; PDT anchor → hold until the
|
|
80
|
+
* establishment reactor stamps it onto this track, so the parse takes the
|
|
81
|
+
* `placeOnAnchor` path. Establishment is sticky-once, so an unanchored first
|
|
82
|
+
* parse would be permanently misaligned — that's the race this gate closes.
|
|
83
|
+
*
|
|
84
|
+
* Pairs with the {@link establishStartMediaTime} reactor (the stamp that opens
|
|
85
|
+
* the anchored branch); wire both or neither.
|
|
86
|
+
*/
|
|
87
|
+
const gateFirstParseOnAnchor = (presentation, ctx, trackId) => {
|
|
88
|
+
const referenceId = referenceTrackId(ctx);
|
|
89
|
+
if (referenceId === void 0 || referenceId === trackId) return true;
|
|
90
|
+
if (!isResolvedPresentation(presentation)) return false;
|
|
91
|
+
const reference = findTrackById(presentation, referenceId);
|
|
92
|
+
if (!reference) return true;
|
|
93
|
+
if (!isResolvedTrack(reference)) return false;
|
|
94
|
+
if (reference.startDate === void 0) return true;
|
|
95
|
+
return findTrackById(presentation, trackId)?.startDate !== void 0;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Stamp the frozen wall-clock anchor as `startDate` onto every track that lacks
|
|
99
|
+
* one (idempotent — same reference when nothing moved). Unresolved shells pick
|
|
100
|
+
* it up as the `placeOnAnchor` preset; covering *all* tracks in one pass means
|
|
101
|
+
* any track selected later — an ABR rung, another audio language, late captions
|
|
102
|
+
* — resolves already anchored. The reference track's own parser-computed
|
|
103
|
+
* `startDate` reads back as the anchor, so it's naturally left untouched.
|
|
104
|
+
*/
|
|
105
|
+
function stampStartDates(presentation, anchor) {
|
|
106
|
+
let changed = false;
|
|
107
|
+
const selectionSets = presentation.selectionSets.map((selectionSet) => ({
|
|
108
|
+
...selectionSet,
|
|
109
|
+
switchingSets: selectionSet.switchingSets.map((switchingSet) => ({
|
|
110
|
+
...switchingSet,
|
|
111
|
+
tracks: switchingSet.tracks.map((track) => {
|
|
112
|
+
if (track.startDate !== void 0) return track;
|
|
113
|
+
changed = true;
|
|
114
|
+
return {
|
|
115
|
+
...track,
|
|
116
|
+
startDate: anchor
|
|
117
|
+
};
|
|
118
|
+
})
|
|
119
|
+
}))
|
|
120
|
+
}));
|
|
121
|
+
return changed ? {
|
|
122
|
+
...presentation,
|
|
123
|
+
selectionSets
|
|
124
|
+
} : presentation;
|
|
125
|
+
}
|
|
126
|
+
/** Stamp the derived per-track `startMediaTime` onto the model (idempotent — same reference when nothing moved). */
|
|
127
|
+
function stampTracks(presentation, startMediaTimes) {
|
|
128
|
+
let changed = false;
|
|
129
|
+
const selectionSets = presentation.selectionSets.map((selectionSet) => ({
|
|
130
|
+
...selectionSet,
|
|
131
|
+
switchingSets: selectionSet.switchingSets.map((switchingSet) => ({
|
|
132
|
+
...switchingSet,
|
|
133
|
+
tracks: switchingSet.tracks.map((track) => {
|
|
134
|
+
const startMediaTime = startMediaTimes[track.type];
|
|
135
|
+
if (startMediaTime === void 0 || track.startMediaTime === startMediaTime) return track;
|
|
136
|
+
changed = true;
|
|
137
|
+
return {
|
|
138
|
+
...track,
|
|
139
|
+
startMediaTime
|
|
140
|
+
};
|
|
141
|
+
})
|
|
142
|
+
}))
|
|
143
|
+
}));
|
|
144
|
+
return changed ? {
|
|
145
|
+
...presentation,
|
|
146
|
+
selectionSets
|
|
147
|
+
} : presentation;
|
|
148
|
+
}
|
|
149
|
+
function establishStartMediaTimeSetup({ state, config = {} }) {
|
|
150
|
+
const derive = config.deriveStartMediaTime ?? deriveSharedMinStartMediaTime;
|
|
151
|
+
const selectionContext = () => ({
|
|
152
|
+
selectedVideoTrackId: state.selectedVideoTrackId?.get(),
|
|
153
|
+
selectedAudioTrackId: state.selectedAudioTrackId?.get()
|
|
154
|
+
});
|
|
155
|
+
/** Established once the selected A/V tracks (whichever exist) carry `startMediaTime`. */
|
|
156
|
+
const established = () => {
|
|
157
|
+
const presentation = state.presentation.get();
|
|
158
|
+
if (!isResolvedPresentation(presentation)) return false;
|
|
159
|
+
const ids = [state.selectedVideoTrackId?.get(), state.selectedAudioTrackId?.get()].filter((id) => id !== void 0);
|
|
160
|
+
return ids.length > 0 && ids.every((id) => findTrackById(presentation, id)?.startMediaTime !== void 0);
|
|
161
|
+
};
|
|
162
|
+
return createMachineReactor({
|
|
163
|
+
initial: "inactive",
|
|
164
|
+
monitor: () => {
|
|
165
|
+
if (!isResolvedPresentation(state.presentation.get())) return "inactive";
|
|
166
|
+
return established() ? "established" : "monitoring";
|
|
167
|
+
},
|
|
168
|
+
states: {
|
|
169
|
+
inactive: { entry: () => state.mediaContainerData.set(void 0) },
|
|
170
|
+
monitoring: { effects: [() => {
|
|
171
|
+
const presentation = state.presentation.get();
|
|
172
|
+
if (!isResolvedPresentation(presentation)) return;
|
|
173
|
+
const referenceId = referenceTrackId(selectionContext());
|
|
174
|
+
const reference = referenceId === void 0 ? void 0 : findTrackById(presentation, referenceId);
|
|
175
|
+
if (!reference || !isResolvedTrack(reference)) return;
|
|
176
|
+
const anchor = reference.startDate;
|
|
177
|
+
if (anchor === void 0) return;
|
|
178
|
+
update(state.presentation, (current) => stampStartDates(current, anchor));
|
|
179
|
+
}, () => {
|
|
180
|
+
const containerData = state.mediaContainerData.get();
|
|
181
|
+
if (!containerData) return;
|
|
182
|
+
const startMediaTimes = derive(containerData, selectionContext());
|
|
183
|
+
update(state.presentation, (current) => stampTracks(current, startMediaTimes));
|
|
184
|
+
}] },
|
|
185
|
+
established: {}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
const establishStartMediaTime = {
|
|
190
|
+
stateKeys: ["presentation", "mediaContainerData"],
|
|
191
|
+
contextKeys: [],
|
|
192
|
+
setup: establishStartMediaTimeSetup
|
|
193
|
+
};
|
|
194
|
+
//#endregion
|
|
195
|
+
export { derivePerTypeStartMediaTime, deriveSharedMinStartMediaTime, establishStartMediaTime, gateFirstParseOnAnchor };
|
|
196
|
+
|
|
197
|
+
//# sourceMappingURL=establish-start-media-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"establish-start-media-time.js","names":[],"sources":["../../../../src/playback/behaviors/establish-start-media-time.ts"],"sourcesContent":["/**\n * Establish the presentation's coordinate origin — the `startTime` /\n * `startMediaTime` / `startDate` triple — once per source, so every track lands\n * on one 0-based presentation timeline. One establishment unit with an internal\n * order: the wall-clock **anchor** settles (live PDT sources), gated parses\n * align to it, then `startMediaTime` latches — see\n * `internal/design/spf/live-presentation-timeline-model.md`.\n *\n * The two coordinate stamps it owns:\n * - **`startDate` (wall-clock anchor).** The designated *reference track*\n * (selected video, else audio) resolves first; its parser-computed\n * `startDate` — PDT at presentation-0 — is frozen as the anchor and stamped\n * onto every other track that lacks one, so their first parses place via\n * `placeOnAnchor` (the pre-applied-anchor path in `parse-media-playlist`)\n * and later-selected renditions resolve already aligned. Ordering is\n * enforced by {@link gateFirstParseOnAnchor}, which `resolve-track` awaits\n * through its injected gate seam. Absent PDT (ordinary VOD), no anchor is\n * stamped and local-from-0 placement is already correct.\n * - **`startMediaTime` (decode origin).** Runs the injected\n * {@link DeriveStartMediaTime} seam over the transient `mediaContainerData`\n * slot (owned here; `inactive` clears it per source) and stamps the settled\n * value onto the model until `established`. The byte-level discover/stamp\n * steps that fill the slot are a separate, config `messagePipelines` array\n * (`primitives/relocation-pipelines`); the two coordinate only through the\n * shared `state.mediaContainerData` slot, never by import. See\n * `internal/design/spf/presentation-timeline-model.md`.\n *\n * DOM-free reactor; sticky per source (`inactive → monitoring → established`).\n */\nimport type { Behavior } from '../../core/composition/create-composition';\nimport { createMachineReactor, type Reactor } from '../../core/reactors/create-machine-reactor';\nimport { type ReadonlySignal, type Signal, update } from '../../core/signals/primitives';\nimport {\n isResolvedPresentation,\n isResolvedTrack,\n type MaybeResolvedPresentation,\n type MediaContainerData,\n type Presentation,\n} from '../../media/types';\nimport { findTrackById } from '../../media/utils/tracks';\nimport type { DeriveStartMediaTime, DeriveStartMediaTimeContext } from '../primitives/derive-start-media-time';\nimport type { GateFirstParse, GateFirstParseContext } from '../primitives/gate-first-parse';\n\nexport type { DeriveStartMediaTime, DeriveStartMediaTimeContext, GateFirstParse, GateFirstParseContext };\n\n// ============================================================================\n// STATE / CONFIG\n// ============================================================================\n\nexport interface EstablishStartMediaTimeState {\n presentation?: MaybeResolvedPresentation;\n /**\n * Transient origin-establishment data, keyed by **track type** (`'video'` /\n * `'audio'`) — per spec one init+media pair per type suffices, and ABR rungs of\n * a type share the origin. Filled by the discover steps across appends, reset\n * per source. Never the model — the churn stays here; only the settled\n * `startMediaTime` reaches `Track`.\n */\n mediaContainerData?: Record<string, MediaContainerData>;\n selectedVideoTrackId?: string;\n selectedAudioTrackId?: string;\n}\n\n/**\n * A single type's own media-timeline origin:\n * `baseMediaDecodeTime/timescale − segmentStartTime` (the `segmentStartTime` term\n * makes it the stream origin even when the first loaded segment isn't the 0th).\n * `undefined` until timescale + baseMediaDecodeTime + segmentStartTime are all present.\n */\nfunction ownOrigin(data: MediaContainerData | undefined): number | undefined {\n const { timescale, baseMediaDecodeTime, segmentStartTime } = data ?? {};\n return timescale != null && baseMediaDecodeTime != null && segmentStartTime != null\n ? baseMediaDecodeTime / timescale - segmentStartTime\n : undefined;\n}\n\n/**\n * Origins below this magnitude (seconds) are treated as `0` — the derive returns `0`, so\n * the presentation is left on its native (~0-based) timeline and no `timestampOffset` is\n * set (the loader stamp no-ops on a derived `0`). Ordinary VOD carries a small nonzero\n * encode origin (audio priming, first-frame CTS, edit lists); relocating by a sub-second\n * amount is pointless, and setting `timestampOffset` at all can ripple edge segments.\n * Relocation targets streams with an intentional large origin (instant clips, bipbop @10s).\n *\n * Absolute basis, not proportional: the DTS-below-zero / ripple risk scales with the\n * origin's absolute size, not with the presentation's duration. Also snaps negatives to\n * `0` — a negative origin would relocate *forward*, which is never the intent.\n */\nexport const NEAR_ZERO_ORIGIN_THRESHOLD = 1;\n\n/** Snap a below-threshold (incl. negative) origin to `0` so it isn't relocated. */\nfunction thresholdOrigin(origin: number): number {\n return origin < NEAR_ZERO_ORIGIN_THRESHOLD ? 0 : origin;\n}\n\n/**\n * The **default** — relocate the whole presentation by one shared origin: the `min`\n * across the *selected* A/V tracks' own origins, denormalized onto every type. This\n * single reduce subsumes the \"per-type\" and \"shared\" tiers:\n * - **aligned A/V** — `min` equals each origin (they're equal), so it matches per-type;\n * - **skewed A/V** (e.g. Apple's 44ms audio-lead) — `min` keeps every track's earliest\n * DTS ≥ 0 (relocating by ≤ each own origin never drives one negative) *and* preserves\n * the real skew (per-type would flatten it, desyncing A/V);\n * - **single type / muxed** — `min` of the one origin is that origin.\n *\n * Returns `undefined` for every type until all *selected* types have a complete origin\n * (the shared-`min` barrier). Which types must contribute is read from `ctx` (the\n * selected v/a ids); with no selection context it coordinates across whatever types\n * have data. A shared origin below {@link NEAR_ZERO_ORIGIN_THRESHOLD} is returned as `0`\n * (native — ordinary ~0-PTS VOD isn't relocated).\n */\nexport const deriveSharedMinStartMediaTime: DeriveStartMediaTime = (containerData, ctx) => {\n const contributingTypes: string[] = [];\n if (ctx.selectedVideoTrackId != null) contributingTypes.push('video');\n if (ctx.selectedAudioTrackId != null) contributingTypes.push('audio');\n const types = contributingTypes.length > 0 ? contributingTypes : Object.keys(containerData);\n\n const origins = types.map((type) => ownOrigin(containerData[type]));\n // Barrier: not ready until every contributing type has a complete origin.\n if (origins.length === 0 || origins.some((origin) => origin === undefined)) return {};\n\n const shared = thresholdOrigin(Math.min(...(origins as number[])));\n const out: Record<string, number | undefined> = {};\n for (const type of Object.keys(containerData)) out[type] = shared;\n return out;\n};\n\n/**\n * Coordination-axis *off* — each type relocates by its own origin, independently. Not\n * the default: it flattens real A/V skew (see {@link deriveSharedMinStartMediaTime}).\n * Kept as an opt-in for compositions that know their A/V is aligned and want to skip\n * the shared-`min` barrier (each type stamps as soon as its own origin is discovered).\n *\n * Caution: {@link NEAR_ZERO_ORIGIN_THRESHOLD} applies independently per type here — if\n * two types' origins straddle it, one snaps to `0` and the other doesn't, injecting up\n * to a threshold's worth of A/V offset. Safe for the intended aligned-A/V opt-in; see\n * `internal/design/spf/live-presentation-timeline-model.md` (collision 5).\n */\nexport const derivePerTypeStartMediaTime: DeriveStartMediaTime = (containerData) => {\n const out: Record<string, number | undefined> = {};\n for (const [type, data] of Object.entries(containerData)) {\n const origin = ownOrigin(data);\n out[type] = origin === undefined ? undefined : thresholdOrigin(origin);\n }\n return out;\n};\n\n/**\n * The anchor-source track: the selected video track, falling back to audio\n * (audio-only). Deterministic — never \"first resolved\", which is race-dependent\n * under concurrent resolves.\n */\nfunction referenceTrackId(ctx: GateFirstParseContext): string | undefined {\n return ctx.selectedVideoTrackId ?? ctx.selectedAudioTrackId;\n}\n\n/**\n * The live-anchor first-parse gate (the \"parses align\" step of the establishment\n * order). Open for the reference track itself — its local-from-0 placement *is*\n * the presentation timeline (presentation-0 = the join point). Every other track\n * holds until the reference's first parse settles the anchor question: no PDT →\n * local placement is already correct, proceed; PDT anchor → hold until the\n * establishment reactor stamps it onto this track, so the parse takes the\n * `placeOnAnchor` path. Establishment is sticky-once, so an unanchored first\n * parse would be permanently misaligned — that's the race this gate closes.\n *\n * Pairs with the {@link establishStartMediaTime} reactor (the stamp that opens\n * the anchored branch); wire both or neither.\n */\nexport const gateFirstParseOnAnchor: GateFirstParse = (presentation, ctx, trackId) => {\n const referenceId = referenceTrackId(ctx);\n if (referenceId === undefined || referenceId === trackId) return true;\n if (!isResolvedPresentation(presentation)) return false;\n const reference = findTrackById(presentation, referenceId);\n // A dangling reference id can never anchor — don't deadlock on it.\n if (!reference) return true;\n if (!isResolvedTrack(reference)) return false;\n if (reference.startDate === undefined) return true;\n return findTrackById(presentation, trackId)?.startDate !== undefined;\n};\n\nexport interface EstablishStartMediaTimeConfig {\n /** The reduce seam (coordination knob). Defaults to {@link deriveSharedMinStartMediaTime}. */\n deriveStartMediaTime?: DeriveStartMediaTime;\n}\n\n// ============================================================================\n// REACTOR (owns the transient slot; derives + consumes onto the model)\n// ============================================================================\n\n/**\n * Stamp the frozen wall-clock anchor as `startDate` onto every track that lacks\n * one (idempotent — same reference when nothing moved). Unresolved shells pick\n * it up as the `placeOnAnchor` preset; covering *all* tracks in one pass means\n * any track selected later — an ABR rung, another audio language, late captions\n * — resolves already anchored. The reference track's own parser-computed\n * `startDate` reads back as the anchor, so it's naturally left untouched.\n */\nfunction stampStartDates(presentation: Presentation, anchor: number): Presentation {\n let changed = false;\n const selectionSets = presentation.selectionSets.map((selectionSet) => ({\n ...selectionSet,\n switchingSets: selectionSet.switchingSets.map((switchingSet) => ({\n ...switchingSet,\n tracks: switchingSet.tracks.map((track) => {\n if (track.startDate !== undefined) return track;\n changed = true;\n return { ...track, startDate: anchor };\n }),\n })),\n }));\n return changed ? ({ ...presentation, selectionSets } as Presentation) : presentation;\n}\n\n/** Stamp the derived per-track `startMediaTime` onto the model (idempotent — same reference when nothing moved). */\nfunction stampTracks(presentation: Presentation, startMediaTimes: Record<string, number | undefined>): Presentation {\n let changed = false;\n const selectionSets = presentation.selectionSets.map((selectionSet) => ({\n ...selectionSet,\n switchingSets: selectionSet.switchingSets.map((switchingSet) => ({\n ...switchingSet,\n tracks: switchingSet.tracks.map((track) => {\n const startMediaTime = startMediaTimes[track.type];\n if (startMediaTime === undefined || track.startMediaTime === startMediaTime) return track;\n changed = true;\n return { ...track, startMediaTime };\n }),\n })),\n }));\n return changed ? ({ ...presentation, selectionSets } as Presentation) : presentation;\n}\n\ntype EstablishFsmState = 'inactive' | 'monitoring' | 'established';\n\ninterface EstablishStartMediaTimeDeps {\n state: {\n presentation: Signal<EstablishStartMediaTimeState['presentation']>;\n mediaContainerData: Signal<EstablishStartMediaTimeState['mediaContainerData']>;\n // Optional so the one behavior composes across video-only / audio-only / both,\n // like other cross-track-type behaviors (present at runtime iff a sibling owns it).\n selectedVideoTrackId?: ReadonlySignal<EstablishStartMediaTimeState['selectedVideoTrackId']>;\n selectedAudioTrackId?: ReadonlySignal<EstablishStartMediaTimeState['selectedAudioTrackId']>;\n };\n config?: EstablishStartMediaTimeConfig;\n}\n\nfunction establishStartMediaTimeSetup({\n state,\n config = {},\n}: EstablishStartMediaTimeDeps): Reactor<EstablishFsmState | 'destroying' | 'destroyed'> {\n const derive = config.deriveStartMediaTime ?? deriveSharedMinStartMediaTime;\n\n const selectionContext = (): DeriveStartMediaTimeContext => ({\n selectedVideoTrackId: state.selectedVideoTrackId?.get(),\n selectedAudioTrackId: state.selectedAudioTrackId?.get(),\n });\n\n /** Established once the selected A/V tracks (whichever exist) carry `startMediaTime`. */\n const established = (): boolean => {\n const presentation = state.presentation.get();\n if (!isResolvedPresentation(presentation)) return false;\n const ids = [state.selectedVideoTrackId?.get(), state.selectedAudioTrackId?.get()].filter(\n (id): id is string => id !== undefined\n );\n return ids.length > 0 && ids.every((id) => findTrackById(presentation, id)?.startMediaTime !== undefined);\n };\n\n return createMachineReactor<EstablishFsmState>({\n initial: 'inactive',\n monitor: () => {\n if (!isResolvedPresentation(state.presentation.get())) return 'inactive';\n return established() ? 'established' : 'monitoring';\n },\n states: {\n // Fresh per source: clear the transient slot so a new source re-discovers.\n inactive: { entry: () => state.mediaContainerData.set(undefined) },\n // The establishment order, as two effects: anchor first, derive second.\n // Both stamps are sticky per source — disabled on entry to `established`,\n // and a new source re-enters through `inactive`.\n monitoring: {\n effects: [\n // Anchor: freeze the wall-clock origin from the reference track's\n // first parse and stamp it onto every other track (the gate's open\n // condition + the `placeOnAnchor` preset for shells). Reads the\n // presentation tracked — it must see the reference resolve — and its\n // own write converges immediately (the stamp is idempotent, so the\n // re-run sets an identical value and stops).\n () => {\n const presentation = state.presentation.get();\n if (!isResolvedPresentation(presentation)) return;\n const referenceId = referenceTrackId(selectionContext());\n const reference = referenceId === undefined ? undefined : findTrackById(presentation, referenceId);\n if (!reference || !isResolvedTrack(reference)) return;\n const anchor = reference.startDate;\n if (anchor === undefined) return;\n update(state.presentation as Signal<MaybeResolvedPresentation>, (current) =>\n stampStartDates(current as Presentation, anchor)\n );\n },\n // Derive: reduce the accumulating container data into per-track startMediaTime\n // and stamp it onto the model. Re-runs as discover fills the slot; the update\n // reads presentation untracked, so no self-loop.\n () => {\n const containerData = state.mediaContainerData.get();\n if (!containerData) return;\n const startMediaTimes = derive(containerData, selectionContext());\n // `current` is always resolved here — the monitor gates `monitoring` on a\n // resolved presentation and transitions before effects re-run — so we cast\n // to `Presentation` rather than re-narrowing.\n update(state.presentation as Signal<MaybeResolvedPresentation>, (current) =>\n stampTracks(current as Presentation, startMediaTimes)\n );\n },\n ],\n },\n established: {},\n },\n });\n}\n\nexport const establishStartMediaTime: Behavior<\n {\n presentation: Signal<EstablishStartMediaTimeState['presentation']>;\n mediaContainerData: Signal<EstablishStartMediaTimeState['mediaContainerData']>;\n },\n Record<never, never>,\n EstablishStartMediaTimeConfig\n> = {\n stateKeys: ['presentation', 'mediaContainerData'],\n contextKeys: [],\n setup: establishStartMediaTimeSetup,\n};\n"],"mappings":";;;;;;;;;;;AAqEA,SAAS,UAAU,MAA0D;CAC3E,MAAM,EAAE,WAAW,qBAAqB,qBAAqB,QAAQ,CAAC;CACtE,OAAO,aAAa,QAAQ,uBAAuB,QAAQ,oBAAoB,OAC3E,sBAAsB,YAAY,mBAClC,KAAA;AACN;;AAiBA,SAAS,gBAAgB,QAAwB;CAC/C,OAAO,SAAA,IAAsC,IAAI;AACnD;;;;;;;;;;;;;;;;;AAkBA,MAAa,iCAAuD,eAAe,QAAQ;CACzF,MAAM,oBAA8B,CAAC;CACrC,IAAI,IAAI,wBAAwB,MAAM,kBAAkB,KAAK,OAAO;CACpE,IAAI,IAAI,wBAAwB,MAAM,kBAAkB,KAAK,OAAO;CAGpE,MAAM,WAFQ,kBAAkB,SAAS,IAAI,oBAAoB,OAAO,KAAK,aAAa,EAAA,CAEpE,KAAK,SAAS,UAAU,cAAc,KAAK,CAAC;CAElE,IAAI,QAAQ,WAAW,KAAK,QAAQ,MAAM,WAAW,WAAW,KAAA,CAAS,GAAG,OAAO,CAAC;CAEpF,MAAM,SAAS,gBAAgB,KAAK,IAAI,GAAI,OAAoB,CAAC;CACjE,MAAM,MAA0C,CAAC;CACjD,KAAK,MAAM,QAAQ,OAAO,KAAK,aAAa,GAAG,IAAI,QAAQ;CAC3D,OAAO;AACT;;;;;;;;;;;;AAaA,MAAa,+BAAqD,kBAAkB;CAClF,MAAM,MAA0C,CAAC;CACjD,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,aAAa,GAAG;EACxD,MAAM,SAAS,UAAU,IAAI;EAC7B,IAAI,QAAQ,WAAW,KAAA,IAAY,KAAA,IAAY,gBAAgB,MAAM;CACvE;CACA,OAAO;AACT;;;;;;AAOA,SAAS,iBAAiB,KAAgD;CACxE,OAAO,IAAI,wBAAwB,IAAI;AACzC;;;;;;;;;;;;;;AAeA,MAAa,0BAA0C,cAAc,KAAK,YAAY;CACpF,MAAM,cAAc,iBAAiB,GAAG;CACxC,IAAI,gBAAgB,KAAA,KAAa,gBAAgB,SAAS,OAAO;CACjE,IAAI,CAAC,uBAAuB,YAAY,GAAG,OAAO;CAClD,MAAM,YAAY,cAAc,cAAc,WAAW;CAEzD,IAAI,CAAC,WAAW,OAAO;CACvB,IAAI,CAAC,gBAAgB,SAAS,GAAG,OAAO;CACxC,IAAI,UAAU,cAAc,KAAA,GAAW,OAAO;CAC9C,OAAO,cAAc,cAAc,OAAO,CAAC,EAAE,cAAc,KAAA;AAC7D;;;;;;;;;AAmBA,SAAS,gBAAgB,cAA4B,QAA8B;CACjF,IAAI,UAAU;CACd,MAAM,gBAAgB,aAAa,cAAc,KAAK,kBAAkB;EACtE,GAAG;EACH,eAAe,aAAa,cAAc,KAAK,kBAAkB;GAC/D,GAAG;GACH,QAAQ,aAAa,OAAO,KAAK,UAAU;IACzC,IAAI,MAAM,cAAc,KAAA,GAAW,OAAO;IAC1C,UAAU;IACV,OAAO;KAAE,GAAG;KAAO,WAAW;IAAO;GACvC,CAAC;EACH,EAAE;CACJ,EAAE;CACF,OAAO,UAAW;EAAE,GAAG;EAAc;CAAc,IAAqB;AAC1E;;AAGA,SAAS,YAAY,cAA4B,iBAAmE;CAClH,IAAI,UAAU;CACd,MAAM,gBAAgB,aAAa,cAAc,KAAK,kBAAkB;EACtE,GAAG;EACH,eAAe,aAAa,cAAc,KAAK,kBAAkB;GAC/D,GAAG;GACH,QAAQ,aAAa,OAAO,KAAK,UAAU;IACzC,MAAM,iBAAiB,gBAAgB,MAAM;IAC7C,IAAI,mBAAmB,KAAA,KAAa,MAAM,mBAAmB,gBAAgB,OAAO;IACpF,UAAU;IACV,OAAO;KAAE,GAAG;KAAO;IAAe;GACpC,CAAC;EACH,EAAE;CACJ,EAAE;CACF,OAAO,UAAW;EAAE,GAAG;EAAc;CAAc,IAAqB;AAC1E;AAgBA,SAAS,6BAA6B,EACpC,OACA,SAAS,CAAC,KAC6E;CACvF,MAAM,SAAS,OAAO,wBAAwB;CAE9C,MAAM,0BAAuD;EAC3D,sBAAsB,MAAM,sBAAsB,IAAI;EACtD,sBAAsB,MAAM,sBAAsB,IAAI;CACxD;;CAGA,MAAM,oBAA6B;EACjC,MAAM,eAAe,MAAM,aAAa,IAAI;EAC5C,IAAI,CAAC,uBAAuB,YAAY,GAAG,OAAO;EAClD,MAAM,MAAM,CAAC,MAAM,sBAAsB,IAAI,GAAG,MAAM,sBAAsB,IAAI,CAAC,CAAC,CAAC,QAChF,OAAqB,OAAO,KAAA,CAC/B;EACA,OAAO,IAAI,SAAS,KAAK,IAAI,OAAO,OAAO,cAAc,cAAc,EAAE,CAAC,EAAE,mBAAmB,KAAA,CAAS;CAC1G;CAEA,OAAO,qBAAwC;EAC7C,SAAS;EACT,eAAe;GACb,IAAI,CAAC,uBAAuB,MAAM,aAAa,IAAI,CAAC,GAAG,OAAO;GAC9D,OAAO,YAAY,IAAI,gBAAgB;EACzC;EACA,QAAQ;GAEN,UAAU,EAAE,aAAa,MAAM,mBAAmB,IAAI,KAAA,CAAS,EAAE;GAIjE,YAAY,EACV,SAAS,OAOD;IACJ,MAAM,eAAe,MAAM,aAAa,IAAI;IAC5C,IAAI,CAAC,uBAAuB,YAAY,GAAG;IAC3C,MAAM,cAAc,iBAAiB,iBAAiB,CAAC;IACvD,MAAM,YAAY,gBAAgB,KAAA,IAAY,KAAA,IAAY,cAAc,cAAc,WAAW;IACjG,IAAI,CAAC,aAAa,CAAC,gBAAgB,SAAS,GAAG;IAC/C,MAAM,SAAS,UAAU;IACzB,IAAI,WAAW,KAAA,GAAW;IAC1B,OAAO,MAAM,eAAoD,YAC/D,gBAAgB,SAAyB,MAAM,CACjD;GACF,SAIM;IACJ,MAAM,gBAAgB,MAAM,mBAAmB,IAAI;IACnD,IAAI,CAAC,eAAe;IACpB,MAAM,kBAAkB,OAAO,eAAe,iBAAiB,CAAC;IAIhE,OAAO,MAAM,eAAoD,YAC/D,YAAY,SAAyB,eAAe,CACtD;GACF,CACF,EACF;GACA,aAAa,CAAC;EAChB;CACF,CAAC;AACH;AAEA,MAAa,0BAOT;CACF,WAAW,CAAC,gBAAgB,oBAAoB;CAChD,aAAa,CAAC;CACd,OAAO;AACT"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { MaybeResolvedPresentation, Presentation } from "../../media/types/index.js";
|
|
2
|
+
import "../../core/signals/primitives.js";
|
|
3
|
+
import "../../core/reactors/create-machine-reactor.js";
|
|
4
|
+
//#region src/playback/behaviors/resolve-presentation.d.ts
|
|
5
|
+
type ParsePresentation = (text: string, presentation: MaybeResolvedPresentation) => Presentation;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { ParsePresentation };
|
|
8
|
+
//# sourceMappingURL=resolve-presentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-presentation.d.ts","names":[],"sources":["../../../../src/playback/behaviors/resolve-presentation.ts"],"mappings":";;;;KAiDY,qBAAqB,cAAc,cAAc,8BAA8B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { computed } from "../../core/signals/primitives.js";
|
|
2
|
+
import { defineBehavior } from "../../core/composition/create-composition.js";
|
|
3
|
+
import { createMachineReactor } from "../../core/reactors/create-machine-reactor.js";
|
|
4
|
+
import { isResolvedPresentation } from "../../media/types/index.js";
|
|
5
|
+
import { fetchResolvable, getResponseText } from "../../network/fetch.js";
|
|
6
|
+
import { isBlockingPreload } from "../../media/utils/preload.js";
|
|
7
|
+
//#region src/playback/behaviors/resolve-presentation.ts
|
|
8
|
+
/**
|
|
9
|
+
* **Resolve an unresolved presentation by fetching and parsing its manifest.**
|
|
10
|
+
*
|
|
11
|
+
* Reads `state.presentation`; when it holds `{ url }` (unresolved) and the
|
|
12
|
+
* preload / load-activation gate is met, fetches the manifest, parses it via
|
|
13
|
+
* the **required** `config.parsePresentation`, and writes the resolved
|
|
14
|
+
* `Presentation` back to the same slot. The behavior is format-neutral: the
|
|
15
|
+
* composing engine wires in its parser (e.g. the HLS engine supplies the
|
|
16
|
+
* multivariant-playlist parser).
|
|
17
|
+
*
|
|
18
|
+
* Source-identity-driven, expressed as a 4-state machine:
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* 'preconditions-unmet' → 'idle' → 'resolving' → 'resolved'
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* - `'preconditions-unmet'`: no presentation, or presentation has no URL.
|
|
25
|
+
* - `'idle'`: URL present, unresolved, gate unmet (blocking preload + no
|
|
26
|
+
* load-activation). Waits for the gate to open.
|
|
27
|
+
* - `'resolving'`: URL present, unresolved, gate met. Entry starts the fetch
|
|
28
|
+
* and returns the AbortController — the reactor calls `.abort()` on state
|
|
29
|
+
* exit, so source change / gate-close / destroy all cancel cleanly.
|
|
30
|
+
* - `'resolved'`: `state.presentation` holds a resolved `Presentation`.
|
|
31
|
+
*
|
|
32
|
+
* Gate semantics: `state.preload` (or `config.defaultPreload`, default
|
|
33
|
+
* `'metadata'`, when state.preload is unset) blocks resolution when its
|
|
34
|
+
* value is `'none'` (see `isBlockingPreload` in `media/utils/preload`).
|
|
35
|
+
* `state.loadActivated` is an override — true bypasses the preload gate
|
|
36
|
+
* entirely.
|
|
37
|
+
*
|
|
38
|
+
* Multi-writer with the engine adapter, which writes the initial unresolved
|
|
39
|
+
* `{ url }` to `state.presentation` from src input. Different domains
|
|
40
|
+
* (config-input vs. derived state via fetch) — legitimate multi-writer.
|
|
41
|
+
*/
|
|
42
|
+
function deriveState(presentation, preload, loadActivated, defaultPreload) {
|
|
43
|
+
if (!presentation?.url) return "preconditions-unmet";
|
|
44
|
+
if (isResolvedPresentation(presentation)) return "resolved";
|
|
45
|
+
return !!loadActivated || !isBlockingPreload(preload, defaultPreload) ? "resolving" : "idle";
|
|
46
|
+
}
|
|
47
|
+
function resolvePresentationSetup({ state, config }) {
|
|
48
|
+
const { parsePresentation } = config;
|
|
49
|
+
const defaultPreload = config.defaultPreload ?? "metadata";
|
|
50
|
+
const derivedStateSignal = computed(() => deriveState(state.presentation.get(), state.preload.get(), state.loadActivated.get(), defaultPreload));
|
|
51
|
+
return createMachineReactor({
|
|
52
|
+
initial: "preconditions-unmet",
|
|
53
|
+
monitor: () => derivedStateSignal.get(),
|
|
54
|
+
states: {
|
|
55
|
+
"preconditions-unmet": {},
|
|
56
|
+
idle: {},
|
|
57
|
+
resolving: { entry: () => {
|
|
58
|
+
const presentation = state.presentation.get();
|
|
59
|
+
const ac = new AbortController();
|
|
60
|
+
fetchResolvable(presentation, { signal: ac.signal }).then((response) => getResponseText(response)).then((text) => {
|
|
61
|
+
const parsed = parsePresentation(text, presentation);
|
|
62
|
+
state.presentation.set(parsed);
|
|
63
|
+
}).catch((error) => {
|
|
64
|
+
if (error instanceof Error && error.name === "AbortError") return;
|
|
65
|
+
console.error("[resolvePresentation] manifest fetch/parse failed:", error);
|
|
66
|
+
});
|
|
67
|
+
return ac;
|
|
68
|
+
} },
|
|
69
|
+
resolved: {}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const resolvePresentation = defineBehavior({
|
|
74
|
+
stateKeys: [
|
|
75
|
+
"presentation",
|
|
76
|
+
"preload",
|
|
77
|
+
"loadActivated"
|
|
78
|
+
],
|
|
79
|
+
contextKeys: [],
|
|
80
|
+
setup: resolvePresentationSetup
|
|
81
|
+
});
|
|
82
|
+
//#endregion
|
|
83
|
+
export { resolvePresentation };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=resolve-presentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-presentation.js","names":[],"sources":["../../../../src/playback/behaviors/resolve-presentation.ts"],"sourcesContent":["/**\n * **Resolve an unresolved presentation by fetching and parsing its manifest.**\n *\n * Reads `state.presentation`; when it holds `{ url }` (unresolved) and the\n * preload / load-activation gate is met, fetches the manifest, parses it via\n * the **required** `config.parsePresentation`, and writes the resolved\n * `Presentation` back to the same slot. The behavior is format-neutral: the\n * composing engine wires in its parser (e.g. the HLS engine supplies the\n * multivariant-playlist parser).\n *\n * Source-identity-driven, expressed as a 4-state machine:\n *\n * ```\n * 'preconditions-unmet' → 'idle' → 'resolving' → 'resolved'\n * ```\n *\n * - `'preconditions-unmet'`: no presentation, or presentation has no URL.\n * - `'idle'`: URL present, unresolved, gate unmet (blocking preload + no\n * load-activation). Waits for the gate to open.\n * - `'resolving'`: URL present, unresolved, gate met. Entry starts the fetch\n * and returns the AbortController — the reactor calls `.abort()` on state\n * exit, so source change / gate-close / destroy all cancel cleanly.\n * - `'resolved'`: `state.presentation` holds a resolved `Presentation`.\n *\n * Gate semantics: `state.preload` (or `config.defaultPreload`, default\n * `'metadata'`, when state.preload is unset) blocks resolution when its\n * value is `'none'` (see `isBlockingPreload` in `media/utils/preload`).\n * `state.loadActivated` is an override — true bypasses the preload gate\n * entirely.\n *\n * Multi-writer with the engine adapter, which writes the initial unresolved\n * `{ url }` to `state.presentation` from src input. Different domains\n * (config-input vs. derived state via fetch) — legitimate multi-writer.\n */\nimport { defineBehavior } from '../../core/composition/create-composition';\nimport type { Reactor } from '../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal, type Signal } from '../../core/signals/primitives';\nimport { isResolvedPresentation, type MaybeResolvedPresentation, type Presentation } from '../../media/types';\nimport { DEFAULT_PRELOAD, isBlockingPreload, type StandardPreload } from '../../media/utils/preload';\nimport { fetchResolvable, getResponseText } from '../../network/fetch';\n\nexport interface PresentationState {\n presentation?: MaybeResolvedPresentation;\n preload?: 'auto' | 'metadata' | 'none' | undefined;\n /** True once a preload-overriding event has fired for the current source — enables resolution regardless of preload. */\n loadActivated?: boolean;\n}\n\nexport type ParsePresentation = (text: string, presentation: MaybeResolvedPresentation) => Presentation;\n\nexport interface ResolvePresentationConfig {\n /**\n * Parses a manifest body into a resolved `Presentation`. **Required** —\n * the behavior is format-neutral and the composing engine supplies its\n * own parser (HLS, DASH, etc.).\n */\n parsePresentation: ParsePresentation;\n /**\n * Fallback used when `state.preload` is unset (undefined / empty) for the\n * resolution-gate decision. Defaults to `'metadata'`, matching\n * `syncPreload`'s own `defaultPreload`.\n */\n defaultPreload?: StandardPreload;\n}\n\nexport type ResolvePresentationState = 'preconditions-unmet' | 'idle' | 'resolving' | 'resolved';\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n preload: PresentationState['preload'],\n loadActivated: boolean | undefined,\n defaultPreload: StandardPreload\n): ResolvePresentationState {\n if (!presentation?.url) return 'preconditions-unmet';\n if (isResolvedPresentation(presentation)) return 'resolved';\n const gateOpen = !!loadActivated || !isBlockingPreload(preload, defaultPreload);\n return gateOpen ? 'resolving' : 'idle';\n}\n\nfunction resolvePresentationSetup({\n state,\n config,\n}: {\n state: {\n presentation: Signal<PresentationState['presentation']>;\n preload: ReadonlySignal<PresentationState['preload']>;\n loadActivated: ReadonlySignal<PresentationState['loadActivated']>;\n };\n config: ResolvePresentationConfig;\n}): Reactor<ResolvePresentationState | 'destroying' | 'destroyed'> {\n const { parsePresentation } = config;\n const defaultPreload: StandardPreload = config.defaultPreload ?? DEFAULT_PRELOAD;\n\n const derivedStateSignal = computed(() =>\n deriveState(state.presentation.get(), state.preload.get(), state.loadActivated.get(), defaultPreload)\n );\n\n return createMachineReactor<ResolvePresentationState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n idle: {},\n resolving: {\n entry: () => {\n const presentation = state.presentation.get()!;\n const ac = new AbortController();\n\n fetchResolvable(presentation, { signal: ac.signal })\n .then((response) => getResponseText(response))\n .then((text) => {\n const parsed = parsePresentation(text, presentation);\n state.presentation.set(parsed);\n })\n .catch((error) => {\n if (error instanceof Error && error.name === 'AbortError') return;\n // TODO(error-management): route to a state-error slot once one exists.\n console.error('[resolvePresentation] manifest fetch/parse failed:', error);\n });\n\n return ac;\n },\n },\n resolved: {},\n },\n });\n}\n\nexport const resolvePresentation = defineBehavior({\n stateKeys: ['presentation', 'preload', 'loadActivated'],\n contextKeys: [],\n setup: resolvePresentationSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,SAAS,YACP,cACA,SACA,eACA,gBAC0B;CAC1B,IAAI,CAAC,cAAc,KAAK,OAAO;CAC/B,IAAI,uBAAuB,YAAY,GAAG,OAAO;CAEjD,OADiB,CAAC,CAAC,iBAAiB,CAAC,kBAAkB,SAAS,cAAc,IAC5D,cAAc;AAClC;AAEA,SAAS,yBAAyB,EAChC,OACA,UAQiE;CACjE,MAAM,EAAE,sBAAsB;CAC9B,MAAM,iBAAkC,OAAO,kBAAA;CAE/C,MAAM,qBAAqB,eACzB,YAAY,MAAM,aAAa,IAAI,GAAG,MAAM,QAAQ,IAAI,GAAG,MAAM,cAAc,IAAI,GAAG,cAAc,CACtG;CAEA,OAAO,qBAA+C;EACpD,SAAS;EACT,eAAe,mBAAmB,IAAI;EACtC,QAAQ;GACN,uBAAuB,CAAC;GACxB,MAAM,CAAC;GACP,WAAW,EACT,aAAa;IACX,MAAM,eAAe,MAAM,aAAa,IAAI;IAC5C,MAAM,KAAK,IAAI,gBAAgB;IAE/B,gBAAgB,cAAc,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CACjD,MAAM,aAAa,gBAAgB,QAAQ,CAAC,CAAC,CAC7C,MAAM,SAAS;KACd,MAAM,SAAS,kBAAkB,MAAM,YAAY;KACnD,MAAM,aAAa,IAAI,MAAM;IAC/B,CAAC,CAAC,CACD,OAAO,UAAU;KAChB,IAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;KAE3D,QAAQ,MAAM,sDAAsD,KAAK;IAC3E,CAAC;IAEH,OAAO;GACT,EACF;GACA,UAAU,CAAC;EACb;CACF,CAAC;AACH;AAEA,MAAa,sBAAsB,eAAe;CAChD,WAAW;EAAC;EAAgB;EAAW;CAAe;CACtD,aAAa,CAAC;CACd,OAAO;AACT,CAAC"}
|