<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: CompressVid</title>
    <description>The latest articles on DEV Community by CompressVid (@compressvid).</description>
    <link>https://dev.to/compressvid</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4186803%2Fc9e14c52-506e-44c8-9d44-97ff07edf79f.png</url>
      <title>DEV Community: CompressVid</title>
      <link>https://dev.to/compressvid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/compressvid"/>
    <language>en</language>
    <item>
      <title>Target-Size Video Compression Is Just Division: The Bitrate Math (With Real Results)</title>
      <dc:creator>CompressVid</dc:creator>
      <pubDate>Thu, 29 Oct 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/compressvid/target-size-video-compression-is-just-division-the-bitrate-math-with-real-results-4ff6</link>
      <guid>https://dev.to/compressvid/target-size-video-compression-is-just-division-the-bitrate-math-with-real-results-4ff6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclosure: I work on &lt;a href="https://compressvid.io/" rel="noopener noreferrer"&gt;CompressVid&lt;/a&gt;, an online video compressor. The numbers below are from our own test runs, recorded while making tutorial videos. I've noted which figures come from the tool's UI and which are my own arithmetic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Compress this video to 24 MB" sounds like a fuzzy, magic request. It isn't. Because a compressed video's size is almost entirely set by its &lt;strong&gt;bitrate&lt;/strong&gt;, a target size is just a bitrate budget, and you get the budget by dividing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one formula
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file size (bits) ≈ (video bitrate + audio bitrate) × duration + container overhead
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rearrange it, and a size target becomes a bitrate target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;video bitrate ≈ (target bytes × 8) / duration − audio bitrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep the arithmetic simple, this post uses 1 MB = 1,000,000 bytes and 1 Mbps = 1,000,000 bits/s. CompressVid actually displays sizes in binary megabytes (MiB, 1,048,576 bytes, about 4.9% bigger), so the true bitrates of the files below are roughly 5% higher than the figures in the table. The ratios and conclusions don't change. The MB/MiB gap matters when a limit is hard, though, and it's one reason to leave headroom.&lt;/p&gt;

&lt;h2&gt;
  
  
  A worked example in JavaScript
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Returns the average video bitrate (kbps) needed to hit a target size.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;videoBitrateForTarget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;targetMB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;durationSec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;audioKbps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;overheadPct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usableBits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetMB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;overheadPct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalKbps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usableBits&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;durationSec&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalKbps&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;audioKbps&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;videoBitrateForTarget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;targetMB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;durationSec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;  &lt;span class="c1"&gt;// ≈ 6143 kbps&lt;/span&gt;
&lt;span class="nf"&gt;videoBitrateForTarget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;targetMB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;durationSec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;  &lt;span class="c1"&gt;// ≈ 4053 kbps&lt;/span&gt;
&lt;span class="nf"&gt;videoBitrateForTarget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;targetMB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;durationSec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// ≈ 4053 kbps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 2% overhead and 128 kbps audio here are assumptions for illustration. Real overhead depends on the container and the stream count.&lt;/p&gt;

&lt;p&gt;Look at the last two calls: a 24 MB target for 45 seconds and a 96 MB target for 3 minutes give the &lt;strong&gt;same&lt;/strong&gt; bitrate budget. Size limits are really bits-per-second limits in disguise. A longer clip under the same cap simply gets fewer bits for every second.&lt;/p&gt;

&lt;p&gt;With ffmpeg, the classic way to spend a budget precisely is two-pass encoding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;.mp4 &lt;span class="nt"&gt;-c&lt;/span&gt;:v libx264 &lt;span class="nt"&gt;-b&lt;/span&gt;:v 4053k &lt;span class="nt"&gt;-pass&lt;/span&gt; 1 &lt;span class="nt"&gt;-an&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; mp4 /dev/null
ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;.mp4 &lt;span class="nt"&gt;-c&lt;/span&gt;:v libx264 &lt;span class="nt"&gt;-b&lt;/span&gt;:v 4053k &lt;span class="nt"&gt;-pass&lt;/span&gt; 2 &lt;span class="nt"&gt;-c&lt;/span&gt;:a aac &lt;span class="nt"&gt;-b&lt;/span&gt;:a 128k out.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What actually happened on real files
&lt;/h2&gt;

