|
|
import LibAVWrapper from "$lib/libav"; |
|
|
import type { FileInfo } from "$lib/types/libav"; |
|
|
|
|
|
const ffmpeg = async ( |
|
|
variant: string, |
|
|
files: File[], |
|
|
args: string[], |
|
|
output: FileInfo, |
|
|
yesthreads: boolean = false, |
|
|
) => { |
|
|
if (!(files && output && args)) { |
|
|
self.postMessage({ |
|
|
cobaltFFmpegWorker: { |
|
|
error: "queue.ffmpeg.no_args", |
|
|
} |
|
|
}); |
|
|
return; |
|
|
} |
|
|
|
|
|
const ff = new LibAVWrapper((progress) => { |
|
|
self.postMessage({ |
|
|
cobaltFFmpegWorker: { |
|
|
progress: { |
|
|
durationProcessed: progress.out_time_sec, |
|
|
speed: progress.speed, |
|
|
size: progress.total_size, |
|
|
currentFrame: progress.frame, |
|
|
fps: progress.fps, |
|
|
} |
|
|
} |
|
|
}) |
|
|
}); |
|
|
|
|
|
ff.init({ variant, yesthreads }); |
|
|
|
|
|
const error = (code: string) => { |
|
|
self.postMessage({ |
|
|
cobaltFFmpegWorker: { |
|
|
error: code, |
|
|
} |
|
|
}); |
|
|
ff.terminate(); |
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
const probeFile = files[0]; |
|
|
if (!probeFile) { |
|
|
return error("queue.ffmpeg.probe_failed"); |
|
|
} |
|
|
|
|
|
let file_info; |
|
|
|
|
|
try { |
|
|
file_info = await ff.probe(probeFile); |
|
|
} catch (e) { |
|
|
console.error("error from ffmpeg worker @ file_info:"); |
|
|
if (e instanceof Error && e?.message?.toLowerCase().includes("out of memory")) { |
|
|
console.error(e); |
|
|
|
|
|
error("queue.ffmpeg.out_of_memory"); |
|
|
return self.close(); |
|
|
} else { |
|
|
console.error(e); |
|
|
return error("queue.ffmpeg.probe_failed"); |
|
|
} |
|
|
} |
|
|
|
|
|
if (!file_info?.format) { |
|
|
return error("queue.ffmpeg.no_input_format"); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (files.length === 1 && file_info.streams?.length === 1) { |
|
|
if (output.type?.startsWith("audio") && file_info.streams[0].codec_type !== "audio") { |
|
|
return error("queue.ffmpeg.no_audio_channel"); |
|
|
} |
|
|
} |
|
|
|
|
|
self.postMessage({ |
|
|
cobaltFFmpegWorker: { |
|
|
progress: { |
|
|
duration: Number(file_info.format.duration), |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
for (const file of files) { |
|
|
if (!file.type) { |
|
|
return error("queue.ffmpeg.no_input_type"); |
|
|
} |
|
|
} |
|
|
|
|
|
let render; |
|
|
|
|
|
try { |
|
|
render = await ff.render({ |
|
|
files, |
|
|
output, |
|
|
args, |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error("error from the ffmpeg worker @ render:"); |
|
|
console.error(e); |
|
|
|
|
|
return error("queue.ffmpeg.crashed"); |
|
|
} |
|
|
|
|
|
if (!render) { |
|
|
return error("queue.ffmpeg.no_render"); |
|
|
} |
|
|
|
|
|
await ff.terminate(); |
|
|
|
|
|
self.postMessage({ |
|
|
cobaltFFmpegWorker: { |
|
|
render |
|
|
} |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error("error from the ffmpeg worker:") |
|
|
console.error(e); |
|
|
return error("queue.ffmpeg.crashed"); |
|
|
} |
|
|
} |
|
|
|
|
|
self.onmessage = async (event: MessageEvent) => { |
|
|
const ed = event.data.cobaltFFmpegWorker; |
|
|
if (ed?.variant && ed?.files && ed?.args && ed?.output) { |
|
|
await ffmpeg(ed.variant, ed.files, ed.args, ed.output, ed.yesthreads); |
|
|
} |
|
|
} |
|
|
|