This commit is contained in:
Manav Rathi 2024-04-20 21:03:05 +05:30
parent 71cf2c30d7
commit d62f6077ce
No known key found for this signature in database
2 changed files with 109 additions and 110 deletions

View file

@ -1,109 +0,0 @@
import { nameAndExtension } from "@/next/file";
import log from "@/next/log";
import { withTimeout } from "@ente/shared/utils";
import QueueProcessor from "@ente/shared/utils/queueProcessor";
import { generateTempName } from "@ente/shared/utils/temp";
import {
FFMPEG_PLACEHOLDER,
INPUT_PATH_PLACEHOLDER,
OUTPUT_PATH_PLACEHOLDER,
} from "constants/ffmpeg";
import { FFmpeg, createFFmpeg } from "ffmpeg-wasm";
import { getUint8ArrayView } from "services/readerService";
const FFMPEG_EXECUTION_WAIT_TIME = 30 * 1000;
export class WasmFFmpeg {
private ffmpeg: FFmpeg;
private ready: Promise<void> = null;
private ffmpegTaskQueue = new QueueProcessor<File>();
constructor() {
this.ffmpeg = createFFmpeg({
corePath: "/js/ffmpeg/ffmpeg-core.js",
mt: false,
});
this.ready = this.init();
}
private async init() {
if (!this.ffmpeg.isLoaded()) {
await this.ffmpeg.load();
}
}
async run(
cmd: string[],
inputFile: File,
outputFileName: string,
dontTimeout = false,
) {
const response = this.ffmpegTaskQueue.queueUpRequest(() => {
if (dontTimeout) {
return this.execute(cmd, inputFile, outputFileName);
} else {
return withTimeout<File>(
this.execute(cmd, inputFile, outputFileName),
FFMPEG_EXECUTION_WAIT_TIME,
);
}
});
try {
return await response.promise;
} catch (e) {
log.error("ffmpeg run failed", e);
throw e;
}
}
private async execute(
cmd: string[],
inputFile: File,
outputFileName: string,
) {
let tempInputFilePath: string;
let tempOutputFilePath: string;
try {
await this.ready;
const [, extension] = nameAndExtension(inputFile.name);
const tempNameSuffix = extension ? `input.${extension}` : "input";
tempInputFilePath = `${generateTempName(10, tempNameSuffix)}`;
this.ffmpeg.FS(
"writeFile",
tempInputFilePath,
await getUint8ArrayView(inputFile),
);
tempOutputFilePath = `${generateTempName(10, outputFileName)}`;
cmd = cmd.map((cmdPart) => {
if (cmdPart === FFMPEG_PLACEHOLDER) {
return "";
} else if (cmdPart === INPUT_PATH_PLACEHOLDER) {
return tempInputFilePath;
} else if (cmdPart === OUTPUT_PATH_PLACEHOLDER) {
return tempOutputFilePath;
} else {
return cmdPart;
}
});
log.info(`${cmd}`);
await this.ffmpeg.run(...cmd);
return new File(
[this.ffmpeg.FS("readFile", tempOutputFilePath)],
outputFileName,
);
} finally {
try {
this.ffmpeg.FS("unlink", tempInputFilePath);
} catch (e) {
log.error("unlink input file failed", e);
}
try {
this.ffmpeg.FS("unlink", tempOutputFilePath);
} catch (e) {
log.error("unlink output file failed", e);
}
}
}
}

View file

@ -1,5 +1,16 @@
import { nameAndExtension } from "@/next/file";
import log from "@/next/log";
import { withTimeout } from "@ente/shared/utils";
import QueueProcessor from "@ente/shared/utils/queueProcessor";
import { generateTempName } from "@ente/shared/utils/temp";
import * as Comlink from "comlink";
import { WasmFFmpeg } from "services/wasm/ffmpeg";
import {
FFMPEG_PLACEHOLDER,
INPUT_PATH_PLACEHOLDER,
OUTPUT_PATH_PLACEHOLDER,
} from "constants/ffmpeg";
import { FFmpeg, createFFmpeg } from "ffmpeg-wasm";
import { getUint8ArrayView } from "services/readerService";
export class DedicatedFFmpegWorker {
wasmFFmpeg: WasmFFmpeg;
@ -13,3 +24,100 @@ export class DedicatedFFmpegWorker {
}
Comlink.expose(DedicatedFFmpegWorker, self);
const FFMPEG_EXECUTION_WAIT_TIME = 30 * 1000;
export class WasmFFmpeg {
private ffmpeg: FFmpeg;
private ready: Promise<void> = null;
private ffmpegTaskQueue = new QueueProcessor<File>();
constructor() {
this.ffmpeg = createFFmpeg({
corePath: "/js/ffmpeg/ffmpeg-core.js",
mt: false,
});
this.ready = this.init();
}
private async init() {
if (!this.ffmpeg.isLoaded()) {
await this.ffmpeg.load();
}
}
async run(
cmd: string[],
inputFile: File,
outputFileName: string,
dontTimeout = false,
) {
const response = this.ffmpegTaskQueue.queueUpRequest(() => {
if (dontTimeout) {
return this.execute(cmd, inputFile, outputFileName);
} else {
return withTimeout<File>(
this.execute(cmd, inputFile, outputFileName),
FFMPEG_EXECUTION_WAIT_TIME,
);
}
});
try {
return await response.promise;
} catch (e) {
log.error("ffmpeg run failed", e);
throw e;
}
}
private async execute(
cmd: string[],
inputFile: File,
outputFileName: string,
) {
let tempInputFilePath: string;
let tempOutputFilePath: string;
try {
await this.ready;
const [, extension] = nameAndExtension(inputFile.name);
const tempNameSuffix = extension ? `input.${extension}` : "input";
tempInputFilePath = `${generateTempName(10, tempNameSuffix)}`;
this.ffmpeg.FS(
"writeFile",
tempInputFilePath,
await getUint8ArrayView(inputFile),
);
tempOutputFilePath = `${generateTempName(10, outputFileName)}`;
cmd = cmd.map((cmdPart) => {
if (cmdPart === FFMPEG_PLACEHOLDER) {
return "";
} else if (cmdPart === INPUT_PATH_PLACEHOLDER) {
return tempInputFilePath;
} else if (cmdPart === OUTPUT_PATH_PLACEHOLDER) {
return tempOutputFilePath;
} else {
return cmdPart;
}
});
log.info(`${cmd}`);
await this.ffmpeg.run(...cmd);
return new File(
[this.ffmpeg.FS("readFile", tempOutputFilePath)],
outputFileName,
);
} finally {
try {
this.ffmpeg.FS("unlink", tempInputFilePath);
} catch (e) {
log.error("unlink input file failed", e);
}
try {
this.ffmpeg.FS("unlink", tempOutputFilePath);
} catch (e) {
log.error("unlink output file failed", e);
}
}
}
}