&lt;p&gt;Here are four runs from our tests. Input and output sizes are as the tool reported them. Bitrates are my own division, using &lt;code&gt;size × 8 / duration&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Input avg bitrate&lt;/th&gt;
&lt;th&gt;Budget&lt;/th&gt;
&lt;th&gt;Output avg bitrate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;135.95 MB, 30 s, 1080p60 MOV&lt;/td&gt;
&lt;td&gt;24 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7.48 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~36.3 Mbps&lt;/td&gt;
&lt;td&gt;~6.4 Mbps&lt;/td&gt;
&lt;td&gt;~2.0 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;65.05 MB, 45 s, 1080p MP4&lt;/td&gt;
&lt;td&gt;24 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;21.08 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~11.6 Mbps&lt;/td&gt;
&lt;td&gt;~4.3 Mbps&lt;/td&gt;
&lt;td&gt;~3.7 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;185.24 MB, 3:00, 1080p MP4&lt;/td&gt;
&lt;td&gt;96 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;32.08 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~8.2 Mbps&lt;/td&gt;
&lt;td&gt;~4.3 Mbps&lt;/td&gt;
&lt;td&gt;~1.4 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;185.24 MB, 3:00, 1080p MP4&lt;/td&gt;
&lt;td&gt;"70% smaller" (55.6 MB)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;23.86 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~8.2 Mbps&lt;/td&gt;
&lt;td&gt;~2.5 Mbps&lt;/td&gt;
&lt;td&gt;~1.1 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuxpki0ioxjtgt1lxauyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuxpki0ioxjtgt1lxauyf.png" alt="Before and after: a 65.05 MB clip compressed to 21.08 MB with a 24 MB target" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 45-second clip used about 88% of its budget. The iPhone MOV used about 31%. Same tool, same target, very different outcomes. Why?&lt;/p&gt;

&lt;h2&gt;
  
  
  The target is a ceiling, not a goal
&lt;/h2&gt;

&lt;p&gt;A size target is a &lt;strong&gt;maximum&lt;/strong&gt;. Modern encoders don't pad a file just to reach a number. In quality-driven modes (CRF in x264/x265, or a capped CRF with &lt;code&gt;-maxrate&lt;/code&gt;/&lt;code&gt;-bufsize&lt;/code&gt;), the encoder spends bits only where the picture needs them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phone footage is over-provisioned.&lt;/strong&gt; The 1080p60 MOV started at ~36 Mbps, a bitrate chosen so the phone can encode in real time with little effort. A slower encoder can describe the same scene in far fewer bits, so it never needed the 6.4 Mbps it was allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content complexity decides the rest.&lt;/strong&gt; Static or slow-moving shots compress very well. Lots of motion, grain or fine texture eats bits. The 45-second clip sat much closer to its ceiling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same input, different targets, both under.&lt;/strong&gt; The 3-minute file landed at 32.08 MB with a 96 MB cap and at 23.86 MB with a 55.6 MB cap. Both runs stayed at 1080p and kept the full 3:00.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46t6uf3r6hm4merr2hi7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46t6uf3r6hm4merr2hi7.png" alt="The '96 MB Chat' target selected for a 3-minute 1080p clip" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're building this yourself, a robust pattern is: &lt;strong&gt;compute the bitrate ceiling from the target, then let a quality-based rate control work beneath it.&lt;/strong&gt; Pure CBR or ABR will hit the number more literally, but it wastes bits on easy footage and can starve hard scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you still need headroom
&lt;/h2&gt;

&lt;p&gt;Even with the maths right, the final file can overshoot slightly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Container overhead&lt;/strong&gt; (MP4 &lt;code&gt;moov&lt;/code&gt; index, per-sample tables) grows with duration and frame count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate control isn't exact.&lt;/strong&gt; Single-pass ABR can drift by a few percent, and VBV buffers allow short bursts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MB vs MiB confusion&lt;/strong&gt;, as described above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We learned this the hard way. An early build produced &lt;strong&gt;20.58 MB from a 19 MB target&lt;/strong&gt;. That has since been fixed, and the reason presets like "19 MB" for a 20 MB limit or "24 MB" for 25 MB exist at all is to leave a margin for exactly these effects. My rule of thumb: aim about 5% under any hard limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Target size ÷ duration = bitrate budget. Subtract audio and overhead, and you have your video bitrate.&lt;/li&gt;
&lt;li&gt;Equal budgets can hide behind different-looking limits (24 MB for 45 s ≈ 96 MB for 3 min).&lt;/li&gt;
&lt;li&gt;Treat the target as a ceiling. Good encoders often finish well under it, especially on high-bitrate phone footage.&lt;/li&gt;
&lt;li&gt;Always leave headroom for container overhead and rate-control drift.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to watch the ceiling-versus-result behaviour on your own clips without writing any ffmpeg, the &lt;a href="https://compressvid.io/" rel="noopener noreferrer"&gt;CompressVid video compressor&lt;/a&gt; lets you type a target size and shows input and output sizes side by side. I'd be curious how close your footage gets to its budget. Drop your numbers in the comments.&lt;/p&gt;

</description>
      <category>video</category>
      <category>ffmpeg</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
