ai-tube-clap-exporter / src /core /ffmpeg /imageToVideoBase64.mts
jbilcke-hf's picture
jbilcke-hf HF staff
work in progress on the AI Stories Factory
2ed47da
raw
history blame
3.09 kB
import { rm, mkdir, writeFile, readFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import ffmpeg from "fluent-ffmpeg";
import { getRandomDirectory } from "../files/getRandomDirectory.mts";
/**
* Converts an image in Base64 format to a video encoded in Base64.
*
* @param inputImageInBase64 - The input image encoded in Base64.
* @param outputVideoFormat - Optional. Format of the output video (default is "mp4").
* @param outputVideoDurationInMs - Optional. Duration of the video in milliseconds (default is 1000ms).
* @param codec - Optional. Codec used for video coding. Defaults differ based on `outputVideoFormat`.
* @param width - Optional. Width of the output video.
* @param height - Optional. Height of the output video.
* @param fps - Optional. Frame rate of the output video.
*
* @returns - A promise that resolves to the video as a Base64 encoded string.
*/
export async function imageToVideoBase64({
inputImageInBase64,
outputFilePath,
outputDir,
clearOutputDirAtTheEnd = true,
outputVideoFormat = "mp4",
outputVideoDurationInMs = 1000,
codec = outputVideoFormat === "webm" ? "libvpx-vp9" : "libx264",
width = 1920,
height = 1080,
fps = 25
}: {
inputImageInBase64: string
outputFilePath?: string
outputDir?: string
clearOutputDirAtTheEnd?: boolean
outputVideoFormat?: string
outputVideoDurationInMs?: number
codec?: string
width?: number
height?: number
fps?: number
}): Promise<string> {
outputDir = outputDir || (await getRandomDirectory())
// Decode the Base64 image and write it to a temporary file.
const base64Data = inputImageInBase64.substring(inputImageInBase64.indexOf(',') + 1);
const buffer = Buffer.from(base64Data, 'base64');
const inputImagePath = path.join(outputDir, 'inputImage.png');
await writeFile(inputImagePath, buffer);
// Set the path for the output video.
outputFilePath = outputFilePath || path.join(outputDir, `output.${outputVideoFormat}`);
const durationInSeconds = outputVideoDurationInMs / 1000;
// Process the image to video conversion using ffmpeg.
await new Promise<void>((resolve, reject) => {
ffmpeg(inputImagePath)
.outputOptions([
`-t ${durationInSeconds}`,
`-r ${fps}`,
`-s ${width}x${height}`, // set frame size
`-c:v ${codec}`, // set the codec
'-tune stillimage',
'-pix_fmt yuv420p'
])
.on('end', () => resolve())
.on('error', (err) => reject(err))
.save(outputFilePath);
});
// Read the video file, encode it to Base64, and format it as a data URI.
const videoBuffer = await readFile(outputFilePath);
const videoBase64 = videoBuffer.toString('base64');
const resultAsBase64DataUri = `data:video/${outputVideoFormat};base64,${videoBase64}`;
// Attempt to clean up temporary work files.
if (clearOutputDirAtTheEnd) {
try {
await rm(outputDir, { recursive: true, force: true });
} catch (error) {
console.error('Error removing temporary files:', error);
}
}
return resultAsBase64DataUri;
}