cobalt/web/src/lib/ffmpeg.ts
2024-08-11 19:13:04 +06:00

94 lines
2.2 KiB
TypeScript

import { FFmpeg } from "@imput/ffmpeg.wasm";
import ffmpegCore from "@imput/ffmpeg-core?url";
import ffmpegCoreWASM from "@imput/ffmpeg-core/wasm?url";
import mime from "mime";
type InputFileKind = "video" | "audio";
type FileInfo = {
type?: string | null,
kind: InputFileKind,
extension: string,
}
type RenderParams = {
file: File,
output?: FileInfo,
args: string[],
}
export default class FFmpegWrapper {
initialized: boolean;
ffmpeg: FFmpeg;
concurrency: number;
constructor() {
this.ffmpeg = new FFmpeg();
this.initialized = false;
this.concurrency = Math.min(4, navigator.hardwareConcurrency);
}
async init() {
if (this.initialized) {
this.ffmpeg.terminate();
} else {
this.initialized = true;
this.ffmpeg.on("log", ({ message }) => {
console.log(message);
});
}
await this.ffmpeg.load({
coreURL: ffmpegCore,
wasmURL: ffmpegCoreWASM,
workerURL: "/ffmpeg-core.worker.js",
});
}
terminate() {
this.initialized = false;
return this.ffmpeg.terminate();
}
async renderFile({ file, output, args }: RenderParams) {
const inputKind = file.type.split("/")[0];
const inputExtension = mime.getExtension(file.type);
if (inputKind !== "video" && inputKind !== "audio") return;
if (!inputExtension) return;
const input: FileInfo = {
kind: inputKind,
extension: inputExtension,
}
if (!output) output = input;
output.type = mime.getType(output.extension);
if (!output.type) return;
const buffer = new Uint8Array(await file.arrayBuffer());
await this.ffmpeg.writeFile(
'input',
buffer
);
await this.ffmpeg.exec([
'-threads', this.concurrency.toString(),
'-i', 'input',
...args,
`output.${output.extension}`
]);
const renderBlob = new Blob(
[await this.ffmpeg.readFile(`output.${output.extension}`)],
{ type: output.type }
);
if (renderBlob.size === 0) return;
return renderBlob;
}
}