/** * Render Orchestrator Service * * `executeRenderJob` is the in-process entry point that composes the * pipeline's six stages. Each stage lives in its own module under * `./render/stages/` so the pure-function primitives can be reused by * the distributed render path without dragging the orchestrator's * cleanup and observability scaffolding with them. * * Stage 1 compile → services/render/stages/compileStage.ts * Stage 1b probe → services/render/stages/probeStage.ts * (browser-driven duration discovery + media reconciliation; * grouped with Stage 1 in the perf summary) * Stage 2 extract videos → services/render/stages/extractVideosStage.ts * Stage 3 audio → services/render/stages/audioStage.ts * Stage 4 capture → services/render/stages/captureStage.ts * services/render/stages/captureStreamingStage.ts * services/render/stages/captureHdrStage.ts * Stage 5 encode → services/render/stages/encodeStage.ts * Stage 6 assemble → services/render/stages/assembleStage.ts * * Resources spawned by stages (file server, capture sessions, streaming * encoders, raw HDR frame files) are tracked in the orchestrator's * `try/finally` so a stage throwing mid-pipeline doesn't leak Chrome * processes or ffmpeg subprocesses. * * Heavy observability: every stage records timing into `perfStages`, * errors carry full context, and failures produce a diagnostic summary * (browser console tail, memory peaks, capture attempts, HDR * diagnostics). */ import { type CanvasResolution, type Fps, type FpsInput } from "@hyperframes/core"; import { type EngineConfig, type ExtractionPhaseBreakdown, type HdrTransfer, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ParallelProgress, type WorkerTask, type ElementStackingInfo, type HfTransitionMeta } from "@hyperframes/engine"; import { type ProducerLogger } from "../logger.js"; import { type HdrImageTransferCache } from "./hdrImageTransferCache.js"; import { type RenderObservabilitySummary } from "./render/observability.js"; import { type HdrPerfCollector, type HdrPerfSummary } from "./render/hdrPerf.js"; /** * Metadata for a shader transition between two scenes, extracted from * `window.__hf.transitions`. Re-exported from the engine so the producer * shares the contract with composition runtime code. */ export type HdrTransitionMeta = HfTransitionMeta; /** Pre-computed frame range for an active transition. */ export interface TransitionRange extends HdrTransitionMeta { startFrame: number; endFrame: number; } export type RenderStatus = "queued" | "preprocessing" | "rendering" | "encoding" | "assembling" | "complete" | "failed" | "cancelled"; export interface RenderConfig { /** * Frame rate as an exact rational. Integer fps is `{ num: 30, den: 1 }`; * NTSC is `{ num: 30000, den: 1001 }`. This shape lets the orchestrator * pass the exact rational through to FFmpeg's `-r` / `-framerate` flags * without a decimal round-trip — see `fpsToFfmpegArg` in @hyperframes/core. * * Use `fpsToNumber(config.fps)` at any site that needs a `number` for * arithmetic (frame-index → time, telemetry, frame-interval ms). Decimal * precision at our scales is more than sufficient. */ fps: Fps; quality: "draft" | "standard" | "high"; /** * Output container format. Defaults to `"mp4"`; existing renders are * unaffected unless this field is set explicitly. * * - `"mp4"`: H.264 by default, or H.265 + HDR10 when HDR auto-detect * engages or `hdrMode: "force-hdr"` is set. Opaque. The * default streaming/social deliverable. Faststart is applied so the * `moov` atom sits at the file start and the file plays from a * partial download. * - `"webm"`: VP9 + `yuva420p` pixel format → **true alpha channel**, no * chroma key. Plays in Chrome, Edge, and Firefox; Safari support for * alpha-WebM is incomplete. Use this when the output should drop * straight into a `