109 lines
3.1 KiB
JavaScript
109 lines
3.1 KiB
JavaScript
const ffmpeg = new FFmpegWASM.FFmpeg();
|
|
|
|
async function compress() {
|
|
document.getElementById("uploaded-video").pause();
|
|
showSection("loading");
|
|
|
|
const filesize = document.getElementById("filesize").value;
|
|
const filesizeUnit = document.getElementById("filesize-unit").value;
|
|
const file = document.getElementById("file-input").files[0];
|
|
|
|
const videoLength = document.getElementById("uploaded-video").duration;
|
|
let targetFilesize; // Stored in kBit
|
|
switch (filesizeUnit) {
|
|
case "K": targetFilesize = filesize * 8; break;
|
|
case "M": targetFilesize = filesize * 8000; break;
|
|
}
|
|
|
|
updateProgress(0);
|
|
|
|
ffmpeg.on("log", event => {
|
|
console.log("[ffmpeg]", event.type, event.message);
|
|
});
|
|
|
|
let pass;
|
|
|
|
ffmpeg.on("progress", event => {
|
|
updateProgress(event.progress, pass);
|
|
});
|
|
|
|
await ffmpeg.load({ coreURL: "/assets/scripts/core/package/dist/umd/ffmpeg-core.js" });
|
|
|
|
await ffmpeg.writeFile(file.name, await readFromBlob(file));
|
|
|
|
// Use Two-Pass to create a video file with desired file size
|
|
// https://trac.ffmpeg.org/wiki/Encode/H.264#twopass
|
|
|
|
const bitrate = Math.floor(targetFilesize / videoLength) - 128; // Subtract audio bitrate
|
|
|
|
console.debug("Target bitrate:", bitrate, "Video length:", videoLength);
|
|
|
|
const options = ["-i", file.name, "-preset", "ultrafast", "-c:v", "libx264", "-b:v", bitrate + "k"];
|
|
|
|
pass = 1;
|
|
|
|
await ffmpeg.exec([...options, "-pass", "1", "-vsync", "cfr", "-f", "null", "/dev/null"]);
|
|
|
|
pass = 2;
|
|
|
|
await ffmpeg.exec([...options, "-pass", "2", "-c:a", "copy", "-b:a", "128k", "compressed.mp4"]);
|
|
|
|
const video = await ffmpeg.readFile("compressed.mp4");
|
|
|
|
location.href = URL.createObjectURL(new Blob([video.buffer], { type: "video/mp4" }));
|
|
}
|
|
|
|
function cancel() {
|
|
ffmpeg.terminate();
|
|
|
|
showSection("file-picker");
|
|
}
|
|
|
|
function updateProgress(progress, pass) {
|
|
const percent = (progress * 100).toFixed(1) + "%";
|
|
|
|
document.getElementById("progress-step-value").innerText = pass;
|
|
document.getElementById("progress-percentage").innerText = percent;
|
|
document.getElementById("progress-indicator").style.clipPath = `rect(0 ${percent} 100% 0)`;
|
|
}
|
|
|
|
function openFileSelector() {
|
|
document.getElementById("file-input").click();
|
|
}
|
|
|
|
function selectFile() {
|
|
setTimeout(() => {
|
|
const file = document.getElementById("file-input").files[0];
|
|
|
|
document.getElementById("uploaded-video").src = URL.createObjectURL(file);
|
|
document.getElementById("uploaded-video").load();
|
|
|
|
hideElements("#file-drop-area", "#file-input-spacing");
|
|
showElements("#uploaded-video", "#change-file");
|
|
}, 200);
|
|
}
|
|
|
|
// https://github.com/ffmpegwasm/ffmpeg.wasm/blob/main/packages/util/src/index.ts
|
|
function readFromBlob(blob) {
|
|
return new Promise((resolve, reject) => {
|
|
const fileReader = new FileReader();
|
|
fileReader.onload = () => {
|
|
const { result } = fileReader;
|
|
if (result instanceof ArrayBuffer) {
|
|
resolve(new Uint8Array(result));
|
|
} else {
|
|
resolve(new Uint8Array());
|
|
}
|
|
};
|
|
fileReader.onerror = (event) => {
|
|
reject(
|
|
Error(
|
|
`File could not be read! Code=${event?.target?.error?.code || -1}`
|
|
)
|
|
);
|
|
};
|
|
fileReader.readAsArrayBuffer(blob);
|
|
});
|
|
}
|
|
|