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.192; break; case "M": targetFilesize = filesize * 8388.608; 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.round(targetFilesize / videoLength) - 128; // Subtract audio bitrate console.debug("Target bitrate:", bitrate); const options = ["-i", file.name, "-preset", "veryfast", "-c:v", "libx264", "-b:v", bitrate + "k"]; pass = 1; await ffmpeg.exec(["-y", ...options, "-pass", "1", "-vsync", "cfr", "-f", "null", "/dev/null"]); pass = 2; await ffmpeg.exec([...options, "-pass", "2", "-c:a", "aac", "-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); }); }