@videojs/spf 10.0.0-beta.30 → 10.0.0-beta.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/default/core/actors/create-machine-actor.js +23 -25
- package/dist/default/core/actors/create-machine-actor.js.map +1 -1
- package/dist/default/core/actors/create-transition-actor.js +7 -9
- package/dist/default/core/actors/create-transition-actor.js.map +1 -1
- package/dist/default/core/composition/create-composition.js +26 -29
- package/dist/default/core/composition/create-composition.js.map +1 -1
- package/dist/default/core/composition/share-signals.js +12 -17
- package/dist/default/core/composition/share-signals.js.map +1 -1
- package/dist/default/core/machine.js +3 -4
- package/dist/default/core/machine.js.map +1 -1
- package/dist/default/core/reactors/create-machine-reactor.js +20 -19
- package/dist/default/core/reactors/create-machine-reactor.js.map +1 -1
- package/dist/default/core/signals/effect.js +24 -4
- package/dist/default/core/signals/effect.js.map +1 -1
- package/dist/default/core/signals/primitives.js +10 -14
- package/dist/default/core/signals/primitives.js.map +1 -1
- package/dist/default/core/signals/when.js +6 -8
- package/dist/default/core/signals/when.js.map +1 -1
- package/dist/default/core/tasks/delayed-reschedule.js +7 -11
- package/dist/default/core/tasks/delayed-reschedule.js.map +1 -1
- package/dist/default/core/tasks/task.js +59 -82
- package/dist/default/core/tasks/task.js.map +1 -1
- package/dist/default/hls.js +3 -1
- package/dist/default/index.js +2 -3
- package/dist/default/index.js.map +1 -1
- package/dist/default/media/abr/quality-selection.js +3 -7
- package/dist/default/media/abr/quality-selection.js.map +1 -1
- package/dist/default/media/buffer/back-buffer.js +15 -19
- package/dist/default/media/buffer/back-buffer.js.map +1 -1
- package/dist/default/media/buffer/forward-buffer.js +32 -41
- package/dist/default/media/buffer/forward-buffer.js.map +1 -1
- package/dist/default/media/dom/capabilities.js +26 -37
- package/dist/default/media/dom/capabilities.js.map +1 -1
- package/dist/default/media/dom/mse/append-segment.js +3 -5
- package/dist/default/media/dom/mse/append-segment.js.map +1 -1
- package/dist/default/media/dom/mse/buffer-flusher.js +5 -5
- package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -1
- package/dist/default/media/dom/mse/duration.js +23 -31
- package/dist/default/media/dom/mse/duration.js.map +1 -1
- package/dist/default/media/dom/mse/end-of-stream.js +3 -6
- package/dist/default/media/dom/mse/end-of-stream.js.map +1 -1
- package/dist/default/media/dom/mse/mediasource-setup.js +65 -81
- package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -1
- package/dist/default/media/dom/screen.js +37 -54
- package/dist/default/media/dom/screen.js.map +1 -1
- package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -1
- package/dist/default/media/dom/text/text-track-slots.js +15 -23
- package/dist/default/media/dom/text/text-track-slots.js.map +1 -1
- package/dist/default/media/errors.js +25 -34
- package/dist/default/media/errors.js.map +1 -1
- package/dist/default/media/hls/parse-attributes.js +9 -26
- package/dist/default/media/hls/parse-attributes.js.map +1 -1
- package/dist/default/media/hls/parse-media-playlist.js +25 -33
- package/dist/default/media/hls/parse-media-playlist.js.map +1 -1
- package/dist/default/media/hls/parse-multivariant.js +3 -3
- package/dist/default/media/hls/parse-multivariant.js.map +1 -1
- package/dist/default/media/hls/reload-policy.js +19 -27
- package/dist/default/media/hls/reload-policy.js.map +1 -1
- package/dist/default/media/hls/resolve-url.js +1 -3
- package/dist/default/media/hls/resolve-url.js.map +1 -1
- package/dist/default/media/live-window.js +9 -13
- package/dist/default/media/live-window.js.map +1 -1
- package/dist/default/media/media-tracks/media-tracks.js +12 -17
- package/dist/default/media/media-tracks/media-tracks.js.map +1 -1
- package/dist/default/media/mp4/box.js +4 -5
- package/dist/default/media/mp4/box.js.map +1 -1
- package/dist/default/media/mp4/timestamp-origin.js +30 -42
- package/dist/default/media/mp4/timestamp-origin.js.map +1 -1
- package/dist/default/media/primitives/resolution.js +5 -8
- package/dist/default/media/primitives/resolution.js.map +1 -1
- package/dist/default/media/primitives/select-tracks.js +24 -34
- package/dist/default/media/primitives/select-tracks.js.map +1 -1
- package/dist/default/media/text/parse-vtt-timestamp-map.js +6 -8
- package/dist/default/media/text/parse-vtt-timestamp-map.js.map +1 -1
- package/dist/default/media/text/resolve-vtt-metadata.js +8 -9
- package/dist/default/media/text/resolve-vtt-metadata.js.map +1 -1
- package/dist/default/media/types/index.js +10 -18
- package/dist/default/media/types/index.js.map +1 -1
- package/dist/default/media/utils/cdn.js +10 -16
- package/dist/default/media/utils/cdn.js.map +1 -1
- package/dist/default/media/utils/preload.js +4 -6
- package/dist/default/media/utils/preload.js.map +1 -1
- package/dist/default/media/utils/track-selection.js +10 -14
- package/dist/default/media/utils/track-selection.js.map +1 -1
- package/dist/default/media/utils/tracks.js +56 -54
- package/dist/default/media/utils/tracks.js.map +1 -1
- package/dist/default/network/bandwidth-estimator.js +18 -19
- package/dist/default/network/bandwidth-estimator.js.map +1 -1
- package/dist/default/network/chunked-stream-iterable.js +4 -6
- package/dist/default/network/chunked-stream-iterable.js.map +1 -1
- package/dist/default/network/ewma.js +22 -25
- package/dist/default/network/ewma.js.map +1 -1
- package/dist/default/network/fetch.js +30 -37
- package/dist/default/network/fetch.js.map +1 -1
- package/dist/default/playback/actors/dom/segment-loader.js +28 -44
- package/dist/default/playback/actors/dom/segment-loader.js.map +1 -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 +24 -35
- package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -1
- package/dist/default/playback/adapters/hls-audio/adapter.js +23 -31
- package/dist/default/playback/adapters/hls-audio/adapter.js.map +1 -1
- package/dist/default/playback/adapters/hls-audio/media.js.map +1 -1
- package/dist/default/playback/adapters/hls-background-video/adapter.js +50 -70
- package/dist/default/playback/adapters/hls-background-video/adapter.js.map +1 -1
- package/dist/default/playback/adapters/hls-background-video/host.js +12 -16
- package/dist/default/playback/adapters/hls-background-video/host.js.map +1 -1
- package/dist/default/playback/adapters/hls-background-video/media.js +5 -8
- package/dist/default/playback/adapters/hls-background-video/media.js.map +1 -1
- package/dist/default/playback/adapters/hls-video/adapter.js +43 -60
- package/dist/default/playback/adapters/hls-video/adapter.js.map +1 -1
- package/dist/default/playback/adapters/hls-video/error-surface.js +13 -18
- package/dist/default/playback/adapters/hls-video/error-surface.js.map +1 -1
- package/dist/default/playback/adapters/hls-video/media-tracks.js +4 -5
- package/dist/default/playback/adapters/hls-video/media-tracks.js.map +1 -1
- package/dist/default/playback/adapters/hls-video/media.js.map +1 -1
- package/dist/default/playback/adapters/mux-audio/media.js +8 -12
- package/dist/default/playback/adapters/mux-audio/media.js.map +1 -1
- package/dist/default/playback/adapters/mux-video/adapter.js +25 -33
- package/dist/default/playback/adapters/mux-video/adapter.js.map +1 -1
- package/dist/default/playback/adapters/mux-video/media.js +7 -9
- package/dist/default/playback/adapters/mux-video/media.js.map +1 -1
- package/dist/default/playback/behaviors/calculate-presentation-duration.js +5 -8
- package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -1
- package/dist/default/playback/behaviors/collect-errors.js +39 -52
- package/dist/default/playback/behaviors/collect-errors.js.map +1 -1
- package/dist/default/playback/behaviors/derive-cdn-priority.js +16 -23
- package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -1
- package/dist/default/playback/behaviors/dom/airplay.js +38 -61
- package/dist/default/playback/behaviors/dom/airplay.js.map +1 -1
- package/dist/default/playback/behaviors/dom/apply-start-position.js +23 -35
- package/dist/default/playback/behaviors/dom/apply-start-position.js.map +1 -1
- package/dist/default/playback/behaviors/dom/end-of-stream.js +11 -16
- package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -1
- package/dist/default/playback/behaviors/dom/load-segments.js +27 -39
- package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -1
- package/dist/default/playback/behaviors/dom/recover-end-stall.js +21 -26
- package/dist/default/playback/behaviors/dom/recover-end-stall.js.map +1 -1
- package/dist/default/playback/behaviors/dom/seek-to-live-edge.js +41 -60
- package/dist/default/playback/behaviors/dom/seek-to-live-edge.js.map +1 -1
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +50 -74
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
- package/dist/default/playback/behaviors/dom/setup-mediasource.js +29 -43
- package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -1
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -1
- package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js +3 -4
- package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -1
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js +30 -45
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-current-time.js +9 -11
- package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-load-triggers.js +24 -32
- package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-playback-rate.js +6 -8
- package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-player-resolution.js +10 -14
- package/dist/default/playback/behaviors/dom/track-player-resolution.js.map +1 -1
- package/dist/default/playback/behaviors/dom/track-screen-resolution.js +8 -9
- package/dist/default/playback/behaviors/dom/track-screen-resolution.js.map +1 -1
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +28 -43
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
- package/dist/default/playback/behaviors/establish-start-media-time.js +32 -42
- package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -1
- package/dist/default/playback/behaviors/resolve-presentation.js +13 -22
- package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -1
- package/dist/default/playback/behaviors/resolve-track.js +5 -12
- package/dist/default/playback/behaviors/resolve-track.js.map +1 -1
- package/dist/default/playback/behaviors/select-tracks.js +77 -104
- package/dist/default/playback/behaviors/select-tracks.js.map +1 -1
- package/dist/default/playback/behaviors/setup-failover-monitor.js +11 -16
- package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -1
- package/dist/default/playback/behaviors/sync-preload.js +9 -13
- package/dist/default/playback/behaviors/sync-preload.js.map +1 -1
- package/dist/default/playback/behaviors/track-switching.js +189 -173
- package/dist/default/playback/behaviors/track-switching.js.map +1 -1
- package/dist/default/playback/engines/hls/engine-audio-only.js +20 -24
- package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -1
- package/dist/default/playback/engines/hls/engine-background-video.js +22 -27
- package/dist/default/playback/engines/hls/engine-background-video.js.map +1 -1
- package/dist/default/playback/engines/hls/engine.js +22 -25
- package/dist/default/playback/engines/hls/engine.js.map +1 -1
- package/dist/default/playback/primitives/error-messages.js +16 -25
- package/dist/default/playback/primitives/error-messages.js.map +1 -1
- package/dist/default/playback/primitives/failover-fetch.js +9 -14
- package/dist/default/playback/primitives/failover-fetch.js.map +1 -1
- package/dist/default/playback/primitives/head-peek.js +7 -10
- package/dist/default/playback/primitives/head-peek.js.map +1 -1
- package/dist/default/playback/primitives/live-window.js +12 -15
- package/dist/default/playback/primitives/live-window.js.map +1 -1
- package/dist/default/playback/primitives/relocation-pipelines.js +34 -46
- package/dist/default/playback/primitives/relocation-pipelines.js.map +1 -1
- package/dist/default/playback/primitives/report-track-conditions.js +26 -38
- package/dist/default/playback/primitives/report-track-conditions.js.map +1 -1
- package/dist/default/playback/primitives/segment-load-pipeline.js +15 -15
- package/dist/default/playback/primitives/segment-load-pipeline.js.map +1 -1
- package/dist/default/playback/primitives/selection-rules.js +65 -36
- package/dist/default/playback/primitives/selection-rules.js.map +1 -1
- package/dist/default/playback/primitives/text-segment-load-pipeline.js +4 -5
- package/dist/default/playback/primitives/text-segment-load-pipeline.js.map +1 -1
- package/dist/default/playback/primitives/track-types.js +10 -18
- package/dist/default/playback/primitives/track-types.js.map +1 -1
- package/dist/dev/core/actors/actor.d.ts +4 -4
- package/dist/dev/core/actors/actor.d.ts.map +1 -1
- package/dist/dev/core/actors/create-machine-actor.d.ts +38 -47
- package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -1
- package/dist/dev/core/actors/create-machine-actor.js +23 -25
- package/dist/dev/core/actors/create-machine-actor.js.map +1 -1
- package/dist/dev/core/actors/create-transition-actor.d.ts +11 -15
- package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -1
- package/dist/dev/core/actors/create-transition-actor.js +7 -9
- package/dist/dev/core/actors/create-transition-actor.js.map +1 -1
- package/dist/dev/core/composition/create-composition.d.ts +70 -102
- package/dist/dev/core/composition/create-composition.d.ts.map +1 -1
- package/dist/dev/core/composition/create-composition.js +26 -29
- package/dist/dev/core/composition/create-composition.js.map +1 -1
- package/dist/dev/core/composition/share-signals.d.ts +17 -24
- package/dist/dev/core/composition/share-signals.d.ts.map +1 -1
- package/dist/dev/core/composition/share-signals.js +12 -17
- package/dist/dev/core/composition/share-signals.js.map +1 -1
- package/dist/dev/core/machine.d.ts +4 -4
- package/dist/dev/core/machine.js +3 -4
- package/dist/dev/core/machine.js.map +1 -1
- package/dist/dev/core/reactors/create-machine-reactor.d.ts +33 -39
- package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -1
- package/dist/dev/core/reactors/create-machine-reactor.js +20 -19
- package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -1
- package/dist/dev/core/signals/effect.d.ts +3 -4
- package/dist/dev/core/signals/effect.d.ts.map +1 -1
- package/dist/dev/core/signals/effect.js +24 -4
- package/dist/dev/core/signals/effect.js.map +1 -1
- package/dist/dev/core/signals/primitives.d.ts +10 -12
- package/dist/dev/core/signals/primitives.d.ts.map +1 -1
- package/dist/dev/core/signals/primitives.js +10 -14
- package/dist/dev/core/signals/primitives.js.map +1 -1
- package/dist/dev/core/signals/when.js +6 -8
- package/dist/dev/core/signals/when.js.map +1 -1
- package/dist/dev/core/tasks/delayed-reschedule.js +7 -11
- package/dist/dev/core/tasks/delayed-reschedule.js.map +1 -1
- package/dist/dev/core/tasks/task.d.ts +51 -73
- package/dist/dev/core/tasks/task.d.ts.map +1 -1
- package/dist/dev/core/tasks/task.js +59 -82
- package/dist/dev/core/tasks/task.js.map +1 -1
- package/dist/dev/hls.d.ts +3 -1
- package/dist/dev/hls.js +3 -1
- package/dist/dev/index.d.ts +2 -3
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/index.js +2 -3
- package/dist/dev/index.js.map +1 -1
- package/dist/dev/media/abr/quality-selection.d.ts +7 -10
- package/dist/dev/media/abr/quality-selection.d.ts.map +1 -1
- package/dist/dev/media/abr/quality-selection.js +3 -7
- package/dist/dev/media/abr/quality-selection.js.map +1 -1
- package/dist/dev/media/buffer/back-buffer.d.ts +2 -7
- package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -1
- package/dist/dev/media/buffer/back-buffer.js +15 -19
- package/dist/dev/media/buffer/back-buffer.js.map +1 -1
- package/dist/dev/media/buffer/forward-buffer.d.ts +2 -7
- package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -1
- package/dist/dev/media/buffer/forward-buffer.js +32 -41
- package/dist/dev/media/buffer/forward-buffer.js.map +1 -1
- package/dist/dev/media/dom/capabilities.js +26 -37
- package/dist/dev/media/dom/capabilities.js.map +1 -1
- package/dist/dev/media/dom/mse/append-segment.d.ts +3 -5
- package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -1
- package/dist/dev/media/dom/mse/append-segment.js +3 -5
- package/dist/dev/media/dom/mse/append-segment.js.map +1 -1
- package/dist/dev/media/dom/mse/buffer-flusher.d.ts +5 -5
- package/dist/dev/media/dom/mse/buffer-flusher.js +5 -5
- package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -1
- package/dist/dev/media/dom/mse/duration.js +23 -31
- package/dist/dev/media/dom/mse/duration.js.map +1 -1
- package/dist/dev/media/dom/mse/end-of-stream.js +3 -6
- package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -1
- package/dist/dev/media/dom/mse/mediasource-setup.js +65 -81
- package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -1
- package/dist/dev/media/dom/screen.d.ts.map +1 -1
- package/dist/dev/media/dom/screen.js +37 -54
- package/dist/dev/media/dom/screen.js.map +1 -1
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -1
- package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -1
- package/dist/dev/media/dom/text/text-track-slots.d.ts +10 -16
- package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -1
- package/dist/dev/media/dom/text/text-track-slots.js +15 -23
- package/dist/dev/media/dom/text/text-track-slots.js.map +1 -1
- package/dist/dev/media/errors.d.ts +32 -46
- package/dist/dev/media/errors.d.ts.map +1 -1
- package/dist/dev/media/errors.js +25 -34
- package/dist/dev/media/errors.js.map +1 -1
- package/dist/dev/media/hls/parse-attributes.js +9 -26
- package/dist/dev/media/hls/parse-attributes.js.map +1 -1
- package/dist/dev/media/hls/parse-media-playlist.js +25 -33
- package/dist/dev/media/hls/parse-media-playlist.js.map +1 -1
- package/dist/dev/media/hls/parse-multivariant.js +3 -3
- package/dist/dev/media/hls/parse-multivariant.js.map +1 -1
- package/dist/dev/media/hls/reload-policy.js +19 -27
- package/dist/dev/media/hls/reload-policy.js.map +1 -1
- package/dist/dev/media/hls/resolve-url.js +1 -3
- package/dist/dev/media/hls/resolve-url.js.map +1 -1
- package/dist/dev/media/live-window.js +9 -13
- package/dist/dev/media/live-window.js.map +1 -1
- package/dist/dev/media/media-tracks/media-tracks.d.ts +8 -13
- package/dist/dev/media/media-tracks/media-tracks.d.ts.map +1 -1
- package/dist/dev/media/media-tracks/media-tracks.js +12 -17
- package/dist/dev/media/media-tracks/media-tracks.js.map +1 -1
- package/dist/dev/media/mp4/box.js +4 -5
- package/dist/dev/media/mp4/box.js.map +1 -1
- package/dist/dev/media/mp4/timestamp-origin.js +30 -42
- package/dist/dev/media/mp4/timestamp-origin.js.map +1 -1
- package/dist/dev/media/primitives/resolution.d.ts +6 -8
- package/dist/dev/media/primitives/resolution.d.ts.map +1 -1
- package/dist/dev/media/primitives/resolution.js +5 -8
- package/dist/dev/media/primitives/resolution.js.map +1 -1
- package/dist/dev/media/primitives/select-tracks.js +24 -34
- package/dist/dev/media/primitives/select-tracks.js.map +1 -1
- package/dist/dev/media/text/parse-vtt-timestamp-map.js +6 -8
- package/dist/dev/media/text/parse-vtt-timestamp-map.js.map +1 -1
- package/dist/dev/media/text/resolve-vtt-metadata.js +8 -9
- package/dist/dev/media/text/resolve-vtt-metadata.js.map +1 -1
- package/dist/dev/media/types/index.d.ts +106 -177
- package/dist/dev/media/types/index.d.ts.map +1 -1
- package/dist/dev/media/types/index.js +10 -18
- package/dist/dev/media/types/index.js.map +1 -1
- package/dist/dev/media/utils/cdn.d.ts +4 -6
- package/dist/dev/media/utils/cdn.d.ts.map +1 -1
- package/dist/dev/media/utils/cdn.js +10 -16
- package/dist/dev/media/utils/cdn.js.map +1 -1
- package/dist/dev/media/utils/preload.js +4 -6
- package/dist/dev/media/utils/preload.js.map +1 -1
- package/dist/dev/media/utils/track-selection.js +10 -14
- package/dist/dev/media/utils/track-selection.js.map +1 -1
- package/dist/dev/media/utils/tracks.js +56 -54
- package/dist/dev/media/utils/tracks.js.map +1 -1
- package/dist/dev/network/bandwidth-estimator.d.ts +7 -9
- package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -1
- package/dist/dev/network/bandwidth-estimator.js +18 -19
- package/dist/dev/network/bandwidth-estimator.js.map +1 -1
- package/dist/dev/network/chunked-stream-iterable.js +4 -6
- package/dist/dev/network/chunked-stream-iterable.js.map +1 -1
- package/dist/dev/network/ewma.js +22 -25
- package/dist/dev/network/ewma.js.map +1 -1
- package/dist/dev/network/fetch.js +30 -37
- package/dist/dev/network/fetch.js.map +1 -1
- package/dist/dev/playback/actors/dom/segment-loader.d.ts +7 -11
- package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -1
- package/dist/dev/playback/actors/dom/segment-loader.js +28 -44
- package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -1
- package/dist/dev/playback/actors/dom/source-buffer.d.ts +5 -7
- package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -1
- package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -1
- package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -1
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts +10 -16
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -1
- package/dist/dev/playback/actors/text-track-segment-loader.js +24 -35
- package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -1
- package/dist/dev/playback/actors/text-tracks.d.ts +5 -8
- package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-audio/adapter.d.ts +11 -14
- package/dist/dev/playback/adapters/hls-audio/adapter.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-audio/adapter.js +23 -31
- package/dist/dev/playback/adapters/hls-audio/adapter.js.map +1 -1
- package/dist/dev/playback/adapters/hls-audio/media.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-audio/media.js.map +1 -1
- package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts +28 -39
- package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-background-video/adapter.js +50 -70
- package/dist/dev/playback/adapters/hls-background-video/adapter.js.map +1 -1
- package/dist/dev/playback/adapters/hls-background-video/host.d.ts +12 -16
- package/dist/dev/playback/adapters/hls-background-video/host.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-background-video/host.js +12 -16
- package/dist/dev/playback/adapters/hls-background-video/host.js.map +1 -1
- package/dist/dev/playback/adapters/hls-background-video/media.d.ts +5 -8
- package/dist/dev/playback/adapters/hls-background-video/media.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-background-video/media.js +5 -8
- package/dist/dev/playback/adapters/hls-background-video/media.js.map +1 -1
- package/dist/dev/playback/adapters/hls-video/adapter.d.ts +12 -14
- package/dist/dev/playback/adapters/hls-video/adapter.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-video/adapter.js +43 -60
- package/dist/dev/playback/adapters/hls-video/adapter.js.map +1 -1
- package/dist/dev/playback/adapters/hls-video/error-surface.d.ts +4 -5
- package/dist/dev/playback/adapters/hls-video/error-surface.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-video/error-surface.js +13 -18
- package/dist/dev/playback/adapters/hls-video/error-surface.js.map +1 -1
- package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts +4 -5
- package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-video/media-tracks.js +4 -5
- package/dist/dev/playback/adapters/hls-video/media-tracks.js.map +1 -1
- package/dist/dev/playback/adapters/hls-video/media.d.ts.map +1 -1
- package/dist/dev/playback/adapters/hls-video/media.js.map +1 -1
- package/dist/dev/playback/adapters/mux-audio/media.d.ts +8 -12
- package/dist/dev/playback/adapters/mux-audio/media.d.ts.map +1 -1
- package/dist/dev/playback/adapters/mux-audio/media.js +8 -12
- package/dist/dev/playback/adapters/mux-audio/media.js.map +1 -1
- package/dist/dev/playback/adapters/mux-video/adapter.d.ts +9 -11
- package/dist/dev/playback/adapters/mux-video/adapter.d.ts.map +1 -1
- package/dist/dev/playback/adapters/mux-video/adapter.js +25 -33
- package/dist/dev/playback/adapters/mux-video/adapter.js.map +1 -1
- package/dist/dev/playback/adapters/mux-video/media.d.ts +7 -9
- package/dist/dev/playback/adapters/mux-video/media.d.ts.map +1 -1
- package/dist/dev/playback/adapters/mux-video/media.js +7 -9
- package/dist/dev/playback/adapters/mux-video/media.js.map +1 -1
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +5 -8
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js +5 -8
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -1
- package/dist/dev/playback/behaviors/collect-errors.d.ts +2 -0
- package/dist/dev/playback/behaviors/collect-errors.js +39 -52
- package/dist/dev/playback/behaviors/collect-errors.js.map +1 -1
- package/dist/dev/playback/behaviors/derive-cdn-priority.js +16 -23
- package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/airplay.js +38 -61
- package/dist/dev/playback/behaviors/dom/airplay.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/apply-start-position.js +23 -35
- package/dist/dev/playback/behaviors/dom/apply-start-position.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/end-of-stream.js +11 -16
- package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts +5 -8
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/load-segments.js +27 -39
- package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/recover-end-stall.js +21 -26
- package/dist/dev/playback/behaviors/dom/recover-end-stall.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js +41 -60
- package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +50 -74
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js +29 -43
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +3 -3
- 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.map +1 -1
- package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js +3 -4
- package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +30 -45
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +2 -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 +9 -11
- package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +6 -12
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js +24 -32
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +2 -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 +6 -8
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-player-resolution.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-player-resolution.js +10 -14
- package/dist/dev/playback/behaviors/dom/track-player-resolution.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/track-screen-resolution.js +8 -9
- package/dist/dev/playback/behaviors/dom/track-screen-resolution.js.map +1 -1
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +28 -43
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
- package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +15 -19
- package/dist/dev/playback/behaviors/establish-start-media-time.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/establish-start-media-time.js +32 -42
- package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -1
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/resolve-presentation.js +13 -22
- package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -1
- package/dist/dev/playback/behaviors/resolve-track.js +5 -12
- package/dist/dev/playback/behaviors/resolve-track.js.map +1 -1
- package/dist/dev/playback/behaviors/select-tracks.d.ts +28 -39
- package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/select-tracks.js +77 -104
- package/dist/dev/playback/behaviors/select-tracks.js.map +1 -1
- package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +1 -4
- package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -1
- package/dist/dev/playback/behaviors/setup-failover-monitor.js +11 -16
- package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -1
- package/dist/dev/playback/behaviors/sync-preload.js +9 -13
- package/dist/dev/playback/behaviors/sync-preload.js.map +1 -1
- package/dist/dev/playback/behaviors/track-switching.d.ts +110 -0
- package/dist/dev/playback/behaviors/track-switching.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/track-switching.js +189 -173
- package/dist/dev/playback/behaviors/track-switching.js.map +1 -1
- package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +59 -72
- package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -1
- package/dist/dev/playback/engines/hls/engine-audio-only.js +20 -24
- package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -1
- package/dist/dev/playback/engines/hls/engine-background-video.d.ts +51 -78
- package/dist/dev/playback/engines/hls/engine-background-video.d.ts.map +1 -1
- package/dist/dev/playback/engines/hls/engine-background-video.js +22 -27
- package/dist/dev/playback/engines/hls/engine-background-video.js.map +1 -1
- package/dist/dev/playback/engines/hls/engine.d.ts +115 -155
- package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -1
- package/dist/dev/playback/engines/hls/engine.js +22 -25
- package/dist/dev/playback/engines/hls/engine.js.map +1 -1
- package/dist/dev/playback/primitives/derive-start-media-time.d.ts +3 -3
- package/dist/dev/playback/primitives/derive-start-media-time.d.ts.map +1 -1
- package/dist/dev/playback/primitives/error-messages.js +16 -25
- package/dist/dev/playback/primitives/error-messages.js.map +1 -1
- package/dist/dev/playback/primitives/failover-fetch.js +9 -14
- package/dist/dev/playback/primitives/failover-fetch.js.map +1 -1
- package/dist/dev/playback/primitives/head-peek.js +7 -10
- package/dist/dev/playback/primitives/head-peek.js.map +1 -1
- package/dist/dev/playback/primitives/live-window.js +12 -15
- package/dist/dev/playback/primitives/live-window.js.map +1 -1
- package/dist/dev/playback/primitives/relocation-pipelines.js +34 -46
- package/dist/dev/playback/primitives/relocation-pipelines.js.map +1 -1
- package/dist/dev/playback/primitives/report-track-conditions.d.ts +1 -4
- package/dist/dev/playback/primitives/report-track-conditions.d.ts.map +1 -1
- package/dist/dev/playback/primitives/report-track-conditions.js +26 -38
- package/dist/dev/playback/primitives/report-track-conditions.js.map +1 -1
- package/dist/dev/playback/primitives/segment-load-pipeline.js +15 -15
- package/dist/dev/playback/primitives/segment-load-pipeline.js.map +1 -1
- package/dist/dev/playback/primitives/selection-rules.d.ts +45 -17
- package/dist/dev/playback/primitives/selection-rules.d.ts.map +1 -1
- package/dist/dev/playback/primitives/selection-rules.js +65 -36
- package/dist/dev/playback/primitives/selection-rules.js.map +1 -1
- package/dist/dev/playback/primitives/source-buffer-messages.d.ts +6 -9
- package/dist/dev/playback/primitives/source-buffer-messages.d.ts.map +1 -1
- package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts +20 -29
- package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts.map +1 -1
- package/dist/dev/playback/primitives/text-segment-load-pipeline.js +4 -5
- package/dist/dev/playback/primitives/text-segment-load-pipeline.js.map +1 -1
- package/dist/dev/playback/primitives/text-track-messages.d.ts.map +1 -1
- package/dist/dev/playback/primitives/track-types.js +10 -18
- package/dist/dev/playback/primitives/track-types.js.map +1 -1
- package/package.json +25 -28
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Dual EWMA Bandwidth Estimator
|
|
4
4
|
*
|
|
5
|
-
* Estimates available bandwidth using two EWMA calculations with different
|
|
6
|
-
*
|
|
5
|
+
* Estimates available bandwidth using two EWMA calculations with different half-lives, taking the minimum of both. This
|
|
6
|
+
* approach (from Shaka Player):
|
|
7
7
|
*
|
|
8
8
|
* - **Fast EWMA** (2s half-life): Reacts quickly to bandwidth drops
|
|
9
9
|
* - **Slow EWMA** (5s half-life): Provides stability during fluctuations
|
|
10
10
|
* - **min(fast, slow)**: Adapts down quickly, up slowly
|
|
11
11
|
*
|
|
12
|
-
* This naturally provides asymmetric behavior needed for good QoE:
|
|
13
|
-
*
|
|
12
|
+
* This naturally provides asymmetric behavior needed for good QoE: avoiding stalls (quick downgrade) while preventing
|
|
13
|
+
* oscillation (slow upgrade).
|
|
14
14
|
*/
|
|
15
15
|
/**
|
|
16
16
|
* Bandwidth estimator state.
|
|
17
17
|
*
|
|
18
|
-
* This state structure will be managed by O1 (State Container).
|
|
19
|
-
*
|
|
18
|
+
* This state structure will be managed by O1 (State Container). Functions in this module operate on this state
|
|
19
|
+
* immutably.
|
|
20
20
|
*/
|
|
21
21
|
interface BandwidthState {
|
|
22
22
|
/** Fast-moving EWMA estimate (raw, uncorrected). */
|
|
@@ -30,9 +30,7 @@ interface BandwidthState {
|
|
|
30
30
|
/** Total bytes sampled across all valid samples. */
|
|
31
31
|
bytesSampled: number;
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Configuration for bandwidth estimation.
|
|
35
|
-
*/
|
|
33
|
+
/** Configuration for bandwidth estimation. */
|
|
36
34
|
interface BandwidthConfig {
|
|
37
35
|
/** Half-life for fast EWMA in seconds. */
|
|
38
36
|
fastHalfLife: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bandwidth-estimator.d.ts","names":[],"sources":["../../../src/network/bandwidth-estimator.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;UAsBiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA
|
|
1
|
+
{"version":3,"file":"bandwidth-estimator.d.ts","names":[],"sources":["../../../src/network/bandwidth-estimator.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;UAsBiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;;UAIe;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA"}
|
|
@@ -3,15 +3,15 @@ import { applyZeroFactor, calculateEwma } from "./ewma.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Dual EWMA Bandwidth Estimator
|
|
5
5
|
*
|
|
6
|
-
* Estimates available bandwidth using two EWMA calculations with different
|
|
7
|
-
*
|
|
6
|
+
* Estimates available bandwidth using two EWMA calculations with different half-lives, taking the minimum of both. This
|
|
7
|
+
* approach (from Shaka Player):
|
|
8
8
|
*
|
|
9
9
|
* - **Fast EWMA** (2s half-life): Reacts quickly to bandwidth drops
|
|
10
10
|
* - **Slow EWMA** (5s half-life): Provides stability during fluctuations
|
|
11
11
|
* - **min(fast, slow)**: Adapts down quickly, up slowly
|
|
12
12
|
*
|
|
13
|
-
* This naturally provides asymmetric behavior needed for good QoE:
|
|
14
|
-
*
|
|
13
|
+
* This naturally provides asymmetric behavior needed for good QoE: avoiding stalls (quick downgrade) while preventing
|
|
14
|
+
* oscillation (slow upgrade).
|
|
15
15
|
*/
|
|
16
16
|
/**
|
|
17
17
|
* Default bandwidth estimator configuration.
|
|
@@ -28,22 +28,21 @@ const DEFAULT_BANDWIDTH_CONFIG = {
|
|
|
28
28
|
/**
|
|
29
29
|
* Add a bandwidth sample from a segment download.
|
|
30
30
|
*
|
|
31
|
-
* Samples are filtered based on:
|
|
32
|
-
*
|
|
33
|
-
* - Minimum duration (filters cached responses)
|
|
31
|
+
* Samples are filtered based on: - Minimum bytes (filters TTFB-dominated small segments) - Minimum duration (filters
|
|
32
|
+
* cached responses)
|
|
34
33
|
*
|
|
35
34
|
* Valid samples update both fast and slow EWMA estimates.
|
|
36
35
|
*
|
|
36
|
+
* @example
|
|
37
|
+
* let state = { fastEstimate: 0, fastTotalWeight: 0, ... };
|
|
38
|
+
* // Sample: 1MB in 1 second
|
|
39
|
+
* state = sampleBandwidth(state, 1000, 1_000_000);
|
|
40
|
+
*
|
|
37
41
|
* @param state - Current estimator state
|
|
38
42
|
* @param durationMs - Download duration in milliseconds
|
|
39
43
|
* @param numBytes - Number of bytes downloaded
|
|
40
44
|
* @param config - Optional estimator configuration (uses defaults if not provided)
|
|
41
45
|
* @returns New estimator state with sample incorporated (or unchanged if filtered)
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* let state = { fastEstimate: 0, fastTotalWeight: 0, ... };
|
|
45
|
-
* // Sample: 1MB in 1 second
|
|
46
|
-
* state = sampleBandwidth(state, 1000, 1_000_000);
|
|
47
46
|
*/
|
|
48
47
|
function sampleBandwidth(state, durationMs, numBytes, config = DEFAULT_BANDWIDTH_CONFIG) {
|
|
49
48
|
const updatedBytesSampled = state.bytesSampled + numBytes;
|
|
@@ -68,21 +67,21 @@ function sampleBandwidth(state, durationMs, numBytes, config = DEFAULT_BANDWIDTH
|
|
|
68
67
|
/**
|
|
69
68
|
* Get the current bandwidth estimate.
|
|
70
69
|
*
|
|
71
|
-
* Returns the **minimum** of the fast and slow EWMA estimates.
|
|
72
|
-
*
|
|
70
|
+
* Returns the **minimum** of the fast and slow EWMA estimates. This provides the key asymmetric behavior:
|
|
71
|
+
*
|
|
73
72
|
* - When bandwidth drops, fast EWMA reacts first and dominates (quick adaptation)
|
|
74
73
|
* - When bandwidth rises, slow EWMA lags behind and dominates (slow adaptation)
|
|
75
74
|
*
|
|
76
|
-
* Uses default estimate until enough data has been sampled — and when no
|
|
77
|
-
*
|
|
75
|
+
* Uses default estimate until enough data has been sampled — and when no estimator state exists at all (`state ===
|
|
76
|
+
* undefined`).
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* const estimate = getBandwidthEstimate(state, 5_000_000); // 5 Mbps default
|
|
78
80
|
*
|
|
79
81
|
* @param state - Current estimator state, or `undefined` before any samples have been collected
|
|
80
82
|
* @param defaultEstimate - Fallback estimate before sufficient samples (bps)
|
|
81
83
|
* @param config - Optional estimator configuration (uses defaults if not provided)
|
|
82
84
|
* @returns Bandwidth estimate in bits per second
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* const estimate = getBandwidthEstimate(state, 5_000_000); // 5 Mbps default
|
|
86
85
|
*/
|
|
87
86
|
function getBandwidthEstimate(state, defaultEstimate, config = DEFAULT_BANDWIDTH_CONFIG) {
|
|
88
87
|
if (!state || state.bytesSampled < config.minTotalBytes) return defaultEstimate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bandwidth-estimator.js","names":[],"sources":["../../../src/network/bandwidth-estimator.ts"],"sourcesContent":["/**\n * Dual EWMA Bandwidth Estimator\n *\n * Estimates available bandwidth using two EWMA calculations with different
|
|
1
|
+
{"version":3,"file":"bandwidth-estimator.js","names":[],"sources":["../../../src/network/bandwidth-estimator.ts"],"sourcesContent":["/**\n * Dual EWMA Bandwidth Estimator\n *\n * Estimates available bandwidth using two EWMA calculations with different half-lives, taking the minimum of both. This\n * approach (from Shaka Player):\n *\n * - **Fast EWMA** (2s half-life): Reacts quickly to bandwidth drops\n * - **Slow EWMA** (5s half-life): Provides stability during fluctuations\n * - **min(fast, slow)**: Adapts down quickly, up slowly\n *\n * This naturally provides asymmetric behavior needed for good QoE: avoiding stalls (quick downgrade) while preventing\n * oscillation (slow upgrade).\n */\n\nimport { applyZeroFactor, calculateEwma } from './ewma';\n\n/**\n * Bandwidth estimator state.\n *\n * This state structure will be managed by O1 (State Container). Functions in this module operate on this state\n * immutably.\n */\nexport interface BandwidthState {\n /** Fast-moving EWMA estimate (raw, uncorrected). */\n fastEstimate: number;\n /** Total weight accumulated in fast EWMA. */\n fastTotalWeight: number;\n /** Slow-moving EWMA estimate (raw, uncorrected). */\n slowEstimate: number;\n /** Total weight accumulated in slow EWMA. */\n slowTotalWeight: number;\n /** Total bytes sampled across all valid samples. */\n bytesSampled: number;\n}\n\n/** Configuration for bandwidth estimation. */\nexport interface BandwidthConfig {\n /** Half-life for fast EWMA in seconds. */\n fastHalfLife: number;\n /** Half-life for slow EWMA in seconds. */\n slowHalfLife: number;\n /** Minimum total bytes before trusting the estimate. */\n minTotalBytes: number;\n /** Minimum bytes per sample to count (filters TTFB-dominated samples). */\n minBytes: number;\n /** Minimum sample duration in ms (filters cached responses). */\n minDuration: number;\n}\n\n/**\n * Default bandwidth estimator configuration.\n *\n * Values match Shaka Player defaults based on experimentation.\n */\nexport const DEFAULT_BANDWIDTH_CONFIG: BandwidthConfig = {\n fastHalfLife: 2, // 2 seconds\n slowHalfLife: 5, // 5 seconds\n minTotalBytes: 128_000, // 128 KB\n minBytes: 16_000, // 16 KB\n minDuration: 5, // 5 ms\n};\n\n/**\n * Add a bandwidth sample from a segment download.\n *\n * Samples are filtered based on: - Minimum bytes (filters TTFB-dominated small segments) - Minimum duration (filters\n * cached responses)\n *\n * Valid samples update both fast and slow EWMA estimates.\n *\n * @example\n * let state = { fastEstimate: 0, fastTotalWeight: 0, ... };\n * // Sample: 1MB in 1 second\n * state = sampleBandwidth(state, 1000, 1_000_000);\n *\n * @param state - Current estimator state\n * @param durationMs - Download duration in milliseconds\n * @param numBytes - Number of bytes downloaded\n * @param config - Optional estimator configuration (uses defaults if not provided)\n * @returns New estimator state with sample incorporated (or unchanged if filtered)\n */\nexport function sampleBandwidth(\n state: BandwidthState,\n durationMs: number,\n numBytes: number,\n config: BandwidthConfig = DEFAULT_BANDWIDTH_CONFIG\n): BandwidthState {\n // Always track bytes for startup phase calculation\n const updatedBytesSampled = state.bytesSampled + numBytes;\n\n // Filter: Ignore samples below minimum bytes (TTFB-dominated)\n // Small segments' download time is mostly connection setup latency,\n // which would artificially lower our bandwidth estimate\n if (numBytes < config.minBytes) {\n return { ...state, bytesSampled: updatedBytesSampled };\n }\n\n // Filter: Ignore samples faster than minimum duration (cached responses)\n // Cached responses load nearly instantly and would artificially inflate\n // our bandwidth estimate\n if (durationMs < config.minDuration) {\n return { ...state, bytesSampled: updatedBytesSampled };\n }\n\n // Calculate bandwidth in bits per second\n // Formula: (bytes * 8 bits/byte * 1000 ms/s) / ms = bits/s\n const bandwidth = (8000 * numBytes) / durationMs;\n\n // Weight by duration in seconds\n // Longer downloads are more reliable indicators of true bandwidth\n const weight = durationMs / 1000;\n\n return {\n fastEstimate: calculateEwma(state.fastEstimate, bandwidth, weight, config.fastHalfLife),\n fastTotalWeight: state.fastTotalWeight + weight,\n slowEstimate: calculateEwma(state.slowEstimate, bandwidth, weight, config.slowHalfLife),\n slowTotalWeight: state.slowTotalWeight + weight,\n bytesSampled: updatedBytesSampled,\n };\n}\n\n/**\n * Get the current bandwidth estimate.\n *\n * Returns the **minimum** of the fast and slow EWMA estimates. This provides the key asymmetric behavior:\n *\n * - When bandwidth drops, fast EWMA reacts first and dominates (quick adaptation)\n * - When bandwidth rises, slow EWMA lags behind and dominates (slow adaptation)\n *\n * Uses default estimate until enough data has been sampled — and when no estimator state exists at all (`state ===\n * undefined`).\n *\n * @example\n * const estimate = getBandwidthEstimate(state, 5_000_000); // 5 Mbps default\n *\n * @param state - Current estimator state, or `undefined` before any samples have been collected\n * @param defaultEstimate - Fallback estimate before sufficient samples (bps)\n * @param config - Optional estimator configuration (uses defaults if not provided)\n * @returns Bandwidth estimate in bits per second\n */\nexport function getBandwidthEstimate(\n state: BandwidthState | undefined,\n defaultEstimate: number,\n config: BandwidthConfig = DEFAULT_BANDWIDTH_CONFIG\n): number {\n // Use default until we have enough samples to trust our estimate\n if (!state || state.bytesSampled < config.minTotalBytes) {\n return defaultEstimate;\n }\n\n // Apply zero-factor correction to both estimates\n const fastEstimate = applyZeroFactor(state.fastEstimate, state.fastTotalWeight, config.fastHalfLife);\n\n const slowEstimate = applyZeroFactor(state.slowEstimate, state.slowTotalWeight, config.slowHalfLife);\n\n // Take the minimum - this is the key insight from Shaka Player\n // It naturally provides \"down quickly, up slowly\" behavior\n return Math.min(fastEstimate, slowEstimate);\n}\n\n/**\n * Check if the estimator has enough data to provide a reliable estimate.\n *\n * Requires both: - Enough total bytes sampled (minTotalBytes threshold) - At least one valid EWMA sample (totalWeight >\n * 0)\n *\n * @example\n * if (hasGoodEstimate(state)) {\n * const estimate = getBandwidthEstimate(state, 5_000_000);\n * }\n *\n * @param state - Current estimator state\n * @param config - Optional estimator configuration (uses defaults if not provided)\n * @returns True if we've sampled enough bytes to trust the estimate\n */\nexport function hasGoodEstimate(state: BandwidthState, config: BandwidthConfig = DEFAULT_BANDWIDTH_CONFIG): boolean {\n // Need enough total bytes AND at least one valid EWMA sample\n return state.bytesSampled >= config.minTotalBytes && state.fastTotalWeight > 0 && state.slowTotalWeight > 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsDA,MAAa,2BAA4C;CACvD,cAAc;CACd,cAAc;CACd,eAAe;CACf,UAAU;CACV,aAAa;AACf;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,gBACd,OACA,YACA,UACA,SAA0B,0BACV;CAEhB,MAAM,sBAAsB,MAAM,eAAe;CAKjD,IAAI,WAAW,OAAO,UACpB,OAAO;EAAE,GAAG;EAAO,cAAc;CAAoB;CAMvD,IAAI,aAAa,OAAO,aACtB,OAAO;EAAE,GAAG;EAAO,cAAc;CAAoB;CAKvD,MAAM,YAAa,MAAO,WAAY;CAItC,MAAM,SAAS,aAAa;CAE5B,OAAO;EACL,cAAc,cAAc,MAAM,cAAc,WAAW,QAAQ,OAAO,YAAY;EACtF,iBAAiB,MAAM,kBAAkB;EACzC,cAAc,cAAc,MAAM,cAAc,WAAW,QAAQ,OAAO,YAAY;EACtF,iBAAiB,MAAM,kBAAkB;EACzC,cAAc;CAChB;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,qBACd,OACA,iBACA,SAA0B,0BAClB;CAER,IAAI,CAAC,SAAS,MAAM,eAAe,OAAO,eACxC,OAAO;CAIT,MAAM,eAAe,gBAAgB,MAAM,cAAc,MAAM,iBAAiB,OAAO,YAAY;CAEnG,MAAM,eAAe,gBAAgB,MAAM,cAAc,MAAM,iBAAiB,OAAO,YAAY;CAInG,OAAO,KAAK,IAAI,cAAc,YAAY;AAC5C"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
//#region src/network/chunked-stream-iterable.ts
|
|
2
2
|
const DEFAULT_MIN_CHUNK_SIZE = 2 ** 17;
|
|
3
3
|
/**
|
|
4
|
-
* Adapts a `ReadableStream<Uint8Array>` (e.g. `response.body`) into an
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
* together once the threshold is met. Any remainder is flushed on stream end.
|
|
4
|
+
* Adapts a `ReadableStream<Uint8Array>` (e.g. `response.body`) into an `AsyncIterable<Uint8Array>` that yields chunks
|
|
5
|
+
* no smaller than `minChunkSize` bytes. Smaller network chunks are accumulated and yielded together once the threshold
|
|
6
|
+
* is met. Any remainder is flushed on stream end.
|
|
8
7
|
*
|
|
9
|
-
* Errors from the underlying stream propagate naturally — the reader lock is
|
|
10
|
-
* always released via `finally`.
|
|
8
|
+
* Errors from the underlying stream propagate naturally — the reader lock is always released via `finally`.
|
|
11
9
|
*/
|
|
12
10
|
var ChunkedStreamIterable = class {
|
|
13
11
|
minChunkSize;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunked-stream-iterable.js","names":["#readableStream"],"sources":["../../../src/network/chunked-stream-iterable.ts"],"sourcesContent":["const DEFAULT_MIN_CHUNK_SIZE = 2 ** 17; // 128 KB\n\nexport interface ChunkedStreamIterableOptions {\n minChunkSize?: number;\n}\n\n/**\n * Adapts a `ReadableStream<Uint8Array>` (e.g. `response.body`) into an
|
|
1
|
+
{"version":3,"file":"chunked-stream-iterable.js","names":["#readableStream"],"sources":["../../../src/network/chunked-stream-iterable.ts"],"sourcesContent":["const DEFAULT_MIN_CHUNK_SIZE = 2 ** 17; // 128 KB\n\nexport interface ChunkedStreamIterableOptions {\n minChunkSize?: number;\n}\n\n/**\n * Adapts a `ReadableStream<Uint8Array>` (e.g. `response.body`) into an `AsyncIterable<Uint8Array>` that yields chunks\n * no smaller than `minChunkSize` bytes. Smaller network chunks are accumulated and yielded together once the threshold\n * is met. Any remainder is flushed on stream end.\n *\n * Errors from the underlying stream propagate naturally — the reader lock is always released via `finally`.\n */\nexport class ChunkedStreamIterable implements AsyncIterable<Uint8Array> {\n readonly minChunkSize: number;\n #readableStream: ReadableStream<Uint8Array>;\n\n constructor(\n readableStream: ReadableStream<Uint8Array>,\n { minChunkSize = DEFAULT_MIN_CHUNK_SIZE }: ChunkedStreamIterableOptions = {}\n ) {\n this.#readableStream = readableStream;\n this.minChunkSize = minChunkSize;\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<Uint8Array> {\n let pending: Uint8Array | undefined;\n const reader = this.#readableStream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n if (pending) yield pending;\n\n break;\n }\n\n pending = pending ? concat(pending, value) : value;\n\n if (pending.length >= this.minChunkSize) {\n yield pending;\n pending = undefined;\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const result = new Uint8Array(a.length + b.length);\n\n result.set(a);\n result.set(b, a.length);\n return result;\n}\n"],"mappings":";AAAA,MAAM,yBAAyB,KAAK;;;;;;;;AAapC,IAAa,wBAAb,MAAwE;CACtE;CACA;CAEA,YACE,gBACA,EAAE,eAAe,2BAAyD,CAAC,GAC3E;EACA,KAAKA,kBAAkB;EACvB,KAAK,eAAe;CACtB;CAEA,QAAQ,OAAO,iBAA6C;EAC1D,IAAI;EACJ,MAAM,SAAS,KAAKA,gBAAgB,UAAU;EAE9C,IAAI;GACF,OAAO,MAAM;IACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAE1C,IAAI,MAAM;KACR,IAAI,SAAS,MAAM;KAEnB;IACF;IAEA,UAAU,UAAU,OAAO,SAAS,KAAK,IAAI;IAE7C,IAAI,QAAQ,UAAU,KAAK,cAAc;KACvC,MAAM;KACN,UAAU,KAAA;IACZ;GACF;EACF,UAAU;GACR,OAAO,YAAY;EACrB;CACF;AACF;AAEA,SAAS,OAAO,GAAe,GAA2B;CACxD,MAAM,SAAS,IAAI,WAAW,EAAE,SAAS,EAAE,MAAM;CAEjD,OAAO,IAAI,CAAC;CACZ,OAAO,IAAI,GAAG,EAAE,MAAM;CACtB,OAAO;AACT"}
|
package/dist/dev/network/ewma.js
CHANGED
|
@@ -2,22 +2,19 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Exponentially Weighted Moving Average (EWMA)
|
|
4
4
|
*
|
|
5
|
-
* Pure functional implementation of EWMA calculations.
|
|
6
|
-
* Based on Shaka Player's EWMA algorithm.
|
|
5
|
+
* Pure functional implementation of EWMA calculations. Based on Shaka Player's EWMA algorithm.
|
|
7
6
|
*/
|
|
8
7
|
/**
|
|
9
8
|
* Calculate alpha (decay factor) from half-life.
|
|
10
9
|
*
|
|
11
|
-
* Alpha determines how quickly old data "expires":
|
|
12
|
-
*
|
|
13
|
-
* - alpha close to 0 = fast decay (short memory)
|
|
14
|
-
*
|
|
15
|
-
* @param halfLife - The quantity of prior samples (by weight) that make up
|
|
16
|
-
* half of the new estimate. Must be positive.
|
|
17
|
-
* @returns Alpha value between 0 and 1
|
|
10
|
+
* Alpha determines how quickly old data "expires": - alpha close to 1 = slow decay (long memory) - alpha close to 0 =
|
|
11
|
+
* fast decay (short memory)
|
|
18
12
|
*
|
|
19
13
|
* @example
|
|
20
|
-
*
|
|
14
|
+
* const alpha = calculateAlpha(2); // ≈ 0.7071 for 2-second half-life
|
|
15
|
+
*
|
|
16
|
+
* @param halfLife - The quantity of prior samples (by weight) that make up half of the new estimate. Must be positive.
|
|
17
|
+
* @returns Alpha value between 0 and 1
|
|
21
18
|
*/
|
|
22
19
|
function calculateAlpha(halfLife) {
|
|
23
20
|
return Math.exp(Math.log(.5) / halfLife);
|
|
@@ -25,19 +22,19 @@ function calculateAlpha(halfLife) {
|
|
|
25
22
|
/**
|
|
26
23
|
* Calculate exponentially weighted moving average.
|
|
27
24
|
*
|
|
28
|
-
* Updates an estimate by blending a new value with the previous estimate,
|
|
29
|
-
*
|
|
25
|
+
* Updates an estimate by blending a new value with the previous estimate, weighted by the sample duration. Longer
|
|
26
|
+
* samples have more influence.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* let estimate = 0;
|
|
30
|
+
* estimate = calculateEwma(estimate, 1_000_000, 1, 2); // First sample
|
|
31
|
+
* estimate = calculateEwma(estimate, 2_000_000, 1, 2); // Second sample
|
|
30
32
|
*
|
|
31
33
|
* @param prevEstimate - Previous EWMA estimate
|
|
32
34
|
* @param value - New sample value to incorporate
|
|
33
35
|
* @param weight - Sample weight (typically duration in seconds)
|
|
34
36
|
* @param halfLife - Half-life for decay (typically 2-5 seconds)
|
|
35
37
|
* @returns Updated EWMA estimate
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* let estimate = 0;
|
|
39
|
-
* estimate = calculateEwma(estimate, 1_000_000, 1, 2); // First sample
|
|
40
|
-
* estimate = calculateEwma(estimate, 2_000_000, 1, 2); // Second sample
|
|
41
38
|
*/
|
|
42
39
|
function calculateEwma(prevEstimate, value, weight, halfLife) {
|
|
43
40
|
const adjAlpha = calculateAlpha(halfLife) ** weight;
|
|
@@ -46,20 +43,20 @@ function calculateEwma(prevEstimate, value, weight, halfLife) {
|
|
|
46
43
|
/**
|
|
47
44
|
* Apply zero-factor correction to EWMA estimate.
|
|
48
45
|
*
|
|
49
|
-
* The zero-factor correction compensates for bias when starting from zero.
|
|
50
|
-
*
|
|
46
|
+
* The zero-factor correction compensates for bias when starting from zero. Without this correction, early estimates
|
|
47
|
+
* would be artificially low.
|
|
48
|
+
*
|
|
49
|
+
* As totalWeight increases, the correction factor approaches 1, meaning the estimate becomes more reliable and needs
|
|
50
|
+
* less correction.
|
|
51
51
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
52
|
+
* @example
|
|
53
|
+
* const raw = calculateEwma(0, 1_000_000, 1, 2);
|
|
54
|
+
* const corrected = applyZeroFactor(raw, 1, 2); // ≈ 1_000_000
|
|
54
55
|
*
|
|
55
56
|
* @param estimate - Raw EWMA estimate (uncorrected)
|
|
56
57
|
* @param totalWeight - Accumulated weight from all samples
|
|
57
58
|
* @param halfLife - Half-life used in EWMA calculation
|
|
58
59
|
* @returns Corrected estimate, or 0 if totalWeight is 0
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* const raw = calculateEwma(0, 1_000_000, 1, 2);
|
|
62
|
-
* const corrected = applyZeroFactor(raw, 1, 2); // ≈ 1_000_000
|
|
63
60
|
*/
|
|
64
61
|
function applyZeroFactor(estimate, totalWeight, halfLife) {
|
|
65
62
|
if (totalWeight === 0) return 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ewma.js","names":[],"sources":["../../../src/network/ewma.ts"],"sourcesContent":["/**\n * Exponentially Weighted Moving Average (EWMA)\n *\n * Pure functional implementation of EWMA calculations
|
|
1
|
+
{"version":3,"file":"ewma.js","names":[],"sources":["../../../src/network/ewma.ts"],"sourcesContent":["/**\n * Exponentially Weighted Moving Average (EWMA)\n *\n * Pure functional implementation of EWMA calculations. Based on Shaka Player's EWMA algorithm.\n */\n\n/**\n * Calculate alpha (decay factor) from half-life.\n *\n * Alpha determines how quickly old data \"expires\": - alpha close to 1 = slow decay (long memory) - alpha close to 0 =\n * fast decay (short memory)\n *\n * @example\n * const alpha = calculateAlpha(2); // ≈ 0.7071 for 2-second half-life\n *\n * @param halfLife - The quantity of prior samples (by weight) that make up half of the new estimate. Must be positive.\n * @returns Alpha value between 0 and 1\n */\nexport function calculateAlpha(halfLife: number): number {\n return Math.exp(Math.log(0.5) / halfLife);\n}\n\n/**\n * Calculate exponentially weighted moving average.\n *\n * Updates an estimate by blending a new value with the previous estimate, weighted by the sample duration. Longer\n * samples have more influence.\n *\n * @example\n * let estimate = 0;\n * estimate = calculateEwma(estimate, 1_000_000, 1, 2); // First sample\n * estimate = calculateEwma(estimate, 2_000_000, 1, 2); // Second sample\n *\n * @param prevEstimate - Previous EWMA estimate\n * @param value - New sample value to incorporate\n * @param weight - Sample weight (typically duration in seconds)\n * @param halfLife - Half-life for decay (typically 2-5 seconds)\n * @returns Updated EWMA estimate\n */\nexport function calculateEwma(prevEstimate: number, value: number, weight: number, halfLife: number): number {\n const alpha = calculateAlpha(halfLife);\n const adjAlpha = alpha ** weight;\n\n return value * (1 - adjAlpha) + adjAlpha * prevEstimate;\n}\n\n/**\n * Apply zero-factor correction to EWMA estimate.\n *\n * The zero-factor correction compensates for bias when starting from zero. Without this correction, early estimates\n * would be artificially low.\n *\n * As totalWeight increases, the correction factor approaches 1, meaning the estimate becomes more reliable and needs\n * less correction.\n *\n * @example\n * const raw = calculateEwma(0, 1_000_000, 1, 2);\n * const corrected = applyZeroFactor(raw, 1, 2); // ≈ 1_000_000\n *\n * @param estimate - Raw EWMA estimate (uncorrected)\n * @param totalWeight - Accumulated weight from all samples\n * @param halfLife - Half-life used in EWMA calculation\n * @returns Corrected estimate, or 0 if totalWeight is 0\n */\nexport function applyZeroFactor(estimate: number, totalWeight: number, halfLife: number): number {\n if (totalWeight === 0) {\n return 0;\n }\n\n const alpha = calculateAlpha(halfLife);\n const zeroFactor = 1 - alpha ** totalWeight;\n\n return estimate / zeroFactor;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,SAAgB,eAAe,UAA0B;CACvD,OAAO,KAAK,IAAI,KAAK,IAAI,EAAG,IAAI,QAAQ;AAC1C;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,cAAc,cAAsB,OAAe,QAAgB,UAA0B;CAE3G,MAAM,WADQ,eAAe,QACR,KAAK;CAE1B,OAAO,SAAS,IAAI,YAAY,WAAW;AAC7C;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,gBAAgB,UAAkB,aAAqB,UAA0B;CAC/F,IAAI,gBAAgB,GAClB,OAAO;CAMT,OAAO,YAFY,IADL,eAAe,QACF,KAAK;AAGlC"}
|
|
@@ -4,35 +4,32 @@ import { ChunkedStreamIterable } from "./chunked-stream-iterable.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* HTTP Fetch Wrapper
|
|
6
6
|
*
|
|
7
|
-
* Composable building blocks:
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* of body consumption matters (e.g., observable fetch timing for ABR).
|
|
14
|
-
* - createTrackedFetch() — factory for a fetchStream-shape function that
|
|
15
|
-
* samples bandwidth (via EWMA) per chunk and notifies via callback.
|
|
7
|
+
* Composable building blocks: - fetchResolvable() — fetch a Resource (handles byte ranges); returns Response -
|
|
8
|
+
* getResponseText() — extract text from Response - fetchResolvableStream() — single-stage async generator over body
|
|
9
|
+
* chunks - fetchStream() — two-stage: await connection establishment, then lazily iterate body chunks. Use when timing
|
|
10
|
+
* the connection start independently of body consumption matters (e.g., observable fetch timing for ABR). -
|
|
11
|
+
* createTrackedFetch() — factory for a fetchStream-shape function that samples bandwidth (via EWMA) per chunk and
|
|
12
|
+
* notifies via callback.
|
|
16
13
|
*/
|
|
17
14
|
/**
|
|
18
15
|
* Fetch resolvable from a Resource.
|
|
19
16
|
*
|
|
20
|
-
* Handles byte range requests if byteRange is present.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @param addressable - Resource to fetch (url + optional byteRange)
|
|
24
|
-
* @returns Promise resolving to Response
|
|
17
|
+
* Handles byte range requests if byteRange is present. Returns native fetch Response for composability (can extract
|
|
18
|
+
* text, stream, etc.).
|
|
25
19
|
*
|
|
26
20
|
* @example
|
|
27
|
-
*
|
|
28
|
-
*
|
|
21
|
+
* const response = await fetchResolvable({ url: 'https://example.com/segment.m4s' });
|
|
22
|
+
* const text = await getResponseText(response);
|
|
29
23
|
*
|
|
30
24
|
* @example
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
25
|
+
* // With byte range
|
|
26
|
+
* const response = await fetchResolvable({
|
|
27
|
+
* url: 'https://example.com/file.mp4',
|
|
28
|
+
* byteRange: { start: 1000, end: 1999 },
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* @param addressable - Resource to fetch (url + optional byteRange)
|
|
32
|
+
* @returns Promise resolving to Response
|
|
36
33
|
*/
|
|
37
34
|
async function fetchResolvable(addressable, options) {
|
|
38
35
|
const headers = new Headers(options?.headers);
|
|
@@ -50,15 +47,14 @@ async function fetchResolvable(addressable, options) {
|
|
|
50
47
|
/**
|
|
51
48
|
* Extract text from Response.
|
|
52
49
|
*
|
|
53
|
-
* Accepts minimal Response-like object (just needs text() method).
|
|
54
|
-
*
|
|
50
|
+
* Accepts minimal Response-like object (just needs text() method). Returns promise from response.text().
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* const response = await fetchResolvable(addressable);
|
|
54
|
+
* const text = await getResponseText(response);
|
|
55
55
|
*
|
|
56
56
|
* @param response - Response-like object with text() method
|
|
57
57
|
* @returns Promise resolving to text content
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* const response = await fetchResolvable(addressable);
|
|
61
|
-
* const text = await getResponseText(response);
|
|
62
58
|
*/
|
|
63
59
|
function getResponseText(response) {
|
|
64
60
|
return response.text();
|
|
@@ -76,18 +72,15 @@ async function fetchStream(addressable, options) {
|
|
|
76
72
|
return new ChunkedStreamIterable(response.body, ...minChunkSize !== void 0 ? [{ minChunkSize }] : []);
|
|
77
73
|
}
|
|
78
74
|
/**
|
|
79
|
-
* Returns a {@link FetchBytes} function that samples bandwidth via EWMA
|
|
80
|
-
* per
|
|
81
|
-
*
|
|
82
|
-
* supplied `onSample` callback.
|
|
75
|
+
* Returns a {@link FetchBytes} function that samples bandwidth via EWMA per body chunk. The factory captures the
|
|
76
|
+
* running bandwidth state internally; per chunk it computes the next state and notifies the supplied `onSample`
|
|
77
|
+
* callback.
|
|
83
78
|
*
|
|
84
|
-
* The factory's internal accumulator is seeded from `initial` and updated
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* samples back into engine state for ABR consumers.
|
|
79
|
+
* The factory's internal accumulator is seeded from `initial` and updated on every chunk; callers don't need to thread
|
|
80
|
+
* it back in. `onSample` receives the _new_ state after each chunk — typical use is to bridge samples back into engine
|
|
81
|
+
* state for ABR consumers.
|
|
88
82
|
*
|
|
89
|
-
* @param initial - Starting `BandwidthState` (commonly zeros or the
|
|
90
|
-
* engine's current accumulator).
|
|
83
|
+
* @param initial - Starting `BandwidthState` (commonly zeros or the engine's current accumulator).
|
|
91
84
|
* @param onSample - Called with the new `BandwidthState` after each chunk.
|
|
92
85
|
*/
|
|
93
86
|
function createTrackedFetch(initial, onSample) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.js","names":[],"sources":["../../../src/network/fetch.ts"],"sourcesContent":["/**\n * HTTP Fetch Wrapper\n *\n * Composable building blocks
|
|
1
|
+
{"version":3,"file":"fetch.js","names":[],"sources":["../../../src/network/fetch.ts"],"sourcesContent":["/**\n * HTTP Fetch Wrapper\n *\n * Composable building blocks: - fetchResolvable() — fetch a Resource (handles byte ranges); returns Response -\n * getResponseText() — extract text from Response - fetchResolvableStream() — single-stage async generator over body\n * chunks - fetchStream() — two-stage: await connection establishment, then lazily iterate body chunks. Use when timing\n * the connection start independently of body consumption matters (e.g., observable fetch timing for ABR). -\n * createTrackedFetch() — factory for a fetchStream-shape function that samples bandwidth (via EWMA) per chunk and\n * notifies via callback.\n */\n\nimport { type BandwidthState, sampleBandwidth } from './bandwidth-estimator';\nimport { ChunkedStreamIterable, type ChunkedStreamIterableOptions } from './chunked-stream-iterable';\n\n/** Minimal Response-like interface for text extraction. Allows testing without full Response object. */\nexport interface ResponseLike {\n text(): Promise<string>;\n}\n\n/**\n * An HTTP-addressable resource — URL plus optional byte range. Media's `AddressableObject` (and anything else with the\n * same shape) is structurally compatible; kept local so this module stays domain-agnostic.\n */\nexport interface Resource {\n url: string;\n byteRange?: {\n start: number;\n end: number;\n };\n}\n\n/**\n * Fetch resolvable from a Resource.\n *\n * Handles byte range requests if byteRange is present. Returns native fetch Response for composability (can extract\n * text, stream, etc.).\n *\n * @example\n * const response = await fetchResolvable({ url: 'https://example.com/segment.m4s' });\n * const text = await getResponseText(response);\n *\n * @example\n * // With byte range\n * const response = await fetchResolvable({\n * url: 'https://example.com/file.mp4',\n * byteRange: { start: 1000, end: 1999 },\n * });\n *\n * @param addressable - Resource to fetch (url + optional byteRange)\n * @returns Promise resolving to Response\n */\nexport async function fetchResolvable(addressable: Resource, options?: RequestInit): Promise<Response> {\n const headers = new Headers(options?.headers);\n\n // Add Range header for byte range requests\n if (addressable.byteRange) {\n const { start, end } = addressable.byteRange;\n\n headers.set('Range', `bytes=${start}-${end}`);\n }\n\n const request = new Request(addressable.url, {\n method: 'GET',\n headers,\n ...options,\n });\n\n return fetch(request);\n}\n\n/**\n * Fetch resolvable as bytes.\n *\n * Convenience wrapper around fetchResolvable that resolves the body as an ArrayBuffer. Use this when you need the raw\n * bytes (e.g. segment appends). For text or streaming consumption, use fetchResolvable directly.\n */\nexport async function fetchResolvableBytes(addressable: Resource, options?: RequestInit): Promise<ArrayBuffer> {\n const response = await fetchResolvable(addressable, options);\n\n return response.arrayBuffer();\n}\n\n/**\n * Fetch resolvable as a stream of Uint8Array chunks.\n *\n * Convenience wrapper around fetchResolvable that yields the body as chunks via ChunkedStreamIterable. Headers are\n * awaited before the first chunk is yielded (TTFB is accounted for before iteration begins).\n *\n * Throws if the response body is null (e.g. non-body HTTP status). Errors from the underlying stream propagate\n * naturally as thrown errors.\n */\nexport async function* fetchResolvableStream(\n addressable: Resource,\n options?: RequestInit & ChunkedStreamIterableOptions\n): AsyncGenerator<Uint8Array> {\n const { minChunkSize, ...fetchOptions } = options ?? {};\n const response = await fetchResolvable(addressable, fetchOptions);\n if (!response.body) throw new Error('Response has no body');\n\n yield* new ChunkedStreamIterable(response.body, ...(minChunkSize !== undefined ? [{ minChunkSize }] : []));\n}\n\n/**\n * Extract text from Response.\n *\n * Accepts minimal Response-like object (just needs text() method). Returns promise from response.text().\n *\n * @example\n * const response = await fetchResolvable(addressable);\n * const text = await getResponseText(response);\n *\n * @param response - Response-like object with text() method\n * @returns Promise resolving to text content\n */\nexport function getResponseText(response: ResponseLike): Promise<string> {\n return response.text();\n}\n\n/**\n * Fetch a resource and resolve its text body — the text analog of {@link FetchBytes}. A non-OK status rejects, so HTTP\n * failures surface as rejections that callers (and decorators like the failover tracker) handle uniformly with network\n * errors.\n */\nexport type FetchText = (addressable: Resource, options?: RequestInit) => Promise<string>;\n\n/** Default {@link FetchText}: fetch the resource, reject on non-OK, return text. */\nexport const fetchResolvableText: FetchText = async (addressable, options) => {\n const response = await fetchResolvable(addressable, options);\n\n if (!response.ok) {\n throw new Error(`fetchResolvableText: ${response.status} ${response.statusText} for ${addressable.url}`);\n }\n\n return getResponseText(response);\n};\n\n/**\n * Two-stage fetch helper: eagerly starts the HTTP request (TTFB is awaited), then returns a lazy iterable over the\n * response body. Separating connection start from body iteration makes fetch timing predictable and observable\n * regardless of when downstream consumers begin pulling chunks.\n *\n * Sibling to {@link fetchResolvableStream}, which is single-stage (calls `fetch` only when iteration starts). Pick\n * `fetchStream` when \"when did the fetch start\" needs to be observable separately from \"when did the body begin\n * arriving.\"\n */\nexport type FetchOptions = RequestInit & ChunkedStreamIterableOptions;\n\nexport type FetchBytes = (addressable: Resource, options?: FetchOptions) => Promise<AsyncIterable<Uint8Array>>;\n\nexport async function fetchStream(addressable: Resource, options?: FetchOptions): Promise<AsyncIterable<Uint8Array>> {\n const { minChunkSize, ...fetchOptions } = options ?? {};\n const response = await fetchResolvable(addressable, fetchOptions);\n if (!response.body) throw new Error('Response has no body');\n\n return new ChunkedStreamIterable(response.body, ...(minChunkSize !== undefined ? [{ minChunkSize }] : []));\n}\n\n/**\n * Returns a {@link FetchBytes} function that samples bandwidth via EWMA per body chunk. The factory captures the\n * running bandwidth state internally; per chunk it computes the next state and notifies the supplied `onSample`\n * callback.\n *\n * The factory's internal accumulator is seeded from `initial` and updated on every chunk; callers don't need to thread\n * it back in. `onSample` receives the _new_ state after each chunk — typical use is to bridge samples back into engine\n * state for ABR consumers.\n *\n * @param initial - Starting `BandwidthState` (commonly zeros or the engine's current accumulator).\n * @param onSample - Called with the new `BandwidthState` after each chunk.\n */\nexport function createTrackedFetch(initial: BandwidthState, onSample: (next: BandwidthState) => void): FetchBytes {\n let state = initial;\n\n return async (addressable, options) => {\n const { minChunkSize, ...fetchOptions } = options ?? {};\n const response = await fetchResolvable(addressable, fetchOptions);\n if (!response.body) throw new Error('Response has no body');\n\n const body = response.body;\n\n return {\n [Symbol.asyncIterator]: async function* () {\n let chunkStart = performance.now();\n\n for await (const chunk of new ChunkedStreamIterable(\n body,\n ...(minChunkSize !== undefined ? [{ minChunkSize }] : [])\n )) {\n const elapsed = performance.now() - chunkStart;\n\n state = sampleBandwidth(state, elapsed, chunk.byteLength);\n onSample(state);\n yield chunk;\n chunkStart = performance.now();\n }\n },\n };\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,eAAsB,gBAAgB,aAAuB,SAA0C;CACrG,MAAM,UAAU,IAAI,QAAQ,SAAS,OAAO;CAG5C,IAAI,YAAY,WAAW;EACzB,MAAM,EAAE,OAAO,QAAQ,YAAY;EAEnC,QAAQ,IAAI,SAAS,SAAS,MAAM,GAAG,KAAK;CAC9C;CAEA,MAAM,UAAU,IAAI,QAAQ,YAAY,KAAK;EAC3C,QAAQ;EACR;EACA,GAAG;CACL,CAAC;CAED,OAAO,MAAM,OAAO;AACtB;;;;;;;;;;;;;AA8CA,SAAgB,gBAAgB,UAAyC;CACvE,OAAO,SAAS,KAAK;AACvB;;AAUA,MAAa,sBAAiC,OAAO,aAAa,YAAY;CAC5E,MAAM,WAAW,MAAM,gBAAgB,aAAa,OAAO;CAE3D,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,wBAAwB,SAAS,OAAO,GAAG,SAAS,WAAW,OAAO,YAAY,KAAK;CAGzG,OAAO,gBAAgB,QAAQ;AACjC;AAeA,eAAsB,YAAY,aAAuB,SAA4D;CACnH,MAAM,EAAE,cAAc,GAAG,iBAAiB,WAAW,CAAC;CACtD,MAAM,WAAW,MAAM,gBAAgB,aAAa,YAAY;CAChE,IAAI,CAAC,SAAS,MAAM,MAAM,IAAI,MAAM,sBAAsB;CAE1D,OAAO,IAAI,sBAAsB,SAAS,MAAM,GAAI,iBAAiB,KAAA,IAAY,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAE;AAC3G;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAyB,UAAsD;CAChH,IAAI,QAAQ;CAEZ,OAAO,OAAO,aAAa,YAAY;EACrC,MAAM,EAAE,cAAc,GAAG,iBAAiB,WAAW,CAAC;EACtD,MAAM,WAAW,MAAM,gBAAgB,aAAa,YAAY;EAChE,IAAI,CAAC,SAAS,MAAM,MAAM,IAAI,MAAM,sBAAsB;EAE1D,MAAM,OAAO,SAAS;EAEtB,OAAO,EACL,CAAC,OAAO,gBAAgB,mBAAmB;GACzC,IAAI,aAAa,YAAY,IAAI;GAEjC,WAAW,MAAM,SAAS,IAAI,sBAC5B,MACA,GAAI,iBAAiB,KAAA,IAAY,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CACzD,GAAG;IACD,MAAM,UAAU,YAAY,IAAI,IAAI;IAEpC,QAAQ,gBAAgB,OAAO,SAAS,MAAM,UAAU;IACxD,SAAS,KAAK;IACd,MAAM;IACN,aAAa,YAAY,IAAI;GAC/B;EACF,EACF;CACF;AACF"}
|
|
@@ -8,12 +8,10 @@ type SegmentLoaderTrack = VideoTrack | AudioTrack;
|
|
|
8
8
|
/**
|
|
9
9
|
* Message sent to a SegmentLoaderActor.
|
|
10
10
|
*
|
|
11
|
-
* `range` is optional to distinguish loading modes:
|
|
12
|
-
*
|
|
13
|
-
* - With range: load init + all segments overlapping [start, end]
|
|
11
|
+
* `range` is optional to distinguish loading modes: - No range: load init segment only (metadata preload mode) - With
|
|
12
|
+
* range: load init + all segments overlapping [start, end]
|
|
14
13
|
*
|
|
15
|
-
* `start` and `end` are raw time values — no segment snapping.
|
|
16
|
-
* The actor maps them onto segment boundaries internally.
|
|
14
|
+
* `start` and `end` are raw time values — no segment snapping. The actor maps them onto segment boundaries internally.
|
|
17
15
|
*/
|
|
18
16
|
type SegmentLoaderMessage = {
|
|
19
17
|
type: 'load';
|
|
@@ -30,12 +28,10 @@ interface SegmentLoaderActorContext {
|
|
|
30
28
|
/** Track ID of the init segment currently being fetched/appended, or null. */
|
|
31
29
|
inFlightInitTrackId: string | null;
|
|
32
30
|
/**
|
|
33
|
-
* Identity of the segment currently being fetched/appended, or null. Tracks
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* handler's continue/preempt decision). Mirrors the text-track loader, which
|
|
38
|
-
* already matches its in-flight segment on `(inFlightTrackId, inFlightSegmentId)`.
|
|
31
|
+
* Identity of the segment currently being fetched/appended, or null. Tracks the `trackId` alongside the positional
|
|
32
|
+
* `id` because renditions number segments independently (`segment-N`): an in-flight LOW `segment-0` must not be
|
|
33
|
+
* mistaken for a needed HIGH `segment-0` on a switch (see the loading handler's continue/preempt decision). Mirrors
|
|
34
|
+
* the text-track loader, which already matches its in-flight segment on `(inFlightTrackId, inFlightSegmentId)`.
|
|
39
35
|
*/
|
|
40
36
|
inFlightSegment: {
|
|
41
37
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"segment-loader.d.ts","names":[],"sources":["../../../../../src/playback/actors/dom/segment-loader.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"segment-loader.d.ts","names":[],"sources":["../../../../../src/playback/actors/dom/segment-loader.ts"],"mappings":";;;;;;KAmDY,qBAAqB,aAAa;;;;;;;;;KAUlC;EACV;EACA,OAAO;EACP;IAAU;IAAe;;;;KAQf;;UAGK;;EAEf;;;;;;;EAOA;IAAmB;IAAY;;;KAGrB,qBAAqB,aAAa,yBAAyB,2BAA2B"}
|
|
@@ -7,11 +7,9 @@ import { DEFAULT_BACK_BUFFER_CONFIG, calculateBackBufferFlushPoint } from "../..
|
|
|
7
7
|
import { DEFAULT_MESSAGE_PIPELINES } from "../../primitives/segment-load-pipeline.js";
|
|
8
8
|
//#region src/playback/actors/dom/segment-loader.ts
|
|
9
9
|
/**
|
|
10
|
-
* Wraps a LoadTask descriptor into a Task that runs the op's message pipeline
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* make accurate continue/preempt decisions at any point, and checks the abort
|
|
14
|
-
* signal before each step.
|
|
10
|
+
* Wraps a LoadTask descriptor into a Task that runs the op's message pipeline (fetch/discover/stamp/dispatch, per the
|
|
11
|
+
* composition's `messagePipelines`). Updates in-flight context around the async region so the loading handler can make
|
|
12
|
+
* accurate continue/preempt decisions at any point, and checks the abort signal before each step.
|
|
15
13
|
*/
|
|
16
14
|
function makeLoadTask(op, { getContext, setContext, pipelines, deps }) {
|
|
17
15
|
return new Task(async (taskSignal) => {
|
|
@@ -51,25 +49,20 @@ function makeLoadTask(op, { getContext, setContext, pipelines, deps }) {
|
|
|
51
49
|
/**
|
|
52
50
|
* Creates a SegmentLoaderActor for one track type (video or audio).
|
|
53
51
|
*
|
|
54
|
-
* Receives load assignments via `send()` and owns all execution: planning,
|
|
55
|
-
*
|
|
56
|
-
* all physical SourceBuffer operations.
|
|
52
|
+
* Receives load assignments via `send()` and owns all execution: planning, removes, fetches, and appends. Coordinates
|
|
53
|
+
* with the SourceBufferActor for all physical SourceBuffer operations.
|
|
57
54
|
*
|
|
58
|
-
* Planning (Cases 1–3) happens in the `load` handler on every incoming
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* + schedule new remainder) or preempts it (abortAll + cancel SourceBuffer
|
|
63
|
-
* if needed + schedule new plan).
|
|
55
|
+
* Planning (Cases 1–3) happens in the `load` handler on every incoming message, producing an ordered LoadTask list. The
|
|
56
|
+
* runner drains that list sequentially via SerialRunner. When a new message arrives mid-run, the handler replans and
|
|
57
|
+
* either continues the in-flight operation (abortPending + schedule new remainder) or preempts it (abortAll + cancel
|
|
58
|
+
* SourceBuffer if needed + schedule new plan).
|
|
64
59
|
*
|
|
65
60
|
* @param sourceBufferActor - Shared SourceBufferActor reference (not owned)
|
|
66
|
-
* @param fetchBytes - Tracked fetch closure (owns throughput sampling for segments).
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* injected steps (relocation) read composition signals at call time. Defaults to
|
|
72
|
-
* empty for standalone / base-pipeline use.
|
|
61
|
+
* @param fetchBytes - Tracked fetch closure (owns throughput sampling for segments). Accepts an optional `minChunkSize`
|
|
62
|
+
* in options; init segments pass `Infinity` so the entire body accumulates as one chunk before appending.
|
|
63
|
+
* @param compositionDeps - The composition's `state`/`context`/`config`, threaded opaquely into each step's
|
|
64
|
+
* {@link StepDeps} (the loader never reads them). Lets injected steps (relocation) read composition signals at call
|
|
65
|
+
* time. Defaults to empty for standalone / base-pipeline use.
|
|
73
66
|
*/
|
|
74
67
|
function createSegmentLoaderActor(sourceBufferActor, fetchBytes, config = {}, compositionDeps = {
|
|
75
68
|
state: {},
|
|
@@ -95,36 +88,27 @@ function createSegmentLoaderActor(sourceBufferActor, fetchBytes, config = {}, co
|
|
|
95
88
|
};
|
|
96
89
|
const pipelines = (config.messagePipelines ?? DEFAULT_MESSAGE_PIPELINES)();
|
|
97
90
|
const getBufferedSegments = (allSegments) => {
|
|
98
|
-
const
|
|
91
|
+
const appended = peek(sourceBufferActor.snapshot).context.segments.filter((s) => !s.partial);
|
|
92
|
+
const merged = mergeTimeRanges(appended.map((s) => ({
|
|
99
93
|
start: s.startTime,
|
|
100
94
|
end: s.startTime + s.duration
|
|
101
95
|
})));
|
|
102
96
|
return allSegments.filter((s) => isTimeRangeCovered(s.startTime, s.startTime + s.duration, merged));
|
|
103
97
|
};
|
|
104
98
|
/**
|
|
105
|
-
* Translate a load message into an ordered LoadTask list based on committed
|
|
106
|
-
*
|
|
99
|
+
* Translate a load message into an ordered LoadTask list based on committed actor state. In-flight awareness is
|
|
100
|
+
* handled separately in the load handler.
|
|
107
101
|
*
|
|
108
|
-
* @todo Rename alongside LoadTask (e.g. planOps).
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* the newly-selected track, so overwrite-on-append would leave stale
|
|
119
|
-
* content playing until each replacement segment lands. Today's predicate:
|
|
120
|
-
* `actorCtx.initTrackLanguage !== track.language` — fires for language
|
|
121
|
-
* changes, no-ops for video / same-language audio bitrate switches.
|
|
122
|
-
* Future stage: pluggable predicate / strategy at actor construction time
|
|
123
|
-
* for codec-change (5.1 surround) and other cross-rendition shapes.
|
|
124
|
-
*
|
|
125
|
-
* Case 2 — Init: schedule if not yet committed for this track.
|
|
126
|
-
*
|
|
127
|
-
* Case 3 — Segments: all segments in the load window not yet committed.
|
|
102
|
+
* @todo Rename alongside LoadTask (e.g. planOps). Case 1 — Removes: forward and back buffer flush points,
|
|
103
|
+
* segment-aligned. ABR-style track switches (same content, different bitrate) do not flush: appending new content
|
|
104
|
+
* overwrites existing buffer ranges, and the actor's time-aligned deduplication keeps the segment model accurate as
|
|
105
|
+
* new segments arrive. Cross-rendition track switches (audio language change, text language change) do flush: the
|
|
106
|
+
* buffered content is semantically incompatible with the newly-selected track, so overwrite-on-append would leave
|
|
107
|
+
* stale content playing until each replacement segment lands. Today's predicate: `actorCtx.initTrackLanguage !==
|
|
108
|
+
* track.language` — fires for language changes, no-ops for video / same-language audio bitrate switches. Future
|
|
109
|
+
* stage: pluggable predicate / strategy at actor construction time for codec-change (5.1 surround) and other
|
|
110
|
+
* cross-rendition shapes. Case 2 — Init: schedule if not yet committed for this track. Case 3 — Segments: all
|
|
111
|
+
* segments in the load window not yet committed.
|
|
128
112
|
*/
|
|
129
113
|
const planTasks = (message) => {
|
|
130
114
|
const { track, range } = message;
|