@videojs/spf 10.0.0-beta.3 → 10.0.0-beta.30
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,42 @@
|
|
|
1
|
+
import { update } from "../../core/signals/primitives.js";
|
|
2
|
+
import { findTrackById } from "../../media/utils/tracks.js";
|
|
3
|
+
import { addFailedCdn, getCdnId } from "../../media/utils/cdn.js";
|
|
4
|
+
//#region src/playback/primitives/failover-fetch.ts
|
|
5
|
+
/**
|
|
6
|
+
* Decorate a fetch so a failed request trips the **selected track's** CDN into
|
|
7
|
+
* `failedCdns`. The decorated fetch's type is preserved, so this wraps both
|
|
8
|
+
* `resolve-track`'s playlist `FetchText` and the segment loaders' `FetchBytes`.
|
|
9
|
+
*
|
|
10
|
+
* The CDN id comes from the selected track's media-playlist URL, never the
|
|
11
|
+
* failed addressable: a segment URL resolves relative to its playlist and, per
|
|
12
|
+
* RFC 3986, drops the playlist's query string (`…/r.m3u8?cdn=fastly` → `…/0.ts`),
|
|
13
|
+
* so a query-keyed `getCdnId` (e.g. Mux's `cdn=`) keyed on it would derive an id
|
|
14
|
+
* that never matches the ones `deriveCdnPriority` / track-switching build from
|
|
15
|
+
* `track.url`. The in-flight fetch belongs to the selected track — a source or
|
|
16
|
+
* track switch aborts it, and aborts don't trip — so the selected track is the
|
|
17
|
+
* right CDN to fail over. For `resolve-track` the resolving track *is* the
|
|
18
|
+
* selected track, so this is identical to keying on its addressable.
|
|
19
|
+
*
|
|
20
|
+
* No-op when no failover monitor is composed (it owns the signal) or the
|
|
21
|
+
* selected track can't be located.
|
|
22
|
+
*/
|
|
23
|
+
function failoverFetch(baseFetch, state, config) {
|
|
24
|
+
const getCdnId$1 = config.getCdnId ?? getCdnId;
|
|
25
|
+
return (async (addressable, options) => {
|
|
26
|
+
try {
|
|
27
|
+
return await baseFetch(addressable, options);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (!options?.signal?.aborted && state.failedCdns) {
|
|
30
|
+
const presentation = state.presentation.get();
|
|
31
|
+
const trackId = state[config.selectedKey].get();
|
|
32
|
+
const track = presentation && trackId ? findTrackById(presentation, trackId) : void 0;
|
|
33
|
+
if (track) update(state.failedCdns, (cdns) => addFailedCdn(cdns, getCdnId$1(track.url)));
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { failoverFetch };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=failover-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failover-fetch.js","names":["getCdnId","defaultGetCdnId"],"sources":["../../../../src/playback/primitives/failover-fetch.ts"],"sourcesContent":["import { type ReadonlySignal, type Signal, update } from '../../core/signals/primitives';\nimport type { MaybeResolvedPresentation } from '../../media/types';\nimport { addFailedCdn, getCdnId as defaultGetCdnId, type GetCdnId } from '../../media/utils/cdn';\nimport { findTrackById } from '../../media/utils/tracks';\nimport type { FetchOptions, Resource } from '../../network/fetch';\n\ntype SelectedTrackKey = 'selectedVideoTrackId' | 'selectedAudioTrackId' | 'selectedTextTrackId';\n\n/**\n * State a failover-decorated fetch reads: the presentation, the per-type\n * selected-track slot, and the failover monitor's `failedCdns`.\n *\n * `failedCdns` is *optional* — the failover monitor owns that slot, so a\n * behavior's narrow state is assignable here without the behavior declaring it\n * (and the intersection shares keys, so it isn't a weak type). When no monitor\n * is composed the slot is absent and tracking no-ops.\n */\ntype FailoverState<K extends SelectedTrackKey> = {\n presentation: ReadonlySignal<MaybeResolvedPresentation | undefined>;\n failedCdns?: Signal<string[] | undefined>;\n} & { [P in K]: ReadonlySignal<string | undefined> };\n\n/** Any `Resource`-addressable fetch — both `FetchText` and `FetchBytes` qualify. */\ntype FailoverableFetch = (addressable: Resource, options?: FetchOptions) => Promise<unknown>;\n\n/**\n * Decorate a fetch so a failed request trips the **selected track's** CDN into\n * `failedCdns`. The decorated fetch's type is preserved, so this wraps both\n * `resolve-track`'s playlist `FetchText` and the segment loaders' `FetchBytes`.\n *\n * The CDN id comes from the selected track's media-playlist URL, never the\n * failed addressable: a segment URL resolves relative to its playlist and, per\n * RFC 3986, drops the playlist's query string (`…/r.m3u8?cdn=fastly` → `…/0.ts`),\n * so a query-keyed `getCdnId` (e.g. Mux's `cdn=`) keyed on it would derive an id\n * that never matches the ones `deriveCdnPriority` / track-switching build from\n * `track.url`. The in-flight fetch belongs to the selected track — a source or\n * track switch aborts it, and aborts don't trip — so the selected track is the\n * right CDN to fail over. For `resolve-track` the resolving track *is* the\n * selected track, so this is identical to keying on its addressable.\n *\n * No-op when no failover monitor is composed (it owns the signal) or the\n * selected track can't be located.\n */\nexport function failoverFetch<K extends SelectedTrackKey, Fetch extends FailoverableFetch>(\n baseFetch: Fetch,\n state: FailoverState<K>,\n config: { selectedKey: K; getCdnId?: GetCdnId }\n): Fetch {\n const getCdnId = config.getCdnId ?? defaultGetCdnId;\n return (async (addressable: Resource, options?: FetchOptions) => {\n try {\n return await baseFetch(addressable, options);\n } catch (error) {\n if (!options?.signal?.aborted && state.failedCdns) {\n const presentation = state.presentation.get();\n const trackId = state[config.selectedKey].get();\n const track = presentation && trackId ? findTrackById(presentation, trackId) : undefined;\n if (track) update(state.failedCdns, (cdns) => addFailedCdn(cdns, getCdnId(track.url)));\n }\n throw error;\n }\n }) as Fetch;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,cACd,WACA,OACA,QACO;CACP,MAAMA,aAAW,OAAO,YAAYC;CACpC,QAAQ,OAAO,aAAuB,YAA2B;EAC/D,IAAI;GACF,OAAO,MAAM,UAAU,aAAa,OAAO;EAC7C,SAAS,OAAO;GACd,IAAI,CAAC,SAAS,QAAQ,WAAW,MAAM,YAAY;IACjD,MAAM,eAAe,MAAM,aAAa,IAAI;IAC5C,MAAM,UAAU,MAAM,OAAO,YAAY,CAAC,IAAI;IAC9C,MAAM,QAAQ,gBAAgB,UAAU,cAAc,cAAc,OAAO,IAAI,KAAA;IAC/E,IAAI,OAAO,OAAO,MAAM,aAAa,SAAS,aAAa,MAAMD,WAAS,MAAM,GAAG,CAAC,CAAC;GACvF;GACA,MAAM;EACR;CACF;AACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/playback/primitives/head-peek.ts
|
|
2
|
+
/**
|
|
3
|
+
* Eager head-of-stream peek. Pulls chunks from an async byte stream only until
|
|
4
|
+
* `tryParse` signals it has read what it needs (or the stream ends), then returns
|
|
5
|
+
* a stream that re-emits the pulled head followed by the untouched tail — so
|
|
6
|
+
* reading a head-of-stream mp4 box (`mdhd` / `tfdt`) doesn't break streaming of
|
|
7
|
+
* the append. Once the caller has what it wants, the rest streams as normal.
|
|
8
|
+
*/
|
|
9
|
+
function concat(chunks) {
|
|
10
|
+
if (chunks.length === 1) return chunks[0];
|
|
11
|
+
const total = chunks.reduce((n, c) => n + c.length, 0);
|
|
12
|
+
const out = new Uint8Array(total);
|
|
13
|
+
let offset = 0;
|
|
14
|
+
for (const chunk of chunks) {
|
|
15
|
+
out.set(chunk, offset);
|
|
16
|
+
offset += chunk.length;
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
/** Re-emit the eagerly-pulled head chunks, then stream the untouched tail. */
|
|
21
|
+
async function* reassemble(head, tail) {
|
|
22
|
+
yield* head;
|
|
23
|
+
for (let next = await tail.next(); !next.done; next = await tail.next()) yield next.value;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Pull chunks until `tryParse(accumulatedHead)` returns `true` — it found and read
|
|
27
|
+
* its box — or the stream ends, then return a stream re-emitting the pulled head
|
|
28
|
+
* followed by the untouched tail. `tryParse` is called with the growing head after
|
|
29
|
+
* each chunk; it performs the side effect (publishing the parsed value) and returns
|
|
30
|
+
* whether it's done.
|
|
31
|
+
*/
|
|
32
|
+
async function peekHead(data, tryParse) {
|
|
33
|
+
const iterator = data[Symbol.asyncIterator]();
|
|
34
|
+
const head = [];
|
|
35
|
+
for (let next = await iterator.next(); !next.done; next = await iterator.next()) {
|
|
36
|
+
head.push(next.value);
|
|
37
|
+
if (tryParse(concat(head))) break;
|
|
38
|
+
}
|
|
39
|
+
return reassemble(head, iterator);
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { peekHead };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=head-peek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head-peek.js","names":[],"sources":["../../../../src/playback/primitives/head-peek.ts"],"sourcesContent":["/**\n * Eager head-of-stream peek. Pulls chunks from an async byte stream only until\n * `tryParse` signals it has read what it needs (or the stream ends), then returns\n * a stream that re-emits the pulled head followed by the untouched tail — so\n * reading a head-of-stream mp4 box (`mdhd` / `tfdt`) doesn't break streaming of\n * the append. Once the caller has what it wants, the rest streams as normal.\n */\n\nfunction concat(chunks: Uint8Array[]): Uint8Array {\n if (chunks.length === 1) return chunks[0]!;\n const total = chunks.reduce((n, c) => n + c.length, 0);\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n}\n\n/** Re-emit the eagerly-pulled head chunks, then stream the untouched tail. */\nasync function* reassemble(head: Uint8Array[], tail: AsyncIterator<Uint8Array>): AsyncIterable<Uint8Array> {\n yield* head;\n for (let next = await tail.next(); !next.done; next = await tail.next()) yield next.value;\n}\n\n/**\n * Pull chunks until `tryParse(accumulatedHead)` returns `true` — it found and read\n * its box — or the stream ends, then return a stream re-emitting the pulled head\n * followed by the untouched tail. `tryParse` is called with the growing head after\n * each chunk; it performs the side effect (publishing the parsed value) and returns\n * whether it's done.\n */\nexport async function peekHead(\n data: AsyncIterable<Uint8Array>,\n tryParse: (bytes: Uint8Array) => boolean\n): Promise<AsyncIterable<Uint8Array>> {\n const iterator = data[Symbol.asyncIterator]();\n const head: Uint8Array[] = [];\n for (let next = await iterator.next(); !next.done; next = await iterator.next()) {\n head.push(next.value);\n if (tryParse(concat(head))) break;\n }\n return reassemble(head, iterator);\n}\n"],"mappings":";;;;;;;;AAQA,SAAS,OAAO,QAAkC;CAChD,IAAI,OAAO,WAAW,GAAG,OAAO,OAAO;CACvC,MAAM,QAAQ,OAAO,QAAQ,GAAG,MAAM,IAAI,EAAE,QAAQ,CAAC;CACrD,MAAM,MAAM,IAAI,WAAW,KAAK;CAChC,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,IAAI,OAAO,MAAM;EACrB,UAAU,MAAM;CAClB;CACA,OAAO;AACT;;AAGA,gBAAgB,WAAW,MAAoB,MAA4D;CACzG,OAAO;CACP,KAAK,IAAI,OAAO,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK,MAAM,OAAO,MAAM,KAAK,KAAK,GAAG,MAAM,KAAK;AACtF;;;;;;;;AASA,eAAsB,SACpB,MACA,UACoC;CACpC,MAAM,WAAW,KAAK,OAAO,cAAc,CAAC;CAC5C,MAAM,OAAqB,CAAC;CAC5B,KAAK,IAAI,OAAO,MAAM,SAAS,KAAK,GAAG,CAAC,KAAK,MAAM,OAAO,MAAM,SAAS,KAAK,GAAG;EAC/E,KAAK,KAAK,KAAK,KAAK;EACpB,IAAI,SAAS,OAAO,IAAI,CAAC,GAAG;CAC9B;CACA,OAAO,WAAW,MAAM,QAAQ;AAClC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { getTracksByType } from "../../media/utils/tracks.js";
|
|
2
|
+
import { liveWindowFor } from "../../media/live-window.js";
|
|
3
|
+
//#region src/playback/primitives/live-window.ts
|
|
4
|
+
/**
|
|
5
|
+
* The id of the timeline-bearing track: the selected video track when present,
|
|
6
|
+
* else the selected audio track. The single pick both the window derivation and
|
|
7
|
+
* the live-latency resolution (`seek-to-live-edge`) share, so they can't drift.
|
|
8
|
+
*/
|
|
9
|
+
function liveTrackId(state) {
|
|
10
|
+
return state.selectedVideoTrackId?.get() ?? state.selectedAudioTrackId?.get();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* One type's window: the selected track's when it's resolved, else any resolved
|
|
14
|
+
* track of the type. The fallback keeps the window from blinking to `null`
|
|
15
|
+
* mid ABR / user switch — all renditions of a type are time-aligned and share
|
|
16
|
+
* the anchor, so a deselected track's window may trail live by up to one reload
|
|
17
|
+
* during the switch gap (acceptable; the selected track's fresh window resumes
|
|
18
|
+
* the moment it resolves). A null blink would flip `seekToLiveEdge` out of
|
|
19
|
+
* `live` and stall the seekable-range writer.
|
|
20
|
+
*/
|
|
21
|
+
function liveWindowForType(presentation, selectedId, type) {
|
|
22
|
+
if (!presentation || selectedId === void 0) return null;
|
|
23
|
+
const selected = liveWindowFor(presentation, selectedId);
|
|
24
|
+
if (selected) return selected;
|
|
25
|
+
for (const track of getTracksByType(presentation, type)) {
|
|
26
|
+
const window = liveWindowFor(presentation, track.id);
|
|
27
|
+
if (window) return window;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function liveWindowFromState(state) {
|
|
32
|
+
const presentation = state.presentation.get();
|
|
33
|
+
const video = liveWindowForType(presentation, state.selectedVideoTrackId?.get(), "video");
|
|
34
|
+
const audio = liveWindowForType(presentation, state.selectedAudioTrackId?.get(), "audio");
|
|
35
|
+
if (video && audio) {
|
|
36
|
+
const start = Math.max(video.start, audio.start);
|
|
37
|
+
const end = Math.min(video.end, audio.end);
|
|
38
|
+
return start < end ? {
|
|
39
|
+
start,
|
|
40
|
+
end
|
|
41
|
+
} : null;
|
|
42
|
+
}
|
|
43
|
+
return video ?? audio;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the live edge — the window bounds plus the target playhead position —
|
|
47
|
+
* from a behavior's setup arguments. Bundles the window geometry and the
|
|
48
|
+
* format-specific latency policy (`config.resolveLiveLatency`) so the consuming
|
|
49
|
+
* behavior never has to compose them; it just forwards its `{ state, config }`.
|
|
50
|
+
* `null` when there is no live edge (VOD / ended / unresolved).
|
|
51
|
+
*
|
|
52
|
+
* Reads signals lazily — call it inside a reactive context (an effect).
|
|
53
|
+
*/
|
|
54
|
+
function getLiveEdge({ state, config }) {
|
|
55
|
+
const window = liveWindowFromState(state);
|
|
56
|
+
if (!window) return null;
|
|
57
|
+
const latency = config?.resolveLiveLatency?.(state.presentation.get(), liveTrackId(state)) ?? 0;
|
|
58
|
+
return {
|
|
59
|
+
...window,
|
|
60
|
+
liveEdgeStart: Math.max(window.start, window.end - latency)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { getLiveEdge, liveTrackId, liveWindowFromState };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=live-window.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-window.js","names":[],"sources":["../../../../src/playback/primitives/live-window.ts"],"sourcesContent":["/**\n * Resolve the live window from engine state — the single call site the\n * seek-to-live-edge and live-seekable-range behaviors share, so their window\n * derivation can't drift apart.\n *\n * The window is the **intersection over the selected A/V tracks'** windows\n * (`max` of starts, `min` of ends): publication skew between the two playlists\n * shrinks the window to what both can actually serve, and the `max` also clamps\n * the legitimate *negative* early `startTime` of a non-reference track whose\n * window precedes the join point (presentation-0) — see\n * `internal/design/spf/live-presentation-timeline-model.md` (Phase 2 window\n * math). With one selected type (video-only / audio-only) its window stands\n * alone.\n *\n * Reads signals lazily — call it inside a reactive context (an effect) so the\n * read tracks `presentation` + the selected-track ids.\n */\nimport type { ReadonlySignal } from '../../core/signals/primitives';\nimport { type LiveWindow, liveWindowFor } from '../../media/live-window';\nimport type { MaybeResolvedPresentation, TrackType } from '../../media/types';\nimport { getTracksByType } from '../../media/utils/tracks';\n\nexport interface LiveWindowState {\n presentation: ReadonlySignal<MaybeResolvedPresentation | undefined>;\n selectedVideoTrackId?: ReadonlySignal<string | undefined>;\n selectedAudioTrackId?: ReadonlySignal<string | undefined>;\n}\n\n/**\n * The id of the timeline-bearing track: the selected video track when present,\n * else the selected audio track. The single pick both the window derivation and\n * the live-latency resolution (`seek-to-live-edge`) share, so they can't drift.\n */\nexport function liveTrackId(state: LiveWindowState): string | undefined {\n return state.selectedVideoTrackId?.get() ?? state.selectedAudioTrackId?.get();\n}\n\n/**\n * One type's window: the selected track's when it's resolved, else any resolved\n * track of the type. The fallback keeps the window from blinking to `null`\n * mid ABR / user switch — all renditions of a type are time-aligned and share\n * the anchor, so a deselected track's window may trail live by up to one reload\n * during the switch gap (acceptable; the selected track's fresh window resumes\n * the moment it resolves). A null blink would flip `seekToLiveEdge` out of\n * `live` and stall the seekable-range writer.\n */\nfunction liveWindowForType(\n presentation: MaybeResolvedPresentation | undefined,\n selectedId: string | undefined,\n type: TrackType\n): LiveWindow | null {\n if (!presentation || selectedId === undefined) return null;\n const selected = liveWindowFor(presentation, selectedId);\n if (selected) return selected;\n for (const track of getTracksByType(presentation, type)) {\n const window = liveWindowFor(presentation, track.id);\n if (window) return window;\n }\n return null;\n}\n\nexport function liveWindowFromState(state: LiveWindowState): LiveWindow | null {\n const presentation = state.presentation.get();\n const video = liveWindowForType(presentation, state.selectedVideoTrackId?.get(), 'video');\n const audio = liveWindowForType(presentation, state.selectedAudioTrackId?.get(), 'audio');\n if (video && audio) {\n const start = Math.max(video.start, audio.start);\n const end = Math.min(video.end, audio.end);\n // A degenerate intersection (disjoint windows — one playlist stalled a full\n // window behind the other) means there is no position both types can serve:\n // no live edge, rather than an invented one.\n return start < end ? { start, end } : null;\n }\n return video ?? audio;\n}\n\n/**\n * Resolve the target live latency (seconds the playhead should trail the live\n * edge) for the timeline-bearing track. Format-specific — supplied by the engine\n * (HLS: `HOLD-BACK`; DASH would use `suggestedPresentationDelay`) — so the live\n * edge stays format-neutral.\n */\nexport type ResolveLiveLatency = (\n presentation: MaybeResolvedPresentation | undefined,\n trackId: string | undefined\n) => number;\n\n/** The live window plus the target playhead position within it. */\nexport interface LiveEdge extends LiveWindow {\n /** Where to sit near the live edge: `end` − live latency, clamped to `start`. */\n liveEdgeStart: number;\n}\n\n/**\n * Resolve the live edge — the window bounds plus the target playhead position —\n * from a behavior's setup arguments. Bundles the window geometry and the\n * format-specific latency policy (`config.resolveLiveLatency`) so the consuming\n * behavior never has to compose them; it just forwards its `{ state, config }`.\n * `null` when there is no live edge (VOD / ended / unresolved).\n *\n * Reads signals lazily — call it inside a reactive context (an effect).\n */\nexport function getLiveEdge({\n state,\n // context, // not needed yet; in the shape for setup-parity when it is\n config,\n}: {\n state: LiveWindowState;\n config?: { resolveLiveLatency?: ResolveLiveLatency };\n}): LiveEdge | null {\n const window = liveWindowFromState(state);\n if (!window) return null;\n const latency = config?.resolveLiveLatency?.(state.presentation.get(), liveTrackId(state)) ?? 0;\n return { ...window, liveEdgeStart: Math.max(window.start, window.end - latency) };\n}\n"],"mappings":";;;;;;;;AAiCA,SAAgB,YAAY,OAA4C;CACtE,OAAO,MAAM,sBAAsB,IAAI,KAAK,MAAM,sBAAsB,IAAI;AAC9E;;;;;;;;;;AAWA,SAAS,kBACP,cACA,YACA,MACmB;CACnB,IAAI,CAAC,gBAAgB,eAAe,KAAA,GAAW,OAAO;CACtD,MAAM,WAAW,cAAc,cAAc,UAAU;CACvD,IAAI,UAAU,OAAO;CACrB,KAAK,MAAM,SAAS,gBAAgB,cAAc,IAAI,GAAG;EACvD,MAAM,SAAS,cAAc,cAAc,MAAM,EAAE;EACnD,IAAI,QAAQ,OAAO;CACrB;CACA,OAAO;AACT;AAEA,SAAgB,oBAAoB,OAA2C;CAC7E,MAAM,eAAe,MAAM,aAAa,IAAI;CAC5C,MAAM,QAAQ,kBAAkB,cAAc,MAAM,sBAAsB,IAAI,GAAG,OAAO;CACxF,MAAM,QAAQ,kBAAkB,cAAc,MAAM,sBAAsB,IAAI,GAAG,OAAO;CACxF,IAAI,SAAS,OAAO;EAClB,MAAM,QAAQ,KAAK,IAAI,MAAM,OAAO,MAAM,KAAK;EAC/C,MAAM,MAAM,KAAK,IAAI,MAAM,KAAK,MAAM,GAAG;EAIzC,OAAO,QAAQ,MAAM;GAAE;GAAO;EAAI,IAAI;CACxC;CACA,OAAO,SAAS;AAClB;;;;;;;;;;AA4BA,SAAgB,YAAY,EAC1B,OAEA,UAIkB;CAClB,MAAM,SAAS,oBAAoB,KAAK;CACxC,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,UAAU,QAAQ,qBAAqB,MAAM,aAAa,IAAI,GAAG,YAAY,KAAK,CAAC,KAAK;CAC9F,OAAO;EAAE,GAAG;EAAQ,eAAe,KAAK,IAAI,OAAO,OAAO,OAAO,MAAM,OAAO;CAAE;AAClF"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { peek, update } from "../../core/signals/primitives.js";
|
|
2
|
+
import { effect } from "../../core/signals/effect.js";
|
|
3
|
+
import { resolveVttSegmentMetadata } from "../../media/text/resolve-vtt-metadata.js";
|
|
4
|
+
import { findTrackById, getTracksByType } from "../../media/utils/tracks.js";
|
|
5
|
+
import { dispatchCuesStep, textStepWiring } from "./text-segment-load-pipeline.js";
|
|
6
|
+
import { dispatchStep, fetchStep } from "./segment-load-pipeline.js";
|
|
7
|
+
import { findMediaTrack, readBaseMediaDecodeTime } from "../../media/mp4/timestamp-origin.js";
|
|
8
|
+
import { peekHead } from "./head-peek.js";
|
|
9
|
+
//#region src/playback/primitives/relocation-pipelines.ts
|
|
10
|
+
/** Assert the relocation state view from the opaque step deps — this module knows the slots the composition provides. */
|
|
11
|
+
function relocationState(deps) {
|
|
12
|
+
return deps.state;
|
|
13
|
+
}
|
|
14
|
+
function containerSlot(deps) {
|
|
15
|
+
return relocationState(deps).mediaContainerData;
|
|
16
|
+
}
|
|
17
|
+
/** Synchronous RMW of the per-type entry — disjoint keys across producers, so no lost update. */
|
|
18
|
+
function writeContainer(slot, trackType, patch) {
|
|
19
|
+
update(slot, (current) => ({
|
|
20
|
+
...current,
|
|
21
|
+
[trackType]: {
|
|
22
|
+
...current?.[trackType],
|
|
23
|
+
...patch
|
|
24
|
+
}
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve once `read()` returns a number. Shared by the A/V stamp (waits for the
|
|
29
|
+
* `derive`d origin — immediate for per-type, the shared-`min` barrier for coordinated)
|
|
30
|
+
* and the text step (waits for the primary A/V origin). No bound: for fMP4 the origin
|
|
31
|
+
* always establishes.
|
|
32
|
+
*/
|
|
33
|
+
function awaitDefined(read) {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
let stop;
|
|
36
|
+
stop = effect(() => {
|
|
37
|
+
const value = read();
|
|
38
|
+
if (value !== void 0) {
|
|
39
|
+
stop?.();
|
|
40
|
+
resolve(value);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Relocation pipelines for one track type — a plain config `messagePipelines`.
|
|
47
|
+
* Keyed by **track type** (`'video'` / `'audio'`), so ABR rungs of a type share the
|
|
48
|
+
* origin (discover skips once the type's value is present). The steps read/write
|
|
49
|
+
* `state.mediaContainerData[trackType]` via their call-time `deps`; the stamp applies
|
|
50
|
+
* the same `derive` seam the reactor uses (pass the composition's resolved
|
|
51
|
+
* `deriveStartMediaTime` so the buffer offset and the model's `startMediaTime` agree).
|
|
52
|
+
*/
|
|
53
|
+
function relocationPipelinesFor(trackType, derive) {
|
|
54
|
+
const handlerType = trackType === "video" ? "vide" : "soun";
|
|
55
|
+
/**
|
|
56
|
+
* Init step: head-peek the buffered media track's `track_id` + `mdhd` timescale into
|
|
57
|
+
* `mediaContainerData[trackType]`. Matching by handler (`vide`/`soun`) skips a muxed
|
|
58
|
+
* `clcp` caption track, and the `track_id` lets `readSegmentOrigin` read *this*
|
|
59
|
+
* track's `tfdt` rather than the first `traf` in the segment.
|
|
60
|
+
*/
|
|
61
|
+
const readInitTrackInfo = async (frame, _signal, deps) => {
|
|
62
|
+
const { op } = frame;
|
|
63
|
+
if (op.type !== "append-init" || !frame.data) return;
|
|
64
|
+
const slot = containerSlot(deps);
|
|
65
|
+
if (peek(slot)?.[trackType]?.timescale !== void 0) return;
|
|
66
|
+
frame.data = await peekHead(frame.data, (bytes) => {
|
|
67
|
+
const track = findMediaTrack(bytes, handlerType);
|
|
68
|
+
if (track === void 0) return false;
|
|
69
|
+
writeContainer(slot, trackType, {
|
|
70
|
+
trackId: track.trackId,
|
|
71
|
+
timescale: track.timescale
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Media-segment step: head-peek the `tfdt` baseMediaDecodeTime of the media track's
|
|
78
|
+
* `traf` (matched by the `track_id` discovered from the init), recording the
|
|
79
|
+
* segment's 0-based `startTime` with it — the origin is `bmdt/ts − segmentStartTime`,
|
|
80
|
+
* so the first *loaded* segment need not be the 0th. Without a discovered `track_id`
|
|
81
|
+
* (non-fMP4 / mock init) there's no media track to relocate, so the step no-ops and
|
|
82
|
+
* the append stays native.
|
|
83
|
+
*/
|
|
84
|
+
const readSegmentOrigin = async (frame, _signal, deps) => {
|
|
85
|
+
const { op } = frame;
|
|
86
|
+
if (op.type !== "append-segment" || !frame.data) return;
|
|
87
|
+
const slot = containerSlot(deps);
|
|
88
|
+
const container = peek(slot)?.[trackType];
|
|
89
|
+
if (container?.baseMediaDecodeTime !== void 0) return;
|
|
90
|
+
const { trackId } = container ?? {};
|
|
91
|
+
if (trackId === void 0) return;
|
|
92
|
+
const segmentStartTime = op.meta.startTime;
|
|
93
|
+
frame.data = await peekHead(frame.data, (bytes) => {
|
|
94
|
+
const baseMediaDecodeTime = readBaseMediaDecodeTime(bytes, trackId);
|
|
95
|
+
if (baseMediaDecodeTime === void 0) return false;
|
|
96
|
+
writeContainer(slot, trackType, {
|
|
97
|
+
baseMediaDecodeTime,
|
|
98
|
+
segmentStartTime
|
|
99
|
+
});
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Stamp step — tier-agnostic apply. Relocate by the `derive`d `startMediaTime` for
|
|
105
|
+
* this type (`offset = −startMediaTime`). Applies the **same** `derive` the reactor
|
|
106
|
+
* uses, over the shared `mediaContainerData` slot — so the buffer offset matches the
|
|
107
|
+
* model's stamped `startMediaTime`, and it's robust to `established` + late tracks
|
|
108
|
+
* (the slot persists; the model value may not be re-stamped after the reactor goes
|
|
109
|
+
* sticky). Awaited: per-type resolves at once (own origin discovered earlier in this
|
|
110
|
+
* pipeline); shared-`min` waits until every selected A/V origin is in — the barrier,
|
|
111
|
+
* filled by the other type's discover step. A derived `0` (0-PTS / below threshold)
|
|
112
|
+
* leaves the append native — setting `timestampOffset` at all can ripple.
|
|
113
|
+
*/
|
|
114
|
+
const stampStartMediaTime = async (frame, signal, deps) => {
|
|
115
|
+
if (frame.op.type !== "append-segment") return;
|
|
116
|
+
const state = relocationState(deps);
|
|
117
|
+
const own = peek(state.mediaContainerData)?.[trackType];
|
|
118
|
+
if (own?.timescale === void 0 || own.baseMediaDecodeTime === void 0 || own.segmentStartTime === void 0) return;
|
|
119
|
+
const startMediaTime = await awaitDefined(() => {
|
|
120
|
+
const containerData = state.mediaContainerData.get();
|
|
121
|
+
if (!containerData) return void 0;
|
|
122
|
+
return derive(containerData, {
|
|
123
|
+
selectedVideoTrackId: state.selectedVideoTrackId?.get(),
|
|
124
|
+
selectedAudioTrackId: state.selectedAudioTrackId?.get()
|
|
125
|
+
})[trackType];
|
|
126
|
+
});
|
|
127
|
+
if (signal.aborted || startMediaTime === 0) return;
|
|
128
|
+
frame.meta = {
|
|
129
|
+
...frame.meta ?? frame.op.meta,
|
|
130
|
+
timestampOffset: -startMediaTime
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
return () => ({
|
|
134
|
+
remove: [dispatchStep],
|
|
135
|
+
"append-init": [
|
|
136
|
+
fetchStep,
|
|
137
|
+
readInitTrackInfo,
|
|
138
|
+
dispatchStep
|
|
139
|
+
],
|
|
140
|
+
"append-segment": [
|
|
141
|
+
fetchStep,
|
|
142
|
+
readSegmentOrigin,
|
|
143
|
+
stampStartMediaTime,
|
|
144
|
+
dispatchStep
|
|
145
|
+
]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Resolve step for the relocation text pipeline. Reuses the injected host resolver
|
|
150
|
+
* (the loader's folded `resolveSegment`, via `textStepWiring`) for cues and fetches
|
|
151
|
+
* the `X-TIMESTAMP-MAP` header in parallel, stashing it on `frame.metadata` for
|
|
152
|
+
* `relocateCuesStep`. Replaces the
|
|
153
|
+
* base `resolveCuesStep` (which fetches cues only) — text's native `<track>` parser
|
|
154
|
+
* discards the header, so the map needs its own raw-bytes fetch.
|
|
155
|
+
*/
|
|
156
|
+
const resolveWithMetadataStep = async (frame, signal, deps) => {
|
|
157
|
+
const [cues, metadata] = await Promise.all([textStepWiring(deps).resolveSegment(frame.op.segment.url), resolveVttSegmentMetadata(frame.op.segment.url)]);
|
|
158
|
+
if (signal.aborted) return;
|
|
159
|
+
frame.cues = cues;
|
|
160
|
+
frame.metadata = metadata;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Relocate step — shifts each cue onto the 0-based presentation timeline:
|
|
164
|
+
* `cueFinal = cueNative − startMediaTime`, where `startMediaTime` is the primary
|
|
165
|
+
* A/V track's origin (selected **video**, else **audio** — the single-anchor rule,
|
|
166
|
+
* and defensive like the reactor's optional selection) and `cueNative` folds in the
|
|
167
|
+
* `X-TIMESTAMP-MAP` correction (`mpegts/90000 − local`) for map-bearing VTT (Apple)
|
|
168
|
+
* or is the absolute cue time (no map, e.g. Mux). Text can resolve before A/V
|
|
169
|
+
* establishes, so the origin is awaited; fMP4 always establishes it (0-PTS → 0),
|
|
170
|
+
* and a text-only source (no A/V selected) simply gets offset 0.
|
|
171
|
+
*/
|
|
172
|
+
const relocateCuesStep = async (frame, signal, deps) => {
|
|
173
|
+
if (!frame.cues?.length) return;
|
|
174
|
+
const state = deps.state;
|
|
175
|
+
const startMediaTime = await awaitDefined(() => {
|
|
176
|
+
const presentation = state.presentation.get();
|
|
177
|
+
if (!presentation) return void 0;
|
|
178
|
+
const primaryId = state.selectedVideoTrackId.get() ?? state.selectedAudioTrackId.get();
|
|
179
|
+
if (primaryId !== void 0) return findTrackById(presentation, primaryId)?.startMediaTime;
|
|
180
|
+
return getTracksByType(presentation, "video").length > 0 || getTracksByType(presentation, "audio").length > 0 ? void 0 : 0;
|
|
181
|
+
});
|
|
182
|
+
if (signal.aborted) return;
|
|
183
|
+
const { timestampMap } = frame.metadata ?? {};
|
|
184
|
+
const delta = (timestampMap ? timestampMap.mpegts / 9e4 - timestampMap.local : 0) - startMediaTime;
|
|
185
|
+
if (delta !== 0) for (const cue of frame.cues) {
|
|
186
|
+
cue.startTime += delta;
|
|
187
|
+
cue.endTime += delta;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Relocation text pipeline — the text analog of `relocationPipelinesFor(type)`.
|
|
192
|
+
* `resolveWithMetadata` (cues + `X-TIMESTAMP-MAP`) → `relocateCues` (shift by the
|
|
193
|
+
* primary A/V origin) → `dispatchCues`.
|
|
194
|
+
*/
|
|
195
|
+
const relocatingTextPipelines = () => [
|
|
196
|
+
resolveWithMetadataStep,
|
|
197
|
+
relocateCuesStep,
|
|
198
|
+
dispatchCuesStep
|
|
199
|
+
];
|
|
200
|
+
//#endregion
|
|
201
|
+
export { relocatingTextPipelines, relocationPipelinesFor };
|
|
202
|
+
|
|
203
|
+
//# sourceMappingURL=relocation-pipelines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relocation-pipelines.js","names":[],"sources":["../../../../src/playback/primitives/relocation-pipelines.ts"],"sourcesContent":["/**\n * Non-zero-PTS relocation pipelines — the config-supplied, loader-facing half of the\n * `establishStartMediaTime` behavior (`../behaviors/establish-start-media-time`). The\n * reactor there owns the lifecycle and the `derive` coordination seam; this file supplies\n * the `messagePipelines` the segment/text loaders run to *fill and act on* that behavior's\n * state. It's the relocation analog of `track-switching`'s config-supplied constraint/rule\n * chain — a pluggable strategy handed to the machinery via config, not applied inline —\n * supplied to the loaders rather than applied by the behavior, so the two coordinate\n * through the shared `mediaContainerData` / `startMediaTime` slots alone, never by import.\n *\n * DOM-free: it composes the loader vocabularies (`segment-load-pipeline`,\n * `text-segment-load-pipeline`) through their structural sink seams and shifts cues via\n * the structural `Cue` type, so it names no `SourceBuffer`, `TextTracksActor`, or\n * `VTTCue`. The A/V pipeline is a plain `messagePipelines` array:\n * - `discover` — init `track_id` + `mdhd` timescale for the buffered media track, then\n * that same track's `tfdt` baseMediaDecodeTime, matched by `track_id` so a muxed\n * segment reads the media track's origin, not the first `traf` — writes\n * `state.mediaContainerData`.\n * - `stamp` — reads that track's derived origin back and relocates via\n * `timestampOffset = −startMediaTime`.\n * Steps read composition `state` from their call-time `deps` (no closures, no context).\n *\n * The text half (`relocatingTextPipelines`) is the same idea for the text-segment loader:\n * a `resolveWithMetadata → relocateCues → dispatchCues` pipeline that shifts cues onto the\n * same 0-based timeline, reading the primary A/V track's `startMediaTime` (the reactor's\n * consumed value) via `deps`.\n */\nimport type { StateSignals } from '../../core/composition/create-composition';\nimport { effect } from '../../core/signals/effect';\nimport { peek, type Signal, update } from '../../core/signals/primitives';\nimport { findMediaTrack, type MediaHandlerType, readBaseMediaDecodeTime } from '../../media/mp4/timestamp-origin';\nimport { resolveVttSegmentMetadata, type TextSegmentMetadata } from '../../media/text/resolve-vtt-metadata';\nimport type { Cue, MaybeResolvedPresentation, MediaContainerData } from '../../media/types';\nimport { findTrackById, getTracksByType } from '../../media/utils/tracks';\nimport type { DeriveStartMediaTime } from './derive-start-media-time';\nimport { peekHead } from './head-peek';\nimport { dispatchStep, fetchStep, type LoadStep, type MessagePipelines, type StepDeps } from './segment-load-pipeline';\nimport {\n dispatchCuesStep,\n type TextFrame,\n type TextLoadStep,\n type TextStepDeps,\n textStepWiring,\n} from './text-segment-load-pipeline';\n\n/** The composition slots these steps read — the view asserted from the opaque `deps.state`. */\ninterface RelocationSlots {\n presentation?: MaybeResolvedPresentation;\n mediaContainerData?: Record<string, MediaContainerData>;\n selectedVideoTrackId?: string;\n selectedAudioTrackId?: string;\n}\n\ntype ContainerSlot = Signal<Record<string, MediaContainerData> | undefined>;\n\n/** Assert the relocation state view from the opaque step deps — this module knows the slots the composition provides. */\nfunction relocationState(deps: StepDeps): StateSignals<RelocationSlots> {\n return deps.state as unknown as StateSignals<RelocationSlots>;\n}\n\nfunction containerSlot(deps: StepDeps): ContainerSlot {\n return relocationState(deps).mediaContainerData;\n}\n\n/** Synchronous RMW of the per-type entry — disjoint keys across producers, so no lost update. */\nfunction writeContainer(slot: ContainerSlot, trackType: string, patch: Partial<MediaContainerData>): void {\n update(slot, (current) => ({ ...current, [trackType]: { ...current?.[trackType], ...patch } }));\n}\n\n/**\n * Resolve once `read()` returns a number. Shared by the A/V stamp (waits for the\n * `derive`d origin — immediate for per-type, the shared-`min` barrier for coordinated)\n * and the text step (waits for the primary A/V origin). No bound: for fMP4 the origin\n * always establishes.\n */\nfunction awaitDefined(read: () => number | undefined): Promise<number> {\n return new Promise((resolve) => {\n let stop: (() => void) | undefined;\n stop = effect(() => {\n const value = read();\n if (value !== undefined) {\n stop?.();\n resolve(value);\n }\n });\n });\n}\n\n/**\n * Relocation pipelines for one track type — a plain config `messagePipelines`.\n * Keyed by **track type** (`'video'` / `'audio'`), so ABR rungs of a type share the\n * origin (discover skips once the type's value is present). The steps read/write\n * `state.mediaContainerData[trackType]` via their call-time `deps`; the stamp applies\n * the same `derive` seam the reactor uses (pass the composition's resolved\n * `deriveStartMediaTime` so the buffer offset and the model's `startMediaTime` agree).\n */\nexport function relocationPipelinesFor(trackType: 'video' | 'audio', derive: DeriveStartMediaTime): MessagePipelines {\n const handlerType: MediaHandlerType = trackType === 'video' ? 'vide' : 'soun';\n\n /**\n * Init step: head-peek the buffered media track's `track_id` + `mdhd` timescale into\n * `mediaContainerData[trackType]`. Matching by handler (`vide`/`soun`) skips a muxed\n * `clcp` caption track, and the `track_id` lets `readSegmentOrigin` read *this*\n * track's `tfdt` rather than the first `traf` in the segment.\n */\n const readInitTrackInfo: LoadStep = async (frame, _signal, deps) => {\n const { op } = frame;\n if (op.type !== 'append-init' || !frame.data) return;\n const slot = containerSlot(deps);\n if (peek(slot)?.[trackType]?.timescale !== undefined) return; // already have it (any rung of this type)\n frame.data = await peekHead(frame.data, (bytes) => {\n const track = findMediaTrack(bytes, handlerType);\n if (track === undefined) return false;\n writeContainer(slot, trackType, { trackId: track.trackId, timescale: track.timescale });\n return true;\n });\n };\n\n /**\n * Media-segment step: head-peek the `tfdt` baseMediaDecodeTime of the media track's\n * `traf` (matched by the `track_id` discovered from the init), recording the\n * segment's 0-based `startTime` with it — the origin is `bmdt/ts − segmentStartTime`,\n * so the first *loaded* segment need not be the 0th. Without a discovered `track_id`\n * (non-fMP4 / mock init) there's no media track to relocate, so the step no-ops and\n * the append stays native.\n */\n const readSegmentOrigin: LoadStep = async (frame, _signal, deps) => {\n const { op } = frame;\n if (op.type !== 'append-segment' || !frame.data) return;\n const slot = containerSlot(deps);\n const container = peek(slot)?.[trackType];\n if (container?.baseMediaDecodeTime !== undefined) return; // established\n const { trackId } = container ?? {};\n if (trackId === undefined) return; // init didn't identify a media track — nothing to match\n const segmentStartTime = op.meta.startTime;\n frame.data = await peekHead(frame.data, (bytes) => {\n const baseMediaDecodeTime = readBaseMediaDecodeTime(bytes, trackId);\n if (baseMediaDecodeTime === undefined) return false;\n writeContainer(slot, trackType, { baseMediaDecodeTime, segmentStartTime });\n return true;\n });\n };\n\n /**\n * Stamp step — tier-agnostic apply. Relocate by the `derive`d `startMediaTime` for\n * this type (`offset = −startMediaTime`). Applies the **same** `derive` the reactor\n * uses, over the shared `mediaContainerData` slot — so the buffer offset matches the\n * model's stamped `startMediaTime`, and it's robust to `established` + late tracks\n * (the slot persists; the model value may not be re-stamped after the reactor goes\n * sticky). Awaited: per-type resolves at once (own origin discovered earlier in this\n * pipeline); shared-`min` waits until every selected A/V origin is in — the barrier,\n * filled by the other type's discover step. A derived `0` (0-PTS / below threshold)\n * leaves the append native — setting `timestampOffset` at all can ripple.\n */\n const stampStartMediaTime: LoadStep = async (frame, signal, deps) => {\n if (frame.op.type !== 'append-segment') return;\n const state = relocationState(deps);\n // Liveness guard: if THIS type's own origin wasn't discovered — `readSegmentOrigin`\n // ran earlier in this pipeline and found no `tfdt` (mock / TS / containerless) — the\n // source isn't relocatable, so leave the append native and DON'T wait. Only a\n // discoverable type awaits the derived origin (which for shared-`min` legitimately\n // blocks on the other selected type — the barrier).\n const own = peek(state.mediaContainerData)?.[trackType];\n if (own?.timescale === undefined || own.baseMediaDecodeTime === undefined || own.segmentStartTime === undefined) {\n return;\n }\n const startMediaTime = await awaitDefined(() => {\n const containerData = state.mediaContainerData.get();\n if (!containerData) return undefined;\n return derive(containerData, {\n selectedVideoTrackId: state.selectedVideoTrackId?.get(),\n selectedAudioTrackId: state.selectedAudioTrackId?.get(),\n })[trackType];\n });\n if (signal.aborted || startMediaTime === 0) return;\n frame.meta = { ...(frame.meta ?? frame.op.meta), timestampOffset: -startMediaTime };\n };\n\n return () => ({\n remove: [dispatchStep],\n 'append-init': [fetchStep, readInitTrackInfo, dispatchStep],\n 'append-segment': [fetchStep, readSegmentOrigin, stampStartMediaTime, dispatchStep],\n });\n}\n\n// ============================================================================\n// TEXT (cue relocation)\n// ============================================================================\n\n/**\n * Resolve step for the relocation text pipeline. Reuses the injected host resolver\n * (the loader's folded `resolveSegment`, via `textStepWiring`) for cues and fetches\n * the `X-TIMESTAMP-MAP` header in parallel, stashing it on `frame.metadata` for\n * `relocateCuesStep`. Replaces the\n * base `resolveCuesStep` (which fetches cues only) — text's native `<track>` parser\n * discards the header, so the map needs its own raw-bytes fetch.\n */\nconst resolveWithMetadataStep = async <C extends Cue>(\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n): Promise<void> => {\n const [cues, metadata] = await Promise.all([\n textStepWiring<C>(deps).resolveSegment(frame.op.segment.url),\n resolveVttSegmentMetadata(frame.op.segment.url),\n ]);\n if (signal.aborted) return;\n frame.cues = cues;\n frame.metadata = metadata;\n};\n\n/**\n * Relocate step — shifts each cue onto the 0-based presentation timeline:\n * `cueFinal = cueNative − startMediaTime`, where `startMediaTime` is the primary\n * A/V track's origin (selected **video**, else **audio** — the single-anchor rule,\n * and defensive like the reactor's optional selection) and `cueNative` folds in the\n * `X-TIMESTAMP-MAP` correction (`mpegts/90000 − local`) for map-bearing VTT (Apple)\n * or is the absolute cue time (no map, e.g. Mux). Text can resolve before A/V\n * establishes, so the origin is awaited; fMP4 always establishes it (0-PTS → 0),\n * and a text-only source (no A/V selected) simply gets offset 0.\n */\nconst relocateCuesStep = async <C extends Cue>(\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n): Promise<void> => {\n if (!frame.cues?.length) return;\n const state = deps.state as unknown as StateSignals<RelocationSlots>;\n const startMediaTime = await awaitDefined(() => {\n const presentation = state.presentation.get();\n if (!presentation) return undefined;\n const primaryId = state.selectedVideoTrackId.get() ?? state.selectedAudioTrackId.get();\n if (primaryId !== undefined) {\n // A/V selected: use its origin once stamped (undefined until then → keep waiting).\n return findTrackById(presentation, primaryId)?.startMediaTime;\n }\n // No A/V selected. Distinguish \"not selected YET\" from \"text-only source\". If the\n // presentation has A/V tracks, one WILL be selected and stamped — wait for it, or the\n // cues get shifted by the full `mapCorrection` (e.g. +10s on Apple X-TIMESTAMP-MAP\n // sources) when text relocation wins the race against A/V selection. Only a genuinely\n // text-only source (no A/V origin ever coming) legitimately relocates by offset 0.\n const hasAv =\n getTracksByType(presentation, 'video').length > 0 || getTracksByType(presentation, 'audio').length > 0;\n return hasAv ? undefined : 0;\n });\n if (signal.aborted) return;\n const { timestampMap } = (frame.metadata as TextSegmentMetadata | undefined) ?? {};\n const mapCorrection = timestampMap ? timestampMap.mpegts / 90000 - timestampMap.local : 0;\n const delta = mapCorrection - startMediaTime;\n if (delta !== 0) {\n for (const cue of frame.cues) {\n cue.startTime += delta;\n cue.endTime += delta;\n }\n }\n};\n\n/**\n * Relocation text pipeline — the text analog of `relocationPipelinesFor(type)`.\n * `resolveWithMetadata` (cues + `X-TIMESTAMP-MAP`) → `relocateCues` (shift by the\n * primary A/V origin) → `dispatchCues`.\n */\nexport const relocatingTextPipelines = <C extends Cue>(): TextLoadStep<C>[] => [\n resolveWithMetadataStep,\n relocateCuesStep,\n dispatchCuesStep,\n];\n"],"mappings":";;;;;;;;;;AAwDA,SAAS,gBAAgB,MAA+C;CACtE,OAAO,KAAK;AACd;AAEA,SAAS,cAAc,MAA+B;CACpD,OAAO,gBAAgB,IAAI,CAAC,CAAC;AAC/B;;AAGA,SAAS,eAAe,MAAqB,WAAmB,OAA0C;CACxG,OAAO,OAAO,aAAa;EAAE,GAAG;GAAU,YAAY;GAAE,GAAG,UAAU;GAAY,GAAG;EAAM;CAAE,EAAE;AAChG;;;;;;;AAQA,SAAS,aAAa,MAAiD;CACrE,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI;EACJ,OAAO,aAAa;GAClB,MAAM,QAAQ,KAAK;GACnB,IAAI,UAAU,KAAA,GAAW;IACvB,OAAO;IACP,QAAQ,KAAK;GACf;EACF,CAAC;CACH,CAAC;AACH;;;;;;;;;AAUA,SAAgB,uBAAuB,WAA8B,QAAgD;CACnH,MAAM,cAAgC,cAAc,UAAU,SAAS;;;;;;;CAQvE,MAAM,oBAA8B,OAAO,OAAO,SAAS,SAAS;EAClE,MAAM,EAAE,OAAO;EACf,IAAI,GAAG,SAAS,iBAAiB,CAAC,MAAM,MAAM;EAC9C,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,cAAc,KAAA,GAAW;EACtD,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO,UAAU;GACjD,MAAM,QAAQ,eAAe,OAAO,WAAW;GAC/C,IAAI,UAAU,KAAA,GAAW,OAAO;GAChC,eAAe,MAAM,WAAW;IAAE,SAAS,MAAM;IAAS,WAAW,MAAM;GAAU,CAAC;GACtF,OAAO;EACT,CAAC;CACH;;;;;;;;;CAUA,MAAM,oBAA8B,OAAO,OAAO,SAAS,SAAS;EAClE,MAAM,EAAE,OAAO;EACf,IAAI,GAAG,SAAS,oBAAoB,CAAC,MAAM,MAAM;EACjD,MAAM,OAAO,cAAc,IAAI;EAC/B,MAAM,YAAY,KAAK,IAAI,CAAC,GAAG;EAC/B,IAAI,WAAW,wBAAwB,KAAA,GAAW;EAClD,MAAM,EAAE,YAAY,aAAa,CAAC;EAClC,IAAI,YAAY,KAAA,GAAW;EAC3B,MAAM,mBAAmB,GAAG,KAAK;EACjC,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO,UAAU;GACjD,MAAM,sBAAsB,wBAAwB,OAAO,OAAO;GAClE,IAAI,wBAAwB,KAAA,GAAW,OAAO;GAC9C,eAAe,MAAM,WAAW;IAAE;IAAqB;GAAiB,CAAC;GACzE,OAAO;EACT,CAAC;CACH;;;;;;;;;;;;CAaA,MAAM,sBAAgC,OAAO,OAAO,QAAQ,SAAS;EACnE,IAAI,MAAM,GAAG,SAAS,kBAAkB;EACxC,MAAM,QAAQ,gBAAgB,IAAI;EAMlC,MAAM,MAAM,KAAK,MAAM,kBAAkB,CAAC,GAAG;EAC7C,IAAI,KAAK,cAAc,KAAA,KAAa,IAAI,wBAAwB,KAAA,KAAa,IAAI,qBAAqB,KAAA,GACpG;EAEF,MAAM,iBAAiB,MAAM,mBAAmB;GAC9C,MAAM,gBAAgB,MAAM,mBAAmB,IAAI;GACnD,IAAI,CAAC,eAAe,OAAO,KAAA;GAC3B,OAAO,OAAO,eAAe;IAC3B,sBAAsB,MAAM,sBAAsB,IAAI;IACtD,sBAAsB,MAAM,sBAAsB,IAAI;GACxD,CAAC,CAAC,CAAC;EACL,CAAC;EACD,IAAI,OAAO,WAAW,mBAAmB,GAAG;EAC5C,MAAM,OAAO;GAAE,GAAI,MAAM,QAAQ,MAAM,GAAG;GAAO,iBAAiB,CAAC;EAAe;CACpF;CAEA,cAAc;EACZ,QAAQ,CAAC,YAAY;EACrB,eAAe;GAAC;GAAW;GAAmB;EAAY;EAC1D,kBAAkB;GAAC;GAAW;GAAmB;GAAqB;EAAY;CACpF;AACF;;;;;;;;;AAcA,MAAM,0BAA0B,OAC9B,OACA,QACA,SACkB;CAClB,MAAM,CAAC,MAAM,YAAY,MAAM,QAAQ,IAAI,CACzC,eAAkB,IAAI,CAAC,CAAC,eAAe,MAAM,GAAG,QAAQ,GAAG,GAC3D,0BAA0B,MAAM,GAAG,QAAQ,GAAG,CAChD,CAAC;CACD,IAAI,OAAO,SAAS;CACpB,MAAM,OAAO;CACb,MAAM,WAAW;AACnB;;;;;;;;;;;AAYA,MAAM,mBAAmB,OACvB,OACA,QACA,SACkB;CAClB,IAAI,CAAC,MAAM,MAAM,QAAQ;CACzB,MAAM,QAAQ,KAAK;CACnB,MAAM,iBAAiB,MAAM,mBAAmB;EAC9C,MAAM,eAAe,MAAM,aAAa,IAAI;EAC5C,IAAI,CAAC,cAAc,OAAO,KAAA;EAC1B,MAAM,YAAY,MAAM,qBAAqB,IAAI,KAAK,MAAM,qBAAqB,IAAI;EACrF,IAAI,cAAc,KAAA,GAEhB,OAAO,cAAc,cAAc,SAAS,CAAC,EAAE;EASjD,OADE,gBAAgB,cAAc,OAAO,CAAC,CAAC,SAAS,KAAK,gBAAgB,cAAc,OAAO,CAAC,CAAC,SAAS,IACxF,KAAA,IAAY;CAC7B,CAAC;CACD,IAAI,OAAO,SAAS;CACpB,MAAM,EAAE,iBAAkB,MAAM,YAAgD,CAAC;CAEjF,MAAM,SADgB,eAAe,aAAa,SAAS,MAAQ,aAAa,QAAQ,KAC1D;CAC9B,IAAI,UAAU,GACZ,KAAK,MAAM,OAAO,MAAM,MAAM;EAC5B,IAAI,aAAa;EACjB,IAAI,WAAW;CACjB;AAEJ;;;;;;AAOA,MAAa,gCAAkE;CAC7E;CACA;CACA;AACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ResolvedTrack } from "../../media/types/index.js";
|
|
2
|
+
import { SvtaError } from "../../media/errors.js";
|
|
3
|
+
//#region src/playback/primitives/report-track-conditions.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Conditions worth reporting about a just-resolved track. Return an empty array
|
|
6
|
+
* (or omit the seam) to report nothing.
|
|
7
|
+
*/
|
|
8
|
+
type ReportUnsupportedTrackConditions = (track: ResolvedTrack) => readonly SvtaError[];
|
|
9
|
+
//#endregion
|
|
10
|
+
export { ReportUnsupportedTrackConditions };
|
|
11
|
+
//# sourceMappingURL=report-track-conditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-track-conditions.d.ts","names":[],"sources":["../../../../src/playback/primitives/report-track-conditions.ts"],"mappings":";;;;;;;KAgCY,oCAAoC,OAAO,2BAA2B"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { getMediaPlaylistMetadata } from "../../media/types/index.js";
|
|
2
|
+
import { SVTA_UNSUPPORTED_AUDIO_FORMAT, SVTA_UNSUPPORTED_DRM_SYSTEM, SVTA_UNSUPPORTED_VIDEO_FORMAT } from "../../media/errors.js";
|
|
3
|
+
import { NON_FMP4_CONTAINER_MIMES } from "../../media/hls/parse-media-playlist.js";
|
|
4
|
+
//#region src/playback/primitives/report-track-conditions.ts
|
|
5
|
+
/**
|
|
6
|
+
* The seam `resolve-track` uses to report conditions discovered in a freshly
|
|
7
|
+
* parsed media playlist.
|
|
8
|
+
*
|
|
9
|
+
* Injected rather than baked in so `resolve-track` stays free of format and
|
|
10
|
+
* policy knowledge: it parses and commits, and whatever the composition wants
|
|
11
|
+
* noticed about the result is this function's business. Which conditions matter,
|
|
12
|
+
* and which codes they carry, therefore vary per composition — a provider that
|
|
13
|
+
* never ships MPEG-TS can drop that check, and future playlist-derived notices
|
|
14
|
+
* (LL-HLS or DVR support that's partial rather than absent) attach here without
|
|
15
|
+
* touching the resolver.
|
|
16
|
+
*
|
|
17
|
+
* Reported **per rendition, as it resolves**, which is why these are causes and
|
|
18
|
+
* not verdicts: one rendition being unplayable doesn't make the source
|
|
19
|
+
* unplayable. The verdict is `track-switching`'s, which reports
|
|
20
|
+
* `SVTA_NO_SUPPORTED_{VIDEO,AUDIO}_TRACK` when a type's candidates actually
|
|
21
|
+
* empty. Keeping the two apart is what lets a mixed source — some renditions
|
|
22
|
+
* encrypted or MPEG-TS, others playable — log its causes and still play.
|
|
23
|
+
*/
|
|
24
|
+
/** Unsupported-format code per type; text has none — absent captions aren't a failure. */
|
|
25
|
+
const UNSUPPORTED_FORMAT_CODE = {
|
|
26
|
+
video: SVTA_UNSUPPORTED_VIDEO_FORMAT,
|
|
27
|
+
audio: SVTA_UNSUPPORTED_AUDIO_FORMAT
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The types a reported cause can legitimately describe: the ones whose candidates
|
|
31
|
+
* `canPlayTrack` actually prunes, via `track-switching`'s `excludeUnplayableTracks`
|
|
32
|
+
* pre-pass.
|
|
33
|
+
*
|
|
34
|
+
* Text is outside it by design — its switching chain runs failed-CDN constraints
|
|
35
|
+
* alone, because an MSE codec probe is the wrong question for a WebVTT rendition
|
|
36
|
+
* (see `track-switching.ts`). So nothing prunes a text rendition, no verdict can
|
|
37
|
+
* follow from one, and a cause reported against it would be an orphan: the
|
|
38
|
+
* adapter's unsupported-feature substitution scans the sequence as a whole, so a
|
|
39
|
+
* lone encrypted subtitle would otherwise recode an unrelated verdict — an
|
|
40
|
+
* all-CDN cooldown among them — as "this engine can't play the source".
|
|
41
|
+
*/
|
|
42
|
+
const CAPABILITY_PRUNED_TYPES = /* @__PURE__ */ new Set(["video", "audio"]);
|
|
43
|
+
/**
|
|
44
|
+
* The default: report what makes a rendition unplayable *to this engine* — a
|
|
45
|
+
* container MSE can't accept, or encryption with no decryption pipeline. Both
|
|
46
|
+
* mirror what `canPlayTrack` prunes on, so a reported cause always has a
|
|
47
|
+
* corresponding exclusion.
|
|
48
|
+
*
|
|
49
|
+
* A condition carries its code and its context, not copy. `data.mimeType` is
|
|
50
|
+
* what preserves the specificity — the format codes cover every non-fMP4
|
|
51
|
+
* container, so "which one" lives there for a consumer that wants to say, rather
|
|
52
|
+
* than being flattened into an English sentence here.
|
|
53
|
+
*
|
|
54
|
+
* Both carry `trackType`, redundantly for the format codes (1004/1005 are
|
|
55
|
+
* already per type) but not for 4008, which isn't per type — SVTA has one
|
|
56
|
+
* content-protection code for both. Nothing branches on the tag: it's diagnostic
|
|
57
|
+
* context, reaching a developer through the logged sequence and `error.data`,
|
|
58
|
+
* which is why it goes on every condition rather than only where the code can't
|
|
59
|
+
* carry it. `trackId` alongside it is what distinguishes two renditions of the
|
|
60
|
+
* same type.
|
|
61
|
+
*/
|
|
62
|
+
function reportUnsupportedTrackConditions(track) {
|
|
63
|
+
if (!CAPABILITY_PRUNED_TYPES.has(track.type)) return [];
|
|
64
|
+
const conditions = [];
|
|
65
|
+
const data = {
|
|
66
|
+
trackType: track.type,
|
|
67
|
+
trackId: track.id
|
|
68
|
+
};
|
|
69
|
+
const formatCode = UNSUPPORTED_FORMAT_CODE[track.type];
|
|
70
|
+
if (formatCode !== void 0 && NON_FMP4_CONTAINER_MIMES.has(track.mimeType)) conditions.push({
|
|
71
|
+
code: formatCode,
|
|
72
|
+
data: {
|
|
73
|
+
...data,
|
|
74
|
+
mimeType: track.mimeType
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (getMediaPlaylistMetadata(track)?.encrypted) conditions.push({
|
|
78
|
+
code: SVTA_UNSUPPORTED_DRM_SYSTEM,
|
|
79
|
+
data
|
|
80
|
+
});
|
|
81
|
+
return conditions;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { reportUnsupportedTrackConditions };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=report-track-conditions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-track-conditions.js","names":[],"sources":["../../../../src/playback/primitives/report-track-conditions.ts"],"sourcesContent":["/**\n * The seam `resolve-track` uses to report conditions discovered in a freshly\n * parsed media playlist.\n *\n * Injected rather than baked in so `resolve-track` stays free of format and\n * policy knowledge: it parses and commits, and whatever the composition wants\n * noticed about the result is this function's business. Which conditions matter,\n * and which codes they carry, therefore vary per composition — a provider that\n * never ships MPEG-TS can drop that check, and future playlist-derived notices\n * (LL-HLS or DVR support that's partial rather than absent) attach here without\n * touching the resolver.\n *\n * Reported **per rendition, as it resolves**, which is why these are causes and\n * not verdicts: one rendition being unplayable doesn't make the source\n * unplayable. The verdict is `track-switching`'s, which reports\n * `SVTA_NO_SUPPORTED_{VIDEO,AUDIO}_TRACK` when a type's candidates actually\n * empty. Keeping the two apart is what lets a mixed source — some renditions\n * encrypted or MPEG-TS, others playable — log its causes and still play.\n */\nimport {\n SVTA_UNSUPPORTED_AUDIO_FORMAT,\n SVTA_UNSUPPORTED_DRM_SYSTEM,\n SVTA_UNSUPPORTED_VIDEO_FORMAT,\n type SvtaError,\n} from '../../media/errors';\nimport { NON_FMP4_CONTAINER_MIMES } from '../../media/hls/parse-media-playlist';\nimport { getMediaPlaylistMetadata, type ResolvedTrack, type TrackType } from '../../media/types';\n\n/**\n * Conditions worth reporting about a just-resolved track. Return an empty array\n * (or omit the seam) to report nothing.\n */\nexport type ReportUnsupportedTrackConditions = (track: ResolvedTrack) => readonly SvtaError[];\n\n/** Unsupported-format code per type; text has none — absent captions aren't a failure. */\nconst UNSUPPORTED_FORMAT_CODE: Partial<Record<TrackType, number>> = {\n video: SVTA_UNSUPPORTED_VIDEO_FORMAT,\n audio: SVTA_UNSUPPORTED_AUDIO_FORMAT,\n};\n\n/**\n * The types a reported cause can legitimately describe: the ones whose candidates\n * `canPlayTrack` actually prunes, via `track-switching`'s `excludeUnplayableTracks`\n * pre-pass.\n *\n * Text is outside it by design — its switching chain runs failed-CDN constraints\n * alone, because an MSE codec probe is the wrong question for a WebVTT rendition\n * (see `track-switching.ts`). So nothing prunes a text rendition, no verdict can\n * follow from one, and a cause reported against it would be an orphan: the\n * adapter's unsupported-feature substitution scans the sequence as a whole, so a\n * lone encrypted subtitle would otherwise recode an unrelated verdict — an\n * all-CDN cooldown among them — as \"this engine can't play the source\".\n */\nconst CAPABILITY_PRUNED_TYPES: ReadonlySet<TrackType> = new Set<TrackType>(['video', 'audio']);\n\n/**\n * The default: report what makes a rendition unplayable *to this engine* — a\n * container MSE can't accept, or encryption with no decryption pipeline. Both\n * mirror what `canPlayTrack` prunes on, so a reported cause always has a\n * corresponding exclusion.\n *\n * A condition carries its code and its context, not copy. `data.mimeType` is\n * what preserves the specificity — the format codes cover every non-fMP4\n * container, so \"which one\" lives there for a consumer that wants to say, rather\n * than being flattened into an English sentence here.\n *\n * Both carry `trackType`, redundantly for the format codes (1004/1005 are\n * already per type) but not for 4008, which isn't per type — SVTA has one\n * content-protection code for both. Nothing branches on the tag: it's diagnostic\n * context, reaching a developer through the logged sequence and `error.data`,\n * which is why it goes on every condition rather than only where the code can't\n * carry it. `trackId` alongside it is what distinguishes two renditions of the\n * same type.\n */\nexport function reportUnsupportedTrackConditions(track: ResolvedTrack): readonly SvtaError[] {\n if (!CAPABILITY_PRUNED_TYPES.has(track.type)) return [];\n\n const conditions: SvtaError[] = [];\n const data = { trackType: track.type, trackId: track.id };\n\n const formatCode = UNSUPPORTED_FORMAT_CODE[track.type];\n if (formatCode !== undefined && NON_FMP4_CONTAINER_MIMES.has(track.mimeType)) {\n conditions.push({ code: formatCode, data: { ...data, mimeType: track.mimeType } });\n }\n if (getMediaPlaylistMetadata(track)?.encrypted) {\n conditions.push({ code: SVTA_UNSUPPORTED_DRM_SYSTEM, data });\n }\n return conditions;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,0BAA8D;CAClE,OAAO;CACP,OAAO;AACT;;;;;;;;;;;;;;AAeA,MAAM,0CAAkD,IAAI,IAAe,CAAC,SAAS,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;AAqB7F,SAAgB,iCAAiC,OAA4C;CAC3F,IAAI,CAAC,wBAAwB,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC;CAEtD,MAAM,aAA0B,CAAC;CACjC,MAAM,OAAO;EAAE,WAAW,MAAM;EAAM,SAAS,MAAM;CAAG;CAExD,MAAM,aAAa,wBAAwB,MAAM;CACjD,IAAI,eAAe,KAAA,KAAa,yBAAyB,IAAI,MAAM,QAAQ,GACzE,WAAW,KAAK;EAAE,MAAM;EAAY,MAAM;GAAE,GAAG;GAAM,UAAU,MAAM;EAAS;CAAE,CAAC;CAEnF,IAAI,yBAAyB,KAAK,CAAC,EAAE,WACnC,WAAW,KAAK;EAAE,MAAM;EAA6B;CAAK,CAAC;CAE7D,OAAO;AACT"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { effect } from "../../core/signals/effect.js";
|
|
2
|
+
//#region src/playback/primitives/segment-load-pipeline.ts
|
|
3
|
+
/**
|
|
4
|
+
* Base-step view of the loader's own wiring. `createSegmentLoaderActor` folds its
|
|
5
|
+
* `sourceBufferActor` + `fetch` into the threaded `config` so base steps read them
|
|
6
|
+
* from the uniform passthrough — present whether the loader runs inside a composition
|
|
7
|
+
* or standalone. `config` is loose (`object`), so assert the shape here (one cast, like
|
|
8
|
+
* relocation's `containerSlot`).
|
|
9
|
+
*/
|
|
10
|
+
function stepWiring(deps) {
|
|
11
|
+
return deps.config;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolves when the SourceBufferActor snapshot reaches 'idle'.
|
|
15
|
+
* Rejects if the signal is aborted or the actor is destroyed.
|
|
16
|
+
*
|
|
17
|
+
* Used to sequence SourceBufferActor operations without awaiting send()
|
|
18
|
+
* directly — send() is fire-and-forget; callers observe completion via
|
|
19
|
+
* state transition.
|
|
20
|
+
*/
|
|
21
|
+
function waitForIdle(snapshot, signal) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
if (snapshot.get().value === "idle") {
|
|
24
|
+
resolve();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (snapshot.get().value === "destroyed") {
|
|
28
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (signal.aborted) {
|
|
32
|
+
reject(signal.reason);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let stop;
|
|
36
|
+
const cleanup = (fn) => {
|
|
37
|
+
stop?.();
|
|
38
|
+
signal.removeEventListener("abort", onAbort);
|
|
39
|
+
fn();
|
|
40
|
+
};
|
|
41
|
+
const onAbort = () => cleanup(() => reject(signal.reason));
|
|
42
|
+
stop = effect(() => {
|
|
43
|
+
const value = snapshot.get().value;
|
|
44
|
+
if (value === "idle") cleanup(resolve);
|
|
45
|
+
else if (value === "destroyed") cleanup(() => reject(new DOMException("Aborted", "AbortError")));
|
|
46
|
+
});
|
|
47
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Build the SourceBuffer message a completed frame dispatches. `fetchStep` always precedes `dispatchStep` in append pipelines, so `data` is set by now. */
|
|
51
|
+
function toMessage({ op, data, meta }) {
|
|
52
|
+
switch (op.type) {
|
|
53
|
+
case "remove": return op;
|
|
54
|
+
case "append-init": return {
|
|
55
|
+
type: "append-init",
|
|
56
|
+
data,
|
|
57
|
+
meta: op.meta
|
|
58
|
+
};
|
|
59
|
+
case "append-segment": return {
|
|
60
|
+
type: "append-segment",
|
|
61
|
+
data,
|
|
62
|
+
meta: meta ?? op.meta
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fetch this op's bytes into the frame. Init segments need the full body
|
|
68
|
+
* (`minChunkSize: Infinity`) before appending; media segments stream so chunks
|
|
69
|
+
* append as they arrive. Awaiting headers eagerly also starts the HTTP
|
|
70
|
+
* connection (and records the fetch in observers like tests).
|
|
71
|
+
*/
|
|
72
|
+
const fetchStep = async (frame, signal, deps) => {
|
|
73
|
+
const { op } = frame;
|
|
74
|
+
if (op.type === "remove") return;
|
|
75
|
+
const { fetch } = stepWiring(deps);
|
|
76
|
+
frame.data = await fetch(op, op.type === "append-init" ? {
|
|
77
|
+
signal,
|
|
78
|
+
minChunkSize: Infinity
|
|
79
|
+
} : { signal });
|
|
80
|
+
};
|
|
81
|
+
/** Dispatch the frame's message to the SourceBufferActor and await its return to idle. */
|
|
82
|
+
const dispatchStep = async (frame, signal, deps) => {
|
|
83
|
+
const { sourceBufferActor } = stepWiring(deps);
|
|
84
|
+
sourceBufferActor.send(toMessage(frame));
|
|
85
|
+
await waitForIdle(sourceBufferActor.snapshot, signal);
|
|
86
|
+
};
|
|
87
|
+
/** Tier 0 default: fetch (for ops that carry bytes) then dispatch. No relocation vocabulary. */
|
|
88
|
+
const DEFAULT_MESSAGE_PIPELINES = () => ({
|
|
89
|
+
remove: [dispatchStep],
|
|
90
|
+
"append-init": [fetchStep, dispatchStep],
|
|
91
|
+
"append-segment": [fetchStep, dispatchStep]
|
|
92
|
+
});
|
|
93
|
+
//#endregion
|
|
94
|
+
export { DEFAULT_MESSAGE_PIPELINES, dispatchStep, fetchStep };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=segment-load-pipeline.js.map
|