/** * Face-tracked reframe — pure planning + ffmpeg-filter synthesis. * * `analyzeReframe` shells out to the MediaPipe / PySceneDetect sidecar to get * a per-shot smoothed face centre. `buildReframeFilter` is a pure function * that compiles the plan into a single ffmpeg `crop=...` expression that * follows the subject across shots. Splitting the heavy I/O from the * filter math keeps the latter unit-testable without Python installed. */ export type Aspect = "9:16" | "1:1" | "4:5" | "16:9"; export interface AspectRatio { /** Width side of the ratio (e.g. 9 for 9:16). */ w: number; /** Height side of the ratio (e.g. 16 for 9:16). */ h: number; } export declare function parseAspect(aspect: Aspect): AspectRatio; export interface ReframeFrameSample { atSec: number; faceCx: number; faceCy: number; faceW: number; faceH: number; } export type ShotMode = "face" | "motion" | "static"; export interface ReframeShot { startSec: number; endSec: number; frames: ReframeFrameSample[]; /** Smoothed centre X (normalized 0-1). */ smoothedX: number; /** Smoothed centre Y (normalized 0-1). */ smoothedY: number; mode: ShotMode; } export interface ReframePlan { shots: ReframeShot[]; totalSec: number; fps: number; sourceWidth: number; sourceHeight: number; } export interface AnalyzeReframeOptions { signal?: AbortSignal; /** Frames per second to face-detect. Default 5. */ sampleFps?: number; /** MediaPipe min_detection_confidence. Default 0.5. */ minDetectionConfidence?: number; /** Smoothing window in seconds (informational; sidecar uses median). Default 0.5. */ smoothingWindowSec?: number; } /** * Spawn the face-reframe sidecar; return the parsed shot plan. * Throws on sidecar failure with the structured error embedded. */ export declare function analyzeReframe(videoPath: string, opts?: AnalyzeReframeOptions): Promise; export interface BuildReframeFilterResult { /** ffmpeg `-vf` value (already includes both crop + scale). */ filter: string; /** Output width (after crop, before scale-back). */ outWidth: number; /** Output height (after crop, before scale-back). */ outHeight: number; /** Per-shot crop X positions (after clamping into [0, srcW-outW]). */ shotXs: number[]; } /** * Compute the crop dimensions for a target aspect when cropping FROM a * source frame. The crop is sized to fit ENTIRELY inside the source — i.e. * we letterbox neither dimension. The returned `outW` × `outH` matches the * target aspect exactly. * * Pure / unit-testable. */ export declare function reframeCropSize(srcW: number, srcH: number, aspect: Aspect): { outW: number; outH: number; }; /** * Compile a per-shot smoothed-face plan into one ffmpeg `crop` expression * that switches X position by timestamp. Each shot contributes one * `if(between(t, a, b), x_i, ...)` tier; the outermost ELSE is a centre * crop so the filter is a no-op outside any defined shot. * * The crop's Y is left static (we don't currently track vertical motion; * faces tend to dominate the upper third on portrait crops anyway). * * Pure / unit-testable. No ffmpeg execution. */ export declare function buildReframeFilter(plan: ReframePlan, aspect: Aspect): BuildReframeFilterResult; //# sourceMappingURL=face-reframe.d.ts.map