@videojs/spf 10.0.0-beta.24 → 10.0.0-beta.26
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 +5 -0
- package/dist/default/background-video.js +3 -0
- package/dist/default/core/actors/create-machine-actor.js.map +1 -1
- package/dist/default/core/actors/create-transition-actor.js.map +1 -1
- package/dist/default/core/composition/create-composition.js +2 -2
- package/dist/default/core/composition/create-composition.js.map +1 -1
- package/dist/default/core/composition/share-signals.js +13 -11
- package/dist/default/core/composition/share-signals.js.map +1 -1
- package/dist/default/core/machine.js.map +1 -1
- package/dist/default/core/reactors/create-machine-reactor.js.map +1 -1
- package/dist/default/core/signals/effect.js.map +1 -1
- package/dist/default/core/signals/primitives.js.map +1 -1
- package/dist/default/core/tasks/task.js.map +1 -1
- package/dist/default/hls.js +4 -1
- package/dist/default/media/abr/quality-selection.js +6 -68
- package/dist/default/media/abr/quality-selection.js.map +1 -1
- package/dist/default/media/buffer/back-buffer.js.map +1 -1
- package/dist/default/media/buffer/forward-buffer.js +37 -3
- package/dist/default/media/buffer/forward-buffer.js.map +1 -1
- package/dist/default/media/dom/capabilities.js +70 -0
- package/dist/default/media/dom/capabilities.js.map +1 -0
- package/dist/default/media/dom/mse/append-segment.js.map +1 -1
- package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -1
- package/dist/default/media/dom/mse/duration.js +27 -10
- package/dist/default/media/dom/mse/duration.js.map +1 -1
- package/dist/default/media/dom/mse/end-of-stream.js.map +1 -1
- package/dist/default/media/dom/mse/mediasource-setup.js +68 -17
- package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -1
- package/dist/default/media/dom/text/resolve-vtt-segment.js +1 -7
- package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -1
- package/dist/default/media/dom/text/text-track-slots.js +3 -3
- package/dist/default/media/dom/text/text-track-slots.js.map +1 -1
- package/dist/default/media/hls/parse-attributes.js +17 -3
- package/dist/default/media/hls/parse-attributes.js.map +1 -1
- package/dist/default/media/hls/parse-media-playlist.js +29 -1
- package/dist/default/media/hls/parse-media-playlist.js.map +1 -1
- package/dist/default/media/hls/parse-multivariant.js +15 -6
- package/dist/default/media/hls/parse-multivariant.js.map +1 -1
- package/dist/default/media/hls/resolve-url.js.map +1 -1
- 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/select-tracks.js +98 -13
- package/dist/default/media/primitives/select-tracks.js.map +1 -1
- 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.map +1 -1
- 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.map +1 -1
- package/dist/default/media/utils/track-selection.js +1 -1
- package/dist/default/media/utils/track-selection.js.map +1 -1
- package/dist/default/media/utils/tracks.js +31 -4
- package/dist/default/media/utils/tracks.js.map +1 -1
- package/dist/default/media-tracks.js +2 -0
- package/dist/default/network/bandwidth-estimator.js.map +1 -1
- package/dist/default/network/chunked-stream-iterable.js.map +1 -1
- package/dist/default/network/ewma.js +1 -1
- package/dist/default/network/ewma.js.map +1 -1
- package/dist/default/network/fetch.js +7 -1
- package/dist/default/network/fetch.js.map +1 -1
- package/dist/default/playback/actors/dom/segment-loader.js +103 -106
- package/dist/default/playback/actors/dom/segment-loader.js.map +1 -1
- package/dist/default/playback/actors/dom/source-buffer.js +3 -1
- package/dist/default/playback/actors/dom/source-buffer.js.map +1 -1
- package/dist/default/playback/actors/dom/text-tracks.js.map +1 -1
- package/dist/default/playback/actors/text-track-segment-loader.js +30 -16
- package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -1
- package/dist/default/playback/behaviors/calculate-presentation-duration.js +21 -43
- package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -1
- 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 +25 -102
- package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -1
- package/dist/default/playback/behaviors/dom/load-segments.js +6 -2
- package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -1
- 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/setup-buffer-actors.js +50 -18
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
- package/dist/default/playback/behaviors/dom/setup-mediasource.js +36 -11
- package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -1
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +11 -25
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -1
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js +43 -18
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-current-time.js +6 -0
- package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-load-triggers.js +4 -4
- package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -1
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +3 -2
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
- package/dist/default/playback/behaviors/establish-start-media-time.js +123 -0
- package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -0
- package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -1
- package/dist/default/playback/behaviors/resolve-track.js +45 -22
- package/dist/default/playback/behaviors/resolve-track.js.map +1 -1
- package/dist/default/playback/behaviors/select-tracks.js +46 -77
- package/dist/default/playback/behaviors/select-tracks.js.map +1 -1
- 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.map +1 -1
- package/dist/default/playback/behaviors/track-switching.js +403 -0
- package/dist/default/playback/behaviors/track-switching.js.map +1 -0
- package/dist/default/playback/engines/background-video/adapter.js +158 -0
- package/dist/default/playback/engines/background-video/adapter.js.map +1 -0
- package/dist/default/playback/engines/background-video/engine.js +75 -0
- package/dist/default/playback/engines/background-video/engine.js.map +1 -0
- package/dist/default/playback/engines/hls/adapter-audio-only.js +124 -0
- package/dist/default/playback/engines/hls/adapter-audio-only.js.map +1 -0
- package/dist/default/playback/engines/hls/adapter.js +20 -11
- package/dist/default/playback/engines/hls/adapter.js.map +1 -1
- package/dist/default/playback/engines/hls/engine-audio-only.js +94 -0
- package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -0
- package/dist/default/playback/engines/hls/engine.js +38 -8
- package/dist/default/playback/engines/hls/engine.js.map +1 -1
- 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/relocation-pipelines.js +203 -0
- package/dist/default/playback/primitives/relocation-pipelines.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/text-segment-load-pipeline.js +37 -0
- package/dist/default/playback/primitives/text-segment-load-pipeline.js.map +1 -0
- package/dist/{dev/playback/behaviors → default/playback/primitives}/track-types.js +1 -1
- package/dist/default/playback/primitives/track-types.js.map +1 -0
- package/dist/dev/background-video.d.ts +3 -0
- package/dist/dev/background-video.js +3 -0
- package/dist/dev/core/actors/actor.d.ts +0 -1
- package/dist/dev/core/actors/actor.d.ts.map +1 -1
- package/dist/dev/core/actors/create-machine-actor.d.ts +9 -6
- package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -1
- package/dist/dev/core/actors/create-machine-actor.js.map +1 -1
- package/dist/dev/core/actors/create-transition-actor.d.ts +0 -1
- package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -1
- package/dist/dev/core/actors/create-transition-actor.js.map +1 -1
- package/dist/dev/core/composition/create-composition.d.ts +10 -11
- package/dist/dev/core/composition/create-composition.d.ts.map +1 -1
- package/dist/dev/core/composition/create-composition.js +2 -2
- package/dist/dev/core/composition/create-composition.js.map +1 -1
- package/dist/dev/core/composition/share-signals.d.ts +11 -10
- package/dist/dev/core/composition/share-signals.d.ts.map +1 -1
- package/dist/dev/core/composition/share-signals.js +13 -11
- package/dist/dev/core/composition/share-signals.js.map +1 -1
- package/dist/dev/core/machine.d.ts +1 -0
- package/dist/dev/core/machine.d.ts.map +1 -1
- package/dist/dev/core/machine.js.map +1 -1
- package/dist/dev/core/reactors/create-machine-reactor.d.ts +2 -2
- package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -1
- package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -1
- package/dist/dev/core/signals/effect.d.ts.map +1 -1
- package/dist/dev/core/signals/effect.js.map +1 -1
- package/dist/dev/core/signals/primitives.d.ts +1 -2
- package/dist/dev/core/signals/primitives.d.ts.map +1 -1
- package/dist/dev/core/signals/primitives.js.map +1 -1
- package/dist/dev/core/tasks/task.d.ts +2 -2
- package/dist/dev/core/tasks/task.d.ts.map +1 -1
- package/dist/dev/core/tasks/task.js.map +1 -1
- package/dist/dev/hls.d.ts +5 -1
- package/dist/dev/hls.js +4 -1
- package/dist/dev/index.d.ts +0 -1
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/media/abr/quality-selection.d.ts.map +1 -1
- package/dist/dev/media/abr/quality-selection.js +6 -68
- package/dist/dev/media/abr/quality-selection.js.map +1 -1
- package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -1
- package/dist/dev/media/buffer/back-buffer.js.map +1 -1
- package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -1
- package/dist/dev/media/buffer/forward-buffer.js +37 -3
- package/dist/dev/media/buffer/forward-buffer.js.map +1 -1
- package/dist/dev/media/dom/capabilities.js +70 -0
- package/dist/dev/media/dom/capabilities.js.map +1 -0
- package/dist/dev/media/dom/mse/append-segment.d.ts +3 -9
- package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -1
- package/dist/dev/media/dom/mse/append-segment.js.map +1 -1
- package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -1
- package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -1
- package/dist/dev/media/dom/mse/duration.js +27 -10
- package/dist/dev/media/dom/mse/duration.js.map +1 -1
- package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -1
- package/dist/dev/media/dom/mse/mediasource-setup.js +68 -17
- package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -1
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +0 -6
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -1
- package/dist/dev/media/dom/text/resolve-vtt-segment.js +1 -7
- package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -1
- package/dist/dev/media/dom/text/text-track-slots.d.ts +0 -1
- package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -1
- package/dist/dev/media/dom/text/text-track-slots.js +3 -3
- package/dist/dev/media/dom/text/text-track-slots.js.map +1 -1
- package/dist/dev/media/hls/parse-attributes.js +17 -3
- package/dist/dev/media/hls/parse-attributes.js.map +1 -1
- package/dist/dev/media/hls/parse-media-playlist.js +29 -1
- package/dist/dev/media/hls/parse-media-playlist.js.map +1 -1
- package/dist/dev/media/hls/parse-multivariant.js +15 -6
- package/dist/dev/media/hls/parse-multivariant.js.map +1 -1
- package/dist/dev/media/hls/resolve-url.js.map +1 -1
- 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/select-tracks.d.ts +35 -0
- package/dist/dev/media/primitives/select-tracks.d.ts.map +1 -0
- package/dist/dev/media/primitives/select-tracks.js +98 -13
- package/dist/dev/media/primitives/select-tracks.js.map +1 -1
- 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 +69 -2
- package/dist/dev/media/types/index.d.ts.map +1 -1
- package/dist/dev/media/types/index.js.map +1 -1
- 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.map +1 -1
- package/dist/dev/media/utils/track-selection.js +1 -1
- package/dist/dev/media/utils/track-selection.js.map +1 -1
- package/dist/dev/media/utils/tracks.js +31 -4
- package/dist/dev/media/utils/tracks.js.map +1 -1
- package/dist/dev/media-tracks.d.ts +3 -0
- package/dist/dev/media-tracks.js +2 -0
- package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -1
- package/dist/dev/network/bandwidth-estimator.js.map +1 -1
- package/dist/dev/network/chunked-stream-iterable.js.map +1 -1
- package/dist/dev/network/ewma.js +1 -1
- package/dist/dev/network/ewma.js.map +1 -1
- package/dist/dev/network/fetch.js +7 -1
- package/dist/dev/network/fetch.js.map +1 -1
- package/dist/dev/playback/actors/dom/segment-loader.d.ts +16 -4
- package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -1
- package/dist/dev/playback/actors/dom/segment-loader.js +103 -106
- package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -1
- package/dist/dev/playback/actors/dom/source-buffer.d.ts +10 -25
- package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -1
- package/dist/dev/playback/actors/dom/source-buffer.js +3 -1
- package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -1
- package/dist/dev/playback/actors/dom/text-tracks.d.ts +3 -1
- package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -1
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts +7 -12
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -1
- package/dist/dev/playback/actors/text-track-segment-loader.js +30 -16
- package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -1
- package/dist/dev/playback/actors/text-tracks.d.ts +3 -12
- package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +11 -2
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js +21 -43
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -1
- 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 +25 -102
- package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts +14 -4
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/load-segments.js +6 -2
- package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -1
- 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/setup-buffer-actors.js +50 -18
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js +36 -11
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +16 -19
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +11 -25
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +43 -18
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +1 -2
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-current-time.js +6 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +1 -2
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js +4 -4
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +1 -2
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +3 -2
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
- package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +31 -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 +123 -0
- package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -0
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts +2 -1
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -1
- package/dist/dev/playback/behaviors/resolve-track.js +45 -22
- package/dist/dev/playback/behaviors/resolve-track.js.map +1 -1
- package/dist/dev/playback/behaviors/select-tracks.d.ts +14 -0
- package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/select-tracks.js +46 -77
- package/dist/dev/playback/behaviors/select-tracks.js.map +1 -1
- 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.map +1 -1
- package/dist/dev/playback/behaviors/track-switching.js +403 -0
- package/dist/dev/playback/behaviors/track-switching.js.map +1 -0
- package/dist/dev/playback/engines/background-video/adapter.d.ts +59 -0
- package/dist/dev/playback/engines/background-video/adapter.d.ts.map +1 -0
- package/dist/dev/playback/engines/background-video/adapter.js +158 -0
- package/dist/dev/playback/engines/background-video/adapter.js.map +1 -0
- package/dist/dev/playback/engines/background-video/engine.d.ts +106 -0
- package/dist/dev/playback/engines/background-video/engine.d.ts.map +1 -0
- package/dist/dev/playback/engines/background-video/engine.js +75 -0
- package/dist/dev/playback/engines/background-video/engine.js.map +1 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts +46 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.js +124 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.js.map +1 -0
- package/dist/dev/playback/engines/hls/adapter.d.ts +3 -5
- package/dist/dev/playback/engines/hls/adapter.d.ts.map +1 -1
- package/dist/dev/playback/engines/hls/adapter.js +20 -11
- package/dist/dev/playback/engines/hls/adapter.js.map +1 -1
- package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +156 -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 +94 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -0
- package/dist/dev/playback/engines/hls/engine.d.ts +109 -4
- package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -1
- package/dist/dev/playback/engines/hls/engine.js +38 -8
- package/dist/dev/playback/engines/hls/engine.js.map +1 -1
- 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/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/relocation-pipelines.js +203 -0
- package/dist/dev/playback/primitives/relocation-pipelines.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/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/{default/playback/behaviors → dev/playback/primitives}/track-types.js +1 -1
- package/dist/dev/playback/primitives/track-types.js.map +1 -0
- package/package.json +17 -7
- package/dist/default/playback/behaviors/quality-switching.js +0 -96
- package/dist/default/playback/behaviors/quality-switching.js.map +0 -1
- package/dist/default/playback/behaviors/track-types.js.map +0 -1
- package/dist/dev/playback/behaviors/quality-switching.js +0 -96
- package/dist/dev/playback/behaviors/quality-switching.js.map +0 -1
- package/dist/dev/playback/behaviors/track-types.js.map +0 -1
|
@@ -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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-load-pipeline.js","names":[],"sources":["../../../../src/playback/primitives/segment-load-pipeline.ts"],"sourcesContent":["/**\n * The v/a segment **load pipeline** — the step vocabulary and base steps a segment\n * loader runs, extracted from the loader actor so the \"what\" (the composable steps)\n * is separable from the \"how\" (the actor's scheduling). A pipeline is a per-op list\n * of {@link LoadStep}s the loader drains in order; the default is `fetch → dispatch`,\n * and a composition can insert its own steps between them (e.g. non-zero-PTS\n * relocation) without the loader knowing.\n *\n * The base steps reach their SourceBuffer sink through the structural {@link AppendSink}\n * seam rather than the concrete actor type — the loader folds the real actor into\n * `config` at construction, and it satisfies the seam by structure — so this module\n * carries no dependency on the actor implementation, and no dependency on the DOM.\n */\nimport type { AnySlotMap } from '../../core/composition/create-composition';\nimport { effect } from '../../core/signals/effect';\nimport type { ReadonlySignal } from '../../core/signals/primitives';\nimport type { AddressableObject } from '../../media/types';\nimport type {\n AppendInitMessage,\n AppendSegmentMessage,\n IndividualSourceBufferMessage,\n RemoveMessage,\n} from './source-buffer-messages';\n\n// ============================================================================\n// SINK SEAM\n// ============================================================================\n\n/**\n * The structural view of a SourceBuffer sink the base steps use — just the ops\n * {@link dispatchStep} needs (`send` + a `snapshot` to await idle). The concrete\n * `SourceBufferActor` satisfies it by structure, so the pipeline names no actor.\n */\nexport interface AppendSink {\n send(message: IndividualSourceBufferMessage): void;\n snapshot: ReadonlySignal<{ value: string }>;\n}\n\n// ============================================================================\n// STEP MODEL\n// ============================================================================\n\n/**\n * A LoadTask is the intent to perform one unit of SegmentLoader work.\n * Unlike SourceBufferMessage, fetch-based tasks carry a URL rather than\n * pre-fetched data — the runner fetches and appends them in sequence.\n *\n * Derived from SourceBufferMessage types by removing `data` and adding\n * a fetch URL via AddressableObject.\n *\n * @todo Rename — \"LoadTask\" risks confusion with the `Task` class used for\n * SourceBufferActor scheduling. These are closer to operation descriptors or\n * messages than tasks in that sense.\n */\nexport type LoadTask =\n | (Omit<AppendInitMessage, 'data'> & AddressableObject)\n | (Omit<AppendSegmentMessage, 'data'> & AddressableObject)\n | RemoveMessage;\n\n/**\n * A {@link LoadTask} mid-reassembly into its append message. `LoadTask` is a\n * message with `data` omitted and a URL added; the pipeline reverses that —\n * `fetchStep` produces `data`, `dispatchStep` reassembles the message. `data` is\n * the omitted payload (kept as the fetched stream — the loader never produces the\n * `ArrayBuffer` arm of `SegmentData` — widened back at dispatch). `meta` overrides\n * `op.meta` for `append-segment` (e.g. a stamped `timestampOffset`); an\n * `append-init` dispatches `op.meta` directly.\n */\nexport interface Frame {\n readonly op: LoadTask;\n data?: AsyncIterable<Uint8Array>;\n meta?: AppendSegmentMessage['meta'];\n}\n\n/**\n * One stage of a message pipeline. Mutates the {@link Frame} in place and may be\n * async; the runner checks `signal.aborted` before each step and passes the\n * actor's {@link StepDeps} on every call, so a stateless step (e.g.\n * {@link fetchStep}) is a plain value, and a step that needs composition signals\n * (relocation's discover/stamp) reads them from `deps` at call time.\n */\nexport type LoadStep = (frame: Frame, signal: AbortSignal, deps: StepDeps) => void | Promise<void>;\n\n/**\n * The uniform passthrough handed to every {@link LoadStep} on every call — the\n * composition triple, nothing more. `state`/`context` are the full composition signal\n * maps; `config` is the threaded config with the loader's own wiring folded in (see\n * {@link stepWiring} + `createSegmentLoaderActor`). All three are typed loose: the\n * loader is a conduit and never reads them; a step asserts the slots it knows are\n * present (composition steps read `state`; base steps read the folded wiring off\n * `config`).\n */\nexport interface StepDeps {\n state: AnySlotMap;\n context: AnySlotMap;\n config: object;\n}\n\n/**\n * Base-step view of the loader's own wiring. `createSegmentLoaderActor` folds its\n * `sourceBufferActor` + `fetch` into the threaded `config` so base steps read them\n * from the uniform passthrough — present whether the loader runs inside a composition\n * or standalone. `config` is loose (`object`), so assert the shape here (one cast, like\n * relocation's `containerSlot`).\n */\nfunction stepWiring(deps: StepDeps): { sourceBufferActor: AppendSink; fetch: FetchBytes } {\n return deps.config as { sourceBufferActor: AppendSink; fetch: FetchBytes };\n}\n\n/**\n * Builds the ordered step list for each message type. Called **once per actor**\n * — its role is per-actor instantiation, so stateful steps (relocation's origin\n * discoverer) get fresh state per source reset; deps arrive at step-call time,\n * not here. The default ({@link DEFAULT_MESSAGE_PIPELINES}) is `fetch → dispatch`;\n * a non-zero-PTS composition returns a map that inserts its own discover/stamp\n * steps between them (see `establishStartMediaTime`), so the loader stays oblivious\n * to relocation and the Tier 0 pipeline carries no relocation vocabulary at all.\n */\nexport type MessagePipelines = () => Record<LoadTask['type'], LoadStep[]>;\n\n// ============================================================================\n// BASE STEPS\n// ============================================================================\n\nexport type FetchBytes = (\n addressable: AddressableObject,\n options?: RequestInit & { minChunkSize?: number }\n) => Promise<AsyncIterable<Uint8Array>>;\n\n/**\n * Resolves when the SourceBufferActor snapshot reaches 'idle'.\n * Rejects if the signal is aborted or the actor is destroyed.\n *\n * Used to sequence SourceBufferActor operations without awaiting send()\n * directly — send() is fire-and-forget; callers observe completion via\n * state transition.\n */\nfunction waitForIdle(snapshot: AppendSink['snapshot'], signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (snapshot.get().value === 'idle') {\n resolve();\n return;\n }\n if (snapshot.get().value === 'destroyed') {\n reject(new DOMException('Aborted', 'AbortError'));\n return;\n }\n if (signal.aborted) {\n reject(signal.reason);\n return;\n }\n\n let stop: (() => void) | undefined;\n\n const cleanup = (fn: () => void) => {\n stop?.();\n signal.removeEventListener('abort', onAbort);\n fn();\n };\n\n const onAbort = () => cleanup(() => reject(signal.reason));\n\n stop = effect(() => {\n const value = snapshot.get().value;\n if (value === 'idle') cleanup(resolve);\n else if (value === 'destroyed') cleanup(() => reject(new DOMException('Aborted', 'AbortError')));\n });\n\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/** Build the SourceBuffer message a completed frame dispatches. `fetchStep` always precedes `dispatchStep` in append pipelines, so `data` is set by now. */\nfunction toMessage({ op, data, meta }: Frame): IndividualSourceBufferMessage {\n switch (op.type) {\n case 'remove':\n return op;\n case 'append-init':\n return { type: 'append-init', data: data!, meta: op.meta };\n case 'append-segment':\n return { type: 'append-segment', data: data!, meta: meta ?? op.meta };\n }\n}\n\n/**\n * Fetch this op's bytes into the frame. Init segments need the full body\n * (`minChunkSize: Infinity`) before appending; media segments stream so chunks\n * append as they arrive. Awaiting headers eagerly also starts the HTTP\n * connection (and records the fetch in observers like tests).\n */\nexport const fetchStep: LoadStep = async (frame, signal, deps) => {\n const { op } = frame;\n if (op.type === 'remove') return; // fetchStep only appears in append pipelines\n const { fetch } = stepWiring(deps);\n frame.data = await fetch(op, op.type === 'append-init' ? { signal, minChunkSize: Infinity } : { signal });\n};\n\n/** Dispatch the frame's message to the SourceBufferActor and await its return to idle. */\nexport const dispatchStep: LoadStep = async (frame, signal, deps) => {\n const { sourceBufferActor } = stepWiring(deps);\n sourceBufferActor.send(toMessage(frame));\n await waitForIdle(sourceBufferActor.snapshot, signal);\n};\n\n/** Tier 0 default: fetch (for ops that carry bytes) then dispatch. No relocation vocabulary. */\nexport const DEFAULT_MESSAGE_PIPELINES: MessagePipelines = () => ({\n remove: [dispatchStep],\n 'append-init': [fetchStep, dispatchStep],\n 'append-segment': [fetchStep, dispatchStep],\n});\n"],"mappings":";;;;;;;;;AAyGA,SAAS,WAAW,MAAsE;CACxF,OAAO,KAAK;AACd;;;;;;;;;AA8BA,SAAS,YAAY,UAAkC,QAAoC;CACzF,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,SAAS,IAAI,CAAC,CAAC,UAAU,QAAQ;GACnC,QAAQ;GACR;EACF;EACA,IAAI,SAAS,IAAI,CAAC,CAAC,UAAU,aAAa;GACxC,OAAO,IAAI,aAAa,WAAW,YAAY,CAAC;GAChD;EACF;EACA,IAAI,OAAO,SAAS;GAClB,OAAO,OAAO,MAAM;GACpB;EACF;EAEA,IAAI;EAEJ,MAAM,WAAW,OAAmB;GAClC,OAAO;GACP,OAAO,oBAAoB,SAAS,OAAO;GAC3C,GAAG;EACL;EAEA,MAAM,gBAAgB,cAAc,OAAO,OAAO,MAAM,CAAC;EAEzD,OAAO,aAAa;GAClB,MAAM,QAAQ,SAAS,IAAI,CAAC,CAAC;GAC7B,IAAI,UAAU,QAAQ,QAAQ,OAAO;QAChC,IAAI,UAAU,aAAa,cAAc,OAAO,IAAI,aAAa,WAAW,YAAY,CAAC,CAAC;EACjG,CAAC;EAED,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC1D,CAAC;AACH;;AAGA,SAAS,UAAU,EAAE,IAAI,MAAM,QAA8C;CAC3E,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OAAO;EACT,KAAK,eACH,OAAO;GAAE,MAAM;GAAqB;GAAO,MAAM,GAAG;EAAK;EAC3D,KAAK,kBACH,OAAO;GAAE,MAAM;GAAwB;GAAO,MAAM,QAAQ,GAAG;EAAK;CACxE;AACF;;;;;;;AAQA,MAAa,YAAsB,OAAO,OAAO,QAAQ,SAAS;CAChE,MAAM,EAAE,OAAO;CACf,IAAI,GAAG,SAAS,UAAU;CAC1B,MAAM,EAAE,UAAU,WAAW,IAAI;CACjC,MAAM,OAAO,MAAM,MAAM,IAAI,GAAG,SAAS,gBAAgB;EAAE;EAAQ,cAAc;CAAS,IAAI,EAAE,OAAO,CAAC;AAC1G;;AAGA,MAAa,eAAyB,OAAO,OAAO,QAAQ,SAAS;CACnE,MAAM,EAAE,sBAAsB,WAAW,IAAI;CAC7C,kBAAkB,KAAK,UAAU,KAAK,CAAC;CACvC,MAAM,YAAY,kBAAkB,UAAU,MAAM;AACtD;;AAGA,MAAa,mCAAqD;CAChE,QAAQ,CAAC,YAAY;CACrB,eAAe,CAAC,WAAW,YAAY;CACvC,kBAAkB,CAAC,WAAW,YAAY;AAC5C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Segment, SegmentData, Track } from "../../media/types/index.js";
|
|
2
|
+
//#region src/playback/primitives/source-buffer-messages.d.ts
|
|
3
|
+
type AppendSegmentMeta = Pick<Segment, 'id' | 'startTime' | 'duration'> & {
|
|
4
|
+
trackId: Track['id'];
|
|
5
|
+
/** Declared track bandwidth in bps (from playlist BANDWIDTH attribute). */
|
|
6
|
+
trackBandwidth?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Non-zero-PTS relocation: when present, applied as `SourceBuffer.timestampOffset`
|
|
9
|
+
* before this append so native PTS is relocated onto a 0-based presentation
|
|
10
|
+
* timeline. A relocating composition stamps it (constant per source) onto each
|
|
11
|
+
* media segment's meta; the apply is idempotent-guarded. Absent = no relocation.
|
|
12
|
+
*/
|
|
13
|
+
timestampOffset?: number;
|
|
14
|
+
};
|
|
15
|
+
type AppendInitMessage = {
|
|
16
|
+
type: 'append-init';
|
|
17
|
+
data: SegmentData;
|
|
18
|
+
/**
|
|
19
|
+
* `language` is captured alongside `trackId` so downstream loaders can
|
|
20
|
+
* compare the buffered track's language to the newly-selected track's
|
|
21
|
+
* language and decide whether ahead-buffer flush is warranted on track
|
|
22
|
+
* switch (see `segment-loader`'s `planTasks`). Undefined for video and
|
|
23
|
+
* for audio without explicit `LANGUAGE` attribute.
|
|
24
|
+
*/
|
|
25
|
+
meta: {
|
|
26
|
+
trackId: Track['id'];
|
|
27
|
+
language?: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
type AppendSegmentMessage = {
|
|
31
|
+
type: 'append-segment';
|
|
32
|
+
data: SegmentData;
|
|
33
|
+
meta: AppendSegmentMeta;
|
|
34
|
+
};
|
|
35
|
+
type RemoveMessage = {
|
|
36
|
+
type: 'remove';
|
|
37
|
+
start: number;
|
|
38
|
+
end: number;
|
|
39
|
+
};
|
|
40
|
+
type IndividualSourceBufferMessage = AppendInitMessage | AppendSegmentMessage | RemoveMessage;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { AppendInitMessage, AppendSegmentMessage, AppendSegmentMeta, IndividualSourceBufferMessage, RemoveMessage };
|
|
43
|
+
//# sourceMappingURL=source-buffer-messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-buffer-messages.d.ts","names":[],"sources":["../../../../src/playback/primitives/source-buffer-messages.ts"],"mappings":";;KAUY,oBAAoB,KAAK;EACnC,SAAS;;EAET;;;;;;;EAOA;;KAGU;EACV;EACA,MAAM;;;;;;;;EAQN;IAAQ,SAAS;IAAa;;;KAEpB;EAAyB;EAAwB,MAAM;EAAa,MAAM;;KAC1E;EAAkB;EAAgB;EAAe;;KACjD,gCAAgC,oBAAoB,uBAAuB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Cue, Segment } from "../../media/types/index.js";
|
|
2
|
+
import { AnySlotMap } from "../../core/composition/create-composition.js";
|
|
3
|
+
//#region src/playback/primitives/text-segment-load-pipeline.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a text-track segment URL into the array of cues it contains.
|
|
6
|
+
*
|
|
7
|
+
* "Resolve" because the fn covers both network fetch and parse into the
|
|
8
|
+
* domain model. Host-agnostic — the concrete resolver (e.g. the
|
|
9
|
+
* browser's native VTT resolver) is supplied at engine-assembly time,
|
|
10
|
+
* so this stays DOM-free. A pure `url → cues` primitive (the text
|
|
11
|
+
* analog of the v/a loader's `fetchBytes`); composition-awareness lives in
|
|
12
|
+
* the injected {@link TextLoadStep}s, not here.
|
|
13
|
+
*/
|
|
14
|
+
type TextTrackSegmentResolver<C extends Cue = Cue> = (url: string) => Promise<C[]>;
|
|
15
|
+
/** Internal load-task descriptor — one segment fetch + dispatch unit. */
|
|
16
|
+
interface TextLoadTask {
|
|
17
|
+
segment: Segment;
|
|
18
|
+
trackId: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A text load in mid-pipeline — the text analog of the v/a loader's `Frame`.
|
|
22
|
+
* `resolveCuesStep` fills `cues`; `dispatchCuesStep` sends them. `metadata` is
|
|
23
|
+
* opaque header metadata a resolve step may attach for a later step to read
|
|
24
|
+
* (e.g. relocation stashes the `X-TIMESTAMP-MAP` correlation here for its rebase
|
|
25
|
+
* step). Typed `unknown` so the generic loader stays host-agnostic — the step
|
|
26
|
+
* that reads it knows its concrete shape (mirrors `StepDeps.state`).
|
|
27
|
+
*/
|
|
28
|
+
interface TextFrame<C extends Cue = Cue> {
|
|
29
|
+
readonly op: TextLoadTask;
|
|
30
|
+
cues?: C[];
|
|
31
|
+
metadata?: unknown;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* One stage of a text message pipeline — the text analog of the v/a loader's
|
|
35
|
+
* `LoadStep`. Mutates the {@link TextFrame} in place and may be async; the runner
|
|
36
|
+
* checks `signal.aborted` before each step and passes the actor's
|
|
37
|
+
* {@link TextStepDeps} on every call, so a stateless step (`resolveCuesStep`) is a
|
|
38
|
+
* plain value and a step that needs composition signals (relocation's cue rebase)
|
|
39
|
+
* reads them from `deps` at call time.
|
|
40
|
+
*/
|
|
41
|
+
type TextLoadStep<C extends Cue = Cue> = (frame: TextFrame<C>, signal: AbortSignal, deps: TextStepDeps) => void | Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* The uniform passthrough handed to each {@link TextLoadStep} — the composition triple,
|
|
44
|
+
* the text analog of `StepDeps`. `state`/`context` are the composition signal maps;
|
|
45
|
+
* `config` is the threaded config with the loader's wiring folded in (see
|
|
46
|
+
* {@link textStepWiring} + `createTextTrackSegmentLoaderActor`). Typed loose: composition
|
|
47
|
+
* steps read `state`; base steps read the folded wiring off `config`.
|
|
48
|
+
*/
|
|
49
|
+
interface TextStepDeps {
|
|
50
|
+
state: AnySlotMap;
|
|
51
|
+
context: AnySlotMap;
|
|
52
|
+
config: object;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Builds the ordered step list, called **once per actor** (mirrors the v/a loader's
|
|
56
|
+
* `MessagePipelines`, but text has a single op type so it's a flat array, not a
|
|
57
|
+
* `Record`). The default ({@link DEFAULT_TEXT_MESSAGE_PIPELINES}) is
|
|
58
|
+
* `resolveCues → dispatchCues`; a non-zero-PTS composition returns a list that
|
|
59
|
+
* inserts a cue-rebase step (see `relocatingTextPipelines`), so the loader stays
|
|
60
|
+
* oblivious to relocation.
|
|
61
|
+
*/
|
|
62
|
+
type TextMessagePipelines<C extends Cue = Cue> = () => TextLoadStep<C>[];
|
|
63
|
+
//#endregion
|
|
64
|
+
export { TextFrame, TextLoadStep, TextLoadTask, TextMessagePipelines, TextStepDeps, TextTrackSegmentResolver };
|
|
65
|
+
//# sourceMappingURL=text-segment-load-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-segment-load-pipeline.d.ts","names":[],"sources":["../../../../src/playback/primitives/text-segment-load-pipeline.ts"],"mappings":";;;;;;;;;;;;;KA6CY,yBAAyB,UAAU,MAAM,QAAQ,gBAAgB,QAAQ;;UAGpE;EACf,SAAS;EACT;;;;;;;;;;UAWe,UAAU,UAAU,MAAM;WAChC,IAAI;EACb,OAAO;EACP;;;;;;;;;;KAWU,aAAa,UAAU,MAAM,QACvC,OAAO,UAAU,IACjB,QAAQ,aACR,MAAM,wBACI;;;;;;;;UASK;EACf,OAAO;EACP,SAAS;EACT;;;;;;;;;;KAwBU,qBAAqB,UAAU,MAAM,aAAa,aAAa"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/playback/primitives/text-segment-load-pipeline.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base-step view of the loader's wiring, folded into `config` by
|
|
4
|
+
* `createTextTrackSegmentLoaderActor` so base steps read it from the uniform passthrough
|
|
5
|
+
* — present in both composition and standalone use. `config` is loose (`object`), so
|
|
6
|
+
* assert the shape here (mirrors the v/a loader's `stepWiring`). The sink is the
|
|
7
|
+
* structural {@link CueSink}, not the concrete actor.
|
|
8
|
+
*/
|
|
9
|
+
function textStepWiring(deps) {
|
|
10
|
+
return deps.config;
|
|
11
|
+
}
|
|
12
|
+
/** Resolve the op's cues (via the injected host primitive) into the frame. The text analog of `fetchStep`. */
|
|
13
|
+
const resolveCuesStep = async (frame, signal, deps) => {
|
|
14
|
+
const cues = await textStepWiring(deps).resolveSegment(frame.op.segment.url);
|
|
15
|
+
if (signal.aborted) return;
|
|
16
|
+
frame.cues = cues;
|
|
17
|
+
};
|
|
18
|
+
/** Dispatch the frame's cues to the TextTracksActor as `add-cues`. The text analog of `dispatchStep`. */
|
|
19
|
+
const dispatchCuesStep = (frame, _signal, deps) => {
|
|
20
|
+
const { op } = frame;
|
|
21
|
+
textStepWiring(deps).textTracksActor.send({
|
|
22
|
+
type: "add-cues",
|
|
23
|
+
meta: {
|
|
24
|
+
trackId: op.trackId,
|
|
25
|
+
id: op.segment.id,
|
|
26
|
+
startTime: op.segment.startTime,
|
|
27
|
+
duration: op.segment.duration
|
|
28
|
+
},
|
|
29
|
+
cues: frame.cues ?? []
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
/** Tier 0 default: resolve then dispatch. No relocation vocabulary. */
|
|
33
|
+
const DEFAULT_TEXT_MESSAGE_PIPELINES = () => [resolveCuesStep, dispatchCuesStep];
|
|
34
|
+
//#endregion
|
|
35
|
+
export { DEFAULT_TEXT_MESSAGE_PIPELINES, dispatchCuesStep, resolveCuesStep, textStepWiring };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=text-segment-load-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-segment-load-pipeline.js","names":[],"sources":["../../../../src/playback/primitives/text-segment-load-pipeline.ts"],"sourcesContent":["/**\n * The text-track **load pipeline** — the step vocabulary and base steps a text-segment\n * loader runs, the text mirror of `segment-load-pipeline`. Extracted from the loader\n * actor so the \"what\" (the composable steps) is separable from the \"how\" (scheduling).\n * A pipeline is a flat, ordered list of {@link TextLoadStep}s (text has a single op\n * type, so no per-type `Record`); the default is `resolveCues → dispatchCues`, and a\n * composition can insert its own steps (e.g. non-zero-PTS cue rebase) without the\n * loader knowing.\n *\n * The dispatch step reaches its cue sink through the structural {@link CueSink} seam\n * rather than the concrete `TextTracksActor` type — the loader folds the real actor\n * into `config`, and it satisfies the seam by structure — so this module names no\n * actor and stays DOM-free (generic over the cue type `C`).\n */\nimport type { AnySlotMap } from '../../core/composition/create-composition';\nimport type { Cue, Segment } from '../../media/types';\nimport type { AddCuesMessage } from './text-track-messages';\n\n// ============================================================================\n// SINK SEAM\n// ============================================================================\n\n/**\n * The structural view of a cue sink the base steps use — just the `send`\n * {@link dispatchCuesStep} needs. The concrete `TextTracksActor` satisfies it by\n * structure, so the pipeline names no actor.\n */\nexport interface CueSink<C extends Cue = Cue> {\n send(message: AddCuesMessage<C>): void;\n}\n\n// ============================================================================\n// STEP MODEL\n// ============================================================================\n\n/**\n * Resolves a text-track segment URL into the array of cues it contains.\n *\n * \"Resolve\" because the fn covers both network fetch and parse into the\n * domain model. Host-agnostic — the concrete resolver (e.g. the\n * browser's native VTT resolver) is supplied at engine-assembly time,\n * so this stays DOM-free. A pure `url → cues` primitive (the text\n * analog of the v/a loader's `fetchBytes`); composition-awareness lives in\n * the injected {@link TextLoadStep}s, not here.\n */\nexport type TextTrackSegmentResolver<C extends Cue = Cue> = (url: string) => Promise<C[]>;\n\n/** Internal load-task descriptor — one segment fetch + dispatch unit. */\nexport interface TextLoadTask {\n segment: Segment;\n trackId: string;\n}\n\n/**\n * A text load in mid-pipeline — the text analog of the v/a loader's `Frame`.\n * `resolveCuesStep` fills `cues`; `dispatchCuesStep` sends them. `metadata` is\n * opaque header metadata a resolve step may attach for a later step to read\n * (e.g. relocation stashes the `X-TIMESTAMP-MAP` correlation here for its rebase\n * step). Typed `unknown` so the generic loader stays host-agnostic — the step\n * that reads it knows its concrete shape (mirrors `StepDeps.state`).\n */\nexport interface TextFrame<C extends Cue = Cue> {\n readonly op: TextLoadTask;\n cues?: C[];\n metadata?: unknown;\n}\n\n/**\n * One stage of a text message pipeline — the text analog of the v/a loader's\n * `LoadStep`. Mutates the {@link TextFrame} in place and may be async; the runner\n * checks `signal.aborted` before each step and passes the actor's\n * {@link TextStepDeps} on every call, so a stateless step (`resolveCuesStep`) is a\n * plain value and a step that needs composition signals (relocation's cue rebase)\n * reads them from `deps` at call time.\n */\nexport type TextLoadStep<C extends Cue = Cue> = (\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n) => void | Promise<void>;\n\n/**\n * The uniform passthrough handed to each {@link TextLoadStep} — the composition triple,\n * the text analog of `StepDeps`. `state`/`context` are the composition signal maps;\n * `config` is the threaded config with the loader's wiring folded in (see\n * {@link textStepWiring} + `createTextTrackSegmentLoaderActor`). Typed loose: composition\n * steps read `state`; base steps read the folded wiring off `config`.\n */\nexport interface TextStepDeps {\n state: AnySlotMap;\n context: AnySlotMap;\n config: object;\n}\n\n/**\n * Base-step view of the loader's wiring, folded into `config` by\n * `createTextTrackSegmentLoaderActor` so base steps read it from the uniform passthrough\n * — present in both composition and standalone use. `config` is loose (`object`), so\n * assert the shape here (mirrors the v/a loader's `stepWiring`). The sink is the\n * structural {@link CueSink}, not the concrete actor.\n */\nexport function textStepWiring<C extends Cue>(\n deps: TextStepDeps\n): { textTracksActor: CueSink<C>; resolveSegment: TextTrackSegmentResolver<C> } {\n return deps.config as { textTracksActor: CueSink<C>; resolveSegment: TextTrackSegmentResolver<C> };\n}\n\n/**\n * Builds the ordered step list, called **once per actor** (mirrors the v/a loader's\n * `MessagePipelines`, but text has a single op type so it's a flat array, not a\n * `Record`). The default ({@link DEFAULT_TEXT_MESSAGE_PIPELINES}) is\n * `resolveCues → dispatchCues`; a non-zero-PTS composition returns a list that\n * inserts a cue-rebase step (see `relocatingTextPipelines`), so the loader stays\n * oblivious to relocation.\n */\nexport type TextMessagePipelines<C extends Cue = Cue> = () => TextLoadStep<C>[];\n\n// ============================================================================\n// BASE STEPS\n// ============================================================================\n\n// Base steps are generic over the cue type `C` (generic arrow consts, not\n// `TextLoadStep<Cue>` values): each touches `C` — `frame.cues: C[]` and the\n// `C`-typed `textStepWiring<C>` — so a `Cue`-typed const wouldn't slot into a\n// `VTTCue` pipeline. A generic function assigns to any `TextLoadStep<C>`.\n\n/** Resolve the op's cues (via the injected host primitive) into the frame. The text analog of `fetchStep`. */\nexport const resolveCuesStep = async <C extends Cue>(\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n): Promise<void> => {\n const cues = await textStepWiring<C>(deps).resolveSegment(frame.op.segment.url);\n if (signal.aborted) return;\n frame.cues = cues;\n};\n\n/** Dispatch the frame's cues to the TextTracksActor as `add-cues`. The text analog of `dispatchStep`. */\nexport const dispatchCuesStep = <C extends Cue>(\n frame: TextFrame<C>,\n _signal: AbortSignal,\n deps: TextStepDeps\n): void => {\n const { op } = frame;\n textStepWiring<C>(deps).textTracksActor.send({\n type: 'add-cues',\n meta: {\n trackId: op.trackId,\n id: op.segment.id,\n startTime: op.segment.startTime,\n duration: op.segment.duration,\n },\n cues: frame.cues ?? [],\n });\n};\n\n/** Tier 0 default: resolve then dispatch. No relocation vocabulary. */\nexport const DEFAULT_TEXT_MESSAGE_PIPELINES = <C extends Cue>(): TextLoadStep<C>[] => [\n resolveCuesStep,\n dispatchCuesStep,\n];\n"],"mappings":";;;;;;;;AAqGA,SAAgB,eACd,MAC8E;CAC9E,OAAO,KAAK;AACd;;AAsBA,MAAa,kBAAkB,OAC7B,OACA,QACA,SACkB;CAClB,MAAM,OAAO,MAAM,eAAkB,IAAI,CAAC,CAAC,eAAe,MAAM,GAAG,QAAQ,GAAG;CAC9E,IAAI,OAAO,SAAS;CACpB,MAAM,OAAO;AACf;;AAGA,MAAa,oBACX,OACA,SACA,SACS;CACT,MAAM,EAAE,OAAO;CACf,eAAkB,IAAI,CAAC,CAAC,gBAAgB,KAAK;EAC3C,MAAM;EACN,MAAM;GACJ,SAAS,GAAG;GACZ,IAAI,GAAG,QAAQ;GACf,WAAW,GAAG,QAAQ;GACtB,UAAU,GAAG,QAAQ;EACvB;EACA,MAAM,MAAM,QAAQ,CAAC;CACvB,CAAC;AACH;;AAGA,MAAa,uCAAyE,CACpF,iBACA,gBACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Cue, Segment } from "../../media/types/index.js";
|
|
2
|
+
//#region src/playback/primitives/text-track-messages.d.ts
|
|
3
|
+
/** Segment identity and timing — mirrors AppendSegmentMeta without trackId (keyed separately). */
|
|
4
|
+
type CueSegmentMeta = Pick<Segment, 'id' | 'startTime' | 'duration'> & {
|
|
5
|
+
trackId: string;
|
|
6
|
+
};
|
|
7
|
+
interface AddCuesMessage<C extends Cue = Cue> {
|
|
8
|
+
type: 'add-cues';
|
|
9
|
+
meta: CueSegmentMeta;
|
|
10
|
+
cues: C[];
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { AddCuesMessage, CueSegmentMeta };
|
|
14
|
+
//# sourceMappingURL=text-track-messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-track-messages.d.ts","names":[],"sources":["../../../../src/playback/primitives/text-track-messages.ts"],"mappings":";;;KASY,iBAAiB,KAAK;EAA8C;;UAE/D,eAAe,UAAU,MAAM;EAC9C;EACA,MAAM;EACN,MAAM"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track-types.js","names":[],"sources":["../../../../src/playback/primitives/track-types.ts"],"sourcesContent":["/**\n * **Per-type config bundles for per-type behavior variants.**\n *\n * Each `*_TYPE_CONFIG` constant names the per-type slot keys that\n * per-type-variant setup-shape helpers consume. Variants reference the\n * shared constant at their `defineBehavior` setup body rather than\n * constructing the config inline, so the per-type identity (which state\n * slot, which context slot, which discriminant) lives in one place per\n * type.\n *\n * Variants spread their composition-time `config` over the defaults so\n * engines can layer composition-supplied additions on top (e.g. a\n * custom fetch closure, a non-default segment resolver). Defaults\n * first, engine config second.\n *\n * Helpers continue to consume their slice via the existing typed-key\n * generics (`<K extends SelectedTrackKey>` etc.); the extra fields on\n * the config object (carrying facets other helpers care about) are fine\n * under structural typing.\n */\n\n/**\n * Type-identity bundle for video-track-typed behaviors.\n *\n * @see setupVideoBufferActors, loadVideoSegments\n */\nexport const VIDEO_TYPE_CONFIG = {\n type: 'video',\n selectedKey: 'selectedVideoTrackId',\n actorKey: 'videoBufferActor',\n loaderKey: 'videoSegmentLoaderActor',\n} as const;\n\n/**\n * Type-identity bundle for audio-track-typed behaviors.\n *\n * @see setupAudioBufferActors, loadAudioSegments\n */\nexport const AUDIO_TYPE_CONFIG = {\n type: 'audio',\n selectedKey: 'selectedAudioTrackId',\n actorKey: 'audioBufferActor',\n loaderKey: 'audioSegmentLoaderActor',\n} as const;\n\n/**\n * Type-identity bundle for text-track-typed behaviors. Text tracks have\n * no `SourceBufferActor` (MSE doesn't apply), so this bundle omits\n * `actorKey`. The `loaderKey` points at the text-track-segment-loader\n * actor (a `MessageActor` with continue-vs-preempt semantics, parallel\n * to v/a's `SegmentLoaderActor`).\n *\n * @see loadTextTrackSegments, setupTextTrackActors\n */\nexport const TEXT_TYPE_CONFIG = {\n type: 'text',\n selectedKey: 'selectedTextTrackId',\n loaderKey: 'textTrackSegmentLoaderActor',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,oBAAoB;CAC/B,MAAM;CACN,aAAa;CACb,UAAU;CACV,WAAW;AACb;;;;;;AAOA,MAAa,oBAAoB;CAC/B,MAAM;CACN,aAAa;CACb,UAAU;CACV,WAAW;AACb;;;;;;;;;;AAWA,MAAa,mBAAmB;CAC9B,MAAM;CACN,aAAa;CACb,WAAW;AACb"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@videojs/spf",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "10.0.0-beta.
|
|
4
|
+
"version": "10.0.0-beta.26",
|
|
5
5
|
"description": "Stream Processing Framework for Video.js 10",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -32,6 +32,16 @@
|
|
|
32
32
|
"types": "./dist/dev/hls.d.ts",
|
|
33
33
|
"development": "./dist/dev/hls.js",
|
|
34
34
|
"default": "./dist/default/hls.js"
|
|
35
|
+
},
|
|
36
|
+
"./media-tracks": {
|
|
37
|
+
"types": "./dist/dev/media-tracks.d.ts",
|
|
38
|
+
"development": "./dist/dev/media-tracks.js",
|
|
39
|
+
"default": "./dist/default/media-tracks.js"
|
|
40
|
+
},
|
|
41
|
+
"./background-video": {
|
|
42
|
+
"types": "./dist/dev/background-video.d.ts",
|
|
43
|
+
"development": "./dist/dev/background-video.js",
|
|
44
|
+
"default": "./dist/default/background-video.js"
|
|
35
45
|
}
|
|
36
46
|
},
|
|
37
47
|
"main": "dist/default/index.js",
|
|
@@ -42,16 +52,16 @@
|
|
|
42
52
|
],
|
|
43
53
|
"dependencies": {
|
|
44
54
|
"signal-polyfill": "^0.2.2",
|
|
45
|
-
"@videojs/utils": "10.0.0-beta.
|
|
55
|
+
"@videojs/utils": "10.0.0-beta.26"
|
|
46
56
|
},
|
|
47
57
|
"devDependencies": {
|
|
48
|
-
"@vitest/browser": "^4.1.
|
|
49
|
-
"@vitest/browser-playwright": "4.1.
|
|
50
|
-
"@vitest/coverage-v8": "^4.1.
|
|
58
|
+
"@vitest/browser": "^4.1.10",
|
|
59
|
+
"@vitest/browser-playwright": "^4.1.10",
|
|
60
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
51
61
|
"jsdom": "^26.1.0",
|
|
52
62
|
"playwright": "^1.59.1",
|
|
53
|
-
"tsdown": "^0.
|
|
54
|
-
"vitest": "^4.1.
|
|
63
|
+
"tsdown": "^0.22.14",
|
|
64
|
+
"vitest": "^4.1.10"
|
|
55
65
|
},
|
|
56
66
|
"publishConfig": {
|
|
57
67
|
"access": "public"
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { computed, peek } from "../../core/signals/primitives.js";
|
|
2
|
-
import { defineBehavior } from "../../core/composition/create-composition.js";
|
|
3
|
-
import { createMachineReactor } from "../../core/reactors/create-machine-reactor.js";
|
|
4
|
-
import { isResolvedPresentation } from "../../media/types/index.js";
|
|
5
|
-
import { getTracksByType } from "../../media/utils/tracks.js";
|
|
6
|
-
import { DEFAULT_BANDWIDTH_CONFIG, getBandwidthEstimate } from "../../network/bandwidth-estimator.js";
|
|
7
|
-
import { DEFAULT_QUALITY_CONFIG, selectLowestQuality, selectQuality } from "../../media/abr/quality-selection.js";
|
|
8
|
-
function setupQualitySwitching({ state, config }) {
|
|
9
|
-
const safetyMargin = config.quality?.safetyMargin ?? DEFAULT_QUALITY_CONFIG.safetyMargin;
|
|
10
|
-
const upgradeMargin = config.quality?.upgradeMargin ?? DEFAULT_QUALITY_CONFIG.upgradeMargin;
|
|
11
|
-
const initialBandwidth = config.initialBandwidth ?? 5e6;
|
|
12
|
-
const bandwidthConfig = {
|
|
13
|
-
...DEFAULT_BANDWIDTH_CONFIG,
|
|
14
|
-
...config.bandwidth
|
|
15
|
-
};
|
|
16
|
-
const { selectionKey, userSelectionKey, getTracks, selectOptimal } = config;
|
|
17
|
-
const derivedStateSignal = computed(() => isResolvedPresentation(state.presentation.get()) ? "presentation-resolved" : "presentation-unresolved");
|
|
18
|
-
return createMachineReactor({
|
|
19
|
-
initial: "presentation-unresolved",
|
|
20
|
-
monitor: () => derivedStateSignal.get(),
|
|
21
|
-
states: {
|
|
22
|
-
"presentation-unresolved": {},
|
|
23
|
-
"presentation-resolved": {
|
|
24
|
-
entry: () => () => state[selectionKey].set(void 0),
|
|
25
|
-
effects: [() => {
|
|
26
|
-
const presentation = peek(state.presentation);
|
|
27
|
-
if (!presentation) return;
|
|
28
|
-
const allTracks = getTracks(presentation);
|
|
29
|
-
const [firstAllTrack] = allTracks;
|
|
30
|
-
if (!firstAllTrack) return;
|
|
31
|
-
const userFilter = state[userSelectionKey].get();
|
|
32
|
-
const matching = userFilter ? allTracks.filter((track) => {
|
|
33
|
-
for (const key in userFilter) {
|
|
34
|
-
const filterValue = userFilter[key];
|
|
35
|
-
if (filterValue !== void 0 && track[key] !== filterValue) return false;
|
|
36
|
-
}
|
|
37
|
-
return true;
|
|
38
|
-
}) : allTracks;
|
|
39
|
-
const candidates = matching.length > 0 ? matching : allTracks;
|
|
40
|
-
if (!candidates.length) return;
|
|
41
|
-
const selectedId = state[selectionKey].get();
|
|
42
|
-
if (candidates.length === 1) {
|
|
43
|
-
if (candidates[0].id !== selectedId) state[selectionKey].set(candidates[0].id);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const bandwidth = getBandwidthEstimate(state.bandwidthState.get(), initialBandwidth, bandwidthConfig);
|
|
47
|
-
if (!selectedId && config.picker) {
|
|
48
|
-
const id = config.picker(presentation, config);
|
|
49
|
-
if (id) {
|
|
50
|
-
state[selectionKey].set(id);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const optimal = selectOptimal(candidates, bandwidth, {
|
|
55
|
-
safetyMargin,
|
|
56
|
-
upgradeMargin,
|
|
57
|
-
currentTrack: candidates.find((t) => t.id === selectedId)
|
|
58
|
-
}) ?? selectLowestQuality(candidates);
|
|
59
|
-
if (optimal.id !== selectedId) state[selectionKey].set(optimal.id);
|
|
60
|
-
}]
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Manage `selectedVideoTrackId`: pick a default on src load, dynamically
|
|
67
|
-
* adjust based on bandwidth, clear on src unload. Honors
|
|
68
|
-
* `userVideoTrackSelection` as a partial-track constraint on candidates;
|
|
69
|
-
* short-circuits ABR when the constraint narrows to a single track.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* const reactor = switchVideoQuality.setup({ state });
|
|
73
|
-
*/
|
|
74
|
-
const switchVideoQuality = defineBehavior({
|
|
75
|
-
stateKeys: [
|
|
76
|
-
"presentation",
|
|
77
|
-
"bandwidthState",
|
|
78
|
-
"selectedVideoTrackId",
|
|
79
|
-
"userVideoTrackSelection"
|
|
80
|
-
],
|
|
81
|
-
contextKeys: [],
|
|
82
|
-
setup: ({ state, config }) => setupQualitySwitching({
|
|
83
|
-
state,
|
|
84
|
-
config: {
|
|
85
|
-
...config,
|
|
86
|
-
selectionKey: "selectedVideoTrackId",
|
|
87
|
-
userSelectionKey: "userVideoTrackSelection",
|
|
88
|
-
getTracks: (presentation) => getTracksByType(presentation, "video"),
|
|
89
|
-
selectOptimal: selectQuality
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
});
|
|
93
|
-
//#endregion
|
|
94
|
-
export { switchVideoQuality };
|
|
95
|
-
|
|
96
|
-
//# sourceMappingURL=quality-switching.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"quality-switching.js","names":[],"sources":["../../../../src/playback/behaviors/quality-switching.ts"],"sourcesContent":["/**\n * **Manage the per-type ABR-eligible track selection slot.** While a\n * presentation is resolved, owns the slot's lifecycle: pick a default,\n * dynamically adjust based on media metrics (today: bandwidth via\n * `selectQuality`), and clear on src unload. User intent is expressed as a\n * partial-track description in a sibling slot (e.g.\n * `userVideoTrackSelection`) which constrains the candidate set for\n * selection — when the constraint narrows candidates to exactly one, the\n * choice is fully determined and ABR is short-circuited (no bandwidth read,\n * no effect re-fire on bandwidth changes).\n *\n * Lifecycle: `'presentation-unresolved'` ↔ `'presentation-resolved'`. The\n * `'presentation-resolved'` state owns the selection slot; its\n * entry-returned cleanup clears the slot on exit (canonical\n * cleanup-binds-to-setup per `reactors.md`).\n *\n * Hysteresis: downgrades apply immediately; upgrades require the optimal\n * track's bandwidth to exceed the current track's by `upgradeMargin`. No\n * temporal state — short-term smoothing is the bandwidth estimator's job.\n *\n * Initial pick is configurable via `config.picker` — pass any `TrackPicker`\n * to override the bandwidth-aware default for the empty-slot case. ABR\n * re-evaluation (downgrade/upgrade) is unaffected. Composing\n * `switchVideoQuality` with `picker: (p) => pickFirstTrackId(p, 'video')`\n * yields \"first track at load, ABR adjusts from there\" — a non-bandwidth\n * initial pick paired with bandwidth-driven re-evaluation.\n */\n\nimport { defineBehavior } from '../../core/composition/create-composition';\nimport { createMachineReactor } from '../../core/reactors/create-machine-reactor';\nimport { computed, peek, type ReadonlySignal, type Signal } from '../../core/signals/primitives';\nimport {\n DEFAULT_QUALITY_CONFIG,\n type QualityConfig,\n selectLowestQuality,\n selectQuality,\n} from '../../media/abr/quality-selection';\nimport type { TrackPicker } from '../../media/primitives/select-tracks';\nimport {\n isResolvedPresentation,\n type MaybeResolvedPresentation,\n type PartiallyResolvedVideoTrack,\n type VideoTrack,\n} from '../../media/types';\nimport { getTracksByType } from '../../media/utils/tracks';\nimport type { BandwidthConfig, BandwidthState } from '../../network/bandwidth-estimator';\nimport { DEFAULT_BANDWIDTH_CONFIG, getBandwidthEstimate } from '../../network/bandwidth-estimator';\n\nexport interface QualitySwitchingState {\n presentation?: MaybeResolvedPresentation;\n bandwidthState?: BandwidthState;\n selectedVideoTrackId?: string;\n /**\n * Partial-track description expressing user intent. When set, narrows\n * the ABR candidate set to tracks matching every present field. The\n * common case is `{ id: 'specific-track-id' }` for a \"manual quality\"\n * pick, but other partial shapes work — e.g., `{ height: 720 }` would\n * constrain to 720p tracks; ABR continues to pick among them.\n *\n * When the narrowed candidates contain exactly one track, ABR is\n * short-circuited entirely (no bandwidth read, no effect re-fire).\n *\n * When the filter matches no tracks in the current presentation (e.g.,\n * user-picked id from a previous source doesn't exist here), falls back\n * to the unfiltered set rather than stalling playback.\n */\n userVideoTrackSelection?: Partial<VideoTrack>;\n}\n\nexport interface QualitySwitchingConfig {\n /**\n * Quality-selection tuning. `safetyMargin` is the bandwidth-headroom\n * multiplier; `upgradeMargin` is the hysteresis ratio gating upgrades.\n * Defaults: `DEFAULT_QUALITY_CONFIG` (0.85 / 1.15).\n */\n quality?: Partial<QualityConfig>;\n\n /**\n * Bandwidth-estimator tuning passed through to `getBandwidthEstimate`.\n * Merged over `DEFAULT_BANDWIDTH_CONFIG`.\n */\n bandwidth?: Partial<BandwidthConfig>;\n\n /**\n * Bandwidth estimate in bps to use before enough samples have been collected.\n * Default: 5_000_000 (5 Mbps).\n */\n initialBandwidth?: number;\n\n /**\n * Override the initial-pick algorithm. When set, the picker is called the\n * first time the slot is empty in the `'presentation-resolved'` state;\n * its returned id is set verbatim (no bandwidth-aware logic). Subsequent\n * ABR re-evaluation (downgrade/upgrade by bandwidth + hysteresis) is\n * unaffected and runs as usual.\n *\n * Default (no picker): a bandwidth-aware initial pick driven by\n * `initialBandwidth` and `quality.safetyMargin`, identical to ABR's\n * downgrade branch.\n *\n * Honors of `userVideoTrackSelection` are the picker's responsibility\n * when overridden. If the picker returns `undefined`, the bandwidth-aware\n * default pick fires (graceful fallback).\n */\n picker?: TrackPicker<QualitySwitchingConfig>;\n}\n\n/** Default initial-bandwidth value used by quality-switching variants before measurements arrive. */\nexport const DEFAULT_INITIAL_BANDWIDTH = 5_000_000;\n\n// ============================================================================\n// Specialization helper\n//\n// `setupQualitySwitching` has the same shape as a Behavior `setup` function:\n// `({ state, config }) => Reactor`. Each `switchXQuality` export below calls\n// it from inside its own `defineBehavior` setup, passing the per-type slot\n// keys, track type, and selection algorithm explicitly via three generic\n// parameters — `S` (selection slot key), `U` (user-selection slot key), `T`\n// (ABR track type). The per-type export is the single point that ties them\n// together; the helper stays slot-agnostic.\n//\n// -- Design note: why narrow `SelectionKey` / `UserSelectionKey` unions ----\n// Goal we did not reach: have callers \"fully pass in\" the slot keys, with\n// the helper enforcing zero internal knowledge of which literals are valid.\n// What blocks it: indexing a mapped-type intersection by a generic key.\n// When `S extends keyof QualitySwitchingState` (or `string`), TS conservatively\n// treats `state[selectionKey]` as the union of every possible match across\n// the intersected mapped portions — including the fixed-key signals\n// (`presentation`, `bandwidthState`) — and widens to their value-type union.\n// The sibling pattern hits the same constraint and answers it the same way:\n// `SelectedTrackKey` in `select-tracks.ts:43` is a hardcoded narrow union\n// for the same reason.\n//\n// Routes considered for \"fully passed in\", with trade-offs (left here for\n// the larger-group conversation):\n//\n// A. Derived constraint — `Exclude<keyof QualitySwitchingState, 'presentation'\n// | 'bandwidthState'>`. Removes the literal enumeration; same narrowness,\n// computed from the state shape. Still \"knows\" the fixed-key names.\n// B. Broad constraint (`S extends keyof QualitySwitchingState`) + access-site\n// casts (`state[selectionKey] as Signal<string | undefined>` at the top\n// of the helper). Caller passes any keys; the type system stops checking\n// S ≠ U or that the keys actually refer to selection slots. Once you\n// cast at the top, the body is structurally the original remap pattern\n// with extra ceremony.\n// C. Remap pattern (pre-refactor) — caller passes signals under logical\n// names (`selection`, `userSelection`); helper has no key generics.\n// Cleanest types; inconsistent with the sibling helpers.\n//\n// Current pick is the narrow-union route because it matches siblings and\n// the unions read as documentation (\"these are the slots this helper\n// manages\") rather than restriction. Extending for audio is one literal\n// per union.\n// --------------------------------------------------------------------------\n// ============================================================================\n\ntype AbrTrack = { id: string; bandwidth: number };\n\ntype SelectionKey = 'selectedVideoTrackId';\ntype UserSelectionKey = 'userVideoTrackSelection';\n\n// Each mapped value references `P` so TS keeps the per-key dependency and\n// resolves `state[selectionKey]` / `state[userSelectionKey]` to the right\n// arm. `T` (track type) deliberately stays out of the state map — pulling\n// it in detaches the user-selection mapped value from `P` and TS collapses\n// the intersection. T flows through `QualitySwitchingSetupConfig` instead;\n// the user-filter access casts at the read site (see below).\ntype QualitySwitchingStateMap<S extends SelectionKey, U extends UserSelectionKey> = {\n presentation: ReadonlySignal<QualitySwitchingState['presentation']>;\n bandwidthState: ReadonlySignal<QualitySwitchingState['bandwidthState']>;\n} & { [P in S]: Signal<QualitySwitchingState[P]> } & { [P in U]: ReadonlySignal<QualitySwitchingState[P]> };\n\ninterface QualitySwitchingSetupConfig<S extends SelectionKey, U extends UserSelectionKey, T extends AbrTrack>\n extends QualitySwitchingConfig {\n selectionKey: S;\n userSelectionKey: U;\n getTracks: (presentation: MaybeResolvedPresentation) => readonly T[];\n selectOptimal: (\n tracks: readonly T[],\n bandwidth: number,\n opts: { safetyMargin: number; upgradeMargin: number; currentTrack?: T }\n ) => T | undefined;\n}\n\nfunction setupQualitySwitching<S extends SelectionKey, U extends UserSelectionKey, T extends AbrTrack>({\n state,\n config,\n}: {\n state: QualitySwitchingStateMap<S, U>;\n config: QualitySwitchingSetupConfig<S, U, T>;\n}) {\n const safetyMargin = config.quality?.safetyMargin ?? DEFAULT_QUALITY_CONFIG.safetyMargin;\n const upgradeMargin = config.quality?.upgradeMargin ?? DEFAULT_QUALITY_CONFIG.upgradeMargin;\n const initialBandwidth = config.initialBandwidth ?? DEFAULT_INITIAL_BANDWIDTH;\n const bandwidthConfig: BandwidthConfig = { ...DEFAULT_BANDWIDTH_CONFIG, ...config.bandwidth };\n const { selectionKey, userSelectionKey, getTracks, selectOptimal } = config;\n\n const derivedStateSignal = computed(() =>\n isResolvedPresentation(state.presentation.get())\n ? ('presentation-resolved' as const)\n : ('presentation-unresolved' as const)\n );\n\n return createMachineReactor({\n initial: 'presentation-unresolved',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'presentation-unresolved': {},\n 'presentation-resolved': {\n // Canonical cleanup-binds-to-setup: the selection slot's valid\n // lifespan is exactly 'presentation-resolved'. Clear fires on\n // 'presentation-resolved' exit, covering both src unload and\n // behavior destroy.\n entry: () => () => state[selectionKey].set(undefined),\n effects: [\n () => {\n const presentation = peek(state.presentation);\n if (!presentation) return;\n\n const allTracks = getTracks(presentation);\n const [firstAllTrack] = allTracks;\n if (!firstAllTrack) return;\n\n // State stores the filter as `Partial<VideoTrack>` (user-facing\n // shape — includes V-only fields like `height`); the helper\n // works against `Partial<T>` so filter and track access share\n // one index type. Filter keys absent on a partially-resolved\n // track read as `undefined` and just exclude that track.\n const userFilter = state[userSelectionKey].get() as Partial<T> | undefined;\n const matching = userFilter\n ? allTracks.filter((track) => {\n for (const key in userFilter) {\n const filterValue = userFilter[key as keyof T];\n if (filterValue !== undefined && track[key as keyof T] !== filterValue) return false;\n }\n return true;\n })\n : allTracks;\n // Fall back to all tracks when the filter excludes everything\n // (e.g., user-picked id doesn't exist in the current source).\n const candidates = matching.length > 0 ? matching : allTracks;\n if (!candidates.length) return;\n\n const selectedId = state[selectionKey].get();\n\n // Common case: user has fully constrained the choice (e.g.,\n // `{ id: 'specific-720p' }` narrows to a single track). Skip\n // the ABR path — and crucially, don't read `bandwidthState`\n // so the effect doesn't re-fire on bandwidth changes while\n // the user's selection holds.\n if (candidates.length === 1) {\n if (candidates[0]!.id !== selectedId) state[selectionKey].set(candidates[0]!.id);\n return;\n }\n\n // Read bandwidth up front to establish the signal subscription —\n // future bandwidth changes must re-fire this effect even when\n // the picker branch below takes the early-return path. (If the\n // picker bails out without ever touching `bandwidthState`, ABR\n // would otherwise be deaf to subsequent bandwidth changes.)\n //\n // Single path for pre-trust and post-trust:\n // `getBandwidthEstimate` returns `initialBandwidth` when state\n // is undefined or bytes sampled hasn't crossed `minTotalBytes`,\n // so the initial pick + early-ABR window run the same\n // `selectOptimal` path as a fully-trusted measurement.\n const bandwidth = getBandwidthEstimate(state.bandwidthState.get(), initialBandwidth, bandwidthConfig);\n\n // Picker-driven initial pick: when the slot is empty and the\n // caller supplied a `picker`, defer to it instead of the\n // bandwidth-aware default. The picker sees the full\n // presentation (not narrowed by `userVideoTrackSelection`) —\n // honoring the filter is the picker's responsibility when\n // overridden. Returning `undefined` falls through to the\n // bandwidth-aware default (graceful fallback).\n //\n // ABR re-evaluation (downgrade/upgrade by bandwidth) runs as\n // usual on subsequent effect re-runs once the slot is set.\n if (!selectedId && config.picker) {\n const id = config.picker(presentation, config);\n if (id) {\n state[selectionKey].set(id);\n return;\n }\n }\n // `selectOptimal` decides the track to apply now given current\n // selection + bandwidth + tuning. It returns:\n // - the optimal when no current track or on a downgrade\n // - the optimal when an upgrade clears `upgradeMargin`\n // - the current track itself when an upgrade doesn't clear margin\n // (caller's id-compare below no-ops in that case)\n // Outer `?? selectLowestQuality` is defensive — `selectQuality` falls\n // back to lowest internally; this catches future impls that don't.\n // `selectLowestQuality` returns `undefined` only on empty input,\n // and `candidates.length >= 2` here.\n const currentTrack = candidates.find((t) => t.id === selectedId);\n const optimal =\n selectOptimal(candidates, bandwidth, { safetyMargin, upgradeMargin, currentTrack }) ??\n selectLowestQuality(candidates)!;\n if (optimal.id !== selectedId) state[selectionKey].set(optimal.id);\n },\n ],\n },\n },\n });\n}\n\n// ============================================================================\n// Specialized exports — one per ABR-enabled track type\n// ============================================================================\n\n/**\n * Manage `selectedVideoTrackId`: pick a default on src load, dynamically\n * adjust based on bandwidth, clear on src unload. Honors\n * `userVideoTrackSelection` as a partial-track constraint on candidates;\n * short-circuits ABR when the constraint narrows to a single track.\n *\n * @example\n * const reactor = switchVideoQuality.setup({ state });\n */\nexport const switchVideoQuality = defineBehavior({\n stateKeys: ['presentation', 'bandwidthState', 'selectedVideoTrackId', 'userVideoTrackSelection'],\n contextKeys: [],\n setup: ({\n state,\n config,\n }: {\n state: QualitySwitchingStateMap<'selectedVideoTrackId', 'userVideoTrackSelection'>;\n config?: QualitySwitchingConfig;\n }) =>\n setupQualitySwitching<'selectedVideoTrackId', 'userVideoTrackSelection', PartiallyResolvedVideoTrack | VideoTrack>({\n state,\n config: {\n ...config,\n selectionKey: 'selectedVideoTrackId',\n userSelectionKey: 'userVideoTrackSelection',\n getTracks: (presentation) =>\n getTracksByType(presentation, 'video') as readonly (PartiallyResolvedVideoTrack | VideoTrack)[],\n selectOptimal: selectQuality,\n },\n }),\n});\n"],"mappings":";;;;;;;AAwLA,SAAS,sBAA8F,EACrG,OACA,UAIC;CACD,MAAM,eAAe,OAAO,SAAS,gBAAgB,uBAAuB;CAC5E,MAAM,gBAAgB,OAAO,SAAS,iBAAiB,uBAAuB;CAC9E,MAAM,mBAAmB,OAAO,oBAAA;CAChC,MAAM,kBAAmC;EAAE,GAAG;EAA0B,GAAG,OAAO;EAAW;CAC7F,MAAM,EAAE,cAAc,kBAAkB,WAAW,kBAAkB;CAErE,MAAM,qBAAqB,eACzB,uBAAuB,MAAM,aAAa,KAAK,CAAC,GAC3C,0BACA,0BACN;AAED,QAAO,qBAAqB;EAC1B,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,2BAA2B,EAAE;GAC7B,yBAAyB;IAKvB,mBAAmB,MAAM,cAAc,IAAI,KAAA,EAAU;IACrD,SAAS,OACD;KACJ,MAAM,eAAe,KAAK,MAAM,aAAa;AAC7C,SAAI,CAAC,aAAc;KAEnB,MAAM,YAAY,UAAU,aAAa;KACzC,MAAM,CAAC,iBAAiB;AACxB,SAAI,CAAC,cAAe;KAOpB,MAAM,aAAa,MAAM,kBAAkB,KAAK;KAChD,MAAM,WAAW,aACb,UAAU,QAAQ,UAAU;AAC1B,WAAK,MAAM,OAAO,YAAY;OAC5B,MAAM,cAAc,WAAW;AAC/B,WAAI,gBAAgB,KAAA,KAAa,MAAM,SAAoB,YAAa,QAAO;;AAEjF,aAAO;OACP,GACF;KAGJ,MAAM,aAAa,SAAS,SAAS,IAAI,WAAW;AACpD,SAAI,CAAC,WAAW,OAAQ;KAExB,MAAM,aAAa,MAAM,cAAc,KAAK;AAO5C,SAAI,WAAW,WAAW,GAAG;AAC3B,UAAI,WAAW,GAAI,OAAO,WAAY,OAAM,cAAc,IAAI,WAAW,GAAI,GAAG;AAChF;;KAcF,MAAM,YAAY,qBAAqB,MAAM,eAAe,KAAK,EAAE,kBAAkB,gBAAgB;AAYrG,SAAI,CAAC,cAAc,OAAO,QAAQ;MAChC,MAAM,KAAK,OAAO,OAAO,cAAc,OAAO;AAC9C,UAAI,IAAI;AACN,aAAM,cAAc,IAAI,GAAG;AAC3B;;;KAcJ,MAAM,UACJ,cAAc,YAAY,WAAW;MAAE;MAAc;MAAe,cAFjD,WAAW,MAAM,MAAM,EAAE,OAAO,WAAW;MAEoB,CAAC,IACnF,oBAAoB,WAAW;AACjC,SAAI,QAAQ,OAAO,WAAY,OAAM,cAAc,IAAI,QAAQ,GAAG;MAErE;IACF;GACF;EACF,CAAC;;;;;;;;;;;AAgBJ,MAAa,qBAAqB,eAAe;CAC/C,WAAW;EAAC;EAAgB;EAAkB;EAAwB;EAA0B;CAChG,aAAa,EAAE;CACf,QAAQ,EACN,OACA,aAKA,sBAAmH;EACjH;EACA,QAAQ;GACN,GAAG;GACH,cAAc;GACd,kBAAkB;GAClB,YAAY,iBACV,gBAAgB,cAAc,QAAQ;GACxC,eAAe;GAChB;EACF,CAAC;CACL,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"track-types.js","names":[],"sources":["../../../../src/playback/behaviors/track-types.ts"],"sourcesContent":["/**\n * **Per-type config bundles for per-type behavior variants.**\n *\n * Each `*_TYPE_CONFIG` constant names the per-type slot keys that\n * per-type-variant setup-shape helpers consume. Variants reference the\n * shared constant at their `defineBehavior` setup body rather than\n * constructing the config inline, so the per-type identity (which state\n * slot, which context slot, which discriminant) lives in one place per\n * type.\n *\n * Variants spread their composition-time `config` over the defaults so\n * engines can layer composition-supplied additions on top (e.g. a\n * custom fetch closure, a non-default segment resolver). Defaults\n * first, engine config second.\n *\n * Helpers continue to consume their slice via the existing typed-key\n * generics (`<K extends SelectedTrackKey>` etc.); the extra fields on\n * the config object (carrying facets other helpers care about) are fine\n * under structural typing.\n */\n\n/**\n * Type-identity bundle for video-track-typed behaviors.\n *\n * @see setupVideoBufferActors, loadVideoSegments\n */\nexport const VIDEO_TYPE_CONFIG = {\n type: 'video',\n selectedKey: 'selectedVideoTrackId',\n actorKey: 'videoBufferActor',\n loaderKey: 'videoSegmentLoaderActor',\n} as const;\n\n/**\n * Type-identity bundle for audio-track-typed behaviors.\n *\n * @see setupAudioBufferActors, loadAudioSegments\n */\nexport const AUDIO_TYPE_CONFIG = {\n type: 'audio',\n selectedKey: 'selectedAudioTrackId',\n actorKey: 'audioBufferActor',\n loaderKey: 'audioSegmentLoaderActor',\n} as const;\n\n/**\n * Type-identity bundle for text-track-typed behaviors. Text tracks have\n * no `SourceBufferActor` (MSE doesn't apply), so this bundle omits\n * `actorKey`. The `loaderKey` points at the text-track-segment-loader\n * actor (a `MessageActor` with continue-vs-preempt semantics, parallel\n * to v/a's `SegmentLoaderActor`).\n *\n * @see loadTextTrackSegments, setupTextTrackActors\n */\nexport const TEXT_TYPE_CONFIG = {\n type: 'text',\n selectedKey: 'selectedTextTrackId',\n loaderKey: 'textTrackSegmentLoaderActor',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,oBAAoB;CAC/B,MAAM;CACN,aAAa;CACb,UAAU;CACV,WAAW;CACZ;;;;;;AAOD,MAAa,oBAAoB;CAC/B,MAAM;CACN,aAAa;CACb,UAAU;CACV,WAAW;CACZ;;;;;;;;;;AAWD,MAAa,mBAAmB;CAC9B,MAAM;CACN,aAAa;CACb,WAAW;CACZ"}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { computed, peek } from "../../core/signals/primitives.js";
|
|
2
|
-
import { defineBehavior } from "../../core/composition/create-composition.js";
|
|
3
|
-
import { createMachineReactor } from "../../core/reactors/create-machine-reactor.js";
|
|
4
|
-
import { isResolvedPresentation } from "../../media/types/index.js";
|
|
5
|
-
import { getTracksByType } from "../../media/utils/tracks.js";
|
|
6
|
-
import { DEFAULT_BANDWIDTH_CONFIG, getBandwidthEstimate } from "../../network/bandwidth-estimator.js";
|
|
7
|
-
import { DEFAULT_QUALITY_CONFIG, selectLowestQuality, selectQuality } from "../../media/abr/quality-selection.js";
|
|
8
|
-
function setupQualitySwitching({ state, config }) {
|
|
9
|
-
const safetyMargin = config.quality?.safetyMargin ?? DEFAULT_QUALITY_CONFIG.safetyMargin;
|
|
10
|
-
const upgradeMargin = config.quality?.upgradeMargin ?? DEFAULT_QUALITY_CONFIG.upgradeMargin;
|
|
11
|
-
const initialBandwidth = config.initialBandwidth ?? 5e6;
|
|
12
|
-
const bandwidthConfig = {
|
|
13
|
-
...DEFAULT_BANDWIDTH_CONFIG,
|
|
14
|
-
...config.bandwidth
|
|
15
|
-
};
|
|
16
|
-
const { selectionKey, userSelectionKey, getTracks, selectOptimal } = config;
|
|
17
|
-
const derivedStateSignal = computed(() => isResolvedPresentation(state.presentation.get()) ? "presentation-resolved" : "presentation-unresolved");
|
|
18
|
-
return createMachineReactor({
|
|
19
|
-
initial: "presentation-unresolved",
|
|
20
|
-
monitor: () => derivedStateSignal.get(),
|
|
21
|
-
states: {
|
|
22
|
-
"presentation-unresolved": {},
|
|
23
|
-
"presentation-resolved": {
|
|
24
|
-
entry: () => () => state[selectionKey].set(void 0),
|
|
25
|
-
effects: [() => {
|
|
26
|
-
const presentation = peek(state.presentation);
|
|
27
|
-
if (!presentation) return;
|
|
28
|
-
const allTracks = getTracks(presentation);
|
|
29
|
-
const [firstAllTrack] = allTracks;
|
|
30
|
-
if (!firstAllTrack) return;
|
|
31
|
-
const userFilter = state[userSelectionKey].get();
|
|
32
|
-
const matching = userFilter ? allTracks.filter((track) => {
|
|
33
|
-
for (const key in userFilter) {
|
|
34
|
-
const filterValue = userFilter[key];
|
|
35
|
-
if (filterValue !== void 0 && track[key] !== filterValue) return false;
|
|
36
|
-
}
|
|
37
|
-
return true;
|
|
38
|
-
}) : allTracks;
|
|
39
|
-
const candidates = matching.length > 0 ? matching : allTracks;
|
|
40
|
-
if (!candidates.length) return;
|
|
41
|
-
const selectedId = state[selectionKey].get();
|
|
42
|
-
if (candidates.length === 1) {
|
|
43
|
-
if (candidates[0].id !== selectedId) state[selectionKey].set(candidates[0].id);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const bandwidth = getBandwidthEstimate(state.bandwidthState.get(), initialBandwidth, bandwidthConfig);
|
|
47
|
-
if (!selectedId && config.picker) {
|
|
48
|
-
const id = config.picker(presentation, config);
|
|
49
|
-
if (id) {
|
|
50
|
-
state[selectionKey].set(id);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const optimal = selectOptimal(candidates, bandwidth, {
|
|
55
|
-
safetyMargin,
|
|
56
|
-
upgradeMargin,
|
|
57
|
-
currentTrack: candidates.find((t) => t.id === selectedId)
|
|
58
|
-
}) ?? selectLowestQuality(candidates);
|
|
59
|
-
if (optimal.id !== selectedId) state[selectionKey].set(optimal.id);
|
|
60
|
-
}]
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Manage `selectedVideoTrackId`: pick a default on src load, dynamically
|
|
67
|
-
* adjust based on bandwidth, clear on src unload. Honors
|
|
68
|
-
* `userVideoTrackSelection` as a partial-track constraint on candidates;
|
|
69
|
-
* short-circuits ABR when the constraint narrows to a single track.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* const reactor = switchVideoQuality.setup({ state });
|
|
73
|
-
*/
|
|
74
|
-
const switchVideoQuality = defineBehavior({
|
|
75
|
-
stateKeys: [
|
|
76
|
-
"presentation",
|
|
77
|
-
"bandwidthState",
|
|
78
|
-
"selectedVideoTrackId",
|
|
79
|
-
"userVideoTrackSelection"
|
|
80
|
-
],
|
|
81
|
-
contextKeys: [],
|
|
82
|
-
setup: ({ state, config }) => setupQualitySwitching({
|
|
83
|
-
state,
|
|
84
|
-
config: {
|
|
85
|
-
...config,
|
|
86
|
-
selectionKey: "selectedVideoTrackId",
|
|
87
|
-
userSelectionKey: "userVideoTrackSelection",
|
|
88
|
-
getTracks: (presentation) => getTracksByType(presentation, "video"),
|
|
89
|
-
selectOptimal: selectQuality
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
});
|
|
93
|
-
//#endregion
|
|
94
|
-
export { switchVideoQuality };
|
|
95
|
-
|
|
96
|
-
//# sourceMappingURL=quality-switching.js.map
|