Files
v10/dist/dev/playback/primitives/segment-load-pipeline.js
T

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