cobalt/web/src/lib/task-manager/runners/ffmpeg.ts
wukko 9cc551008d
Some checks are pending
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
Run tests / check lockfile correctness (push) Waiting to run
Run tests / web sanity check (push) Waiting to run
Run tests / api sanity check (push) Waiting to run
web/ffmpeg: define multithreading support outside of web worker context
there's no navigator.maxTouchPoints in web worker context, so previously there was no way to detect whether safari is running on ipad or not
2025-06-29 13:45:39 +06:00

106 lines
3.1 KiB
TypeScript

import FFmpegWorker from "$lib/task-manager/workers/ffmpeg?worker";
import { device } from "$lib/device";
import { killWorker } from "$lib/task-manager/run-worker";
import { updateWorkerProgress } from "$lib/state/task-manager/current-tasks";
import { pipelineTaskDone, itemError, queue } from "$lib/state/task-manager/queue";
import type { FileInfo } from "$lib/types/libav";
import type { CobaltQueue } from "$lib/types/queue";
let startAttempts = 0;
export const runFFmpegWorker = async (
workerId: string,
parentId: string,
files: File[],
args: string[],
output: FileInfo,
variant: 'remux' | 'encode',
resetStartCounter = false
) => {
const worker = new FFmpegWorker();
// sometimes chrome refuses to start libav wasm,
// so we check if it started, try 10 more times if not, and kill self if it still doesn't work
// TODO: fix the underlying issue because this is ridiculous
if (resetStartCounter) startAttempts = 0;
let bumpAttempts = 0;
const startCheck = setInterval(async () => {
bumpAttempts++;
if (bumpAttempts === 10) {
startAttempts++;
if (startAttempts <= 10) {
killWorker(worker, unsubscribe, startCheck);
return await runFFmpegWorker(
workerId, parentId,
files, args, output,
variant, device.supports.multithreading
);
} else {
killWorker(worker, unsubscribe, startCheck);
return itemError(parentId, workerId, "queue.worker_didnt_start");
}
}
}, 500);
const unsubscribe = queue.subscribe((queue: CobaltQueue) => {
if (!queue[parentId]) {
killWorker(worker, unsubscribe, startCheck);
}
});
worker.postMessage({
cobaltFFmpegWorker: {
variant,
files,
args,
output,
}
});
worker.onerror = (e) => {
console.error("ffmpeg worker crashed:", e);
killWorker(worker, unsubscribe, startCheck);
return itemError(parentId, workerId, "queue.generic_error");
};
let totalDuration: number | null = null;
worker.onmessage = (event) => {
const eventData = event.data.cobaltFFmpegWorker;
if (!eventData) return;
clearInterval(startCheck);
if (eventData.progress) {
if (eventData.progress.duration) {
totalDuration = eventData.progress.duration;
}
updateWorkerProgress(workerId, {
percentage: totalDuration ? (eventData.progress.durationProcessed / totalDuration) * 100 : 0,
size: eventData.progress.size,
})
}
if (eventData.render) {
killWorker(worker, unsubscribe, startCheck);
return pipelineTaskDone(
parentId,
workerId,
eventData.render,
);
}
if (eventData.error) {
killWorker(worker, unsubscribe, startCheck);
return itemError(parentId, workerId, eventData.error);
}
};
}