← Files Cecil-IA Labs FFmpegARCHIVED FILE

dist/video/speed.js

2.38 KB · Sep 30, 2026 · 23:17 UTC

↓ Download file

import { ToolkitRuntimeError } from "../core/errors.js";
import { encodingArgs, resolveEncodingProfile } from "./encoding.js";
import { executeVideoTransform, inspectInput, positiveFinite, requireVideo } from "./helpers.js";
import { deriveOutputPath, preflightOutputPath, resolveReadableFile } from "./io.js";
export function buildAtempoChain(factor) {
    positiveFinite(factor, "factor");
    const stages = [];
    let remaining = factor;
    while (remaining > 2) {
        stages.push(2);
        remaining /= 2;
    }
    while (remaining < 0.5) {
        stages.push(0.5);
        remaining /= 0.5;
    }
    stages.push(remaining);
    return stages.map((value) => `atempo=${Number(value.toFixed(8))}`).join(",");
}
function validateAudioMode(value) {
    const mode = value ?? "sync";
    if (mode !== "sync" && mode !== "drop") {
        throw new ToolkitRuntimeError("E_USAGE_INVALID_ARGUMENT", "audio must be either sync or drop.", {
            details: { audio: mode },
        });
    }
    return mode;
}
export async function changeVideoSpeed(input, request) {
    const source = await resolveReadableFile(input, request.cwd);
    const factor = positiveFinite(request.factor, "factor");
    const audioMode = validateAudioMode(request.audio);
    const suffixFactor = Number(factor.toFixed(6)).toString();
    const output = deriveOutputPath(source, `speed-${suffixFactor}x`, request.output, { ...(request.cwd !== undefined ? { cwd: request.cwd } : {}) });
    await preflightOutputPath({ source, output, overwrite: request.overwrite ?? false });
    const media = await inspectInput(source, request);
    requireVideo(media, source);
    const profile = resolveEncodingProfile(output);
    const hasSyncedAudio = audioMode === "sync" && media.audio.length > 0;
    const args = [
        "-i", source,
        "-map", "0:v:0",
        ...(hasSyncedAudio ? ["-map", "0:a?"] : []),
        "-vf", `setpts=PTS/${factor}`,
        ...(hasSyncedAudio ? ["-af", buildAtempoChain(factor)] : []),
        ...encodingArgs(profile, hasSyncedAudio),
        "-map_metadata", "0",
    ];
    return await executeVideoTransform({
        operation: "speed",
        source,
        output,
        argsBeforeOutput: args,
        runtime: request,
        inputMedia: media,
        details: { factor, audio: audioMode, audioStreamsPreserved: hasSyncedAudio ? media.audio.length : 0 },
    });
}
//# sourceMappingURL=speed.js.map

SHA-256: 9efdee6e7b045abc685ec4cb69aaf3412c8ad7a716a33398bb8900f424757744