// The shared shape of an encode-ladder rung and a named preset, single-sourced // for every ctl product. Products previously each carried their own // near-identical copy of these types in `shared/src/workflow/ladder-presets.ts` // (commentary's set, playout's `flagship`/`uhd-hevc`), whose comments already // noted they "mirror" one another — this is the shared home they mirror. // // What lives here is the SHAPE and the product-agnostic helpers. The catalogue // of presets and its key union stay per-product (they are genuinely different: // commentary ships `single`/`mobile`/`abr-internet`/`tv`/`vertical-mobile`, // playout ships `flagship`/`uhd-hevc`), as do product-specific rungs such as // commentary's fixed low-latency return-video rung. A product declares its // catalogue as `Record` over these types. // // Deliberately zero-dependency (plain types + a pure helper, no zod): the // per-product zod `RungSchema`s carry product-specific tuning (differing kbps // ceilings, playout's `codec` axis, distinct `.meta` ids) and the cycle-B // constraint that a schema module must not import a workflow module, so those // stay per-product and are a separate consolidation question. /** * One rung of an encode ladder: a single rendition's name, bitrate, resolution * and frame rate. `codec` is optional and absent by default — absent ⇒ the * product's H.264 default; `"hevc"` selects the HEVC encoder path. Products * that don't expose a codec axis simply never set it. */ export type LadderRung = { name: string; kbps: number; width: number; height: number; fps: number; bitrateMode: "abr" | "cbr" | "vbr"; // Absent ⇒ h264 (the composer default). "hevc" selects the HEVC encoder trio. codec?: "h264" | "hevc"; }; /** A named, operator-selectable ladder: a label plus its ordered rungs. */ export type LadderPreset = { label: string; rungs: LadderRung[]; }; /** * True when a rung is taller than it is wide (portrait / vertical delivery). * Orientation is read off a rung's dimensions rather than a separate flag, so * a product can derive a compose shape or a matching low-latency rung from the * top user rung without threading an extra config axis. */ export function isPortraitRung(rung: Pick): boolean { return rung.height > rung.width; }