2024-08-11 13:24:29 +01:00
|
|
|
import { FFmpeg } from "@imput/ffmpeg.wasm";
|
2024-08-10 12:21:39 +01:00
|
|
|
import ffmpegCore from "@imput/ffmpeg-core?url";
|
|
|
|
import ffmpegCoreWASM from "@imput/ffmpeg-core/wasm?url";
|
|
|
|
|
2024-08-11 13:24:29 +01:00
|
|
|
import mime from "mime";
|
|
|
|
|
|
|
|
type InputFileKind = "video" | "audio";
|
|
|
|
|
|
|
|
type FileInfo = {
|
|
|
|
type?: string | null,
|
|
|
|
kind: InputFileKind,
|
|
|
|
extension: string,
|
|
|
|
}
|
|
|
|
|
|
|
|
type RenderParams = {
|
|
|
|
file: File,
|
|
|
|
output?: FileInfo,
|
2024-08-11 08:04:40 +01:00
|
|
|
args: string[],
|
|
|
|
}
|
|
|
|
|
2024-08-10 12:21:39 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-08-11 13:24:29 +01:00
|
|
|
async renderFile({ file, output, args }: RenderParams) {
|
|
|
|
const inputKind = file.type.split("/")[0];
|
|
|
|
const inputExtension = mime.getExtension(file.type);
|
2024-08-10 12:21:39 +01:00
|
|
|
|
2024-08-11 13:24:29 +01:00
|
|
|
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());
|
2024-08-10 12:21:39 +01:00
|
|
|
await this.ffmpeg.writeFile(
|
2024-08-11 13:24:29 +01:00
|
|
|
'input',
|
|
|
|
buffer
|
|
|
|
);
|
2024-08-10 12:21:39 +01:00
|
|
|
|
|
|
|
await this.ffmpeg.exec([
|
|
|
|
'-threads', this.concurrency.toString(),
|
2024-08-11 13:24:29 +01:00
|
|
|
'-i', 'input',
|
2024-08-11 08:04:40 +01:00
|
|
|
...args,
|
2024-08-11 13:24:29 +01:00
|
|
|
`output.${output.extension}`
|
2024-08-10 12:21:39 +01:00
|
|
|
]);
|
|
|
|
|
2024-08-11 13:24:29 +01:00
|
|
|
const renderBlob = new Blob(
|
|
|
|
[await this.ffmpeg.readFile(`output.${output.extension}`)],
|
|
|
|
{ type: output.type }
|
2024-08-10 12:21:39 +01:00
|
|
|
);
|
|
|
|
|
2024-08-11 14:13:04 +01:00
|
|
|
if (renderBlob.size === 0) return;
|
2024-08-11 13:24:29 +01:00
|
|
|
return renderBlob;
|
2024-08-10 12:21:39 +01:00
|
|
|
}
|
|
|
|
}
|