@videojs/spf 10.0.0-beta.22 → 10.0.0-beta.24
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/{dev/core → default/core/actors}/create-machine-actor.js +3 -3
- package/dist/default/core/actors/create-machine-actor.js.map +1 -0
- package/dist/default/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
- package/dist/default/core/actors/create-transition-actor.js.map +1 -0
- package/dist/default/core/composition/create-composition.js +85 -0
- package/dist/default/core/composition/create-composition.js.map +1 -0
- package/dist/default/core/composition/share-signals.js +37 -0
- package/dist/default/core/composition/share-signals.js.map +1 -0
- package/dist/{dev/core → default/core/reactors}/create-machine-reactor.js +4 -4
- package/dist/default/core/reactors/create-machine-reactor.js.map +1 -0
- package/dist/default/core/signals/primitives.js +33 -11
- package/dist/default/core/signals/primitives.js.map +1 -1
- package/dist/default/core/{task.js → tasks/task.js} +2 -2
- package/dist/default/core/tasks/task.js.map +1 -0
- package/dist/default/dom.js +9 -10
- package/dist/default/hls.js +3 -0
- package/dist/default/index.js +12 -3
- package/dist/default/index.js.map +1 -1
- package/dist/default/media/abr/quality-selection.js +83 -0
- package/dist/default/media/abr/quality-selection.js.map +1 -0
- package/dist/default/{core → media}/buffer/back-buffer.js +2 -2
- package/dist/default/media/buffer/back-buffer.js.map +1 -0
- package/dist/{dev/core → default/media}/buffer/forward-buffer.js +14 -2
- package/dist/default/media/buffer/forward-buffer.js.map +1 -0
- package/dist/{dev/dom/media → default/media/dom/mse}/append-segment.js +1 -1
- package/dist/default/media/dom/mse/append-segment.js.map +1 -0
- package/dist/default/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
- package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -0
- package/dist/default/media/dom/mse/duration.js +78 -0
- package/dist/default/media/dom/mse/duration.js.map +1 -0
- package/dist/default/media/dom/mse/end-of-stream.js +20 -0
- package/dist/default/media/dom/mse/end-of-stream.js.map +1 -0
- package/dist/default/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
- package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -0
- package/dist/default/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
- package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -0
- package/dist/default/media/dom/text/text-track-slots.js +69 -0
- package/dist/default/media/dom/text/text-track-slots.js.map +1 -0
- package/dist/{dev/core → default/media}/hls/parse-attributes.js +1 -1
- package/dist/default/media/hls/parse-attributes.js.map +1 -0
- package/dist/{dev/core → default/media}/hls/parse-media-playlist.js +1 -1
- package/dist/default/media/hls/parse-media-playlist.js.map +1 -0
- package/dist/{dev/core → default/media}/hls/parse-multivariant.js +2 -2
- package/dist/default/media/hls/parse-multivariant.js.map +1 -0
- package/dist/default/{core → media}/hls/resolve-url.js +1 -1
- package/dist/default/media/hls/resolve-url.js.map +1 -0
- package/dist/default/media/primitives/select-tracks.js +45 -0
- package/dist/default/media/primitives/select-tracks.js.map +1 -0
- package/dist/default/media/types/index.js +36 -0
- package/dist/default/media/types/index.js.map +1 -0
- package/dist/default/media/utils/preload.js +22 -0
- package/dist/default/media/utils/preload.js.map +1 -0
- package/dist/default/media/utils/track-selection.js +47 -0
- package/dist/default/media/utils/track-selection.js.map +1 -0
- package/dist/default/media/utils/tracks.js +103 -0
- package/dist/default/media/utils/tracks.js.map +1 -0
- package/dist/default/{core/abr → network}/bandwidth-estimator.js +6 -5
- package/dist/default/network/bandwidth-estimator.js.map +1 -0
- package/dist/{dev/dom → default}/network/chunked-stream-iterable.js +1 -1
- package/dist/default/network/chunked-stream-iterable.js.map +1 -0
- package/dist/{dev/core/abr → default/network}/ewma.js +1 -1
- package/dist/default/network/ewma.js.map +1 -0
- package/dist/default/network/fetch.js +109 -0
- package/dist/default/network/fetch.js.map +1 -0
- package/dist/{dev/dom/features/segment-loader-actor.js → default/playback/actors/dom/segment-loader.js} +24 -15
- package/dist/default/playback/actors/dom/segment-loader.js.map +1 -0
- package/dist/{dev/dom/media/source-buffer-actor.js → default/playback/actors/dom/source-buffer.js} +8 -8
- package/dist/default/playback/actors/dom/source-buffer.js.map +1 -0
- package/dist/{dev/dom/features/text-tracks-actor.js → default/playback/actors/dom/text-tracks.js} +7 -3
- package/dist/default/playback/actors/dom/text-tracks.js.map +1 -0
- package/dist/default/playback/actors/text-track-segment-loader.js +135 -0
- package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -0
- package/dist/default/playback/behaviors/calculate-presentation-duration.js +58 -0
- package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -0
- package/dist/default/playback/behaviors/dom/end-of-stream.js +178 -0
- package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -0
- package/dist/default/playback/behaviors/dom/load-segments.js +177 -0
- package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +173 -0
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-mediasource.js +89 -0
- package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +54 -0
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js +99 -0
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-current-time.js +46 -0
- package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-load-triggers.js +94 -0
- package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-playback-rate.js +37 -0
- package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -0
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +90 -0
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
- package/dist/default/playback/behaviors/quality-switching.js +96 -0
- package/dist/default/playback/behaviors/quality-switching.js.map +1 -0
- package/dist/default/playback/behaviors/resolve-presentation.js +85 -0
- package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -0
- package/dist/default/playback/behaviors/resolve-track.js +97 -0
- package/dist/default/playback/behaviors/resolve-track.js.map +1 -0
- package/dist/default/playback/behaviors/select-tracks.js +132 -0
- package/dist/default/playback/behaviors/select-tracks.js.map +1 -0
- package/dist/default/playback/behaviors/sync-preload.js +59 -0
- package/dist/default/playback/behaviors/sync-preload.js.map +1 -0
- package/dist/default/playback/behaviors/track-types.js +61 -0
- package/dist/default/playback/behaviors/track-types.js.map +1 -0
- package/dist/default/{dom/playback-engine → playback/engines/hls}/adapter.js +36 -25
- package/dist/default/playback/engines/hls/adapter.js.map +1 -0
- package/dist/default/playback/engines/hls/engine.js +102 -0
- package/dist/default/playback/engines/hls/engine.js.map +1 -0
- package/dist/dev/core/{actor.d.ts → actors/actor.d.ts} +2 -2
- package/dist/dev/core/actors/actor.d.ts.map +1 -0
- package/dist/dev/core/actors/create-machine-actor.d.ts +125 -0
- package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -0
- package/dist/{default/core → dev/core/actors}/create-machine-actor.js +3 -3
- package/dist/dev/core/actors/create-machine-actor.js.map +1 -0
- package/dist/dev/core/actors/create-transition-actor.d.ts +42 -0
- package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -0
- package/dist/dev/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
- package/dist/dev/core/actors/create-transition-actor.js.map +1 -0
- package/dist/dev/core/composition/create-composition.d.ts +274 -0
- package/dist/dev/core/composition/create-composition.d.ts.map +1 -0
- package/dist/dev/core/composition/create-composition.js +85 -0
- package/dist/dev/core/composition/create-composition.js.map +1 -0
- package/dist/dev/core/composition/share-signals.d.ts +45 -0
- package/dist/dev/core/composition/share-signals.d.ts.map +1 -0
- package/dist/dev/core/composition/share-signals.js +37 -0
- package/dist/dev/core/composition/share-signals.js.map +1 -0
- package/dist/dev/core/machine.d.ts +0 -1
- package/dist/dev/core/machine.d.ts.map +1 -1
- package/dist/dev/core/reactors/create-machine-reactor.d.ts +93 -0
- package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -0
- package/dist/{default/core → dev/core/reactors}/create-machine-reactor.js +4 -4
- package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -0
- package/dist/dev/core/signals/primitives.d.ts +38 -1
- package/dist/dev/core/signals/primitives.d.ts.map +1 -1
- package/dist/dev/core/signals/primitives.js +33 -11
- package/dist/dev/core/signals/primitives.js.map +1 -1
- package/dist/dev/core/tasks/task.d.ts +115 -0
- package/dist/dev/core/tasks/task.d.ts.map +1 -0
- package/dist/dev/core/{task.js → tasks/task.js} +2 -2
- package/dist/dev/core/tasks/task.js.map +1 -0
- package/dist/dev/dom.d.ts +9 -10
- package/dist/dev/dom.js +9 -10
- package/dist/dev/hls.d.ts +3 -0
- package/dist/dev/hls.js +3 -0
- package/dist/dev/index.d.ts +15 -3
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/index.js +12 -3
- package/dist/dev/index.js.map +1 -1
- package/dist/dev/media/abr/quality-selection.d.ts +22 -0
- package/dist/dev/media/abr/quality-selection.d.ts.map +1 -0
- package/dist/dev/media/abr/quality-selection.js +83 -0
- package/dist/dev/media/abr/quality-selection.js.map +1 -0
- package/dist/dev/media/buffer/back-buffer.d.ts +14 -0
- package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -0
- package/dist/dev/{core → media}/buffer/back-buffer.js +2 -2
- package/dist/dev/media/buffer/back-buffer.js.map +1 -0
- package/dist/dev/media/buffer/forward-buffer.d.ts +14 -0
- package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -0
- package/dist/{default/core → dev/media}/buffer/forward-buffer.js +14 -2
- package/dist/dev/media/buffer/forward-buffer.js.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/append-segment.d.ts +1 -1
- package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -0
- package/dist/{default/dom/media → dev/media/dom/mse}/append-segment.js +1 -1
- package/dist/dev/media/dom/mse/append-segment.js.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.d.ts +1 -1
- package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
- package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -0
- package/dist/dev/media/dom/mse/duration.js +78 -0
- package/dist/dev/media/dom/mse/duration.js.map +1 -0
- package/dist/dev/media/dom/mse/end-of-stream.js +20 -0
- package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
- package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +12 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -0
- package/dist/dev/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
- package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -0
- package/dist/dev/media/dom/text/text-track-slots.d.ts +31 -0
- package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -0
- package/dist/dev/media/dom/text/text-track-slots.js +69 -0
- package/dist/dev/media/dom/text/text-track-slots.js.map +1 -0
- package/dist/{default/core → dev/media}/hls/parse-attributes.js +1 -1
- package/dist/dev/media/hls/parse-attributes.js.map +1 -0
- package/dist/{default/core → dev/media}/hls/parse-media-playlist.js +1 -1
- package/dist/dev/media/hls/parse-media-playlist.js.map +1 -0
- package/dist/{default/core → dev/media}/hls/parse-multivariant.js +2 -2
- package/dist/dev/media/hls/parse-multivariant.js.map +1 -0
- package/dist/dev/{core → media}/hls/resolve-url.js +1 -1
- package/dist/dev/media/hls/resolve-url.js.map +1 -0
- package/dist/dev/media/primitives/select-tracks.js +45 -0
- package/dist/dev/media/primitives/select-tracks.js.map +1 -0
- package/dist/dev/{core → media}/types/index.d.ts +30 -2
- package/dist/dev/media/types/index.d.ts.map +1 -0
- package/dist/dev/media/types/index.js +36 -0
- package/dist/dev/media/types/index.js.map +1 -0
- package/dist/dev/media/utils/preload.js +22 -0
- package/dist/dev/media/utils/preload.js.map +1 -0
- package/dist/dev/media/utils/track-selection.js +47 -0
- package/dist/dev/media/utils/track-selection.js.map +1 -0
- package/dist/dev/media/utils/tracks.js +103 -0
- package/dist/dev/media/utils/tracks.js.map +1 -0
- package/dist/dev/{core/abr → network}/bandwidth-estimator.d.ts +17 -2
- package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -0
- package/dist/dev/{core/abr → network}/bandwidth-estimator.js +6 -5
- package/dist/dev/network/bandwidth-estimator.js.map +1 -0
- package/dist/{default/dom → dev}/network/chunked-stream-iterable.js +1 -1
- package/dist/dev/network/chunked-stream-iterable.js.map +1 -0
- package/dist/{default/core/abr → dev/network}/ewma.js +1 -1
- package/dist/dev/network/ewma.js.map +1 -0
- package/dist/dev/network/fetch.js +109 -0
- package/dist/dev/network/fetch.js.map +1 -0
- package/dist/dev/playback/actors/dom/segment-loader.d.ts +36 -0
- package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -0
- package/dist/{default/dom/features/segment-loader-actor.js → dev/playback/actors/dom/segment-loader.js} +24 -15
- package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -0
- package/dist/dev/{dom/media/source-buffer-actor.d.ts → playback/actors/dom/source-buffer.d.ts} +5 -5
- package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -0
- package/dist/{default/dom/media/source-buffer-actor.js → dev/playback/actors/dom/source-buffer.js} +8 -8
- package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -0
- package/dist/dev/playback/actors/dom/text-tracks.d.ts +1 -0
- package/dist/{default/dom/features/text-tracks-actor.js → dev/playback/actors/dom/text-tracks.js} +7 -3
- package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -0
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts +59 -0
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -0
- package/dist/dev/playback/actors/text-track-segment-loader.js +135 -0
- package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -0
- package/dist/dev/playback/actors/text-tracks.d.ts +42 -0
- package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +17 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js +58 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/end-of-stream.js +178 -0
- package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts +62 -0
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/load-segments.js +177 -0
- package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +173 -0
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js +89 -0
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +31 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +54 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +99 -0
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +34 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.js +46 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +48 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js +94 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +34 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js +37 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +90 -0
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
- package/dist/dev/playback/behaviors/quality-switching.js +96 -0
- package/dist/dev/playback/behaviors/quality-switching.js.map +1 -0
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts +7 -0
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/resolve-presentation.js +85 -0
- package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -0
- package/dist/dev/playback/behaviors/resolve-track.js +97 -0
- package/dist/dev/playback/behaviors/resolve-track.js.map +1 -0
- package/dist/dev/playback/behaviors/select-tracks.js +132 -0
- package/dist/dev/playback/behaviors/select-tracks.js.map +1 -0
- package/dist/dev/playback/behaviors/sync-preload.js +59 -0
- package/dist/dev/playback/behaviors/sync-preload.js.map +1 -0
- package/dist/dev/playback/behaviors/track-types.js +61 -0
- package/dist/dev/playback/behaviors/track-types.js.map +1 -0
- package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.d.ts +14 -10
- package/dist/dev/playback/engines/hls/adapter.d.ts.map +1 -0
- package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.js +36 -25
- package/dist/dev/playback/engines/hls/adapter.js.map +1 -0
- package/dist/dev/playback/engines/hls/engine.d.ts +177 -0
- package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/engine.js +102 -0
- package/dist/dev/playback/engines/hls/engine.js.map +1 -0
- package/package.json +8 -9
- package/dist/default/core/abr/bandwidth-estimator.js.map +0 -1
- package/dist/default/core/abr/ewma.js.map +0 -1
- package/dist/default/core/abr/quality-selection.js +0 -53
- package/dist/default/core/abr/quality-selection.js.map +0 -1
- package/dist/default/core/buffer/back-buffer.js.map +0 -1
- package/dist/default/core/buffer/forward-buffer.js.map +0 -1
- package/dist/default/core/create-machine-actor.js.map +0 -1
- package/dist/default/core/create-machine-reactor.js.map +0 -1
- package/dist/default/core/create-transition-actor.js.map +0 -1
- package/dist/default/core/features/calculate-presentation-duration.js +0 -53
- package/dist/default/core/features/calculate-presentation-duration.js.map +0 -1
- package/dist/default/core/features/quality-switching.js +0 -69
- package/dist/default/core/features/quality-switching.js.map +0 -1
- package/dist/default/core/features/resolve-presentation.js +0 -73
- package/dist/default/core/features/resolve-presentation.js.map +0 -1
- package/dist/default/core/features/resolve-track.js +0 -66
- package/dist/default/core/features/resolve-track.js.map +0 -1
- package/dist/default/core/features/select-tracks.js +0 -130
- package/dist/default/core/features/select-tracks.js.map +0 -1
- package/dist/default/core/features/sync-preload-attribute.js +0 -26
- package/dist/default/core/features/sync-preload-attribute.js.map +0 -1
- package/dist/default/core/hls/parse-attributes.js.map +0 -1
- package/dist/default/core/hls/parse-media-playlist.js.map +0 -1
- package/dist/default/core/hls/parse-multivariant.js.map +0 -1
- package/dist/default/core/hls/resolve-url.js.map +0 -1
- package/dist/default/core/task.js.map +0 -1
- package/dist/default/core/types/index.js +0 -15
- package/dist/default/core/types/index.js.map +0 -1
- package/dist/default/core/utils/generate-id.js +0 -21
- package/dist/default/core/utils/generate-id.js.map +0 -1
- package/dist/default/core/utils/track-selection.js +0 -39
- package/dist/default/core/utils/track-selection.js.map +0 -1
- package/dist/default/dom/features/end-of-stream.js +0 -145
- package/dist/default/dom/features/end-of-stream.js.map +0 -1
- package/dist/default/dom/features/load-segments.js +0 -192
- package/dist/default/dom/features/load-segments.js.map +0 -1
- package/dist/default/dom/features/load-text-track-cues.js +0 -104
- package/dist/default/dom/features/load-text-track-cues.js.map +0 -1
- package/dist/default/dom/features/segment-loader-actor.js.map +0 -1
- package/dist/default/dom/features/setup-mediasource.js +0 -48
- package/dist/default/dom/features/setup-mediasource.js.map +0 -1
- package/dist/default/dom/features/setup-sourcebuffer.js +0 -78
- package/dist/default/dom/features/setup-sourcebuffer.js.map +0 -1
- package/dist/default/dom/features/sync-text-tracks.js +0 -95
- package/dist/default/dom/features/sync-text-tracks.js.map +0 -1
- package/dist/default/dom/features/text-track-segment-loader-actor.js +0 -55
- package/dist/default/dom/features/text-track-segment-loader-actor.js.map +0 -1
- package/dist/default/dom/features/text-tracks-actor.js.map +0 -1
- package/dist/default/dom/features/track-current-time.js +0 -49
- package/dist/default/dom/features/track-current-time.js.map +0 -1
- package/dist/default/dom/features/track-playback-initiated.js +0 -66
- package/dist/default/dom/features/track-playback-initiated.js.map +0 -1
- package/dist/default/dom/features/track-playback-rate.js +0 -32
- package/dist/default/dom/features/track-playback-rate.js.map +0 -1
- package/dist/default/dom/features/update-duration.js +0 -78
- package/dist/default/dom/features/update-duration.js.map +0 -1
- package/dist/default/dom/media/append-segment.js.map +0 -1
- package/dist/default/dom/media/buffer-flusher.js.map +0 -1
- package/dist/default/dom/media/mediasource-setup.js.map +0 -1
- package/dist/default/dom/media/source-buffer-actor.js.map +0 -1
- package/dist/default/dom/network/chunked-stream-iterable.js.map +0 -1
- package/dist/default/dom/network/fetch.js +0 -54
- package/dist/default/dom/network/fetch.js.map +0 -1
- package/dist/default/dom/playback-engine/adapter.js.map +0 -1
- package/dist/default/dom/playback-engine/engine.js +0 -142
- package/dist/default/dom/playback-engine/engine.js.map +0 -1
- package/dist/default/dom/playback-engine.js +0 -4
- package/dist/default/dom/text/parse-vtt-segment.js.map +0 -1
- package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +0 -1
- package/dist/dev/core/abr/bandwidth-estimator.js.map +0 -1
- package/dist/dev/core/abr/ewma.js.map +0 -1
- package/dist/dev/core/abr/quality-selection.js +0 -53
- package/dist/dev/core/abr/quality-selection.js.map +0 -1
- package/dist/dev/core/actor.d.ts.map +0 -1
- package/dist/dev/core/buffer/back-buffer.js.map +0 -1
- package/dist/dev/core/buffer/forward-buffer.js.map +0 -1
- package/dist/dev/core/create-machine-actor.d.ts +0 -12
- package/dist/dev/core/create-machine-actor.d.ts.map +0 -1
- package/dist/dev/core/create-machine-actor.js.map +0 -1
- package/dist/dev/core/create-machine-reactor.d.ts +0 -8
- package/dist/dev/core/create-machine-reactor.d.ts.map +0 -1
- package/dist/dev/core/create-machine-reactor.js.map +0 -1
- package/dist/dev/core/create-transition-actor.d.ts +0 -23
- package/dist/dev/core/create-transition-actor.d.ts.map +0 -1
- package/dist/dev/core/create-transition-actor.js.map +0 -1
- package/dist/dev/core/features/calculate-presentation-duration.js +0 -53
- package/dist/dev/core/features/calculate-presentation-duration.js.map +0 -1
- package/dist/dev/core/features/quality-switching.js +0 -69
- package/dist/dev/core/features/quality-switching.js.map +0 -1
- package/dist/dev/core/features/resolve-presentation.js +0 -73
- package/dist/dev/core/features/resolve-presentation.js.map +0 -1
- package/dist/dev/core/features/resolve-track.js +0 -66
- package/dist/dev/core/features/resolve-track.js.map +0 -1
- package/dist/dev/core/features/select-tracks.js +0 -130
- package/dist/dev/core/features/select-tracks.js.map +0 -1
- package/dist/dev/core/features/sync-preload-attribute.js +0 -26
- package/dist/dev/core/features/sync-preload-attribute.js.map +0 -1
- package/dist/dev/core/hls/parse-attributes.js.map +0 -1
- package/dist/dev/core/hls/parse-media-playlist.js.map +0 -1
- package/dist/dev/core/hls/parse-multivariant.js.map +0 -1
- package/dist/dev/core/hls/resolve-url.js.map +0 -1
- package/dist/dev/core/task.js.map +0 -1
- package/dist/dev/core/types/index.d.ts.map +0 -1
- package/dist/dev/core/types/index.js +0 -15
- package/dist/dev/core/types/index.js.map +0 -1
- package/dist/dev/core/utils/generate-id.js +0 -21
- package/dist/dev/core/utils/generate-id.js.map +0 -1
- package/dist/dev/core/utils/track-selection.d.ts +0 -16
- package/dist/dev/core/utils/track-selection.d.ts.map +0 -1
- package/dist/dev/core/utils/track-selection.js +0 -39
- package/dist/dev/core/utils/track-selection.js.map +0 -1
- package/dist/dev/dom/features/end-of-stream.js +0 -145
- package/dist/dev/dom/features/end-of-stream.js.map +0 -1
- package/dist/dev/dom/features/load-segments.d.ts +0 -72
- package/dist/dev/dom/features/load-segments.d.ts.map +0 -1
- package/dist/dev/dom/features/load-segments.js +0 -192
- package/dist/dev/dom/features/load-segments.js.map +0 -1
- package/dist/dev/dom/features/load-text-track-cues.d.ts +0 -85
- package/dist/dev/dom/features/load-text-track-cues.d.ts.map +0 -1
- package/dist/dev/dom/features/load-text-track-cues.js +0 -104
- package/dist/dev/dom/features/load-text-track-cues.js.map +0 -1
- package/dist/dev/dom/features/segment-loader-actor.d.ts +0 -1
- package/dist/dev/dom/features/segment-loader-actor.js.map +0 -1
- package/dist/dev/dom/features/setup-mediasource.js +0 -48
- package/dist/dev/dom/features/setup-mediasource.js.map +0 -1
- package/dist/dev/dom/features/setup-sourcebuffer.d.ts +0 -9
- package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +0 -1
- package/dist/dev/dom/features/setup-sourcebuffer.js +0 -78
- package/dist/dev/dom/features/setup-sourcebuffer.js.map +0 -1
- package/dist/dev/dom/features/sync-text-tracks.js +0 -95
- package/dist/dev/dom/features/sync-text-tracks.js.map +0 -1
- package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts +0 -12
- package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts.map +0 -1
- package/dist/dev/dom/features/text-track-segment-loader-actor.js +0 -55
- package/dist/dev/dom/features/text-track-segment-loader-actor.js.map +0 -1
- package/dist/dev/dom/features/text-tracks-actor.d.ts +0 -31
- package/dist/dev/dom/features/text-tracks-actor.d.ts.map +0 -1
- package/dist/dev/dom/features/text-tracks-actor.js.map +0 -1
- package/dist/dev/dom/features/track-current-time.d.ts +0 -40
- package/dist/dev/dom/features/track-current-time.d.ts.map +0 -1
- package/dist/dev/dom/features/track-current-time.js +0 -49
- package/dist/dev/dom/features/track-current-time.js.map +0 -1
- package/dist/dev/dom/features/track-playback-initiated.d.ts +0 -46
- package/dist/dev/dom/features/track-playback-initiated.d.ts.map +0 -1
- package/dist/dev/dom/features/track-playback-initiated.js +0 -66
- package/dist/dev/dom/features/track-playback-initiated.js.map +0 -1
- package/dist/dev/dom/features/track-playback-rate.d.ts +0 -34
- package/dist/dev/dom/features/track-playback-rate.d.ts.map +0 -1
- package/dist/dev/dom/features/track-playback-rate.js +0 -32
- package/dist/dev/dom/features/track-playback-rate.js.map +0 -1
- package/dist/dev/dom/features/update-duration.js +0 -78
- package/dist/dev/dom/features/update-duration.js.map +0 -1
- package/dist/dev/dom/media/append-segment.d.ts.map +0 -1
- package/dist/dev/dom/media/append-segment.js.map +0 -1
- package/dist/dev/dom/media/buffer-flusher.d.ts.map +0 -1
- package/dist/dev/dom/media/buffer-flusher.js.map +0 -1
- package/dist/dev/dom/media/mediasource-setup.js.map +0 -1
- package/dist/dev/dom/media/source-buffer-actor.d.ts.map +0 -1
- package/dist/dev/dom/media/source-buffer-actor.js.map +0 -1
- package/dist/dev/dom/network/chunked-stream-iterable.js.map +0 -1
- package/dist/dev/dom/network/fetch.js +0 -54
- package/dist/dev/dom/network/fetch.js.map +0 -1
- package/dist/dev/dom/playback-engine/adapter.d.ts.map +0 -1
- package/dist/dev/dom/playback-engine/adapter.js.map +0 -1
- package/dist/dev/dom/playback-engine/engine.d.ts +0 -124
- package/dist/dev/dom/playback-engine/engine.d.ts.map +0 -1
- package/dist/dev/dom/playback-engine/engine.js +0 -142
- package/dist/dev/dom/playback-engine/engine.js.map +0 -1
- package/dist/dev/dom/playback-engine.d.ts +0 -4
- package/dist/dev/dom/playback-engine.js +0 -4
- package/dist/dev/dom/text/parse-vtt-segment.d.ts +0 -12
- package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +0 -1
- package/dist/dev/dom/text/parse-vtt-segment.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-track-segment-loader.js","names":[],"sources":["../../../../src/playback/actors/text-track-segment-loader.ts"],"sourcesContent":["import { createMachineActor, type HandlerContext, type MessageActor } from '../../core/actors/create-machine-actor';\nimport { peek } from '../../core/signals/primitives';\nimport { SerialRunner, Task } from '../../core/tasks/task';\nimport {\n DEFAULT_FORWARD_BUFFER_CONFIG,\n type ForwardBufferConfig,\n getSegmentsToLoad,\n} from '../../media/buffer/forward-buffer';\nimport type { Cue, Segment, TextTrack } from '../../media/types';\nimport type { TextTracksActor } from './text-tracks';\n\n// =============================================================================\n// Message / actor types\n// =============================================================================\n\n/**\n * Mirrors the v/a `SegmentLoaderMessage` shape. `range` carries the\n * forward-window anchor (`range.start` is treated as the load anchor;\n * the actor computes its own forward window internally via\n * `getSegmentsToLoad`). When `range` is omitted (metadata mode), this\n * actor is a no-op — text tracks have no init-segment concept.\n */\nexport type TextTrackSegmentLoaderMessage = {\n type: 'load';\n track: TextTrack;\n range?: { start: number; end: number };\n};\n\n/** Finite states of the actor. */\nexport type TextTrackSegmentLoaderActorState = 'idle' | 'loading' | 'destroyed';\n\n/** Non-finite (extended) data managed by the actor. */\nexport interface TextTrackSegmentLoaderActorContext {\n /**\n * Track ID of the segment currently being fetched, or null. Paired\n * with `inFlightSegmentId` so the continue-vs-preempt check survives\n * cross-track segment-id collisions (e.g. each track starts at\n * `seg-0`).\n */\n inFlightTrackId: string | null;\n /**\n * Segment ID currently being fetched, or null. Used together with\n * `inFlightTrackId` by the `loading` state's `load` handler.\n */\n inFlightSegmentId: string | null;\n}\n\nexport type TextTrackSegmentLoaderActor = MessageActor<\n TextTrackSegmentLoaderActorState,\n TextTrackSegmentLoaderActorContext,\n TextTrackSegmentLoaderMessage\n>;\n\n/**\n * Resolves a text-track segment URL into the array of cues it contains.\n *\n * \"Resolve\" because the fn covers both network fetch and parse into the\n * domain model. Host-agnostic — the concrete resolver (e.g. the\n * browser's native VTT resolver) is supplied at engine-assembly time,\n * so this actor stays DOM-free.\n */\nexport type TextTrackSegmentResolver<C extends Cue = Cue> = (url: string) => Promise<C[]>;\n\n/**\n * Configuration for `createTextTrackSegmentLoaderActor`. Spread over\n * `DEFAULT_FORWARD_BUFFER_CONFIG` to override individual forward-window\n * fields (e.g. `bufferDuration`). Text tracks don't have a back-buffer\n * concern — cues evict by their playhead-relative window at runtime —\n * so no `backBuffer` config field.\n */\nexport interface TextTrackSegmentLoaderActorConfig {\n forwardBuffer?: Partial<ForwardBufferConfig>;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Internal load-task descriptor — one segment fetch + dispatch unit. */\ninterface TextLoadTask {\n segment: Segment;\n trackId: string;\n}\n\n/**\n * Loads text-track segments for a track and delegates cue management\n * to a TextTracksActor. Mirrors the v/a `SegmentLoaderActor` shape (FSM\n * with `idle` / `loading` and `inFlight*` context for continue-vs-preempt),\n * adapted to text:\n *\n * - No init segment, no flush ops (text cues don't need eviction — they're\n * small and the playhead-relative window is enforced by the runtime).\n * - Single in-flight identity (`inFlightSegmentId`) — text has only the\n * media-segment path, no init-segment path.\n *\n * Planning is done in the load handler on every incoming message:\n * `getSegmentsToLoad` filters to the forward window, then the segments\n * not already in `TextTracksActor`'s context are scheduled. When a new\n * `load` arrives mid-run, the handler replans and either:\n *\n * - **Continues**: the in-flight segment is still in the new plan →\n * `abortPending` only, schedule the rest of the plan (minus the\n * in-flight item, which covers its slot).\n * - **Preempts**: in-flight segment is no longer wanted (track switch,\n * large seek out of window) → `abortAll`, schedule the new plan\n * from scratch.\n *\n * The cue parser is injected so this factory is host-agnostic. A DOM\n * host supplies a VTT parser backed by `<track>`/`TextTrack` APIs; a\n * non-DOM host (worker, test fake, alternate runtime) supplies its own.\n */\nexport function createTextTrackSegmentLoaderActor<C extends Cue>(\n textTracksActor: TextTracksActor<C>,\n resolveSegment: TextTrackSegmentResolver<C>,\n config: TextTrackSegmentLoaderActorConfig = {}\n): TextTrackSegmentLoaderActor {\n type UserState = Exclude<TextTrackSegmentLoaderActorState, 'destroyed'>;\n type Ctx = HandlerContext<UserState, TextTrackSegmentLoaderActorContext, () => SerialRunner>;\n\n const forwardBufferConfig: ForwardBufferConfig = { ...DEFAULT_FORWARD_BUFFER_CONFIG, ...config.forwardBuffer };\n\n /**\n * Translate a load message into an ordered TextLoadTask list based on\n * committed actor state. In-flight awareness is handled separately in\n * the `loading` state's load handler.\n *\n * Metadata mode (no `range`) is a no-op for text — text tracks have\n * no init-segment concept, so there's nothing to load until a range\n * arrives via `'full-range'` dispatch.\n */\n const planTasks = (message: TextTrackSegmentLoaderMessage): TextLoadTask[] => {\n const { track, range } = message;\n if (!range) return [];\n const trackId = track.id;\n // Peek (don't track) the snapshot: the loader's `send` is typically\n // invoked from inside the dispatcher's effect body, so a tracked\n // `.get()` here would leak the textTracksActor snapshot into the\n // dispatcher's dep set — every `add-cues` would re-fire the dispatcher\n // and schedule duplicate loads.\n const bufferedSegments = peek(textTracksActor.snapshot).context.segments[trackId] ?? [];\n // `getSegmentsToLoad` uses the actor's threaded `forwardBufferConfig`\n // to compute its forward-window; only `range.start` is consulted\n // here as the playhead anchor. `range.end` is informational — it\n // carries the dispatcher's intended window upper bound but doesn't\n // override the actor's planning.\n const segmentsToLoad = getSegmentsToLoad(track.segments, bufferedSegments, range.start, forwardBufferConfig);\n return segmentsToLoad.map((segment) => ({ segment, trackId }));\n };\n\n /**\n * Wraps a TextLoadTask into a Task that fetches + dispatches `add-cues`.\n * Updates `inFlightSegmentId` around the fetch so the load handler can\n * make accurate continue/preempt decisions.\n */\n const makeLoadTask = (op: TextLoadTask, { getContext, setContext }: Ctx): Task<void> => {\n return new Task(async (signal) => {\n if (signal.aborted) return;\n setContext({ ...getContext(), inFlightTrackId: op.trackId, inFlightSegmentId: op.segment.id });\n try {\n const cues = await resolveSegment(op.segment.url);\n if (signal.aborted) return;\n textTracksActor.send({\n type: 'add-cues',\n meta: {\n trackId: op.trackId,\n id: op.segment.id,\n startTime: op.segment.startTime,\n duration: op.segment.duration,\n },\n cues,\n });\n } catch (error) {\n // Graceful degradation: log and continue to the next segment.\n console.error('Failed to load text-track segment:', error);\n } finally {\n setContext({ ...getContext(), inFlightTrackId: null, inFlightSegmentId: null });\n }\n });\n };\n\n const scheduleAll = (tasks: TextLoadTask[], ctx: Ctx): void => {\n for (const op of tasks) {\n ctx.runner.schedule(makeLoadTask(op, ctx)).then(undefined, (e: unknown) => {\n if (e instanceof Error && e.name === 'AbortError') return;\n console.error('Unexpected error in text-track segment loader:', e);\n ctx.runner.abortPending();\n });\n }\n };\n\n return createMachineActor<\n UserState,\n TextTrackSegmentLoaderActorContext,\n TextTrackSegmentLoaderMessage,\n () => SerialRunner\n >({\n runner: () => new SerialRunner(),\n initial: 'idle',\n context: { inFlightTrackId: null, inFlightSegmentId: null },\n states: {\n idle: {\n on: {\n load: (msg, ctx) => {\n const tasks = planTasks(msg);\n if (tasks.length === 0) return;\n ctx.transition('loading');\n scheduleAll(tasks, ctx);\n },\n },\n },\n loading: {\n onSettled: 'idle',\n on: {\n load: (msg, ctx) => {\n const { context, runner } = ctx;\n const tasks = planTasks(msg);\n\n const inFlightStillNeeded =\n context.inFlightTrackId !== null &&\n context.inFlightSegmentId !== null &&\n tasks.some((t) => t.trackId === context.inFlightTrackId && t.segment.id === context.inFlightSegmentId);\n\n if (inFlightStillNeeded) {\n // Continue: keep the in-flight fetch, schedule the rest.\n runner.abortPending();\n scheduleAll(\n tasks.filter(\n (t) => !(t.trackId === context.inFlightTrackId && t.segment.id === context.inFlightSegmentId)\n ),\n ctx\n );\n } else {\n // Preempt: abort everything (including in-flight) and replan.\n runner.abortAll();\n scheduleAll(tasks, ctx);\n }\n },\n },\n },\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+GA,SAAgB,kCACd,iBACA,gBACA,SAA4C,EAAE,EACjB;CAI7B,MAAM,sBAA2C;EAAE,GAAG;EAA+B,GAAG,OAAO;EAAe;;;;;;;;;;CAW9G,MAAM,aAAa,YAA2D;EAC5E,MAAM,EAAE,OAAO,UAAU;AACzB,MAAI,CAAC,MAAO,QAAO,EAAE;EACrB,MAAM,UAAU,MAAM;EAMtB,MAAM,mBAAmB,KAAK,gBAAgB,SAAS,CAAC,QAAQ,SAAS,YAAY,EAAE;AAOvF,SADuB,kBAAkB,MAAM,UAAU,kBAAkB,MAAM,OAAO,oBAAoB,CACtF,KAAK,aAAa;GAAE;GAAS;GAAS,EAAE;;;;;;;CAQhE,MAAM,gBAAgB,IAAkB,EAAE,YAAY,iBAAkC;AACtF,SAAO,IAAI,KAAK,OAAO,WAAW;AAChC,OAAI,OAAO,QAAS;AACpB,cAAW;IAAE,GAAG,YAAY;IAAE,iBAAiB,GAAG;IAAS,mBAAmB,GAAG,QAAQ;IAAI,CAAC;AAC9F,OAAI;IACF,MAAM,OAAO,MAAM,eAAe,GAAG,QAAQ,IAAI;AACjD,QAAI,OAAO,QAAS;AACpB,oBAAgB,KAAK;KACnB,MAAM;KACN,MAAM;MACJ,SAAS,GAAG;MACZ,IAAI,GAAG,QAAQ;MACf,WAAW,GAAG,QAAQ;MACtB,UAAU,GAAG,QAAQ;MACtB;KACD;KACD,CAAC;YACK,OAAO;AAEd,YAAQ,MAAM,sCAAsC,MAAM;aAClD;AACR,eAAW;KAAE,GAAG,YAAY;KAAE,iBAAiB;KAAM,mBAAmB;KAAM,CAAC;;IAEjF;;CAGJ,MAAM,eAAe,OAAuB,QAAmB;AAC7D,OAAK,MAAM,MAAM,MACf,KAAI,OAAO,SAAS,aAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAA,IAAY,MAAe;AACzE,OAAI,aAAa,SAAS,EAAE,SAAS,aAAc;AACnD,WAAQ,MAAM,kDAAkD,EAAE;AAClE,OAAI,OAAO,cAAc;IACzB;;AAIN,QAAO,mBAKL;EACA,cAAc,IAAI,cAAc;EAChC,SAAS;EACT,SAAS;GAAE,iBAAiB;GAAM,mBAAmB;GAAM;EAC3D,QAAQ;GACN,MAAM,EACJ,IAAI,EACF,OAAO,KAAK,QAAQ;IAClB,MAAM,QAAQ,UAAU,IAAI;AAC5B,QAAI,MAAM,WAAW,EAAG;AACxB,QAAI,WAAW,UAAU;AACzB,gBAAY,OAAO,IAAI;MAE1B,EACF;GACD,SAAS;IACP,WAAW;IACX,IAAI,EACF,OAAO,KAAK,QAAQ;KAClB,MAAM,EAAE,SAAS,WAAW;KAC5B,MAAM,QAAQ,UAAU,IAAI;AAO5B,SAJE,QAAQ,oBAAoB,QAC5B,QAAQ,sBAAsB,QAC9B,MAAM,MAAM,MAAM,EAAE,YAAY,QAAQ,mBAAmB,EAAE,QAAQ,OAAO,QAAQ,kBAAkB,EAE/E;AAEvB,aAAO,cAAc;AACrB,kBACE,MAAM,QACH,MAAM,EAAE,EAAE,YAAY,QAAQ,mBAAmB,EAAE,QAAQ,OAAO,QAAQ,mBAC5E,EACD,IACD;YACI;AAEL,aAAO,UAAU;AACjB,kBAAY,OAAO,IAAI;;OAG5B;IACF;GACF;EACF,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { TransitionActor } from "../../core/actors/create-transition-actor.js";
|
|
2
|
+
import { Cue, Segment } from "../../media/types/index.js";
|
|
3
|
+
|
|
4
|
+
//#region src/playback/actors/text-tracks.d.ts
|
|
5
|
+
/** Segment identity and timing — mirrors AppendSegmentMeta without trackId (keyed separately). */
|
|
6
|
+
type CueSegmentMeta = Pick<Segment, 'id' | 'startTime' | 'duration'> & {
|
|
7
|
+
trackId: string;
|
|
8
|
+
};
|
|
9
|
+
/** Non-finite (extended) data managed by the actor — the XState "context". */
|
|
10
|
+
interface TextTracksActorContext {
|
|
11
|
+
/** Cues added per track ID. Used for duplicate detection and snapshot observability. */
|
|
12
|
+
loaded: Record<string, Cue[]>;
|
|
13
|
+
/** Segments whose cues have been fully added, keyed by track ID. Used for load planning. */
|
|
14
|
+
segments: Record<string, Array<Pick<Segment, 'id' | 'startTime' | 'duration'>>>;
|
|
15
|
+
}
|
|
16
|
+
interface AddCuesMessage<C extends Cue = Cue> {
|
|
17
|
+
type: 'add-cues';
|
|
18
|
+
meta: CueSegmentMeta;
|
|
19
|
+
cues: C[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Wipe the actor's `loaded` + `segments` context. Sent on source reset
|
|
23
|
+
* (typically by `syncTextTracks` on state exit) so a subsequent
|
|
24
|
+
* presentation starts with a fresh cue+segment cache. Without this, a
|
|
25
|
+
* new presentation reusing trackIds from the prior source would have
|
|
26
|
+
* `getSegmentsToLoad` treat its segments as already-buffered and skip
|
|
27
|
+
* loading them.
|
|
28
|
+
*/
|
|
29
|
+
interface ClearMessage {
|
|
30
|
+
type: 'clear';
|
|
31
|
+
}
|
|
32
|
+
type TextTracksActorMessage<C extends Cue = Cue> = AddCuesMessage<C> | ClearMessage;
|
|
33
|
+
/**
|
|
34
|
+
* Host-agnostic text-track actor type.
|
|
35
|
+
*
|
|
36
|
+
* Generic over the concrete cue shape `C` so DOM hosts (using `VTTCue`)
|
|
37
|
+
* and non-DOM hosts (custom cue representations) can both satisfy it.
|
|
38
|
+
*/
|
|
39
|
+
type TextTracksActor<C extends Cue = Cue> = TransitionActor<TextTracksActorContext, TextTracksActorMessage<C>>;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { AddCuesMessage, ClearMessage, CueSegmentMeta, TextTracksActor, TextTracksActorContext, TextTracksActorMessage };
|
|
42
|
+
//# sourceMappingURL=text-tracks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-tracks.d.ts","names":[],"sources":["../../../../src/playback/actors/text-tracks.ts"],"mappings":";;;;;KAQY,cAAA,GAAiB,IAAA,CAAK,OAAA;EAA8C,OAAA;AAAA;;UAG/D,sBAAA;EAHY;EAK3B,MAAA,EAAQ,MAAA,SAAe,GAAA;EALuD;EAO9E,QAAA,EAAU,MAAA,SAAe,KAAA,CAAM,IAAA,CAAK,OAAA;AAAA;AAAA,UAGrB,cAAA,WAAyB,GAAA,GAAM,GAAA;EAC9C,IAAA;EACA,IAAA,EAAM,cAAA;EACN,IAAA,EAAM,CAAA;AAAA;;;;;;;;;UAWS,YAAA;EACf,IAAA;AAAA;AAAA,KAGU,sBAAA,WAAiC,GAAA,GAAM,GAAA,IAAO,cAAA,CAAe,CAAA,IAAK,YAAA;;;;;AAlB9E;;KA0BY,eAAA,WAA0B,GAAA,GAAM,GAAA,IAAO,eAAA,CAAgB,sBAAA,EAAwB,sBAAA,CAAuB,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MaybeResolvedPresentation } from "../../media/types/index.js";
|
|
2
|
+
|
|
3
|
+
//#region src/playback/behaviors/calculate-presentation-duration.d.ts
|
|
4
|
+
interface PresentationDurationState {
|
|
5
|
+
presentation?: MaybeResolvedPresentation;
|
|
6
|
+
selectedVideoTrackId?: string;
|
|
7
|
+
selectedAudioTrackId?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolver supplied via `config.resolveDuration`. Returns the duration to
|
|
11
|
+
* write to `presentation.duration`, or `undefined` when it isn't yet
|
|
12
|
+
* derivable. Positive `Infinity` is the canonical live value.
|
|
13
|
+
*/
|
|
14
|
+
type PresentationDurationResolver = (state: PresentationDurationState) => number | undefined;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { PresentationDurationResolver };
|
|
17
|
+
//# sourceMappingURL=calculate-presentation-duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculate-presentation-duration.d.ts","names":[],"sources":["../../../../src/playback/behaviors/calculate-presentation-duration.ts"],"mappings":";;;UAkCiB,yBAAA;EACf,YAAA,GAAe,yBAAA;EACf,oBAAA;EACA,oBAAA;AAAA;;;;;;KAQU,4BAAA,IAAgC,KAAA,EAAO,yBAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { snapshot, untrack, update } from "../../core/signals/primitives.js";
|
|
2
|
+
import { defineBehavior } from "../../core/composition/create-composition.js";
|
|
3
|
+
import { effect } from "../../core/signals/effect.js";
|
|
4
|
+
//#region src/playback/behaviors/calculate-presentation-duration.ts
|
|
5
|
+
/**
|
|
6
|
+
* **Populate presentation duration via a config-supplied resolver.**
|
|
7
|
+
*
|
|
8
|
+
* Once a presentation is in place and its `duration` is still undefined,
|
|
9
|
+
* calls `config.resolveDuration(state)` whenever a tracked slot changes. The
|
|
10
|
+
* resolver decides what counts as "duration is now derivable" for the variant
|
|
11
|
+
* in play; the behavior itself stays variant-agnostic:
|
|
12
|
+
*
|
|
13
|
+
* - **VoD** — derive from the first resolved selected track's `duration`
|
|
14
|
+
* (video preferred, audio fallback). The HLS engine wires
|
|
15
|
+
* `getResolvedSelectedTrackDuration` from `media/utils/track-selection.ts`
|
|
16
|
+
* as the default. Audio-only falls out of the same resolver naturally
|
|
17
|
+
* (no video selected → audio is the first resolved track).
|
|
18
|
+
* - **Live** — return `Number.POSITIVE_INFINITY` once the presentation is
|
|
19
|
+
* established as live. This is the MSE-spec value for `mediaSource.duration`
|
|
20
|
+
* under live playback; downstream `updateMediaSourceDuration` propagates it through.
|
|
21
|
+
*
|
|
22
|
+
* Validation: writes whatever the resolver returns as long as it's a positive
|
|
23
|
+
* number, including `Infinity`. `undefined` / `NaN` / `<= 0` are skipped.
|
|
24
|
+
*
|
|
25
|
+
* Fires at most once per presentation — an already-set `duration` is never
|
|
26
|
+
* overwritten, and the next reset arrives structurally when a new
|
|
27
|
+
* (unresolved) presentation replaces the current one. The resolver may
|
|
28
|
+
* return `undefined` while duration is still indeterminate; subsequent
|
|
29
|
+
* tracked-slot changes re-run the effect until the resolver commits a value.
|
|
30
|
+
*
|
|
31
|
+
* Downstream of `resolveVideoTrack` / `resolveAudioTrack`; upstream of
|
|
32
|
+
* `updateMediaSourceDuration` (which writes the value through to `mediaSource.duration`).
|
|
33
|
+
*/
|
|
34
|
+
function calculatePresentationDurationSetup({ state, config }) {
|
|
35
|
+
return effect(() => {
|
|
36
|
+
const presentation = state.presentation.get();
|
|
37
|
+
if (!presentation || presentation.duration !== void 0) return;
|
|
38
|
+
const duration = config.resolveDuration(untrack(() => snapshot(state)));
|
|
39
|
+
if (duration === void 0 || Number.isNaN(duration) || duration <= 0) return;
|
|
40
|
+
update(state.presentation, (current) => current ? {
|
|
41
|
+
...current,
|
|
42
|
+
duration
|
|
43
|
+
} : current);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const calculatePresentationDuration = defineBehavior({
|
|
47
|
+
stateKeys: [
|
|
48
|
+
"presentation",
|
|
49
|
+
"selectedVideoTrackId",
|
|
50
|
+
"selectedAudioTrackId"
|
|
51
|
+
],
|
|
52
|
+
contextKeys: [],
|
|
53
|
+
setup: calculatePresentationDurationSetup
|
|
54
|
+
});
|
|
55
|
+
//#endregion
|
|
56
|
+
export { calculatePresentationDuration };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=calculate-presentation-duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculate-presentation-duration.js","names":[],"sources":["../../../../src/playback/behaviors/calculate-presentation-duration.ts"],"sourcesContent":["/**\n * **Populate presentation duration via a config-supplied resolver.**\n *\n * Once a presentation is in place and its `duration` is still undefined,\n * calls `config.resolveDuration(state)` whenever a tracked slot changes. The\n * resolver decides what counts as \"duration is now derivable\" for the variant\n * in play; the behavior itself stays variant-agnostic:\n *\n * - **VoD** — derive from the first resolved selected track's `duration`\n * (video preferred, audio fallback). The HLS engine wires\n * `getResolvedSelectedTrackDuration` from `media/utils/track-selection.ts`\n * as the default. Audio-only falls out of the same resolver naturally\n * (no video selected → audio is the first resolved track).\n * - **Live** — return `Number.POSITIVE_INFINITY` once the presentation is\n * established as live. This is the MSE-spec value for `mediaSource.duration`\n * under live playback; downstream `updateMediaSourceDuration` propagates it through.\n *\n * Validation: writes whatever the resolver returns as long as it's a positive\n * number, including `Infinity`. `undefined` / `NaN` / `<= 0` are skipped.\n *\n * Fires at most once per presentation — an already-set `duration` is never\n * overwritten, and the next reset arrives structurally when a new\n * (unresolved) presentation replaces the current one. The resolver may\n * return `undefined` while duration is still indeterminate; subsequent\n * tracked-slot changes re-run the effect until the resolver commits a value.\n *\n * Downstream of `resolveVideoTrack` / `resolveAudioTrack`; upstream of\n * `updateMediaSourceDuration` (which writes the value through to `mediaSource.duration`).\n */\nimport { defineBehavior } from '../../core/composition/create-composition';\nimport { effect } from '../../core/signals/effect';\nimport { type ReadonlySignal, type Signal, snapshot, untrack, update } from '../../core/signals/primitives';\nimport type { MaybeResolvedPresentation } from '../../media/types';\n\nexport interface PresentationDurationState {\n presentation?: MaybeResolvedPresentation;\n selectedVideoTrackId?: string;\n selectedAudioTrackId?: string;\n}\n\n/**\n * Resolver supplied via `config.resolveDuration`. Returns the duration to\n * write to `presentation.duration`, or `undefined` when it isn't yet\n * derivable. Positive `Infinity` is the canonical live value.\n */\nexport type PresentationDurationResolver = (state: PresentationDurationState) => number | undefined;\n\nexport interface PresentationDurationConfig {\n resolveDuration: PresentationDurationResolver;\n}\n\nfunction calculatePresentationDurationSetup({\n state,\n config,\n}: {\n state: {\n presentation: Signal<PresentationDurationState['presentation']>;\n selectedVideoTrackId: ReadonlySignal<PresentationDurationState['selectedVideoTrackId']>;\n selectedAudioTrackId: ReadonlySignal<PresentationDurationState['selectedAudioTrackId']>;\n };\n config: PresentationDurationConfig;\n}): () => void {\n return effect(() => {\n const presentation = state.presentation.get();\n if (!presentation || presentation.duration !== undefined) return;\n\n // presentation drives the effect; selection-only changes can't yield a\n // writable duration on their own (resolver returns undefined until a\n // track resolves, at which point resolve-track writes back through\n // state.presentation anyway), so we read the rest untracked.\n const duration = config.resolveDuration(untrack(() => snapshot(state)));\n if (duration === undefined || Number.isNaN(duration) || duration <= 0) return;\n\n update(state.presentation, (current) => (current ? { ...current, duration } : current));\n });\n}\n\nexport const calculatePresentationDuration = defineBehavior({\n stateKeys: ['presentation', 'selectedVideoTrackId', 'selectedAudioTrackId'],\n contextKeys: [],\n setup: calculatePresentationDurationSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAS,mCAAmC,EAC1C,OACA,UAQa;AACb,QAAO,aAAa;EAClB,MAAM,eAAe,MAAM,aAAa,KAAK;AAC7C,MAAI,CAAC,gBAAgB,aAAa,aAAa,KAAA,EAAW;EAM1D,MAAM,WAAW,OAAO,gBAAgB,cAAc,SAAS,MAAM,CAAC,CAAC;AACvE,MAAI,aAAa,KAAA,KAAa,OAAO,MAAM,SAAS,IAAI,YAAY,EAAG;AAEvE,SAAO,MAAM,eAAe,YAAa,UAAU;GAAE,GAAG;GAAS;GAAU,GAAG,QAAS;GACvF;;AAGJ,MAAa,gCAAgC,eAAe;CAC1D,WAAW;EAAC;EAAgB;EAAwB;EAAuB;CAC3E,aAAa,EAAE;CACf,OAAO;CACR,CAAC"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { computed, signal } from "../../../core/signals/primitives.js";
|
|
2
|
+
import { defineBehavior } from "../../../core/composition/create-composition.js";
|
|
3
|
+
import { effect } from "../../../core/signals/effect.js";
|
|
4
|
+
import { createMachineReactor } from "../../../core/reactors/create-machine-reactor.js";
|
|
5
|
+
import { isResolvedTrack } from "../../../media/types/index.js";
|
|
6
|
+
import { findTrackById } from "../../../media/utils/tracks.js";
|
|
7
|
+
import { getMaxBufferedEnd, waitForSourceBuffersReady } from "../../../media/dom/mse/duration.js";
|
|
8
|
+
import { isLastSegmentAppended } from "../../../media/dom/mse/end-of-stream.js";
|
|
9
|
+
import { onMediaSourceReadyStateChange } from "../../../media/dom/mse/mediasource-setup.js";
|
|
10
|
+
//#region src/playback/behaviors/dom/end-of-stream.ts
|
|
11
|
+
/**
|
|
12
|
+
* **Drive each `open → ended` transition of the MediaSource.** Calls
|
|
13
|
+
* `MediaSource.endOfStream()` once the temporally last segments of every
|
|
14
|
+
* active buffer actor's currently-loading track are fully appended and
|
|
15
|
+
* the user has reached them — letting the browser finalize duration and
|
|
16
|
+
* fire `ended` on the media element.
|
|
17
|
+
*
|
|
18
|
+
* Re-fires on every subsequent `open → ended → open` cycle. Per the MSE
|
|
19
|
+
* spec, `appendBuffer()` after `endOfStream()` transitions the MediaSource
|
|
20
|
+
* back to `'open'`; seek-back replays and back-buffer refills that re-load
|
|
21
|
+
* earlier segments take this path, so the behavior must call
|
|
22
|
+
* `endOfStream()` again once the last segments are reappended. The reactor's
|
|
23
|
+
* `'preconditions-unmet'` ↔ `'eos-ready'` cycle *is* the re-arm mechanism:
|
|
24
|
+
* a successful `endOfStream()` flips `mediaSource.readyState` to `'ended'`,
|
|
25
|
+
* which (via the local `msIsOpen` signal) exits `'eos-ready'`; the next
|
|
26
|
+
* `'open'` re-evaluates preconditions and may re-enter.
|
|
27
|
+
*
|
|
28
|
+
* # Tracking what's in the buffer
|
|
29
|
+
*
|
|
30
|
+
* Each `SourceBufferActor` knows which track it's currently loading via
|
|
31
|
+
* `initTrackId` (set on the most recent `append-init` message) and which
|
|
32
|
+
* segments it has appended. `deriveState` iterates over the available
|
|
33
|
+
* buffer actors (`[videoBufferActor, audioBufferActor].filter(Boolean)`),
|
|
34
|
+
* resolves each to its track via `findTrackById`, and checks that track's
|
|
35
|
+
* last segment is appended. This means audio-only / video-only / mixed
|
|
36
|
+
* configurations compose uniformly — the body iterates whatever's in
|
|
37
|
+
* scope. No reliance on `selectedTrackId` slots: the actor's view IS the
|
|
38
|
+
* source of truth for "what's being loaded into this buffer."
|
|
39
|
+
*
|
|
40
|
+
* **Two-fire on mid-end ABR switches**: when a quality switch occurs near
|
|
41
|
+
* end-of-stream, the actor's `initTrackId` still reflects the *old* track
|
|
42
|
+
* until the new init segment is appended. The reactor may fire
|
|
43
|
+
* `endOfStream()` against the old track's last-segment-appended state,
|
|
44
|
+
* then the new init's `appendBuffer()` re-opens the MS, which re-arms us
|
|
45
|
+
* to fire again once the new track's last segment lands. Functionally
|
|
46
|
+
* correct (the browser re-fires `ended` after the re-arm), at the cost of
|
|
47
|
+
* one extra call. Accepted as the price of dropping `selectedTrackId`
|
|
48
|
+
* dependence.
|
|
49
|
+
*
|
|
50
|
+
* # Buffer-actor idle gate
|
|
51
|
+
*
|
|
52
|
+
* Each actor must be `'idle'` (no queued or in-flight tasks) before we
|
|
53
|
+
* fire. `buffer.updating === false` alone is insufficient: for chunked
|
|
54
|
+
* fMP4 streaming, `updateend` fires after each chunk while the actor's
|
|
55
|
+
* for-await loop synchronously enqueues the next `appendBuffer()` — so
|
|
56
|
+
* the buffer flips `!updating → updating` across a microtask boundary.
|
|
57
|
+
* The actor's `'updating'` state spans the entire multi-chunk append, so
|
|
58
|
+
* `actor.snapshot.value === 'idle'` is the canonical "no more pending or
|
|
59
|
+
* in-flight work" oracle.
|
|
60
|
+
*
|
|
61
|
+
* This bet (actor models the pipeline; `mediaSource.sourceBuffers` models
|
|
62
|
+
* DOM state) is durable as long as the segment loader is the sole writer
|
|
63
|
+
* of `appendBuffer()` calls. A future loop-mode that auto-fetches earlier
|
|
64
|
+
* segments mid-`ended` would require a broader coordination story
|
|
65
|
+
* between SourceBuffers and the MediaSource — possibly via a
|
|
66
|
+
* `MediaSourceActor` — and that's where this gate would want
|
|
67
|
+
* re-evaluating.
|
|
68
|
+
*
|
|
69
|
+
* # currentTime gate
|
|
70
|
+
*
|
|
71
|
+
* `currentTime` must have reached at least one active track's last
|
|
72
|
+
* segment startTime. Prevents `'eos-ready'` entry when a back-buffer
|
|
73
|
+
* `remove()` / `appendBuffer()` briefly re-opens the MediaSource while
|
|
74
|
+
* the user is mid-stream. HLS rendition time-alignment means any active
|
|
75
|
+
* track works as the reference.
|
|
76
|
+
*
|
|
77
|
+
* # MS readyState — local subscription
|
|
78
|
+
*
|
|
79
|
+
* The behavior subscribes to `mediaSource`'s readyState changes inside
|
|
80
|
+
* its setup (via `onMediaSourceReadyStateChange`) and mirrors `'open'` to
|
|
81
|
+
* a behavior-local `msIsOpen` signal that `deriveState` reads. No shared
|
|
82
|
+
* `mediaSourceReadyState` slot dependency. Anticipates a future
|
|
83
|
+
* `MediaSourceActor` whose snapshot would expose the same signal — the
|
|
84
|
+
* consumer code wouldn't change.
|
|
85
|
+
*
|
|
86
|
+
* # `endOfStream` entry sequence
|
|
87
|
+
*
|
|
88
|
+
* 1. Wait for all SourceBuffers to be idle (DOM-level — defensive,
|
|
89
|
+
* should already hold given the actor-idle gate in `deriveState`).
|
|
90
|
+
* 2. Set `mediaSource.duration` from `getMaxBufferedEnd` to match actual
|
|
91
|
+
* container timestamps (`endOfStream()` only clamps up implicitly;
|
|
92
|
+
* setting it explicitly here keeps the final value deterministic).
|
|
93
|
+
* 3. Call `mediaSource.endOfStream()`.
|
|
94
|
+
*
|
|
95
|
+
* State-exit cleanup (`controller.abort()`) cancels any in-flight wait
|
|
96
|
+
* on source unload, presentation replace, or behavior destroy.
|
|
97
|
+
*
|
|
98
|
+
* # Coordination with `updateMediaSourceDuration`
|
|
99
|
+
*
|
|
100
|
+
* `updateMediaSourceDuration` writes the initial `mediaSource.duration`
|
|
101
|
+
* from `presentation.duration` once per source; this behavior writes the
|
|
102
|
+
* final value from the buffered end. Decision domains don't overlap.
|
|
103
|
+
*/
|
|
104
|
+
function deriveState(presentation, mediaSource, msIsOpen, videoBufferActor, audioBufferActor, currentTime) {
|
|
105
|
+
if (!mediaSource || !presentation || !msIsOpen) return "preconditions-unmet";
|
|
106
|
+
const actors = [videoBufferActor, audioBufferActor].filter((a) => a !== void 0);
|
|
107
|
+
if (actors.length === 0) return "preconditions-unmet";
|
|
108
|
+
let lastSegStart;
|
|
109
|
+
for (const actor of actors) {
|
|
110
|
+
const snapshot = actor.snapshot.get();
|
|
111
|
+
if (snapshot.value !== "idle") return "preconditions-unmet";
|
|
112
|
+
const { initTrackId, segments: appended } = snapshot.context;
|
|
113
|
+
if (!initTrackId) return "preconditions-unmet";
|
|
114
|
+
const track = findTrackById(presentation, initTrackId);
|
|
115
|
+
if (!track || !isResolvedTrack(track)) return "preconditions-unmet";
|
|
116
|
+
if (!isLastSegmentAppended(track.segments, appended)) return "preconditions-unmet";
|
|
117
|
+
if (track.segments.length > 0) {
|
|
118
|
+
const start = track.segments[track.segments.length - 1].startTime;
|
|
119
|
+
if (lastSegStart === void 0 || start > lastSegStart) lastSegStart = start;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (lastSegStart !== void 0 && (currentTime ?? 0) < lastSegStart) return "preconditions-unmet";
|
|
123
|
+
return "eos-ready";
|
|
124
|
+
}
|
|
125
|
+
function endOfStreamSetup({ state, context }) {
|
|
126
|
+
const msIsOpen = signal(false);
|
|
127
|
+
const cleanupMsListener = effect(() => {
|
|
128
|
+
const mediaSource = context.mediaSource.get();
|
|
129
|
+
if (!mediaSource) {
|
|
130
|
+
msIsOpen.set(false);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
msIsOpen.set(mediaSource.readyState === "open");
|
|
134
|
+
const controller = new AbortController();
|
|
135
|
+
onMediaSourceReadyStateChange(mediaSource, controller.signal, (rs) => {
|
|
136
|
+
msIsOpen.set(rs === "open");
|
|
137
|
+
});
|
|
138
|
+
return () => controller.abort();
|
|
139
|
+
});
|
|
140
|
+
const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaSource.get(), msIsOpen.get(), context.videoBufferActor.get(), context.audioBufferActor.get(), state.currentTime.get()));
|
|
141
|
+
const reactor = createMachineReactor({
|
|
142
|
+
initial: "preconditions-unmet",
|
|
143
|
+
monitor: () => derivedStateSignal.get(),
|
|
144
|
+
states: {
|
|
145
|
+
"preconditions-unmet": {},
|
|
146
|
+
"eos-ready": { entry: () => {
|
|
147
|
+
const mediaSource = context.mediaSource.get();
|
|
148
|
+
const controller = new AbortController();
|
|
149
|
+
const endStreamWhenReady = async () => {
|
|
150
|
+
await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);
|
|
151
|
+
if (controller.signal.aborted) return;
|
|
152
|
+
const bufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);
|
|
153
|
+
if (bufferedEnd > 0) mediaSource.duration = bufferedEnd;
|
|
154
|
+
mediaSource.endOfStream();
|
|
155
|
+
};
|
|
156
|
+
endStreamWhenReady().catch((err) => console.error("Failed to call endOfStream:", err));
|
|
157
|
+
return () => controller.abort();
|
|
158
|
+
} }
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return () => {
|
|
162
|
+
cleanupMsListener();
|
|
163
|
+
reactor.destroy();
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const endOfStream = defineBehavior({
|
|
167
|
+
stateKeys: ["presentation", "currentTime"],
|
|
168
|
+
contextKeys: [
|
|
169
|
+
"mediaSource",
|
|
170
|
+
"videoBufferActor",
|
|
171
|
+
"audioBufferActor"
|
|
172
|
+
],
|
|
173
|
+
setup: endOfStreamSetup
|
|
174
|
+
});
|
|
175
|
+
//#endregion
|
|
176
|
+
export { endOfStream };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=end-of-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"end-of-stream.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/end-of-stream.ts"],"sourcesContent":["/**\n * **Drive each `open → ended` transition of the MediaSource.** Calls\n * `MediaSource.endOfStream()` once the temporally last segments of every\n * active buffer actor's currently-loading track are fully appended and\n * the user has reached them — letting the browser finalize duration and\n * fire `ended` on the media element.\n *\n * Re-fires on every subsequent `open → ended → open` cycle. Per the MSE\n * spec, `appendBuffer()` after `endOfStream()` transitions the MediaSource\n * back to `'open'`; seek-back replays and back-buffer refills that re-load\n * earlier segments take this path, so the behavior must call\n * `endOfStream()` again once the last segments are reappended. The reactor's\n * `'preconditions-unmet'` ↔ `'eos-ready'` cycle *is* the re-arm mechanism:\n * a successful `endOfStream()` flips `mediaSource.readyState` to `'ended'`,\n * which (via the local `msIsOpen` signal) exits `'eos-ready'`; the next\n * `'open'` re-evaluates preconditions and may re-enter.\n *\n * # Tracking what's in the buffer\n *\n * Each `SourceBufferActor` knows which track it's currently loading via\n * `initTrackId` (set on the most recent `append-init` message) and which\n * segments it has appended. `deriveState` iterates over the available\n * buffer actors (`[videoBufferActor, audioBufferActor].filter(Boolean)`),\n * resolves each to its track via `findTrackById`, and checks that track's\n * last segment is appended. This means audio-only / video-only / mixed\n * configurations compose uniformly — the body iterates whatever's in\n * scope. No reliance on `selectedTrackId` slots: the actor's view IS the\n * source of truth for \"what's being loaded into this buffer.\"\n *\n * **Two-fire on mid-end ABR switches**: when a quality switch occurs near\n * end-of-stream, the actor's `initTrackId` still reflects the *old* track\n * until the new init segment is appended. The reactor may fire\n * `endOfStream()` against the old track's last-segment-appended state,\n * then the new init's `appendBuffer()` re-opens the MS, which re-arms us\n * to fire again once the new track's last segment lands. Functionally\n * correct (the browser re-fires `ended` after the re-arm), at the cost of\n * one extra call. Accepted as the price of dropping `selectedTrackId`\n * dependence.\n *\n * # Buffer-actor idle gate\n *\n * Each actor must be `'idle'` (no queued or in-flight tasks) before we\n * fire. `buffer.updating === false` alone is insufficient: for chunked\n * fMP4 streaming, `updateend` fires after each chunk while the actor's\n * for-await loop synchronously enqueues the next `appendBuffer()` — so\n * the buffer flips `!updating → updating` across a microtask boundary.\n * The actor's `'updating'` state spans the entire multi-chunk append, so\n * `actor.snapshot.value === 'idle'` is the canonical \"no more pending or\n * in-flight work\" oracle.\n *\n * This bet (actor models the pipeline; `mediaSource.sourceBuffers` models\n * DOM state) is durable as long as the segment loader is the sole writer\n * of `appendBuffer()` calls. A future loop-mode that auto-fetches earlier\n * segments mid-`ended` would require a broader coordination story\n * between SourceBuffers and the MediaSource — possibly via a\n * `MediaSourceActor` — and that's where this gate would want\n * re-evaluating.\n *\n * # currentTime gate\n *\n * `currentTime` must have reached at least one active track's last\n * segment startTime. Prevents `'eos-ready'` entry when a back-buffer\n * `remove()` / `appendBuffer()` briefly re-opens the MediaSource while\n * the user is mid-stream. HLS rendition time-alignment means any active\n * track works as the reference.\n *\n * # MS readyState — local subscription\n *\n * The behavior subscribes to `mediaSource`'s readyState changes inside\n * its setup (via `onMediaSourceReadyStateChange`) and mirrors `'open'` to\n * a behavior-local `msIsOpen` signal that `deriveState` reads. No shared\n * `mediaSourceReadyState` slot dependency. Anticipates a future\n * `MediaSourceActor` whose snapshot would expose the same signal — the\n * consumer code wouldn't change.\n *\n * # `endOfStream` entry sequence\n *\n * 1. Wait for all SourceBuffers to be idle (DOM-level — defensive,\n * should already hold given the actor-idle gate in `deriveState`).\n * 2. Set `mediaSource.duration` from `getMaxBufferedEnd` to match actual\n * container timestamps (`endOfStream()` only clamps up implicitly;\n * setting it explicitly here keeps the final value deterministic).\n * 3. Call `mediaSource.endOfStream()`.\n *\n * State-exit cleanup (`controller.abort()`) cancels any in-flight wait\n * on source unload, presentation replace, or behavior destroy.\n *\n * # Coordination with `updateMediaSourceDuration`\n *\n * `updateMediaSourceDuration` writes the initial `mediaSource.duration`\n * from `presentation.duration` once per source; this behavior writes the\n * final value from the buffered end. Decision domains don't overlap.\n */\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { effect } from '../../../core/signals/effect';\nimport { computed, type ReadonlySignal, signal } from '../../../core/signals/primitives';\nimport { getMaxBufferedEnd, waitForSourceBuffersReady } from '../../../media/dom/mse/duration';\nimport { isLastSegmentAppended } from '../../../media/dom/mse/end-of-stream';\nimport { onMediaSourceReadyStateChange } from '../../../media/dom/mse/mediasource-setup';\nimport type { MaybeResolvedPresentation } from '../../../media/types';\nimport { isResolvedTrack } from '../../../media/types';\nimport { findTrackById } from '../../../media/utils/tracks';\nimport type { SourceBufferActor } from '../../actors/dom/source-buffer';\n\nexport interface EndOfStreamState {\n presentation?: MaybeResolvedPresentation;\n currentTime?: number;\n}\n\nexport interface EndOfStreamContext {\n mediaSource?: MediaSource;\n videoBufferActor?: SourceBufferActor;\n audioBufferActor?: SourceBufferActor;\n}\n\ntype EndOfStreamFsmState = 'preconditions-unmet' | 'eos-ready';\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaSource: MediaSource | undefined,\n msIsOpen: boolean,\n videoBufferActor: SourceBufferActor | undefined,\n audioBufferActor: SourceBufferActor | undefined,\n currentTime: number | undefined\n): EndOfStreamFsmState {\n if (!mediaSource || !presentation || !msIsOpen) return 'preconditions-unmet';\n\n const actors = [videoBufferActor, audioBufferActor].filter((a): a is SourceBufferActor => a !== undefined);\n // No active buffer actors means setup hasn't completed wiring yet.\n if (actors.length === 0) return 'preconditions-unmet';\n\n // Track the latest last-segment startTime across actors as the\n // currentTime reference. HLS rendition time-alignment makes any active\n // track usable; using the max is safest when timings drift slightly.\n let lastSegStart: number | undefined;\n\n for (const actor of actors) {\n const snapshot = actor.snapshot.get();\n if (snapshot.value !== 'idle') return 'preconditions-unmet';\n\n const { initTrackId, segments: appended } = snapshot.context;\n if (!initTrackId) return 'preconditions-unmet';\n\n const track = findTrackById(presentation, initTrackId);\n if (!track || !isResolvedTrack(track)) return 'preconditions-unmet';\n if (!isLastSegmentAppended(track.segments, appended)) return 'preconditions-unmet';\n\n if (track.segments.length > 0) {\n const start = track.segments[track.segments.length - 1]!.startTime;\n if (lastSegStart === undefined || start > lastSegStart) lastSegStart = start;\n }\n }\n\n if (lastSegStart !== undefined && (currentTime ?? 0) < lastSegStart) {\n return 'preconditions-unmet';\n }\n\n return 'eos-ready';\n}\n\nfunction endOfStreamSetup({\n state,\n context,\n}: {\n state: {\n presentation: ReadonlySignal<EndOfStreamState['presentation']>;\n currentTime: ReadonlySignal<EndOfStreamState['currentTime']>;\n };\n context: {\n mediaSource: ReadonlySignal<EndOfStreamContext['mediaSource']>;\n videoBufferActor: ReadonlySignal<EndOfStreamContext['videoBufferActor']>;\n audioBufferActor: ReadonlySignal<EndOfStreamContext['audioBufferActor']>;\n };\n}): () => void {\n // Behavior-local mirror of `mediaSource.readyState === 'open'`. Subscribes\n // to MS events whenever a MediaSource is in scope; tears down when the\n // MS is swapped or the behavior destroys. Anticipates a future\n // `MediaSourceActor` whose snapshot would expose the same value — the\n // consumer code (deriveState) wouldn't change.\n const msIsOpen = signal(false);\n const cleanupMsListener = effect(() => {\n const mediaSource = context.mediaSource.get();\n if (!mediaSource) {\n msIsOpen.set(false);\n return;\n }\n msIsOpen.set(mediaSource.readyState === 'open');\n const controller = new AbortController();\n onMediaSourceReadyStateChange(mediaSource, controller.signal, (rs) => {\n msIsOpen.set(rs === 'open');\n });\n return () => controller.abort();\n });\n\n const derivedStateSignal = computed(() =>\n deriveState(\n state.presentation.get(),\n context.mediaSource.get(),\n msIsOpen.get(),\n context.videoBufferActor.get(),\n context.audioBufferActor.get(),\n state.currentTime.get()\n )\n );\n\n const reactor = createMachineReactor<EndOfStreamFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n 'eos-ready': {\n // entry body is auto-untracked. deriveState handles source resets\n // and the MS readyState flip from 'open' to 'ended' that\n // endOfStream() produces; this entry resolves the MSE-spec\n // preconditions and binds its cleanup (abort) to state exit +\n // destroy.\n entry: () => {\n const mediaSource = context.mediaSource.get()!;\n const controller = new AbortController();\n\n const endStreamWhenReady = async () => {\n // Defensive: the actor-idle gate in deriveState should mean\n // buffers are already settled, but a microtask boundary\n // between deriveState's read and entry firing leaves a thin\n // gap. The DOM-level wait closes it.\n await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);\n if (controller.signal.aborted) return;\n\n // MSE spec: duration cannot be less than any buffered range, and\n // endOfStream() will only clamp up implicitly. Setting it\n // explicitly here from actual container timestamps keeps the\n // final value deterministic for assets whose declared duration\n // disagrees with the buffered end (common with CMAF).\n const bufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);\n if (bufferedEnd > 0) mediaSource.duration = bufferedEnd;\n\n mediaSource.endOfStream();\n };\n\n endStreamWhenReady().catch((err) => console.error('Failed to call endOfStream:', err));\n\n return () => controller.abort();\n },\n },\n },\n });\n\n return () => {\n cleanupMsListener();\n reactor.destroy();\n };\n}\n\nexport const endOfStream = defineBehavior({\n stateKeys: ['presentation', 'currentTime'],\n contextKeys: ['mediaSource', 'videoBufferActor', 'audioBufferActor'],\n setup: endOfStreamSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHA,SAAS,YACP,cACA,aACA,UACA,kBACA,kBACA,aACqB;AACrB,KAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,SAAU,QAAO;CAEvD,MAAM,SAAS,CAAC,kBAAkB,iBAAiB,CAAC,QAAQ,MAA8B,MAAM,KAAA,EAAU;AAE1G,KAAI,OAAO,WAAW,EAAG,QAAO;CAKhC,IAAI;AAEJ,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,MAAM,SAAS,KAAK;AACrC,MAAI,SAAS,UAAU,OAAQ,QAAO;EAEtC,MAAM,EAAE,aAAa,UAAU,aAAa,SAAS;AACrD,MAAI,CAAC,YAAa,QAAO;EAEzB,MAAM,QAAQ,cAAc,cAAc,YAAY;AACtD,MAAI,CAAC,SAAS,CAAC,gBAAgB,MAAM,CAAE,QAAO;AAC9C,MAAI,CAAC,sBAAsB,MAAM,UAAU,SAAS,CAAE,QAAO;AAE7D,MAAI,MAAM,SAAS,SAAS,GAAG;GAC7B,MAAM,QAAQ,MAAM,SAAS,MAAM,SAAS,SAAS,GAAI;AACzD,OAAI,iBAAiB,KAAA,KAAa,QAAQ,aAAc,gBAAe;;;AAI3E,KAAI,iBAAiB,KAAA,MAAc,eAAe,KAAK,aACrD,QAAO;AAGT,QAAO;;AAGT,SAAS,iBAAiB,EACxB,OACA,WAWa;CAMb,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,oBAAoB,aAAa;EACrC,MAAM,cAAc,QAAQ,YAAY,KAAK;AAC7C,MAAI,CAAC,aAAa;AAChB,YAAS,IAAI,MAAM;AACnB;;AAEF,WAAS,IAAI,YAAY,eAAe,OAAO;EAC/C,MAAM,aAAa,IAAI,iBAAiB;AACxC,gCAA8B,aAAa,WAAW,SAAS,OAAO;AACpE,YAAS,IAAI,OAAO,OAAO;IAC3B;AACF,eAAa,WAAW,OAAO;GAC/B;CAEF,MAAM,qBAAqB,eACzB,YACE,MAAM,aAAa,KAAK,EACxB,QAAQ,YAAY,KAAK,EACzB,SAAS,KAAK,EACd,QAAQ,iBAAiB,KAAK,EAC9B,QAAQ,iBAAiB,KAAK,EAC9B,MAAM,YAAY,KAAK,CACxB,CACF;CAED,MAAM,UAAU,qBAA0C;EACxD,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,uBAAuB,EAAE;GAEzB,aAAa,EAMX,aAAa;IACX,MAAM,cAAc,QAAQ,YAAY,KAAK;IAC7C,MAAM,aAAa,IAAI,iBAAiB;IAExC,MAAM,qBAAqB,YAAY;AAKrC,WAAM,0BAA0B,YAAY,eAAe,WAAW,OAAO;AAC7E,SAAI,WAAW,OAAO,QAAS;KAO/B,MAAM,cAAc,kBAAkB,YAAY,cAAc;AAChE,SAAI,cAAc,EAAG,aAAY,WAAW;AAE5C,iBAAY,aAAa;;AAG3B,wBAAoB,CAAC,OAAO,QAAQ,QAAQ,MAAM,+BAA+B,IAAI,CAAC;AAEtF,iBAAa,WAAW,OAAO;MAElC;GACF;EACF,CAAC;AAEF,cAAa;AACX,qBAAmB;AACnB,UAAQ,SAAS;;;AAIrB,MAAa,cAAc,eAAe;CACxC,WAAW,CAAC,gBAAgB,cAAc;CAC1C,aAAa;EAAC;EAAe;EAAoB;EAAmB;CACpE,OAAO;CACR,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ReadonlySignal } from "../../../core/signals/primitives.js";
|
|
2
|
+
import { Reactor } from "../../../core/reactors/create-machine-reactor.js";
|
|
3
|
+
import { MaybeResolvedPresentation } from "../../../media/types/index.js";
|
|
4
|
+
import { SegmentLoaderActor } from "../../actors/dom/segment-loader.js";
|
|
5
|
+
import { TextTrackSegmentLoaderActor } from "../../actors/text-track-segment-loader.js";
|
|
6
|
+
|
|
7
|
+
//#region src/playback/behaviors/dom/load-segments.d.ts
|
|
8
|
+
/** State shape for segment loading. */
|
|
9
|
+
interface SegmentLoadingState {
|
|
10
|
+
presentation?: MaybeResolvedPresentation;
|
|
11
|
+
preload?: string;
|
|
12
|
+
/** Current playback position in seconds. Defaults to 0 when undefined. */
|
|
13
|
+
currentTime?: number;
|
|
14
|
+
/** True once a preload-overriding event has fired for the current source. */
|
|
15
|
+
loadActivated?: boolean;
|
|
16
|
+
selectedVideoTrackId?: string;
|
|
17
|
+
selectedAudioTrackId?: string;
|
|
18
|
+
selectedTextTrackId?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Context shape for segment loading. Each variant only consumes its own type's loader actor. */
|
|
21
|
+
interface SegmentLoadingContext {
|
|
22
|
+
videoSegmentLoaderActor?: SegmentLoaderActor;
|
|
23
|
+
audioSegmentLoaderActor?: SegmentLoaderActor;
|
|
24
|
+
textTrackSegmentLoaderActor?: TextTrackSegmentLoaderActor;
|
|
25
|
+
}
|
|
26
|
+
type SegmentLoadingFsmState = 'preconditions-unmet' | 'dormant' | 'metadata-only' | 'full-range';
|
|
27
|
+
type SelectedTrackKey = 'selectedVideoTrackId' | 'selectedAudioTrackId' | 'selectedTextTrackId';
|
|
28
|
+
type SegmentLoadingStateMap<K extends SelectedTrackKey> = {
|
|
29
|
+
presentation: ReadonlySignal<SegmentLoadingState['presentation']>;
|
|
30
|
+
preload: ReadonlySignal<SegmentLoadingState['preload']>;
|
|
31
|
+
currentTime: ReadonlySignal<SegmentLoadingState['currentTime']>;
|
|
32
|
+
loadActivated: ReadonlySignal<SegmentLoadingState['loadActivated']>;
|
|
33
|
+
} & { [P in K]: ReadonlySignal<SegmentLoadingState[P]> };
|
|
34
|
+
declare const loadVideoSegments: {
|
|
35
|
+
stateKeys: readonly ["presentation", "preload", "currentTime", "loadActivated", "selectedVideoTrackId"];
|
|
36
|
+
contextKeys: readonly ["videoSegmentLoaderActor"];
|
|
37
|
+
setup: (deps: {
|
|
38
|
+
state: SegmentLoadingStateMap<"selectedVideoTrackId">;
|
|
39
|
+
} & {
|
|
40
|
+
context: {
|
|
41
|
+
videoSegmentLoaderActor: ReadonlySignal<SegmentLoadingContext['videoSegmentLoaderActor']>;
|
|
42
|
+
};
|
|
43
|
+
} & {
|
|
44
|
+
config?: object;
|
|
45
|
+
}) => Reactor<"destroyed" | "destroying" | SegmentLoadingFsmState>;
|
|
46
|
+
};
|
|
47
|
+
declare const loadAudioSegments: {
|
|
48
|
+
stateKeys: readonly ["presentation", "preload", "currentTime", "loadActivated", "selectedAudioTrackId"];
|
|
49
|
+
contextKeys: readonly ["audioSegmentLoaderActor"];
|
|
50
|
+
setup: (deps: {
|
|
51
|
+
state: SegmentLoadingStateMap<"selectedAudioTrackId">;
|
|
52
|
+
} & {
|
|
53
|
+
context: {
|
|
54
|
+
audioSegmentLoaderActor: ReadonlySignal<SegmentLoadingContext['audioSegmentLoaderActor']>;
|
|
55
|
+
};
|
|
56
|
+
} & {
|
|
57
|
+
config?: object;
|
|
58
|
+
}) => Reactor<"destroyed" | "destroying" | SegmentLoadingFsmState>;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { loadAudioSegments, loadVideoSegments };
|
|
62
|
+
//# sourceMappingURL=load-segments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-segments.d.ts","names":[],"sources":["../../../../../src/playback/behaviors/dom/load-segments.ts"],"mappings":";;;;;;;;UAoEiB,mBAAA;EACf,YAAA,GAAe,yBAAA;EACf,OAAA;EADwC;EAGxC,WAAA;EAHe;EAKf,aAAA;EACA,oBAAA;EACA,oBAAA;EACA,mBAAA;AAAA;;UAIe,qBAAA;EACf,uBAAA,GAA0B,kBAAA;EAC1B,uBAAA,GAA0B,kBAAA;EAC1B,2BAAA,GAA8B,2BAAA;AAAA;AAAA,KAO3B,sBAAA;AAAA,KAEA,gBAAA;AAAA,KAGA,sBAAA,WAAiC,gBAAA;EACpC,YAAA,EAAc,cAAA,CAAe,mBAAA;EAC7B,OAAA,EAAS,cAAA,CAAe,mBAAA;EACxB,WAAA,EAAa,cAAA,CAAe,mBAAA;EAC5B,aAAA,EAAe,cAAA,CAAe,mBAAA;AAAA,YACpB,CAAA,GAAI,cAAA,CAAe,mBAAA,CAAoB,CAAA;AAAA,cAiItC,iBAAA;;;;;;;+BAUkB,cAAA,CAAe,qBAAA;IAAA;EAAA;;;;cAWjC,iBAAA;;;;;;;+BAUkB,cAAA,CAAe,qBAAA;IAAA;EAAA"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { computed, peek } from "../../../core/signals/primitives.js";
|
|
2
|
+
import { defineBehavior } from "../../../core/composition/create-composition.js";
|
|
3
|
+
import { createMachineReactor } from "../../../core/reactors/create-machine-reactor.js";
|
|
4
|
+
import { DEFAULT_FORWARD_BUFFER_CONFIG, segmentStartForTime } from "../../../media/buffer/forward-buffer.js";
|
|
5
|
+
import { findResolvedAudioTrack, findResolvedTextTrack, findResolvedVideoTrack } from "../../../media/utils/tracks.js";
|
|
6
|
+
import { AUDIO_TYPE_CONFIG, TEXT_TYPE_CONFIG, VIDEO_TYPE_CONFIG } from "../track-types.js";
|
|
7
|
+
//#region src/playback/behaviors/dom/load-segments.ts
|
|
8
|
+
/**
|
|
9
|
+
* **Per-type segment loading dispatch.** Per available track type (video /
|
|
10
|
+
* audio / text), reads the per-type segment-loader actor from context and
|
|
11
|
+
* sends typed `'load'` messages whenever a meaningful loading condition
|
|
12
|
+
* changes (selected track, current time crossing a segment boundary).
|
|
13
|
+
*
|
|
14
|
+
* Loader-actor lifecycle is owned upstream:
|
|
15
|
+
* - Video: `setupVideoBufferActors`
|
|
16
|
+
* - Audio: `setupAudioBufferActors`
|
|
17
|
+
* - Text: `setupTextTrackActors`
|
|
18
|
+
*
|
|
19
|
+
* This behavior is pure-consumer: it reads `context[loaderKey]` and
|
|
20
|
+
* dispatches typed messages via the variant's per-type `findResolvedTrack`
|
|
21
|
+
* resolver.
|
|
22
|
+
*
|
|
23
|
+
* # Load modes as reactor states
|
|
24
|
+
*
|
|
25
|
+
* Four states encode the load-gating policy directly:
|
|
26
|
+
*
|
|
27
|
+
* - `'preconditions-unmet'` — no loader actor in context, or the selected
|
|
28
|
+
* track hasn't resolved.
|
|
29
|
+
* - `'dormant'` — `preload === 'none' && !loadActivated`. Nothing fires.
|
|
30
|
+
* - `'metadata-only'` — `!loadActivated && preload !== 'auto' && preload !== 'none'`.
|
|
31
|
+
* Fires an init-segment-only `load` message **once on entry**. The
|
|
32
|
+
* variant's loader actor decides what to do — v/a's actor fetches the
|
|
33
|
+
* init segment; text's actor no-ops (no init concept).
|
|
34
|
+
* - `'full-range'` — `loadActivated || preload === 'auto'`. Effect re-fires
|
|
35
|
+
* on selected-track change and on segment-boundary crossing.
|
|
36
|
+
*
|
|
37
|
+
* # Per-type parameterization (inference-driven)
|
|
38
|
+
*
|
|
39
|
+
* The helper is generic over `Track` — the resolved-track type. Each
|
|
40
|
+
* variant supplies its own `findResolvedTrack` resolver via config; TS
|
|
41
|
+
* infers `Track` from the resolver's return type. The loader signal's
|
|
42
|
+
* value type is constrained to `SegmentLoaderLike<Track>` — anything with
|
|
43
|
+
* a `send` method accepting the `Track`-parameterized message. Concrete
|
|
44
|
+
* actor types (`SegmentLoaderActor`, `TextTrackSegmentLoaderActor`)
|
|
45
|
+
* satisfy this via function-parameter contravariance: an actor whose
|
|
46
|
+
* `.send` accepts a wider track type is assignable to a slot expecting a
|
|
47
|
+
* narrower track type.
|
|
48
|
+
*
|
|
49
|
+
* No widening of actor message types is needed; no casts inside the
|
|
50
|
+
* helper. Per-variant wiring (right loader paired with right resolver)
|
|
51
|
+
* is enforced at the variant call site.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* Specialization helper. Generic over `Track` (inferred from
|
|
55
|
+
* `findResolvedTrack`'s return type). The loader value type is
|
|
56
|
+
* constrained to `SegmentLoaderLike<Track>` — concrete actor types
|
|
57
|
+
* (whose `.send` accepts a wider track union) satisfy this via
|
|
58
|
+
* function-parameter contravariance.
|
|
59
|
+
*
|
|
60
|
+
* Tracks that the helper handles must have a `segments` field — used by
|
|
61
|
+
* `segmentBoundarySignal` to compute the load-anchor boundary. Each
|
|
62
|
+
* variant's resolver narrows to the right resolved-track shape.
|
|
63
|
+
*/
|
|
64
|
+
function setupSegmentLoading({ state, context, config }) {
|
|
65
|
+
const { selectedKey, loaderKey, findResolvedTrack } = config;
|
|
66
|
+
const bufferDuration = config.forwardBuffer?.bufferDuration ?? DEFAULT_FORWARD_BUFFER_CONFIG.bufferDuration;
|
|
67
|
+
const selectedTrack = computed(() => findResolvedTrack(state.presentation.get(), state[selectedKey].get()));
|
|
68
|
+
const segmentBoundarySignal = computed(() => {
|
|
69
|
+
const track = selectedTrack.get();
|
|
70
|
+
if (!track) return void 0;
|
|
71
|
+
return segmentStartForTime(state.currentTime.get() ?? 0, track.segments);
|
|
72
|
+
});
|
|
73
|
+
const derivedStateSignal = computed(() => {
|
|
74
|
+
if (!context[loaderKey].get() || !selectedTrack.get()) return "preconditions-unmet";
|
|
75
|
+
if (state.loadActivated.get() || state.preload.get() === "auto") return "full-range";
|
|
76
|
+
if (state.preload.get() === "none") return "dormant";
|
|
77
|
+
return "metadata-only";
|
|
78
|
+
});
|
|
79
|
+
return createMachineReactor({
|
|
80
|
+
initial: "preconditions-unmet",
|
|
81
|
+
monitor: () => derivedStateSignal.get(),
|
|
82
|
+
states: {
|
|
83
|
+
"preconditions-unmet": {},
|
|
84
|
+
dormant: {},
|
|
85
|
+
"metadata-only": { entry: () => {
|
|
86
|
+
const track = selectedTrack.get();
|
|
87
|
+
context[loaderKey].get().send({
|
|
88
|
+
type: "load",
|
|
89
|
+
track
|
|
90
|
+
});
|
|
91
|
+
} },
|
|
92
|
+
"full-range": { effects: () => {
|
|
93
|
+
const track = selectedTrack.get();
|
|
94
|
+
segmentBoundarySignal.get();
|
|
95
|
+
const currentTime = peek(state.currentTime) ?? 0;
|
|
96
|
+
peek(context[loaderKey]).send({
|
|
97
|
+
type: "load",
|
|
98
|
+
track,
|
|
99
|
+
range: {
|
|
100
|
+
start: currentTime,
|
|
101
|
+
end: currentTime + bufferDuration
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
} }
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const VIDEO_SEGMENT_LOADING_CONFIG = {
|
|
109
|
+
...VIDEO_TYPE_CONFIG,
|
|
110
|
+
findResolvedTrack: findResolvedVideoTrack
|
|
111
|
+
};
|
|
112
|
+
const AUDIO_SEGMENT_LOADING_CONFIG = {
|
|
113
|
+
...AUDIO_TYPE_CONFIG,
|
|
114
|
+
findResolvedTrack: findResolvedAudioTrack
|
|
115
|
+
};
|
|
116
|
+
const TEXT_SEGMENT_LOADING_CONFIG = {
|
|
117
|
+
...TEXT_TYPE_CONFIG,
|
|
118
|
+
findResolvedTrack: findResolvedTextTrack
|
|
119
|
+
};
|
|
120
|
+
const loadVideoSegments = defineBehavior({
|
|
121
|
+
stateKeys: [
|
|
122
|
+
"presentation",
|
|
123
|
+
"preload",
|
|
124
|
+
"currentTime",
|
|
125
|
+
"loadActivated",
|
|
126
|
+
"selectedVideoTrackId"
|
|
127
|
+
],
|
|
128
|
+
contextKeys: ["videoSegmentLoaderActor"],
|
|
129
|
+
setup: ({ state, context, config = {} }) => setupSegmentLoading({
|
|
130
|
+
state,
|
|
131
|
+
context,
|
|
132
|
+
config: {
|
|
133
|
+
...VIDEO_SEGMENT_LOADING_CONFIG,
|
|
134
|
+
...config
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
});
|
|
138
|
+
const loadAudioSegments = defineBehavior({
|
|
139
|
+
stateKeys: [
|
|
140
|
+
"presentation",
|
|
141
|
+
"preload",
|
|
142
|
+
"currentTime",
|
|
143
|
+
"loadActivated",
|
|
144
|
+
"selectedAudioTrackId"
|
|
145
|
+
],
|
|
146
|
+
contextKeys: ["audioSegmentLoaderActor"],
|
|
147
|
+
setup: ({ state, context, config = {} }) => setupSegmentLoading({
|
|
148
|
+
state,
|
|
149
|
+
context,
|
|
150
|
+
config: {
|
|
151
|
+
...AUDIO_SEGMENT_LOADING_CONFIG,
|
|
152
|
+
...config
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
});
|
|
156
|
+
const loadTextTrackSegments = defineBehavior({
|
|
157
|
+
stateKeys: [
|
|
158
|
+
"presentation",
|
|
159
|
+
"preload",
|
|
160
|
+
"currentTime",
|
|
161
|
+
"loadActivated",
|
|
162
|
+
"selectedTextTrackId"
|
|
163
|
+
],
|
|
164
|
+
contextKeys: ["textTrackSegmentLoaderActor"],
|
|
165
|
+
setup: ({ state, context, config = {} }) => setupSegmentLoading({
|
|
166
|
+
state,
|
|
167
|
+
context,
|
|
168
|
+
config: {
|
|
169
|
+
...TEXT_SEGMENT_LOADING_CONFIG,
|
|
170
|
+
...config
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
});
|
|
174
|
+
//#endregion
|
|
175
|
+
export { loadAudioSegments, loadTextTrackSegments, loadVideoSegments };
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=load-segments.js.map
|