mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
96 lines
3.2 KiB
JavaScript
96 lines
3.2 KiB
JavaScript
import { effect } from "../../core/signals/effect.js";
|
|
//#region src/playback/primitives/segment-load-pipeline.ts
|
|
/**
|
|
* Base-step view of the loader's own wiring. `createSegmentLoaderActor` folds its
|
|
* `sourceBufferActor` + `fetch` into the threaded `config` so base steps read them
|
|
* from the uniform passthrough — present whether the loader runs inside a composition
|
|
* or standalone. `config` is loose (`object`), so assert the shape here (one cast, like
|
|
* relocation's `containerSlot`).
|
|
*/
|
|
function stepWiring(deps) {
|
|
return deps.config;
|
|
}
|
|
/**
|
|
* Resolves when the SourceBufferActor snapshot reaches 'idle'.
|
|
* Rejects if the signal is aborted or the actor is destroyed.
|
|
*
|
|
* Used to sequence SourceBufferActor operations without awaiting send()
|
|
* directly — send() is fire-and-forget; callers observe completion via
|
|
* state transition.
|
|
*/
|
|
function waitForIdle(snapshot, signal) {
|
|
return new Promise((resolve, reject) => {
|
|
if (snapshot.get().value === "idle") {
|
|
resolve();
|
|
return;
|
|
}
|
|
if (snapshot.get().value === "destroyed") {
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
return;
|
|
}
|
|
if (signal.aborted) {
|
|
reject(signal.reason);
|
|
return;
|
|
}
|
|
let stop;
|
|
const cleanup = (fn) => {
|
|
stop?.();
|
|
signal.removeEventListener("abort", onAbort);
|
|
fn();
|
|
};
|
|
const onAbort = () => cleanup(() => reject(signal.reason));
|
|
stop = effect(() => {
|
|
const value = snapshot.get().value;
|
|
if (value === "idle") cleanup(resolve);
|
|
else if (value === "destroyed") cleanup(() => reject(new DOMException("Aborted", "AbortError")));
|
|
});
|
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
});
|
|
}
|
|
/** Build the SourceBuffer message a completed frame dispatches. `fetchStep` always precedes `dispatchStep` in append pipelines, so `data` is set by now. */
|
|
function toMessage({ op, data, meta }) {
|
|
switch (op.type) {
|
|
case "remove": return op;
|
|
case "append-init": return {
|
|
type: "append-init",
|
|
data,
|
|
meta: op.meta
|
|
};
|
|
case "append-segment": return {
|
|
type: "append-segment",
|
|
data,
|
|
meta: meta ?? op.meta
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Fetch this op's bytes into the frame. Init segments need the full body
|
|
* (`minChunkSize: Infinity`) before appending; media segments stream so chunks
|
|
* append as they arrive. Awaiting headers eagerly also starts the HTTP
|
|
* connection (and records the fetch in observers like tests).
|
|
*/
|
|
const fetchStep = async (frame, signal, deps) => {
|
|
const { op } = frame;
|
|
if (op.type === "remove") return;
|
|
const { fetch } = stepWiring(deps);
|
|
frame.data = await fetch(op, op.type === "append-init" ? {
|
|
signal,
|
|
minChunkSize: Infinity
|
|
} : { signal });
|
|
};
|
|
/** Dispatch the frame's message to the SourceBufferActor and await its return to idle. */
|
|
const dispatchStep = async (frame, signal, deps) => {
|
|
const { sourceBufferActor } = stepWiring(deps);
|
|
sourceBufferActor.send(toMessage(frame));
|
|
await waitForIdle(sourceBufferActor.snapshot, signal);
|
|
};
|
|
/** Tier 0 default: fetch (for ops that carry bytes) then dispatch. No relocation vocabulary. */
|
|
const DEFAULT_MESSAGE_PIPELINES = () => ({
|
|
remove: [dispatchStep],
|
|
"append-init": [fetchStep, dispatchStep],
|
|
"append-segment": [fetchStep, dispatchStep]
|
|
});
|
|
//#endregion
|
|
export { DEFAULT_MESSAGE_PIPELINES, dispatchStep, fetchStep };
|
|
|
|
//# sourceMappingURL=segment-load-pipeline.js.map
|