import { type RunFfmpegResult } from './assemble/ffmpeg.js'; /** Canonical target ids, ordered smallest first. `--target` validates against this. */ export declare const FFMPEG_UPSCALE_TARGET_IDS: readonly ["1080p", "1440p", "2160p"]; export type FfmpegUpscaleTarget = (typeof FFMPEG_UPSCALE_TARGET_IDS)[number]; /** Target id → the exact `[width, height]` box it means. */ export declare const FFMPEG_UPSCALE_TARGETS: Record; /** * The encoders this backend will drive. `libx264` is the portable default; * `hevc_videotoolbox` is macOS hardware H.265 — roughly 3× faster and smaller at * matched quality, but it is opt-in because it does not exist on every box and a * silent substitution across encoders is exactly the kind of invisible route * change ADR 0001 forbids. */ export declare const FFMPEG_UPSCALE_ENCODERS: readonly ["libx264", "hevc_videotoolbox"]; export type FfmpegUpscaleEncoder = (typeof FFMPEG_UPSCALE_ENCODERS)[number]; export declare const FFMPEG_UPSCALE_DEFAULT_TARGET: FfmpegUpscaleTarget; export declare const FFMPEG_UPSCALE_DEFAULT_ENCODER: FfmpegUpscaleEncoder; export declare const FFMPEG_UPSCALE_DEFAULT_SHARPEN = 0.8; export declare const FFMPEG_UPSCALE_DEFAULT_CRF = 18; /** Light temporal denoise, run BEFORE the scaler. See the module header. */ export declare const FFMPEG_UPSCALE_DENOISE_FILTER = "hqdn3d=1.5:1.5:6:6"; /** hevc_videotoolbox quality (0..100, higher is better) — visually matched to crf 18. */ export declare const FFMPEG_UPSCALE_VIDEOTOOLBOX_QUALITY = 65; /** Knobs shared by the pure builder, the runner and the CLI flag parser. */ export interface FfmpegUpscaleConfig { /** Target box (default 1080p). Ignored when explicit width+height are given. */ target?: FfmpegUpscaleTarget; /** Explicit target width (requires height; wins over `target`). */ width?: number; /** Explicit target height (requires width; wins over `target`). */ height?: number; /** Run the pre-scale denoise (default true). `--no-denoise` turns it off. */ denoise?: boolean; /** unsharp luma amount (default 0.8). 0 drops the unsharp stage entirely. */ sharpen?: number; /** Video encoder (default libx264). */ encoder?: FfmpegUpscaleEncoder; /** libx264 CRF (default 18). Rejected for hevc_videotoolbox, which is -q:v driven. */ crf?: number; } export interface FfmpegUpscaleArgsOptions extends FfmpegUpscaleConfig { input: string; output: string; /** Source width, when known — used ONLY to refuse a downscale. */ sourceWidth?: number; /** Source height, when known — used ONLY to refuse a downscale. */ sourceHeight?: number; /** * Source pixel shape as ffprobe reports it (`'4:3'`, `'1:1'`, `'0:1'`). Anything * but square makes `sourceWidth`x`sourceHeight` storage geometry, which the * chain corrects before fitting. Absent/square → the chain is unchanged. */ sourceSampleAspect?: string; } /** The resolved target box plus the id it came from (absent for explicit w/h). */ export interface ResolvedFfmpegUpscaleSize { width: number; height: number; target?: FfmpegUpscaleTarget; } /** * Resolve the target box: explicit `width`+`height` win over `target`, which * defaults to 1080p. PURE. Supplying only one of width/height is an error rather * than a half-applied override. */ export declare function resolveFfmpegUpscaleSize(config: FfmpegUpscaleConfig): ResolvedFfmpegUpscaleSize; /** * The display geometry of a source, given its storage dimensions and the pixel * shape ffprobe reported. PURE. Returns `undefined` when the pixels are already * square (`1:1`, the unspecified `0:1`, absent, or unparseable) — the caller then * leaves the chain exactly as it was. * * Width is rounded to an EVEN number: yuv420p has no odd dimensions, and this * stage sets the geometry every later stage inherits. */ export declare function resolveSquarePixelSize(sourceWidth: number | undefined, sourceHeight: number | undefined, sampleAspect: string | undefined): { width: number; height: number; } | undefined; /** * The dimensions a source actually DISPLAYS at — storage dims corrected for a * non-square pixel shape. PURE. This, not the storage geometry, is what the * downscale refusal has to compare against. */ export declare function resolveDisplaySize(sourceWidth: number | undefined, sourceHeight: number | undefined, sampleAspect: string | undefined): { width: number; height: number; } | undefined; /** * Build the `-vf` filter chain. PURE. Stage order is the recipe and is not * configurable: denoise → [square-pixel correction] → scale → pad → unsharp. */ export declare function buildFfmpegUpscaleFilters(width: number, height: number, opts?: { denoise?: boolean; sharpen?: number; squarePixel?: { width: number; height: number; }; }): string; /** The encoder half of the argv. PURE. */ export declare function buildFfmpegUpscaleEncoderArgs(encoder: FfmpegUpscaleEncoder, crf: number): string[]; /** * Build the full ffmpeg argv for one upscale. PURE — never probes, never spawns. * `-y` is NOT included: {@link runFfmpeg} prepends it. * * Throws `VclawError('invalid_flag_value')` when the requested box would SHRINK a * known source. "Upscale" that quietly downscales is the worst possible outcome * of a finishing pass, and the operator asked for a hi-res master. */ export declare function buildFfmpegUpscaleArgs(opts: FfmpegUpscaleArgsOptions): string[]; /** * The encoder names ` -encoders` advertises. Returns an EMPTY set when the * binary could not be run at all — callers must treat empty as "unknown", not as * "the encoder is missing", so a machine without ffmpeg still plans. */ export declare function probeFfmpegEncoders(bin: string): Promise>; /** Test seam — clears the per-binary encoder cache. */ export declare function resetFfmpegEncoderCache(): void; export interface RunFfmpegUpscaleOptions extends FfmpegUpscaleArgsOptions { /** Build the command without spawning ffmpeg. */ dryRun?: boolean; /** Override the ffmpeg binary (else VCLAW_FFMPEG_BIN, then `ffmpeg`). */ ffmpegBin?: string; /** Injectable ffmpeg runner (default {@link runFfmpeg}) — for offline tests. */ runner?: (args: string[], opts: { dryRun?: boolean; ffmpegBin?: string; }) => Promise; /** Injectable encoder probe (default {@link probeFfmpegEncoders}) — for offline tests. */ encoderProbe?: (bin: string) => Promise>; /** Non-fatal notes from the caller (e.g. "planned without a probe"). */ warnings?: string[]; } /** What one ffmpeg upscale did (or would do). `providerCalls`/`spend` are always 0. */ export interface FfmpegUpscaleResult { backend: 'ffmpeg-upscale'; input: string; output: string; /** The target id, when the box came from `--target` rather than explicit w/h. */ target?: FfmpegUpscaleTarget; width: number; height: number; encoder: FfmpegUpscaleEncoder; /** The exact `-vf` chain. */ filters: string; /** The ffmpeg argv (no `-y`, no binary) — the byte-exact contract. */ command: string[]; /** The resolved, copy-pasteable command line including the binary and `-y`. */ commandLine: string; denoise: boolean; sharpen: number; /** Present for libx264 only. */ crf?: number; sourceWidth?: number; sourceHeight?: number; /** Source pixel shape, when probed. Anything but `1:1`/`0:1` triggered a correction. */ sourceSampleAspect?: string; /** The square-pixel geometry the source was corrected to, when it was anamorphic. */ squarePixelCorrection?: { width: number; height: number; }; /** Non-fatal notes — e.g. a plan made without a probe. Omitted when empty. */ warnings?: string[]; providerCalls: 0; spend: 0; dryRun: boolean; } /** * Run one local upscale. Free: no upload, no submit, no credits — the only * process it starts is ffmpeg on this machine. * * A non-default encoder is checked against `ffmpeg -encoders` first and REFUSED * when the binary demonstrably lacks it, rather than discovered 20 minutes into * a render. An unprobeable binary is not treated as a refusal (see * {@link probeFfmpegEncoders}). */ export declare function runFfmpegUpscale(opts: RunFfmpegUpscaleOptions): Promise; /** * Parse the ffmpeg-upscale flags out of `args`. * * Lives here rather than in the handler for two reasons: `media-production.ts` * is at its module-size ceiling, and this keeps the flag contract next to the * knobs it sets. On a DIFFERENT backend it returns `undefined` — after refusing * any ffmpeg-only flag that was passed, matching how `--target-resolution` and * `--normalize` are rejected off `magnific-precision` instead of silently * ignored. */ export declare function parseFfmpegUpscaleFlags(args: string[], backend: string): FfmpegUpscaleConfig | undefined; //# sourceMappingURL=finish-ffmpeg.d.ts.map