Use one-pass encoding with more accurate bitrate

This commit is contained in:
Reimar 2026-01-06 09:20:24 +01:00
parent c0a1b0fca3
commit 4feab31ef0
Signed by: Reimar
GPG Key ID: 93549FA07F0AE268
3 changed files with 16 additions and 24 deletions

View File

@ -21,31 +21,31 @@ async function compress() {
console.log("[ffmpeg]", event.type, event.message); console.log("[ffmpeg]", event.type, event.message);
}); });
let pass = 1; updateProgress(0);
updateProgress(0, pass);
ffmpeg.on("progress", event => { ffmpeg.on("progress", event => {
updateProgress(event.progress, pass); updateProgress(event.progress);
}); });
await ffmpeg.load({ coreURL: "/assets/scripts/core/package/dist/umd/ffmpeg-core.js" }); await ffmpeg.load({ coreURL: "/assets/scripts/core/package/dist/umd/ffmpeg-core.js" });
await ffmpeg.writeFile(file.name, await readFromBlob(file)); await ffmpeg.writeFile(file.name, await readFromBlob(file));
// Use Two-Pass to create a video file with desired file size // Calculate target bitrate to create a video file with desired file size (https://trac.ffmpeg.org/wiki/Encode/H.264#twopass)
// https://trac.ffmpeg.org/wiki/Encode/H.264#twopass let bitrate = targetFilesize / videoLength;
bitrate -= 128; // Subtract audio bitrate
bitrate -= (bitrate / 100) * 5; // Subtract 5% to compensate for overhead
bitrate = Math.floor(bitrate);
const bitrate = Math.floor(targetFilesize / videoLength) - 128; // Subtract audio bitrate if (bitrate <= 0) {
showSection("file-picker");
alert("Selected file size is too low for this video");
return;
}
console.debug("Target bitrate:", bitrate, "Video length:", videoLength); console.debug("Target bitrate:", bitrate, "Video length:", videoLength);
const options = ["-i", file.name, "-preset", "ultrafast", "-c:v", "libx264", "-b:v", bitrate + "k"]; await ffmpeg.exec(["-i", file.name, "-preset", "ultrafast", "-c:v", "libx264", "-b:v", bitrate + "k", "-c:a", "copy", "-b:a", "128k", "compressed.mp4"]);
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"); const video = await ffmpeg.readFile("compressed.mp4");
@ -60,10 +60,9 @@ function cancel() {
showSection("file-picker"); showSection("file-picker");
} }
function updateProgress(progress, pass) { function updateProgress(progress) {
const percent = (progress * 100).toFixed(1) + "%"; const percent = (progress * 100).toFixed(1) + "%";
document.getElementById("progress-step-value").innerText = pass;
document.getElementById("progress-percentage").innerText = percent; document.getElementById("progress-percentage").innerText = percent;
document.getElementById("progress-indicator").style.clipPath = `rect(0 ${percent} 100% 0)`; document.getElementById("progress-indicator").style.clipPath = `rect(0 ${percent} 100% 0)`;
} }

View File

@ -180,7 +180,7 @@ label {
height: 2rem; height: 2rem;
max-width: 500px; max-width: 500px;
border-radius: 2rem; border-radius: 2rem;
margin: auto; margin: 3rem auto auto;
background-color: #E0E0E0; background-color: #E0E0E0;
position: relative; position: relative;
} }
@ -198,9 +198,3 @@ label {
font-weight: bold; font-weight: bold;
font-size: 1.2rem; font-size: 1.2rem;
} }
#progress-step {
font-weight: bold;
margin-top: 3rem;
margin-bottom: 0.5rem;
}

View File

@ -39,7 +39,6 @@
<h3 id="loading-title">Please wait...</h3> <h3 id="loading-title">Please wait...</h3>
<p id="loading-description">Your video is being compressed. This may take a while.</p> <p id="loading-description">Your video is being compressed. This may take a while.</p>
<p id="progress-step">Step <span id="progress-step-value">1</span>/2</p>
<div id="progress-container"> <div id="progress-container">
<div id="progress-indicator"></div> <div id="progress-indicator"></div>
</div> </div>