@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-multivariant.js","names":[],"sources":["../../../../src/media/hls/parse-multivariant.ts"],"sourcesContent":["import { generateId } from '@videojs/utils/string';\nimport type {\n AddressableObject,\n AudioSelectionSet,\n AudioSwitchingSet,\n FrameRate,\n PartiallyResolvedAudioTrack,\n PartiallyResolvedTextTrack,\n PartiallyResolvedVideoTrack,\n Presentation,\n SelectionSet,\n TextSelectionSet,\n TextSwitchingSet,\n VideoSelectionSet,\n VideoSwitchingSet,\n} from '../types';\nimport { matchTag, parseCodecs } from './parse-attributes';\nimport { resolveUrl } from './resolve-url';\n\n/**\n * Parse HLS multivariant playlist into a Presentation.\n *\n * Returns Presentation with partially resolved tracks (no segment information).\n * Tracks contain metadata from multivariant playlist (bandwidth, resolution, codecs)\n * but segment information is added when media playlists are fetched.\n *\n * @param text - Raw playlist text content\n * @param unresolved - Unresolved presentation (contains URL for base URL resolution)\n * @returns Presentation with partially resolved tracks (duration is undefined)\n */\nexport function parseMultivariantPlaylist(text: string, unresolved: AddressableObject): Presentation {\n const baseUrl = unresolved.url;\n const lines = text.split(/\\r?\\n/);\n\n // Intermediate parsing structures\n interface StreamInfo {\n uri: string;\n bandwidth: number;\n resolution?: { width: number; height: number } | undefined;\n codecs?: string | undefined;\n frameRate?: FrameRate | undefined;\n audioGroupId?: string | undefined;\n }\n\n interface AudioRenditionInfo {\n groupId: string;\n name: string;\n language?: string | undefined;\n uri?: string | undefined;\n default?: boolean | undefined;\n autoselect?: boolean | undefined;\n channels?: number | undefined;\n }\n\n interface SubtitleRenditionInfo {\n groupId: string;\n name: string;\n language?: string | undefined;\n uri: string;\n default?: boolean | undefined;\n autoselect?: boolean | undefined;\n forced?: boolean | undefined;\n }\n\n const streams: StreamInfo[] = [];\n const audioRenditions: AudioRenditionInfo[] = [];\n const subtitleRenditions: SubtitleRenditionInfo[] = [];\n\n // State for STREAM-INF parsing (URI follows on next line)\n let pendingStreamInfo: Omit<StreamInfo, 'uri'> | null = null;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || (trimmed.startsWith('#') && !trimmed.startsWith('#EXT'))) {\n continue;\n }\n\n // Skip tags not used in Presentation model\n if (\n trimmed === '#EXTM3U' ||\n trimmed.startsWith('#EXT-X-VERSION:') ||\n trimmed.startsWith('#EXT-X-INDEPENDENT-SEGMENTS')\n ) {\n continue;\n }\n\n // #EXT-X-MEDIA:TYPE=AUDIO/SUBTITLES\n const mediaAttrs = matchTag(trimmed, 'EXT-X-MEDIA');\n if (mediaAttrs) {\n const type = mediaAttrs.get('TYPE');\n const groupId = mediaAttrs.get('GROUP-ID');\n const name = mediaAttrs.get('NAME');\n\n if (type === 'AUDIO' && groupId && name) {\n const uri = mediaAttrs.get('URI');\n audioRenditions.push({\n groupId,\n name,\n language: mediaAttrs.get('LANGUAGE'),\n uri: uri ? resolveUrl(uri, baseUrl) : undefined,\n default: mediaAttrs.getBool('DEFAULT'),\n autoselect: mediaAttrs.getBool('AUTOSELECT'),\n // CHANNELS is a quoted string whose first parameter is the channel\n // count (\"6\", or \"16/JOC\" for spatial audio); getInt reads the\n // leading integer.\n channels: mediaAttrs.getInt('CHANNELS'),\n });\n }\n\n if (type === 'SUBTITLES' && groupId && name) {\n const uri = mediaAttrs.get('URI');\n // URI is required for subtitle tracks\n if (uri) {\n subtitleRenditions.push({\n groupId,\n name,\n language: mediaAttrs.get('LANGUAGE'),\n uri: resolveUrl(uri, baseUrl),\n default: mediaAttrs.getBool('DEFAULT'),\n autoselect: mediaAttrs.getBool('AUTOSELECT'),\n forced: mediaAttrs.getBool('FORCED'),\n });\n }\n }\n continue;\n }\n\n // #EXT-X-STREAM-INF:BANDWIDTH=...\n const streamInfAttrs = matchTag(trimmed, 'EXT-X-STREAM-INF');\n if (streamInfAttrs) {\n pendingStreamInfo = {\n bandwidth: streamInfAttrs.getInt('BANDWIDTH', 0)!,\n resolution: streamInfAttrs.getResolution('RESOLUTION'),\n codecs: streamInfAttrs.get('CODECS'),\n frameRate: streamInfAttrs.getFrameRate('FRAME-RATE'),\n audioGroupId: streamInfAttrs.get('AUDIO'),\n };\n continue;\n }\n\n // URI line following STREAM-INF\n if (!trimmed.startsWith('#') && pendingStreamInfo) {\n streams.push({\n ...pendingStreamInfo,\n uri: resolveUrl(trimmed, baseUrl),\n });\n pendingStreamInfo = null;\n }\n }\n\n // Separate streams into video and audio based on codecs\n // If CODECS has single codec, use parseCodecs to determine type\n const videoStreams: typeof streams = [];\n const audioOnlyStreams: typeof streams = [];\n\n for (const stream of streams) {\n if (!stream.codecs) {\n // No codecs - assume video (default behavior)\n videoStreams.push(stream);\n continue;\n }\n\n const parsedCodecs = parseCodecs(stream.codecs);\n const codecCount = stream.codecs.split(',').length;\n\n // Single codec - determine type from parseCodecs result\n if (codecCount === 1) {\n if (parsedCodecs.audio && !parsedCodecs.video) {\n // Audio-only stream\n audioOnlyStreams.push(stream);\n } else {\n // Video stream (or unknown - default to video)\n videoStreams.push(stream);\n }\n } else {\n // Multiple codecs - video stream with muxed audio\n videoStreams.push(stream);\n }\n }\n\n // Build PartiallyResolvedVideoTracks from video streams, de-duplicating the\n // HLS cross-product: one video rendition is listed across several\n // `EXT-X-STREAM-INF` entries — one per audio group it can pair with, all\n // sharing the same media-playlist URI. Collapse them to one track per URI,\n // accumulating every advertised audio group. (Redundant-stream renditions\n // live at *distinct* per-CDN URIs, so they stay separate — only the same-URI\n // cross-product merges.)\n const videoTracksByUrl = new Map<string, PartiallyResolvedVideoTrack>();\n for (const stream of videoStreams) {\n const existing = videoTracksByUrl.get(stream.uri);\n if (existing) {\n if (stream.audioGroupId && !existing.audioGroupIds?.includes(stream.audioGroupId)) {\n existing.audioGroupIds = [...(existing.audioGroupIds ?? []), stream.audioGroupId];\n }\n // BANDWIDTH is video + audio combined; the duplicates differ only in the\n // paired audio. Keep the lowest as the closest proxy to video-only, which\n // is what ABR should rank on.\n if (stream.bandwidth < existing.bandwidth) {\n existing.bandwidth = stream.bandwidth;\n }\n continue;\n }\n\n const codecs = stream.codecs ? parseCodecs(stream.codecs) : undefined;\n\n const track: PartiallyResolvedVideoTrack = {\n type: 'video' as const,\n id: generateId(),\n url: stream.uri,\n bandwidth: stream.bandwidth,\n // Type-specific defaults (CMAF video)\n mimeType: 'video/mp4',\n codecs: [],\n };\n\n if (stream.resolution?.width !== undefined) {\n track.width = stream.resolution.width;\n }\n if (stream.resolution?.height !== undefined) {\n track.height = stream.resolution.height;\n }\n if (codecs?.video) {\n // When the STREAM-INF lists an audio codec but declares no AUDIO group,\n // the audio is muxed into this rendition's segments — keep both codecs so\n // the SourceBuffer mimetype matches the muxed media (otherwise the muxed\n // audio fails to append: \"audio object type does not match the mimetype\").\n // With an AUDIO group the audio is a separate rendition, so only the video\n // codec belongs here.\n track.codecs = codecs.audio && !stream.audioGroupId ? [codecs.video, codecs.audio] : [codecs.video];\n }\n if (stream.frameRate) {\n track.frameRate = stream.frameRate;\n }\n if (stream.audioGroupId) {\n track.audioGroupIds = [stream.audioGroupId];\n }\n\n videoTracksByUrl.set(stream.uri, track);\n }\n const videoTracks: PartiallyResolvedVideoTrack[] = [...videoTracksByUrl.values()];\n\n // Build PartiallyResolvedAudioTracks from audio-only streams\n const audioOnlyTracks: PartiallyResolvedAudioTrack[] = audioOnlyStreams.map((stream) => {\n const codecs = stream.codecs ? parseCodecs(stream.codecs) : undefined;\n\n const track: PartiallyResolvedAudioTrack = {\n type: 'audio' as const,\n id: generateId(),\n url: stream.uri,\n bandwidth: stream.bandwidth,\n mimeType: 'audio/mp4',\n codecs: codecs?.audio ? [codecs.audio] : [],\n groupId: stream.audioGroupId || 'default',\n name: 'Default',\n sampleRate: 48000, // Default - will be in media playlist if available\n channels: 2, // Default - will be in media playlist if available\n };\n\n return track;\n });\n\n // Build PartiallyResolvedAudioTracks from audio renditions (EXT-X-MEDIA)\n // Extract audio codecs from referencing streams\n const audioRenditionTracks: PartiallyResolvedAudioTrack[] = audioRenditions.flatMap((rendition) => {\n let audioCodecs: string[] | undefined;\n for (const stream of streams) {\n if (stream.audioGroupId === rendition.groupId && stream.codecs) {\n const codecs = parseCodecs(stream.codecs);\n if (codecs.audio) {\n audioCodecs = [codecs.audio];\n break;\n }\n }\n }\n\n // A rendition with no URI names media carried in the streams referencing its\n // group. When such a stream is audio-only it is already a track of its own, so\n // the two describe one rendition: merge rather than add a second entry that has\n // nothing to fetch. The rendition holds the naming and selection metadata, the\n // stream the URL and bandwidth.\n if (!rendition.uri) {\n const carrier = audioOnlyTracks.find((track) => track.groupId === rendition.groupId);\n if (carrier) {\n carrier.name = rendition.name;\n if (rendition.language) carrier.language = rendition.language;\n if (rendition.channels) carrier.channels = rendition.channels;\n if (rendition.default) carrier.default = rendition.default;\n if (rendition.autoselect) carrier.autoselect = rendition.autoselect;\n return [];\n }\n }\n\n const track: PartiallyResolvedAudioTrack = {\n type: 'audio' as const,\n id: generateId(),\n url: rendition.uri ?? '',\n groupId: rendition.groupId,\n name: rendition.name,\n // Type-specific defaults (CMAF audio)\n mimeType: 'audio/mp4',\n bandwidth: 0, // Not available in multivariant for demuxed audio\n sampleRate: 48000, // CMAF default\n channels: rendition.channels ?? 2, // From EXT-X-MEDIA CHANNELS; stereo default\n codecs: [],\n };\n\n if (rendition.language) {\n track.language = rendition.language;\n }\n if (audioCodecs) {\n track.codecs = audioCodecs;\n }\n if (rendition.default) {\n track.default = rendition.default;\n }\n if (rendition.autoselect) {\n track.autoselect = rendition.autoselect;\n }\n\n return [track];\n });\n\n // Combine audio tracks from both EXT-X-MEDIA renditions and audio-only STREAM-INF\n const audioTracks = [...audioRenditionTracks, ...audioOnlyTracks];\n\n // Build PartiallyResolvedTextTracks from subtitle renditions\n const textTracks: PartiallyResolvedTextTrack[] = subtitleRenditions.map((rendition) => {\n const track: PartiallyResolvedTextTrack = {\n type: 'text' as const,\n id: generateId(),\n url: rendition.uri,\n groupId: rendition.groupId,\n label: rendition.name,\n kind: 'subtitles' as const,\n // Type-specific defaults (VTT)\n mimeType: 'text/vtt',\n bandwidth: 0, // Text tracks don't consume bandwidth\n };\n\n if (rendition.language) {\n track.language = rendition.language;\n }\n // Match hls.js/http-streaming: only set default=true when BOTH DEFAULT=YES AND AUTOSELECT=YES\n if (rendition.default && rendition.autoselect) {\n track.default = true;\n }\n if (rendition.autoselect) {\n track.autoselect = rendition.autoselect;\n }\n if (rendition.forced) {\n track.forced = rendition.forced;\n }\n\n return track;\n });\n\n // Build selection sets\n const selectionSets: SelectionSet[] = [];\n\n if (videoTracks.length > 0) {\n const videoSwitchingSet: VideoSwitchingSet = {\n id: generateId(),\n type: 'video',\n tracks: videoTracks,\n };\n\n const videoSelectionSet: VideoSelectionSet = {\n id: generateId(),\n type: 'video',\n switchingSets: [videoSwitchingSet],\n };\n\n selectionSets.push(videoSelectionSet);\n }\n\n if (audioTracks.length > 0) {\n const audioSwitchingSet: AudioSwitchingSet = {\n id: generateId(),\n type: 'audio',\n tracks: audioTracks,\n };\n\n const audioSelectionSet: AudioSelectionSet = {\n id: generateId(),\n type: 'audio',\n switchingSets: [audioSwitchingSet],\n };\n\n selectionSets.push(audioSelectionSet);\n }\n\n if (textTracks.length > 0) {\n const textSwitchingSet: TextSwitchingSet = {\n id: generateId(),\n type: 'text',\n tracks: textTracks,\n };\n\n const textSelectionSet: TextSelectionSet = {\n id: generateId(),\n type: 'text',\n switchingSets: [textSwitchingSet],\n };\n\n selectionSets.push(textSelectionSet);\n }\n\n // Build presentation (duration is undefined until tracks are resolved)\n return {\n id: generateId(),\n url: unresolved.url,\n startTime: 0,\n // duration: undefined, // Won't be known until after at least one media playlist is fetched + parsed\n selectionSets,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parse-multivariant.js","names":[],"sources":["../../../../src/media/hls/parse-multivariant.ts"],"sourcesContent":["import { generateId } from '@videojs/utils/string';\n\nimport type {\n AddressableObject,\n AudioSelectionSet,\n AudioSwitchingSet,\n FrameRate,\n PartiallyResolvedAudioTrack,\n PartiallyResolvedTextTrack,\n PartiallyResolvedVideoTrack,\n Presentation,\n SelectionSet,\n TextSelectionSet,\n TextSwitchingSet,\n VideoSelectionSet,\n VideoSwitchingSet,\n} from '../types';\nimport { matchTag, parseCodecs } from './parse-attributes';\nimport { resolveUrl } from './resolve-url';\n\n/**\n * Parse HLS multivariant playlist into a Presentation.\n *\n * Returns Presentation with partially resolved tracks (no segment information). Tracks contain metadata from\n * multivariant playlist (bandwidth, resolution, codecs) but segment information is added when media playlists are\n * fetched.\n *\n * @param text - Raw playlist text content\n * @param unresolved - Unresolved presentation (contains URL for base URL resolution)\n * @returns Presentation with partially resolved tracks (duration is undefined)\n */\nexport function parseMultivariantPlaylist(text: string, unresolved: AddressableObject): Presentation {\n const baseUrl = unresolved.url;\n const lines = text.split(/\\r?\\n/);\n\n // Intermediate parsing structures\n interface StreamInfo {\n uri: string;\n bandwidth: number;\n resolution?: { width: number; height: number } | undefined;\n codecs?: string | undefined;\n frameRate?: FrameRate | undefined;\n audioGroupId?: string | undefined;\n }\n\n interface AudioRenditionInfo {\n groupId: string;\n name: string;\n language?: string | undefined;\n uri?: string | undefined;\n default?: boolean | undefined;\n autoselect?: boolean | undefined;\n channels?: number | undefined;\n }\n\n interface SubtitleRenditionInfo {\n groupId: string;\n name: string;\n language?: string | undefined;\n uri: string;\n default?: boolean | undefined;\n autoselect?: boolean | undefined;\n forced?: boolean | undefined;\n }\n\n const streams: StreamInfo[] = [];\n const audioRenditions: AudioRenditionInfo[] = [];\n const subtitleRenditions: SubtitleRenditionInfo[] = [];\n\n // State for STREAM-INF parsing (URI follows on next line)\n let pendingStreamInfo: Omit<StreamInfo, 'uri'> | null = null;\n\n for (const line of lines) {\n const trimmed = line.trim();\n // Skip empty lines and comments\n if (!trimmed || (trimmed.startsWith('#') && !trimmed.startsWith('#EXT'))) continue;\n\n // Skip tags not used in Presentation model\n if (\n trimmed === '#EXTM3U' ||\n trimmed.startsWith('#EXT-X-VERSION:') ||\n trimmed.startsWith('#EXT-X-INDEPENDENT-SEGMENTS')\n ) {\n continue;\n }\n\n // #EXT-X-MEDIA:TYPE=AUDIO/SUBTITLES\n const mediaAttrs = matchTag(trimmed, 'EXT-X-MEDIA');\n\n if (mediaAttrs) {\n const type = mediaAttrs.get('TYPE');\n const groupId = mediaAttrs.get('GROUP-ID');\n const name = mediaAttrs.get('NAME');\n\n if (type === 'AUDIO' && groupId && name) {\n const uri = mediaAttrs.get('URI');\n\n audioRenditions.push({\n groupId,\n name,\n language: mediaAttrs.get('LANGUAGE'),\n uri: uri ? resolveUrl(uri, baseUrl) : undefined,\n default: mediaAttrs.getBool('DEFAULT'),\n autoselect: mediaAttrs.getBool('AUTOSELECT'),\n // CHANNELS is a quoted string whose first parameter is the channel\n // count (\"6\", or \"16/JOC\" for spatial audio); getInt reads the\n // leading integer.\n channels: mediaAttrs.getInt('CHANNELS'),\n });\n }\n\n if (type === 'SUBTITLES' && groupId && name) {\n const uri = mediaAttrs.get('URI');\n\n // URI is required for subtitle tracks\n if (uri) {\n subtitleRenditions.push({\n groupId,\n name,\n language: mediaAttrs.get('LANGUAGE'),\n uri: resolveUrl(uri, baseUrl),\n default: mediaAttrs.getBool('DEFAULT'),\n autoselect: mediaAttrs.getBool('AUTOSELECT'),\n forced: mediaAttrs.getBool('FORCED'),\n });\n }\n }\n\n continue;\n }\n\n // #EXT-X-STREAM-INF:BANDWIDTH=...\n const streamInfAttrs = matchTag(trimmed, 'EXT-X-STREAM-INF');\n\n if (streamInfAttrs) {\n pendingStreamInfo = {\n bandwidth: streamInfAttrs.getInt('BANDWIDTH', 0)!,\n resolution: streamInfAttrs.getResolution('RESOLUTION'),\n codecs: streamInfAttrs.get('CODECS'),\n frameRate: streamInfAttrs.getFrameRate('FRAME-RATE'),\n audioGroupId: streamInfAttrs.get('AUDIO'),\n };\n continue;\n }\n\n // URI line following STREAM-INF\n if (!trimmed.startsWith('#') && pendingStreamInfo) {\n streams.push({\n ...pendingStreamInfo,\n uri: resolveUrl(trimmed, baseUrl),\n });\n pendingStreamInfo = null;\n }\n }\n\n // Separate streams into video and audio based on codecs\n // If CODECS has single codec, use parseCodecs to determine type\n const videoStreams: typeof streams = [];\n const audioOnlyStreams: typeof streams = [];\n\n for (const stream of streams) {\n if (!stream.codecs) {\n // No codecs - assume video (default behavior)\n videoStreams.push(stream);\n continue;\n }\n\n const parsedCodecs = parseCodecs(stream.codecs);\n const codecCount = stream.codecs.split(',').length;\n\n // Single codec - determine type from parseCodecs result\n if (codecCount === 1) {\n if (parsedCodecs.audio && !parsedCodecs.video) {\n // Audio-only stream\n audioOnlyStreams.push(stream);\n } else {\n // Video stream (or unknown - default to video)\n videoStreams.push(stream);\n }\n } else {\n // Multiple codecs - video stream with muxed audio\n videoStreams.push(stream);\n }\n }\n\n // Build PartiallyResolvedVideoTracks from video streams, de-duplicating the\n // HLS cross-product: one video rendition is listed across several\n // `EXT-X-STREAM-INF` entries — one per audio group it can pair with, all\n // sharing the same media-playlist URI. Collapse them to one track per URI,\n // accumulating every advertised audio group. (Redundant-stream renditions\n // live at *distinct* per-CDN URIs, so they stay separate — only the same-URI\n // cross-product merges.)\n const videoTracksByUrl = new Map<string, PartiallyResolvedVideoTrack>();\n\n for (const stream of videoStreams) {\n const existing = videoTracksByUrl.get(stream.uri);\n\n if (existing) {\n if (stream.audioGroupId && !existing.audioGroupIds?.includes(stream.audioGroupId)) {\n existing.audioGroupIds = [...(existing.audioGroupIds ?? []), stream.audioGroupId];\n }\n\n // BANDWIDTH is video + audio combined; the duplicates differ only in the\n // paired audio. Keep the lowest as the closest proxy to video-only, which\n // is what ABR should rank on.\n if (stream.bandwidth < existing.bandwidth) {\n existing.bandwidth = stream.bandwidth;\n }\n\n continue;\n }\n\n const codecs = stream.codecs ? parseCodecs(stream.codecs) : undefined;\n\n const track: PartiallyResolvedVideoTrack = {\n type: 'video' as const,\n id: generateId(),\n url: stream.uri,\n bandwidth: stream.bandwidth,\n // Type-specific defaults (CMAF video)\n mimeType: 'video/mp4',\n codecs: [],\n };\n\n if (stream.resolution?.width !== undefined) {\n track.width = stream.resolution.width;\n }\n\n if (stream.resolution?.height !== undefined) {\n track.height = stream.resolution.height;\n }\n\n if (codecs?.video) {\n // When the STREAM-INF lists an audio codec but declares no AUDIO group,\n // the audio is muxed into this rendition's segments — keep both codecs so\n // the SourceBuffer mimetype matches the muxed media (otherwise the muxed\n // audio fails to append: \"audio object type does not match the mimetype\").\n // With an AUDIO group the audio is a separate rendition, so only the video\n // codec belongs here.\n track.codecs = codecs.audio && !stream.audioGroupId ? [codecs.video, codecs.audio] : [codecs.video];\n }\n\n if (stream.frameRate) {\n track.frameRate = stream.frameRate;\n }\n\n if (stream.audioGroupId) {\n track.audioGroupIds = [stream.audioGroupId];\n }\n\n videoTracksByUrl.set(stream.uri, track);\n }\n\n const videoTracks: PartiallyResolvedVideoTrack[] = [...videoTracksByUrl.values()];\n\n // Build PartiallyResolvedAudioTracks from audio-only streams\n const audioOnlyTracks: PartiallyResolvedAudioTrack[] = audioOnlyStreams.map((stream) => {\n const codecs = stream.codecs ? parseCodecs(stream.codecs) : undefined;\n\n const track: PartiallyResolvedAudioTrack = {\n type: 'audio' as const,\n id: generateId(),\n url: stream.uri,\n bandwidth: stream.bandwidth,\n mimeType: 'audio/mp4',\n codecs: codecs?.audio ? [codecs.audio] : [],\n groupId: stream.audioGroupId || 'default',\n name: 'Default',\n sampleRate: 48000, // Default - will be in media playlist if available\n channels: 2, // Default - will be in media playlist if available\n };\n\n return track;\n });\n\n // Build PartiallyResolvedAudioTracks from audio renditions (EXT-X-MEDIA)\n // Extract audio codecs from referencing streams\n const audioRenditionTracks: PartiallyResolvedAudioTrack[] = audioRenditions.flatMap((rendition) => {\n let audioCodecs: string[] | undefined;\n\n for (const stream of streams) {\n if (stream.audioGroupId === rendition.groupId && stream.codecs) {\n const codecs = parseCodecs(stream.codecs);\n\n if (codecs.audio) {\n audioCodecs = [codecs.audio];\n break;\n }\n }\n }\n\n // A rendition with no URI names media carried in the streams referencing its\n // group. When such a stream is audio-only it is already a track of its own, so\n // the two describe one rendition: merge rather than add a second entry that has\n // nothing to fetch. The rendition holds the naming and selection metadata, the\n // stream the URL and bandwidth.\n if (!rendition.uri) {\n const carrier = audioOnlyTracks.find((track) => track.groupId === rendition.groupId);\n\n if (carrier) {\n carrier.name = rendition.name;\n\n if (rendition.language) carrier.language = rendition.language;\n\n if (rendition.channels) carrier.channels = rendition.channels;\n\n if (rendition.default) carrier.default = rendition.default;\n\n if (rendition.autoselect) carrier.autoselect = rendition.autoselect;\n\n return [];\n }\n }\n\n const track: PartiallyResolvedAudioTrack = {\n type: 'audio' as const,\n id: generateId(),\n url: rendition.uri ?? '',\n groupId: rendition.groupId,\n name: rendition.name,\n // Type-specific defaults (CMAF audio)\n mimeType: 'audio/mp4',\n bandwidth: 0, // Not available in multivariant for demuxed audio\n sampleRate: 48000, // CMAF default\n channels: rendition.channels ?? 2, // From EXT-X-MEDIA CHANNELS; stereo default\n codecs: [],\n };\n\n if (rendition.language) {\n track.language = rendition.language;\n }\n\n if (audioCodecs) {\n track.codecs = audioCodecs;\n }\n\n if (rendition.default) {\n track.default = rendition.default;\n }\n\n if (rendition.autoselect) {\n track.autoselect = rendition.autoselect;\n }\n\n return [track];\n });\n\n // Combine audio tracks from both EXT-X-MEDIA renditions and audio-only STREAM-INF\n const audioTracks = [...audioRenditionTracks, ...audioOnlyTracks];\n\n // Build PartiallyResolvedTextTracks from subtitle renditions\n const textTracks: PartiallyResolvedTextTrack[] = subtitleRenditions.map((rendition) => {\n const track: PartiallyResolvedTextTrack = {\n type: 'text' as const,\n id: generateId(),\n url: rendition.uri,\n groupId: rendition.groupId,\n label: rendition.name,\n kind: 'subtitles' as const,\n // Type-specific defaults (VTT)\n mimeType: 'text/vtt',\n bandwidth: 0, // Text tracks don't consume bandwidth\n };\n\n if (rendition.language) {\n track.language = rendition.language;\n }\n\n // Match hls.js/http-streaming: only set default=true when BOTH DEFAULT=YES AND AUTOSELECT=YES\n if (rendition.default && rendition.autoselect) {\n track.default = true;\n }\n\n if (rendition.autoselect) {\n track.autoselect = rendition.autoselect;\n }\n\n if (rendition.forced) {\n track.forced = rendition.forced;\n }\n\n return track;\n });\n\n // Build selection sets\n const selectionSets: SelectionSet[] = [];\n\n if (videoTracks.length > 0) {\n const videoSwitchingSet: VideoSwitchingSet = {\n id: generateId(),\n type: 'video',\n tracks: videoTracks,\n };\n\n const videoSelectionSet: VideoSelectionSet = {\n id: generateId(),\n type: 'video',\n switchingSets: [videoSwitchingSet],\n };\n\n selectionSets.push(videoSelectionSet);\n }\n\n if (audioTracks.length > 0) {\n const audioSwitchingSet: AudioSwitchingSet = {\n id: generateId(),\n type: 'audio',\n tracks: audioTracks,\n };\n\n const audioSelectionSet: AudioSelectionSet = {\n id: generateId(),\n type: 'audio',\n switchingSets: [audioSwitchingSet],\n };\n\n selectionSets.push(audioSelectionSet);\n }\n\n if (textTracks.length > 0) {\n const textSwitchingSet: TextSwitchingSet = {\n id: generateId(),\n type: 'text',\n tracks: textTracks,\n };\n\n const textSelectionSet: TextSelectionSet = {\n id: generateId(),\n type: 'text',\n switchingSets: [textSwitchingSet],\n };\n\n selectionSets.push(textSelectionSet);\n }\n\n // Build presentation (duration is undefined until tracks are resolved)\n return {\n id: generateId(),\n url: unresolved.url,\n startTime: 0,\n // duration: undefined, // Won't be known until after at least one media playlist is fetched + parsed\n selectionSets,\n };\n}\n"],"mappings":"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"}
|
|
@@ -4,8 +4,8 @@ import { findTrackById } from "../utils/tracks.js";
|
|
|
4
4
|
/** Reload cadence when a playlist carries no usable target duration. */
|
|
5
5
|
const FALLBACK_TARGET_DURATION = 6;
|
|
6
6
|
/**
|
|
7
|
-
* Default `HOLD-BACK` as a multiple of the target duration when the playlist
|
|
8
|
-
*
|
|
7
|
+
* Default `HOLD-BACK` as a multiple of the target duration when the playlist declares none — the HLS spec default (RFC
|
|
8
|
+
* 8216bis `EXT-X-SERVER-CONTROL`).
|
|
9
9
|
*/
|
|
10
10
|
const HOLD_BACK_TARGET_MULTIPLIER = 3;
|
|
11
11
|
/** Identity of a reload snapshot — window position + length. Changes when the window slid or grew. */
|
|
@@ -16,21 +16,17 @@ function targetDurationOf(track) {
|
|
|
16
16
|
return getMediaPlaylistMetadata(track)?.targetDuration || FALLBACK_TARGET_DURATION;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Live media-playlist reload cadence, per RFC 8216bis §6.3.4 — a
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* importing it (media stays core-free): `current` is the freshly resolved track,
|
|
23
|
-
* `previous` the prior resolved snapshot.
|
|
19
|
+
* Live media-playlist reload cadence, per RFC 8216bis §6.3.4 — a {@link RecurrencePolicy} for a `RecurringRunner`
|
|
20
|
+
* re-resolving the selected track. Structurally matches `RecurrencePolicy<ResolvedTrack>` without importing it (media
|
|
21
|
+
* stays core-free): `current` is the freshly resolved track, `previous` the prior resolved snapshot.
|
|
24
22
|
*
|
|
25
|
-
* - Complete playlist (VoD, or live that hit `#EXT-X-ENDLIST`) → `null`: stop.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* at half the target duration; a moved/grown window (or the first reload) →
|
|
30
|
-
* full target duration.
|
|
23
|
+
* - Complete playlist (VoD, or live that hit `#EXT-X-ENDLIST`) → `null`: stop. Keys off `Track.duration` (finite once
|
|
24
|
+
* complete), the single completeness source of truth.
|
|
25
|
+
* - Unchanged window (same media sequence + segment count as `previous`) → poll at half the target duration; a
|
|
26
|
+
* moved/grown window (or the first reload) → full target duration.
|
|
31
27
|
*
|
|
32
|
-
* A failed reload doesn't reach here — the rejection propagates through the
|
|
33
|
-
*
|
|
28
|
+
* A failed reload doesn't reach here — the rejection propagates through the `RecurringRunner`; transient-failure
|
|
29
|
+
* recovery belongs at the fetch layer.
|
|
34
30
|
*
|
|
35
31
|
* Returned delays are milliseconds.
|
|
36
32
|
*/
|
|
@@ -40,24 +36,20 @@ function mediaPlaylistReloadDelay(current, previous) {
|
|
|
40
36
|
return (!previous || snapshotSignature(current) !== snapshotSignature(previous) ? target : target / 2) * 1e3;
|
|
41
37
|
}
|
|
42
38
|
/**
|
|
43
|
-
* Target live latency (seconds) for a resolved track — how far behind the live
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* spec default). This is the HLS side of the format-neutral
|
|
48
|
-
* `resolveLiveLatency` seam consumed by `seek-to-live-edge`; a DASH engine
|
|
49
|
-
* supplies its own (`suggestedPresentationDelay`).
|
|
39
|
+
* Target live latency (seconds) for a resolved track — how far behind the live edge the playhead should sit. Prefers
|
|
40
|
+
* the server's declared `EXT-X-SERVER-CONTROL` `HOLD-BACK`, falling back to {@link HOLD_BACK_TARGET_MULTIPLIER}× the
|
|
41
|
+
* target duration when absent (the spec default). This is the HLS side of the format-neutral `resolveLiveLatency` seam
|
|
42
|
+
* consumed by `seek-to-live-edge`; a DASH engine supplies its own (`suggestedPresentationDelay`).
|
|
50
43
|
*
|
|
51
|
-
* Only `HOLD-BACK` — never `PART-HOLD-BACK`, which assumes partial-segment
|
|
52
|
-
*
|
|
44
|
+
* Only `HOLD-BACK` — never `PART-HOLD-BACK`, which assumes partial-segment playback. See
|
|
45
|
+
* {@link MediaPlaylistMetadata.holdBack}.
|
|
53
46
|
*/
|
|
54
47
|
function liveLatencyFor(track) {
|
|
55
48
|
return getMediaPlaylistMetadata(track)?.holdBack ?? HOLD_BACK_TARGET_MULTIPLIER * targetDurationOf(track);
|
|
56
49
|
}
|
|
57
50
|
/**
|
|
58
|
-
* Resolve the target live latency for a presentation's timeline-bearing track —
|
|
59
|
-
*
|
|
60
|
-
* `resolveLiveLatency` seam. `0` when there is no resolved track to read (the
|
|
51
|
+
* Resolve the target live latency for a presentation's timeline-bearing track — the HLS engine injects this as
|
|
52
|
+
* `seekToLiveEdge`'s format-neutral `resolveLiveLatency` seam. `0` when there is no resolved track to read (the
|
|
61
53
|
* behavior then seeks straight to the edge).
|
|
62
54
|
*/
|
|
63
55
|
function resolveLiveLatency(presentation, trackId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reload-policy.js","names":[],"sources":["../../../../src/media/hls/reload-policy.ts"],"sourcesContent":["import {\n getMediaPlaylistMetadata,\n isResolvedPresentation,\n isResolvedTrack,\n type MaybeResolvedPresentation,\n type ResolvedTrack,\n} from '../types';\nimport { findTrackById } from '../utils/tracks';\n\n/** Reload cadence when a playlist carries no usable target duration. */\nconst FALLBACK_TARGET_DURATION = 6;\n\n/**\n * Default `HOLD-BACK` as a multiple of the target duration when the playlist
|
|
1
|
+
{"version":3,"file":"reload-policy.js","names":[],"sources":["../../../../src/media/hls/reload-policy.ts"],"sourcesContent":["import {\n getMediaPlaylistMetadata,\n isResolvedPresentation,\n isResolvedTrack,\n type MaybeResolvedPresentation,\n type ResolvedTrack,\n} from '../types';\nimport { findTrackById } from '../utils/tracks';\n\n/** Reload cadence when a playlist carries no usable target duration. */\nconst FALLBACK_TARGET_DURATION = 6;\n\n/**\n * Default `HOLD-BACK` as a multiple of the target duration when the playlist declares none — the HLS spec default (RFC\n * 8216bis `EXT-X-SERVER-CONTROL`).\n */\nconst HOLD_BACK_TARGET_MULTIPLIER = 3;\n\n/** Identity of a reload snapshot — window position + length. Changes when the window slid or grew. */\nfunction snapshotSignature(track: ResolvedTrack): string {\n return `${getMediaPlaylistMetadata(track)?.mediaSequence ?? 0}:${track.segments.length}`;\n}\n\nfunction targetDurationOf(track: ResolvedTrack): number {\n return getMediaPlaylistMetadata(track)?.targetDuration || FALLBACK_TARGET_DURATION;\n}\n\n/**\n * Live media-playlist reload cadence, per RFC 8216bis §6.3.4 — a {@link RecurrencePolicy} for a `RecurringRunner`\n * re-resolving the selected track. Structurally matches `RecurrencePolicy<ResolvedTrack>` without importing it (media\n * stays core-free): `current` is the freshly resolved track, `previous` the prior resolved snapshot.\n *\n * - Complete playlist (VoD, or live that hit `#EXT-X-ENDLIST`) → `null`: stop. Keys off `Track.duration` (finite once\n * complete), the single completeness source of truth.\n * - Unchanged window (same media sequence + segment count as `previous`) → poll at half the target duration; a\n * moved/grown window (or the first reload) → full target duration.\n *\n * A failed reload doesn't reach here — the rejection propagates through the `RecurringRunner`; transient-failure\n * recovery belongs at the fetch layer.\n *\n * Returned delays are milliseconds.\n */\nexport function mediaPlaylistReloadDelay(current: ResolvedTrack, previous: ResolvedTrack | undefined): number | null {\n if (Number.isFinite(current.duration)) return null;\n\n const target = targetDurationOf(current);\n const changed = !previous || snapshotSignature(current) !== snapshotSignature(previous);\n\n return (changed ? target : target / 2) * 1000;\n}\n\n/**\n * Target live latency (seconds) for a resolved track — how far behind the live edge the playhead should sit. Prefers\n * the server's declared `EXT-X-SERVER-CONTROL` `HOLD-BACK`, falling back to {@link HOLD_BACK_TARGET_MULTIPLIER}× the\n * target duration when absent (the spec default). This is the HLS side of the format-neutral `resolveLiveLatency` seam\n * consumed by `seek-to-live-edge`; a DASH engine supplies its own (`suggestedPresentationDelay`).\n *\n * Only `HOLD-BACK` — never `PART-HOLD-BACK`, which assumes partial-segment playback. See\n * {@link MediaPlaylistMetadata.holdBack}.\n */\nexport function liveLatencyFor(track: ResolvedTrack): number {\n return getMediaPlaylistMetadata(track)?.holdBack ?? HOLD_BACK_TARGET_MULTIPLIER * targetDurationOf(track);\n}\n\n/**\n * Resolve the target live latency for a presentation's timeline-bearing track — the HLS engine injects this as\n * `seekToLiveEdge`'s format-neutral `resolveLiveLatency` seam. `0` when there is no resolved track to read (the\n * behavior then seeks straight to the edge).\n */\nexport function resolveLiveLatency(\n presentation: MaybeResolvedPresentation | undefined,\n trackId: string | undefined\n): number {\n if (!isResolvedPresentation(presentation) || !trackId) return 0;\n\n const track = findTrackById(presentation, trackId);\n\n return track && isResolvedTrack(track) ? liveLatencyFor(track) : 0;\n}\n"],"mappings":";;;;AAUA,MAAM,2BAA2B;;;;;AAMjC,MAAM,8BAA8B;;AAGpC,SAAS,kBAAkB,OAA8B;CACvD,OAAO,GAAG,yBAAyB,KAAK,CAAC,EAAE,iBAAiB,EAAE,GAAG,MAAM,SAAS;AAClF;AAEA,SAAS,iBAAiB,OAA8B;CACtD,OAAO,yBAAyB,KAAK,CAAC,EAAE,kBAAkB;AAC5D;;;;;;;;;;;;;;;;AAiBA,SAAgB,yBAAyB,SAAwB,UAAoD;CACnH,IAAI,OAAO,SAAS,QAAQ,QAAQ,GAAG,OAAO;CAE9C,MAAM,SAAS,iBAAiB,OAAO;CAGvC,QAFgB,CAAC,YAAY,kBAAkB,OAAO,MAAM,kBAAkB,QAAQ,IAEpE,SAAS,SAAS,KAAK;AAC3C;;;;;;;;;;AAWA,SAAgB,eAAe,OAA8B;CAC3D,OAAO,yBAAyB,KAAK,CAAC,EAAE,YAAY,8BAA8B,iBAAiB,KAAK;AAC1G;;;;;;AAOA,SAAgB,mBACd,cACA,SACQ;CACR,IAAI,CAAC,uBAAuB,YAAY,KAAK,CAAC,SAAS,OAAO;CAE9D,MAAM,QAAQ,cAAc,cAAc,OAAO;CAEjD,OAAO,SAAS,gBAAgB,KAAK,IAAI,eAAe,KAAK,IAAI;AACnE"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
//#region src/media/hls/resolve-url.ts
|
|
2
|
-
/**
|
|
3
|
-
* Resolve a potentially relative URL against a base URL using native URL API.
|
|
4
|
-
*/
|
|
2
|
+
/** Resolve a potentially relative URL against a base URL using native URL API. */
|
|
5
3
|
function resolveUrl(url, baseUrl) {
|
|
6
4
|
return new URL(url, baseUrl).href;
|
|
7
5
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-url.js","names":[],"sources":["../../../../src/media/hls/resolve-url.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"resolve-url.js","names":[],"sources":["../../../../src/media/hls/resolve-url.ts"],"sourcesContent":["/** Resolve a potentially relative URL against a base URL using native URL API. */\nexport function resolveUrl(url: string, baseUrl: string): string {\n return new URL(url, baseUrl).href;\n}\n"],"mappings":";;AACA,SAAgB,WAAW,KAAa,SAAyB;CAC/D,OAAO,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC;AAC/B"}
|
|
@@ -2,21 +2,17 @@ import { isResolvedPresentation, isResolvedTrack } from "./types/index.js";
|
|
|
2
2
|
import { findTrackById } from "./utils/tracks.js";
|
|
3
3
|
//#region src/media/live-window.ts
|
|
4
4
|
/**
|
|
5
|
-
* Derive the live window of the track with the given id — the single source of
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* bears the timeline (video when present, else audio).
|
|
5
|
+
* Derive the live window of the track with the given id — the single source of truth for "where is live," consumed (via
|
|
6
|
+
* `liveWindowFromState`) by the seek-to-live-edge and live-seekable-range behaviors so neither re-derives (or
|
|
7
|
+
* re-presumes) the window shape. Type-agnostic: the caller decides which track bears the timeline (video when present,
|
|
8
|
+
* else audio).
|
|
10
9
|
*
|
|
11
|
-
* The window is a **live read** over the current `segments` array — never
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* here slides while the origin stays fixed (see
|
|
15
|
-
* `internal/design/spf/live-presentation-timeline-model.md`).
|
|
10
|
+
* The window is a **live read** over the current `segments` array — never stored on the track. The anchor triple is
|
|
11
|
+
* frozen per source; per-segment `startTime` values are re-derived against it on every reload, so the edge here slides
|
|
12
|
+
* while the origin stays fixed (see `internal/design/spf/live-presentation-timeline-model.md`).
|
|
16
13
|
*
|
|
17
|
-
* Returns `null` when there is no live edge to track: an unresolved
|
|
18
|
-
*
|
|
19
|
-
* (VoD, or live that has ended — a finite `Track.duration`).
|
|
14
|
+
* Returns `null` when there is no live edge to track: an unresolved presentation or track, a track with no segments, or
|
|
15
|
+
* a **complete** playlist (VoD, or live that has ended — a finite `Track.duration`).
|
|
20
16
|
*/
|
|
21
17
|
function liveWindowFor(presentation, trackId) {
|
|
22
18
|
if (!isResolvedPresentation(presentation) || !trackId) return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live-window.js","names":[],"sources":["../../../src/media/live-window.ts"],"sourcesContent":["/**\n * Derive the live window of the track with the given id — the single source of
|
|
1
|
+
{"version":3,"file":"live-window.js","names":[],"sources":["../../../src/media/live-window.ts"],"sourcesContent":["/**\n * Derive the live window of the track with the given id — the single source of truth for \"where is live,\" consumed (via\n * `liveWindowFromState`) by the seek-to-live-edge and live-seekable-range behaviors so neither re-derives (or\n * re-presumes) the window shape. Type-agnostic: the caller decides which track bears the timeline (video when present,\n * else audio).\n *\n * The window is a **live read** over the current `segments` array — never stored on the track. The anchor triple is\n * frozen per source; per-segment `startTime` values are re-derived against it on every reload, so the edge here slides\n * while the origin stays fixed (see `internal/design/spf/live-presentation-timeline-model.md`).\n *\n * Returns `null` when there is no live edge to track: an unresolved presentation or track, a track with no segments, or\n * a **complete** playlist (VoD, or live that has ended — a finite `Track.duration`).\n */\nimport { isResolvedPresentation, isResolvedTrack, type MaybeResolvedPresentation } from './types';\nimport { findTrackById } from './utils/tracks';\n\nexport interface LiveWindow {\n /** Earliest time still in the window (seconds, presentation timeline). */\n start: number;\n /** The live edge — latest time in the window (seconds). */\n end: number;\n}\n\nexport function liveWindowFor(\n presentation: MaybeResolvedPresentation | undefined,\n trackId: string | undefined\n): LiveWindow | null {\n if (!isResolvedPresentation(presentation) || !trackId) return null;\n\n const track = findTrackById(presentation, trackId);\n if (!track || !isResolvedTrack(track) || track.segments.length === 0) return null;\n\n // Complete playlist (VoD, or live that has ended) → no live edge. `Track.duration`\n // is the parser's completeness signal (finite = complete, Infinity = still growing).\n if (Number.isFinite(track.duration)) return null;\n\n const { segments } = track;\n const last = segments[segments.length - 1]!;\n\n return {\n start: segments[0]!.startTime,\n end: last.startTime + last.duration,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,SAAgB,cACd,cACA,SACmB;CACnB,IAAI,CAAC,uBAAuB,YAAY,KAAK,CAAC,SAAS,OAAO;CAE9D,MAAM,QAAQ,cAAc,cAAc,OAAO;CACjD,IAAI,CAAC,SAAS,CAAC,gBAAgB,KAAK,KAAK,MAAM,SAAS,WAAW,GAAG,OAAO;CAI7E,IAAI,OAAO,SAAS,MAAM,QAAQ,GAAG,OAAO;CAE5C,MAAM,EAAE,aAAa;CACrB,MAAM,OAAO,SAAS,SAAS,SAAS;CAExC,OAAO;EACL,OAAO,SAAS,EAAE,CAAE;EACpB,KAAK,KAAK,YAAY,KAAK;CAC7B;AACF"}
|
|
@@ -13,8 +13,8 @@ function dedupedVideoTracks(presentation) {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* The distinct audio tracks of a presentation, deduped by `language` + `name`
|
|
17
|
-
*
|
|
16
|
+
* The distinct audio tracks of a presentation, deduped by `language` + `name` (first occurrence wins). Returns `[]`
|
|
17
|
+
* when the presentation is unresolved or has no audio tracks.
|
|
18
18
|
*/
|
|
19
19
|
function dedupedAudioTracks(presentation) {
|
|
20
20
|
if (!presentation) return [];
|
|
@@ -24,11 +24,10 @@ function dedupedAudioTracks(presentation) {
|
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Find a video track by id, searching the same candidate set the engine resolves
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* isn't the representative {@link dedupedVideoTracks} kept.
|
|
27
|
+
* Find a video track by id, searching the same candidate set the engine resolves against ({@link dedupedVideoTracks}'s
|
|
28
|
+
* pre-dedupe source). Returns `undefined` when absent. Maps the engine's resolved `selectedVideoTrackId` back to its
|
|
29
|
+
* properties for `active` reflection — the resolved id may be a per-CDN copy that isn't the representative
|
|
30
|
+
* {@link dedupedVideoTracks} kept.
|
|
32
31
|
*/
|
|
33
32
|
function findVideoTrackById(presentation, id) {
|
|
34
33
|
if (!presentation || !id) return void 0;
|
|
@@ -42,16 +41,16 @@ function findAudioTrackById(presentation, id) {
|
|
|
42
41
|
return track?.type === "audio" ? track : void 0;
|
|
43
42
|
}
|
|
44
43
|
/**
|
|
45
|
-
* Shallow-equal two key objects by their own properties. Both come from the same
|
|
46
|
-
*
|
|
44
|
+
* Shallow-equal two key objects by their own properties. Both come from the same key builder, so they carry the same
|
|
45
|
+
* keys — a one-directional scan suffices.
|
|
47
46
|
*/
|
|
48
47
|
function sameKey(a, b) {
|
|
49
48
|
for (const attr in a) if (a[attr] !== b[attr]) return false;
|
|
50
49
|
return true;
|
|
51
50
|
}
|
|
52
51
|
/**
|
|
53
|
-
* Dedupe tracks by a key function, keeping the first occurrence of each key.
|
|
54
|
-
*
|
|
52
|
+
* Dedupe tracks by a key function, keeping the first occurrence of each key. Keys are compared field-by-field ({@link
|
|
53
|
+
* sameKey}).
|
|
55
54
|
*/
|
|
56
55
|
function dedupe({ tracks, keyFn }) {
|
|
57
56
|
const seen = [];
|
|
@@ -64,9 +63,7 @@ function dedupe({ tracks, keyFn }) {
|
|
|
64
63
|
}
|
|
65
64
|
return kept;
|
|
66
65
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Build a partial video track that can be used as `userVideoTrackSelection`.
|
|
69
|
-
*/
|
|
66
|
+
/** Build a partial video track that can be used as `userVideoTrackSelection`. */
|
|
70
67
|
function toUserVideoTrackSelection(rendition) {
|
|
71
68
|
return rendition ? {
|
|
72
69
|
width: rendition.width,
|
|
@@ -74,9 +71,7 @@ function toUserVideoTrackSelection(rendition) {
|
|
|
74
71
|
bandwidth: rendition.bandwidth
|
|
75
72
|
} : void 0;
|
|
76
73
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Build a partial audio track that can be used as a `userAudioTrackSelection`.
|
|
79
|
-
*/
|
|
74
|
+
/** Build a partial audio track that can be used as a `userAudioTrackSelection`. */
|
|
80
75
|
function toUserAudioTrackSelection(track) {
|
|
81
76
|
return track ? {
|
|
82
77
|
language: track.language,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-tracks.js","names":[],"sources":["../../../../src/media/media-tracks/media-tracks.ts"],"sourcesContent":["/**\n * Media-track translation utilities.\n *\n * Pure, DOM-free transforms from SPF's CMAF-HAM track model onto the deduped
|
|
1
|
+
{"version":3,"file":"media-tracks.js","names":[],"sources":["../../../../src/media/media-tracks/media-tracks.ts"],"sourcesContent":["/**\n * Media-track translation utilities.\n *\n * Pure, DOM-free transforms from SPF's CMAF-HAM track model onto the deduped lists a media-element adapter exposes\n * (video renditions, audio tracks), plus the selection-criteria builders that turn a chosen rendition/track back into a\n * `user*TrackSelection` partial the engine's track-switching reads.\n *\n * These return SPF _model vocabulary_ (`bandwidth`, `codecs: string[]`, `frameRate` as a rational `FrameRate`); the\n * consuming adapter owns the mapping (e.g. for DOM `bandwidth` -> `bitrate`, `codecs.join(',')` -> `codec`, `name` ->\n * `label`).\n *\n * Deduplication is by _properties_, never URL, so a multi-CDN source that lists the same rendition on several hosts\n * collapses to one entry: video renditions by `width` + `height` + `bandwidth`, audio tracks by `language` + `name`.\n * The selection builders emit those same properties as the match criteria, so selecting a collapsed entry re-selects,\n * for example, every underlying per-CDN track.\n */\n\nimport type { AudioTrack, FrameRate, MaybeResolvedPresentation, VideoTrack } from '../types';\nimport { findTrackById, getTracksByType } from '../utils/tracks';\n\nexport type { AudioTrack, VideoTrack };\n\n/** Properties that identify a distinct video rendition (multi-CDN copies share them). */\nexport interface VideoDedupeKey {\n width?: VideoTrack['width'];\n height?: VideoTrack['height'];\n bandwidth?: VideoTrack['bandwidth'];\n}\n\n/** Properties that identify a distinct audio track (multi-CDN copies share them). */\nexport interface AudioDedupeKey {\n language?: AudioTrack['language'];\n name?: AudioTrack['name'];\n}\n\n/**\n * The distinct video tracks of a presentation, deduped by {@link VideoDedupeKey} (first occurrence wins).\n *\n * Returns `[]` when the presentation is unresolved or has no video tracks.\n */\nexport function dedupedVideoTracks(presentation: MaybeResolvedPresentation | undefined): VideoTrack[] {\n if (!presentation) return [];\n\n return dedupe({\n tracks: getTracksByType(presentation, 'video') as readonly VideoTrack[],\n keyFn: toUserVideoTrackSelection,\n });\n}\n\n/**\n * The distinct audio tracks of a presentation, deduped by `language` + `name` (first occurrence wins). Returns `[]`\n * when the presentation is unresolved or has no audio tracks.\n */\nexport function dedupedAudioTracks(presentation: MaybeResolvedPresentation | undefined): AudioTrack[] {\n if (!presentation) return [];\n\n return dedupe({\n tracks: getTracksByType(presentation, 'audio') as readonly AudioTrack[],\n keyFn: toUserAudioTrackSelection,\n });\n}\n\n/**\n * Find a video track by id, searching the same candidate set the engine resolves against ({@link dedupedVideoTracks}'s\n * pre-dedupe source). Returns `undefined` when absent. Maps the engine's resolved `selectedVideoTrackId` back to its\n * properties for `active` reflection — the resolved id may be a per-CDN copy that isn't the representative\n * {@link dedupedVideoTracks} kept.\n */\nexport function findVideoTrackById(\n presentation: MaybeResolvedPresentation | undefined,\n id: string | undefined\n): VideoTrack | undefined {\n if (!presentation || !id) return undefined;\n\n const track = findTrackById(presentation, id);\n\n return track?.type === 'video' ? (track as VideoTrack) : undefined;\n}\n\n/** Audio counterpart of {@link findVideoTrackById}, for `enabled` reflection. */\nexport function findAudioTrackById(\n presentation: MaybeResolvedPresentation | undefined,\n id: string | undefined\n): AudioTrack | undefined {\n if (!presentation || !id) return undefined;\n\n const track = findTrackById(presentation, id);\n\n return track?.type === 'audio' ? (track as AudioTrack) : undefined;\n}\n\n/**\n * Shallow-equal two key objects by their own properties. Both come from the same key builder, so they carry the same\n * keys — a one-directional scan suffices.\n */\nfunction sameKey<K extends object>(a: K, b: K): boolean {\n for (const attr in a) {\n if (a[attr] !== b[attr]) return false;\n }\n\n return true;\n}\n\n/**\n * Dedupe tracks by a key function, keeping the first occurrence of each key. Keys are compared field-by-field ({@link\n * sameKey}).\n */\nfunction dedupe<T, K extends object>({\n tracks,\n keyFn,\n}: {\n tracks: readonly T[];\n keyFn: (track: T) => K | undefined;\n}): T[] {\n const seen: K[] = [];\n const kept: T[] = [];\n\n for (const track of tracks) {\n const key = keyFn(track);\n if (!key || seen.some((other) => sameKey(other, key))) continue;\n\n seen.push(key);\n kept.push(track);\n }\n\n return kept;\n}\n\n/** Build a partial video track that can be used as `userVideoTrackSelection`. */\nexport function toUserVideoTrackSelection<T extends VideoDedupeKey>(rendition?: T): Partial<VideoTrack> | undefined {\n return rendition ? { width: rendition.width, height: rendition.height, bandwidth: rendition.bandwidth } : undefined;\n}\n\n/** Build a partial audio track that can be used as a `userAudioTrackSelection`. */\nexport function toUserAudioTrackSelection<T extends AudioDedupeKey>(track?: T): Partial<AudioTrack> | undefined {\n return track ? { language: track.language, name: track.name } : undefined;\n}\n\n/** Whether two video tracks are the same by dedupe key */\nexport function isSameVideoTrack(a: VideoDedupeKey, b: VideoDedupeKey | undefined): boolean {\n return !!b && a.width === b.width && a.height === b.height && a.bandwidth === b.bandwidth;\n}\n\n/** Whether two audio tracks are the same by dedupe key */\nexport function isSameAudioTrack(a: AudioDedupeKey, b: AudioDedupeKey | undefined): boolean {\n return !!b && (a.language ?? '') === (b.language ?? '') && a.name === b.name;\n}\n\n/** Collapse a rational frame rate (numerator/denominator) to frames per second. */\nexport const frameRateToNumber = (frameRate: FrameRate) => {\n return frameRate.frameRateNumerator / (frameRate.frameRateDenominator ?? 1);\n};\n"],"mappings":";;;;;;;AAwCA,SAAgB,mBAAmB,cAAmE;CACpG,IAAI,CAAC,cAAc,OAAO,CAAC;CAE3B,OAAO,OAAO;EACZ,QAAQ,gBAAgB,cAAc,OAAO;EAC7C,OAAO;CACT,CAAC;AACH;;;;;AAMA,SAAgB,mBAAmB,cAAmE;CACpG,IAAI,CAAC,cAAc,OAAO,CAAC;CAE3B,OAAO,OAAO;EACZ,QAAQ,gBAAgB,cAAc,OAAO;EAC7C,OAAO;CACT,CAAC;AACH;;;;;;;AAQA,SAAgB,mBACd,cACA,IACwB;CACxB,IAAI,CAAC,gBAAgB,CAAC,IAAI,OAAO,KAAA;CAEjC,MAAM,QAAQ,cAAc,cAAc,EAAE;CAE5C,OAAO,OAAO,SAAS,UAAW,QAAuB,KAAA;AAC3D;;AAGA,SAAgB,mBACd,cACA,IACwB;CACxB,IAAI,CAAC,gBAAgB,CAAC,IAAI,OAAO,KAAA;CAEjC,MAAM,QAAQ,cAAc,cAAc,EAAE;CAE5C,OAAO,OAAO,SAAS,UAAW,QAAuB,KAAA;AAC3D;;;;;AAMA,SAAS,QAA0B,GAAM,GAAe;CACtD,KAAK,MAAM,QAAQ,GACjB,IAAI,EAAE,UAAU,EAAE,OAAO,OAAO;CAGlC,OAAO;AACT;;;;;AAMA,SAAS,OAA4B,EACnC,QACA,SAIM;CACN,MAAM,OAAY,CAAC;CACnB,MAAM,OAAY,CAAC;CAEnB,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM,KAAK;EACvB,IAAI,CAAC,OAAO,KAAK,MAAM,UAAU,QAAQ,OAAO,GAAG,CAAC,GAAG;EAEvD,KAAK,KAAK,GAAG;EACb,KAAK,KAAK,KAAK;CACjB;CAEA,OAAO;AACT;;AAGA,SAAgB,0BAAoD,WAAgD;CAClH,OAAO,YAAY;EAAE,OAAO,UAAU;EAAO,QAAQ,UAAU;EAAQ,WAAW,UAAU;CAAU,IAAI,KAAA;AAC5G;;AAGA,SAAgB,0BAAoD,OAA4C;CAC9G,OAAO,QAAQ;EAAE,UAAU,MAAM;EAAU,MAAM,MAAM;CAAK,IAAI,KAAA;AAClE;;AAGA,SAAgB,iBAAiB,GAAmB,GAAwC;CAC1F,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE;AAClF;;AAGA,SAAgB,iBAAiB,GAAmB,GAAwC;CAC1F,OAAO,CAAC,CAAC,MAAM,EAAE,YAAY,SAAS,EAAE,YAAY,OAAO,EAAE,SAAS,EAAE;AAC1E;;AAGA,MAAa,qBAAqB,cAAyB;CACzD,OAAO,UAAU,sBAAsB,UAAU,wBAAwB;AAC3E"}
|
|
@@ -32,9 +32,8 @@ function* iterateBoxesOfType(view, type, start = 0, end = view.byteLength) {
|
|
|
32
32
|
for (const box of iterateBoxes(view, start, end)) if (box.type === type) yield box;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* Depth-first descent to the first box matching a nested path, e.g.
|
|
36
|
-
* `
|
|
37
|
-
* absent.
|
|
35
|
+
* Depth-first descent to the first box matching a nested path, e.g. `['moov', 'trak', 'mdia', 'mdhd']`. Returns
|
|
36
|
+
* `undefined` if any level is absent.
|
|
38
37
|
*/
|
|
39
38
|
function findBox(view, path, start = 0, end = view.byteLength) {
|
|
40
39
|
const [head, ...rest] = path;
|
|
@@ -46,8 +45,8 @@ function findBox(view, path, start = 0, end = view.byteLength) {
|
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
/**
|
|
49
|
-
* Read the version byte of a FullBox — the `version(1) + flags(3)` header at the
|
|
50
|
-
*
|
|
48
|
+
* Read the version byte of a FullBox — the `version(1) + flags(3)` header at the start of the payload of `mdhd` /
|
|
49
|
+
* `tkhd` / `tfdt` / `hdlr` / `elst` / etc.
|
|
51
50
|
*/
|
|
52
51
|
function readFullBoxVersion(view, dataStart) {
|
|
53
52
|
return view.getUint8(dataStart);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.js","names":[],"sources":["../../../../src/media/mp4/box.ts"],"sourcesContent":["/**\n * Minimal ISO-BMFF (MP4/CMAF) box walker.\n *\n * Just enough to locate boxes by nested path — the framework needs a couple of
|
|
1
|
+
{"version":3,"file":"box.js","names":[],"sources":["../../../../src/media/mp4/box.ts"],"sourcesContent":["/**\n * Minimal ISO-BMFF (MP4/CMAF) box walker.\n *\n * Just enough to locate boxes by nested path — the framework needs a couple of leaf fields (`mdhd` timescale, `tfdt`\n * baseMediaDecodeTime) to derive a segment's decode-time origin, not a full demuxer. DOM-free: operates on an\n * `ArrayBuffer` / `Uint8Array` via `DataView`.\n *\n * Box layout: `[u32 size][u32 type][payload]`. `size === 1` means a `u64 largesize` follows the type (payload after\n * it); `size === 0` means the box runs to the end of its container.\n */\n\n/** A located box: its 4-char type and byte offsets within the buffer. */\nexport interface Box {\n type: string;\n /** Offset of the box's first byte (its size field). */\n start: number;\n /** Offset of the box's payload — after `size` + `type` (+ `largesize`). */\n dataStart: number;\n /** Offset one past the box's last byte. */\n end: number;\n}\n\nexport function toDataView(data: ArrayBuffer | Uint8Array): DataView {\n return data instanceof Uint8Array ? new DataView(data.buffer, data.byteOffset, data.byteLength) : new DataView(data);\n}\n\n/** Read a 4-character box type / FourCC at `offset`. */\nexport function readFourCC(view: DataView, offset: number): string {\n return String.fromCharCode(\n view.getUint8(offset),\n view.getUint8(offset + 1),\n view.getUint8(offset + 2),\n view.getUint8(offset + 3)\n );\n}\n\n/** Iterate the boxes directly contained in `[start, end)`. */\nexport function* iterateBoxes(view: DataView, start = 0, end = view.byteLength): Generator<Box> {\n let offset = start;\n\n while (offset + 8 <= end) {\n let size = view.getUint32(offset);\n const type = readFourCC(view, offset + 4);\n let dataStart = offset + 8;\n\n if (size === 1) {\n size = Number(view.getBigUint64(offset + 8));\n dataStart = offset + 16;\n } else if (size === 0) {\n size = end - offset;\n }\n\n // A size smaller than its own header is malformed — stop rather than loop.\n if (size < dataStart - offset) return;\n\n yield { type, start: offset, dataStart, end: offset + size };\n offset += size;\n }\n}\n\n/** Iterate the direct child boxes of a given `type` within `[start, end)`. */\nexport function* iterateBoxesOfType(view: DataView, type: string, start = 0, end = view.byteLength): Generator<Box> {\n for (const box of iterateBoxes(view, start, end)) {\n if (box.type === type) yield box;\n }\n}\n\n/**\n * Depth-first descent to the first box matching a nested path, e.g. `['moov', 'trak', 'mdia', 'mdhd']`. Returns\n * `undefined` if any level is absent.\n */\nexport function findBox(view: DataView, path: readonly string[], start = 0, end = view.byteLength): Box | undefined {\n const [head, ...rest] = path;\n\n for (const box of iterateBoxes(view, start, end)) {\n if (box.type !== head) continue;\n\n if (rest.length === 0) return box;\n\n const found = findBox(view, rest, box.dataStart, box.end);\n if (found) return found;\n }\n\n return undefined;\n}\n\n/**\n * Read the version byte of a FullBox — the `version(1) + flags(3)` header at the start of the payload of `mdhd` /\n * `tkhd` / `tfdt` / `hdlr` / `elst` / etc.\n */\nexport function readFullBoxVersion(view: DataView, dataStart: number): number {\n return view.getUint8(dataStart);\n}\n"],"mappings":";AAsBA,SAAgB,WAAW,MAA0C;CACnE,OAAO,gBAAgB,aAAa,IAAI,SAAS,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,IAAI,IAAI,SAAS,IAAI;AACrH;;AAGA,SAAgB,WAAW,MAAgB,QAAwB;CACjE,OAAO,OAAO,aACZ,KAAK,SAAS,MAAM,GACpB,KAAK,SAAS,SAAS,CAAC,GACxB,KAAK,SAAS,SAAS,CAAC,GACxB,KAAK,SAAS,SAAS,CAAC,CAC1B;AACF;;AAGA,UAAiB,aAAa,MAAgB,QAAQ,GAAG,MAAM,KAAK,YAA4B;CAC9F,IAAI,SAAS;CAEb,OAAO,SAAS,KAAK,KAAK;EACxB,IAAI,OAAO,KAAK,UAAU,MAAM;EAChC,MAAM,OAAO,WAAW,MAAM,SAAS,CAAC;EACxC,IAAI,YAAY,SAAS;EAEzB,IAAI,SAAS,GAAG;GACd,OAAO,OAAO,KAAK,aAAa,SAAS,CAAC,CAAC;GAC3C,YAAY,SAAS;EACvB,OAAO,IAAI,SAAS,GAClB,OAAO,MAAM;EAIf,IAAI,OAAO,YAAY,QAAQ;EAE/B,MAAM;GAAE;GAAM,OAAO;GAAQ;GAAW,KAAK,SAAS;EAAK;EAC3D,UAAU;CACZ;AACF;;AAGA,UAAiB,mBAAmB,MAAgB,MAAc,QAAQ,GAAG,MAAM,KAAK,YAA4B;CAClH,KAAK,MAAM,OAAO,aAAa,MAAM,OAAO,GAAG,GAC7C,IAAI,IAAI,SAAS,MAAM,MAAM;AAEjC;;;;;AAMA,SAAgB,QAAQ,MAAgB,MAAyB,QAAQ,GAAG,MAAM,KAAK,YAA6B;CAClH,MAAM,CAAC,MAAM,GAAG,QAAQ;CAExB,KAAK,MAAM,OAAO,aAAa,MAAM,OAAO,GAAG,GAAG;EAChD,IAAI,IAAI,SAAS,MAAM;EAEvB,IAAI,KAAK,WAAW,GAAG,OAAO;EAE9B,MAAM,QAAQ,QAAQ,MAAM,MAAM,IAAI,WAAW,IAAI,GAAG;EACxD,IAAI,OAAO,OAAO;CACpB;AAGF;;;;;AAMA,SAAgB,mBAAmB,MAAgB,WAA2B;CAC5E,OAAO,KAAK,SAAS,SAAS;AAChC"}
|
|
@@ -3,54 +3,43 @@ import { findBox, iterateBoxesOfType, readFourCC, readFullBoxVersion, toDataView
|
|
|
3
3
|
/**
|
|
4
4
|
* Decode-time origin extraction from fMP4/CMAF segments.
|
|
5
5
|
*
|
|
6
|
-
* Non-zero-PTS sources encode media at a non-zero start time (an instant clip
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* **decode-time origin** — read straight from the container:
|
|
6
|
+
* Non-zero-PTS sources encode media at a non-zero start time (an instant clip starting at asset second 60, an Apple
|
|
7
|
+
* bipbop asset starting at 10s, …). To relocate such a source onto a 0-based presentation timeline via
|
|
8
|
+
* `SourceBuffer.timestampOffset`, the engine needs that start time — the **decode-time origin** — read straight from
|
|
9
|
+
* the container:
|
|
11
10
|
*
|
|
12
|
-
* - `tfdt.baseMediaDecodeTime` (from a media segment's `moof`) — the decode
|
|
13
|
-
*
|
|
14
|
-
* a DTS
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* - `mdhd.timescale` (from the init segment's `moov`) — ticks per second for
|
|
18
|
-
* that track, needed to convert the raw tick count to seconds.
|
|
11
|
+
* - `tfdt.baseMediaDecodeTime` (from a media segment's `moof`) — the decode time of the segment's first sample, in the
|
|
12
|
+
* track's media timescale. This is a DTS: relocating by `−(baseMediaDecodeTime / timescale)` lands the earliest DTS
|
|
13
|
+
* at exactly 0, so a negative-DTS append failure on Chromium is impossible by construction.
|
|
14
|
+
* - `mdhd.timescale` (from the init segment's `moov`) — ticks per second for that track, needed to convert the raw tick
|
|
15
|
+
* count to seconds.
|
|
19
16
|
*
|
|
20
17
|
* ## Presumptive vs. track-selected reads
|
|
21
18
|
*
|
|
22
|
-
* The two variants share only the leaf field-readers (`mdhd` timescale, `tfdt`
|
|
23
|
-
*
|
|
24
|
-
* split is deliberate so the presumptive pair is *proportionally* smaller under
|
|
19
|
+
* The two variants share only the leaf field-readers (`mdhd` timescale, `tfdt` baseMediaDecodeTime) and the box walker.
|
|
20
|
+
* Everything else differs, and the split is deliberate so the presumptive pair is _proportionally_ smaller under
|
|
25
21
|
* tree-shaking:
|
|
26
22
|
*
|
|
27
|
-
* - **Presumptive** — {@link readFirstMediaTimescale} / {@link readFirstBaseMediaDecodeTime}
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* `
|
|
35
|
-
*
|
|
36
|
-
* `
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* presumptive read there risks `300000 / 6000 = 50s` instead of
|
|
41
|
-
* `60000 / 6000 = 10s`). This pair adds `trak`/`traf` iteration plus the handler
|
|
42
|
-
* and `track_id` reads. We only ever ask for buffered media handlers
|
|
43
|
-
* (`vide` / `soun`); the caption track is never selected — we read the origin,
|
|
44
|
-
* not the captions (caption *rendering* is out of scope).
|
|
23
|
+
* - **Presumptive** — {@link readFirstMediaTimescale} / {@link readFirstBaseMediaDecodeTime} read the first `mdhd`
|
|
24
|
+
* timescale and first `tfdt` baseMediaDecodeTime. There's no `track_id` (nothing to match against) and no
|
|
25
|
+
* `trak`/`traf` iteration — a direct `findBox` to the first leaf. Correct when the init/segment holds a single media
|
|
26
|
+
* track (the common CMAF case). A caption-free platform imports only this pair and tree-shakes away all the matching
|
|
27
|
+
* machinery below.
|
|
28
|
+
* - **Track-selected** — {@link findMediaTrack} / {@link readBaseMediaDecodeTime}. `findMediaTrack` returns `{ trackId,
|
|
29
|
+
* timescale }` for the `trak` whose `hdlr` handler matches; `readBaseMediaDecodeTime` takes that `trackId` and reads
|
|
30
|
+
* the `traf` whose `tfhd.track_id` matches. The `track_id` is the join that ties one track's timescale to the _same_
|
|
31
|
+
* track's baseMediaDecodeTime — required when a source muxes CEA-608/708 captions (a `clcp` track shares the same
|
|
32
|
+
* `moov` and `moof`, each track with its own timescale + baseMediaDecodeTime, so a presumptive read there risks
|
|
33
|
+
* `300000 / 6000 = 50s` instead of `60000 / 6000 = 10s`). This pair adds `trak`/`traf` iteration plus the handler and
|
|
34
|
+
* `track_id` reads. We only ever ask for buffered media handlers (`vide` / `soun`); the caption track is never
|
|
35
|
+
* selected — we read the origin, not the captions (caption _rendering_ is out of scope).
|
|
45
36
|
*
|
|
46
|
-
* Both raw values are returned un-divided to leave room for an edit-list (`elst`)
|
|
47
|
-
*
|
|
48
|
-
* streams do not.
|
|
37
|
+
* Both raw values are returned un-divided to leave room for an edit-list (`elst`) presentation-time correction term if
|
|
38
|
+
* a source ever carries one — the validated streams do not.
|
|
49
39
|
*/
|
|
50
40
|
/**
|
|
51
|
-
* The `track_ID` + timescale of the `trak` whose `mdhd` handler matches
|
|
52
|
-
*
|
|
53
|
-
* matching track exists.
|
|
41
|
+
* The `track_ID` + timescale of the `trak` whose `mdhd` handler matches `handlerType`, skipping a muxed `clcp` caption
|
|
42
|
+
* track. `undefined` if no matching track exists.
|
|
54
43
|
*/
|
|
55
44
|
function findMediaTrack(initSegment, handlerType) {
|
|
56
45
|
const view = toDataView(initSegment);
|
|
@@ -68,9 +57,8 @@ function findMediaTrack(initSegment, handlerType) {
|
|
|
68
57
|
}
|
|
69
58
|
}
|
|
70
59
|
/**
|
|
71
|
-
* `baseMediaDecodeTime` (in the track's media timescale) from the `traf` matching
|
|
72
|
-
*
|
|
73
|
-
* `undefined` if no matching `tfdt` exists.
|
|
60
|
+
* `baseMediaDecodeTime` (in the track's media timescale) from the `traf` matching `trackId` (via `tfhd.track_id`) —
|
|
61
|
+
* required for muxed multi-`traf` segments. `undefined` if no matching `tfdt` exists.
|
|
74
62
|
*/
|
|
75
63
|
function readBaseMediaDecodeTime(mediaSegment, trackId) {
|
|
76
64
|
const view = toDataView(mediaSegment);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timestamp-origin.js","names":[],"sources":["../../../../src/media/mp4/timestamp-origin.ts"],"sourcesContent":["/**\n * Decode-time origin extraction from fMP4/CMAF segments.\n *\n * Non-zero-PTS sources encode media at a non-zero start time (an instant clip
|
|
1
|
+
{"version":3,"file":"timestamp-origin.js","names":[],"sources":["../../../../src/media/mp4/timestamp-origin.ts"],"sourcesContent":["/**\n * Decode-time origin extraction from fMP4/CMAF segments.\n *\n * Non-zero-PTS sources encode media at a non-zero start time (an instant clip starting at asset second 60, an Apple\n * bipbop asset starting at 10s, …). To relocate such a source onto a 0-based presentation timeline via\n * `SourceBuffer.timestampOffset`, the engine needs that start time — the **decode-time origin** — read straight from\n * the container:\n *\n * - `tfdt.baseMediaDecodeTime` (from a media segment's `moof`) — the decode time of the segment's first sample, in the\n * track's media timescale. This is a DTS: relocating by `−(baseMediaDecodeTime / timescale)` lands the earliest DTS\n * at exactly 0, so a negative-DTS append failure on Chromium is impossible by construction.\n * - `mdhd.timescale` (from the init segment's `moov`) — ticks per second for that track, needed to convert the raw tick\n * count to seconds.\n *\n * ## Presumptive vs. track-selected reads\n *\n * The two variants share only the leaf field-readers (`mdhd` timescale, `tfdt` baseMediaDecodeTime) and the box walker.\n * Everything else differs, and the split is deliberate so the presumptive pair is _proportionally_ smaller under\n * tree-shaking:\n *\n * - **Presumptive** — {@link readFirstMediaTimescale} / {@link readFirstBaseMediaDecodeTime} read the first `mdhd`\n * timescale and first `tfdt` baseMediaDecodeTime. There's no `track_id` (nothing to match against) and no\n * `trak`/`traf` iteration — a direct `findBox` to the first leaf. Correct when the init/segment holds a single media\n * track (the common CMAF case). A caption-free platform imports only this pair and tree-shakes away all the matching\n * machinery below.\n * - **Track-selected** — {@link findMediaTrack} / {@link readBaseMediaDecodeTime}. `findMediaTrack` returns `{ trackId,\n * timescale }` for the `trak` whose `hdlr` handler matches; `readBaseMediaDecodeTime` takes that `trackId` and reads\n * the `traf` whose `tfhd.track_id` matches. The `track_id` is the join that ties one track's timescale to the _same_\n * track's baseMediaDecodeTime — required when a source muxes CEA-608/708 captions (a `clcp` track shares the same\n * `moov` and `moof`, each track with its own timescale + baseMediaDecodeTime, so a presumptive read there risks\n * `300000 / 6000 = 50s` instead of `60000 / 6000 = 10s`). This pair adds `trak`/`traf` iteration plus the handler and\n * `track_id` reads. We only ever ask for buffered media handlers (`vide` / `soun`); the caption track is never\n * selected — we read the origin, not the captions (caption _rendering_ is out of scope).\n *\n * Both raw values are returned un-divided to leave room for an edit-list (`elst`) presentation-time correction term if\n * a source ever carries one — the validated streams do not.\n */\nimport { type Box, findBox, iterateBoxesOfType, readFourCC, readFullBoxVersion, toDataView } from './box';\n\n/** MSE-buffered media handler types (`mdhd`/`hdlr`). Captions/subtitles excluded. */\nexport type MediaHandlerType = 'vide' | 'soun';\n\nexport interface MediaTrackInfo {\n /** `tkhd.track_id` — used to match the corresponding `traf` in media segments. */\n trackId: number;\n /** `mdhd.timescale` — ticks per second for this track. */\n timescale: number;\n}\n\n// --- presumptive: first leaf, no track_id, no iteration -----------------------\n\n/**\n * Presumptive: the `timescale` of the **first** `mdhd` in an init segment. Correct only for single-media-track inits —\n * for muxed captions use {@link findMediaTrack}. `undefined` if no `mdhd` exists.\n */\nexport function readFirstMediaTimescale(initSegment: ArrayBuffer | Uint8Array): number | undefined {\n const view = toDataView(initSegment);\n const mdhd = findBox(view, ['moov', 'trak', 'mdia', 'mdhd']);\n\n return mdhd ? readMdhdTimescale(view, mdhd) : undefined;\n}\n\n/**\n * Presumptive: `baseMediaDecodeTime` from the **first** `tfdt` of a media segment. Correct only for single-`traf`\n * segments — for muxed captions use {@link readBaseMediaDecodeTime}. `undefined` if no `tfdt` exists.\n */\nexport function readFirstBaseMediaDecodeTime(mediaSegment: ArrayBuffer | Uint8Array): number | undefined {\n const view = toDataView(mediaSegment);\n const tfdt = findBox(view, ['moof', 'traf', 'tfdt']);\n\n return tfdt ? readTfdtBaseMediaDecodeTime(view, tfdt) : undefined;\n}\n\n// --- track-selected: iterate + match by handler / track_id --------------------\n\n/**\n * The `track_ID` + timescale of the `trak` whose `mdhd` handler matches `handlerType`, skipping a muxed `clcp` caption\n * track. `undefined` if no matching track exists.\n */\nexport function findMediaTrack(\n initSegment: ArrayBuffer | Uint8Array,\n handlerType: MediaHandlerType\n): MediaTrackInfo | undefined {\n const view = toDataView(initSegment);\n const moov = findBox(view, ['moov']);\n if (!moov) return undefined;\n\n for (const trak of iterateBoxesOfType(view, 'trak', moov.dataStart, moov.end)) {\n if (readTrakHandler(view, trak) !== handlerType) continue;\n\n const tkhd = findBox(view, ['tkhd'], trak.dataStart, trak.end);\n const mdhd = findBox(view, ['mdia', 'mdhd'], trak.dataStart, trak.end);\n if (!tkhd || !mdhd) return undefined;\n\n // tkhd FullBox: version(1)+flags(3), creation/modification dates (v0: 4+4, v1:\n // 8+8), then track_id.\n const trackId = view.getUint32(tkhd.dataStart + 4 + (readFullBoxVersion(view, tkhd.dataStart) === 1 ? 16 : 8));\n\n return { trackId, timescale: readMdhdTimescale(view, mdhd) };\n }\n\n return undefined;\n}\n\n/**\n * `baseMediaDecodeTime` (in the track's media timescale) from the `traf` matching `trackId` (via `tfhd.track_id`) —\n * required for muxed multi-`traf` segments. `undefined` if no matching `tfdt` exists.\n */\nexport function readBaseMediaDecodeTime(mediaSegment: ArrayBuffer | Uint8Array, trackId: number): number | undefined {\n const view = toDataView(mediaSegment);\n const moof = findBox(view, ['moof']);\n if (!moof) return undefined;\n\n for (const traf of iterateBoxesOfType(view, 'traf', moof.dataStart, moof.end)) {\n if (readTrafTrackId(view, traf) !== trackId) continue;\n\n const tfdt = findBox(view, ['tfdt'], traf.dataStart, traf.end);\n\n return tfdt ? readTfdtBaseMediaDecodeTime(view, tfdt) : undefined;\n }\n\n return undefined;\n}\n\n// --- shared leaf field-readers ------------------------------------------------\n\n/** `mdhd.timescale`: FullBox version(1)+flags(3) + dates (v0: 4+4, v1: 8+8) + timescale. */\nfunction readMdhdTimescale(view: DataView, mdhd: Box): number {\n return view.getUint32(mdhd.dataStart + 4 + (readFullBoxVersion(view, mdhd.dataStart) === 1 ? 16 : 8));\n}\n\n/** `tfdt.baseMediaDecodeTime`: FullBox, then the value — v0: (4), v1: (8). */\nfunction readTfdtBaseMediaDecodeTime(view: DataView, tfdt: Box): number {\n const at = tfdt.dataStart + 4;\n\n return readFullBoxVersion(view, tfdt.dataStart) === 1 ? Number(view.getBigUint64(at)) : view.getUint32(at);\n}\n\n// --- track-selection readers (referenced only by the track-selected variants) -\n\n/** `hdlr.handler_type` for a `trak`: FullBox version(1)+flags(3) + pre_defined(4) + handler_type(4). */\nfunction readTrakHandler(view: DataView, trak: Box): string | undefined {\n const hdlr = findBox(view, ['mdia', 'hdlr'], trak.dataStart, trak.end);\n\n return hdlr ? readFourCC(view, hdlr.dataStart + 8) : undefined;\n}\n\n/** `tfhd.track_id` for a `traf`: FullBox version(1)+flags(3) + track_id(4). */\nfunction readTrafTrackId(view: DataView, traf: Box): number | undefined {\n const tfhd = findBox(view, ['tfhd'], traf.dataStart, traf.end);\n\n return tfhd ? view.getUint32(tfhd.dataStart + 4) : undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+EA,SAAgB,eACd,aACA,aAC4B;CAC5B,MAAM,OAAO,WAAW,WAAW;CACnC,MAAM,OAAO,QAAQ,MAAM,CAAC,MAAM,CAAC;CACnC,IAAI,CAAC,MAAM,OAAO,KAAA;CAElB,KAAK,MAAM,QAAQ,mBAAmB,MAAM,QAAQ,KAAK,WAAW,KAAK,GAAG,GAAG;EAC7E,IAAI,gBAAgB,MAAM,IAAI,MAAM,aAAa;EAEjD,MAAM,OAAO,QAAQ,MAAM,CAAC,MAAM,GAAG,KAAK,WAAW,KAAK,GAAG;EAC7D,MAAM,OAAO,QAAQ,MAAM,CAAC,QAAQ,MAAM,GAAG,KAAK,WAAW,KAAK,GAAG;EACrE,IAAI,CAAC,QAAQ,CAAC,MAAM,OAAO,KAAA;EAM3B,OAAO;GAAE,SAFO,KAAK,UAAU,KAAK,YAAY,KAAK,mBAAmB,MAAM,KAAK,SAAS,MAAM,IAAI,KAAK,EAE5F;GAAG,WAAW,kBAAkB,MAAM,IAAI;EAAE;CAC7D;AAGF;;;;;AAMA,SAAgB,wBAAwB,cAAwC,SAAqC;CACnH,MAAM,OAAO,WAAW,YAAY;CACpC,MAAM,OAAO,QAAQ,MAAM,CAAC,MAAM,CAAC;CACnC,IAAI,CAAC,MAAM,OAAO,KAAA;CAElB,KAAK,MAAM,QAAQ,mBAAmB,MAAM,QAAQ,KAAK,WAAW,KAAK,GAAG,GAAG;EAC7E,IAAI,gBAAgB,MAAM,IAAI,MAAM,SAAS;EAE7C,MAAM,OAAO,QAAQ,MAAM,CAAC,MAAM,GAAG,KAAK,WAAW,KAAK,GAAG;EAE7D,OAAO,OAAO,4BAA4B,MAAM,IAAI,IAAI,KAAA;CAC1D;AAGF;;AAKA,SAAS,kBAAkB,MAAgB,MAAmB;CAC5D,OAAO,KAAK,UAAU,KAAK,YAAY,KAAK,mBAAmB,MAAM,KAAK,SAAS,MAAM,IAAI,KAAK,EAAE;AACtG;;AAGA,SAAS,4BAA4B,MAAgB,MAAmB;CACtE,MAAM,KAAK,KAAK,YAAY;CAE5B,OAAO,mBAAmB,MAAM,KAAK,SAAS,MAAM,IAAI,OAAO,KAAK,aAAa,EAAE,CAAC,IAAI,KAAK,UAAU,EAAE;AAC3G;;AAKA,SAAS,gBAAgB,MAAgB,MAA+B;CACtE,MAAM,OAAO,QAAQ,MAAM,CAAC,QAAQ,MAAM,GAAG,KAAK,WAAW,KAAK,GAAG;CAErE,OAAO,OAAO,WAAW,MAAM,KAAK,YAAY,CAAC,IAAI,KAAA;AACvD;;AAGA,SAAS,gBAAgB,MAAgB,MAA+B;CACtE,MAAM,OAAO,QAAQ,MAAM,CAAC,MAAM,GAAG,KAAK,WAAW,KAAK,GAAG;CAE7D,OAAO,OAAO,KAAK,UAAU,KAAK,YAAY,CAAC,IAAI,KAAA;AACrD"}
|