A hard 10 MB upload cap is easy to trip over, and I wanted to see whether a bitrate calculation could fit a demo clip under it without re-encoding until it works. The textbook formula is target size × 8 ÷ duration. I ran it on two clips on an M4 Mac with FFmpeg 7.1 (x264 medium, AAC 128 kbps). Every number below is one run, on these two clips only.
Clip A is a 60-second 1080p street-performance clip by Isak Zvegelj (CC BY 3.0, Wikimedia Commons), which I re-encoded to a phone-like bitrate beforehand. Clip B is a generated screen recording, much quieter than a real capture would be.
First, what does 10 MB mean to your server
A cap written as 10 MB is either 10,000,000 bytes or 10 MiB, which is 10,485,760 bytes. I use decimal MB for everything below, so check which one your limit is before you pick a margin. The gap is 4.9%, which is more than any margin I ended up needing on clip A.
One example of how this bites: the smallest-size preset in ImgIng (https://imging.ai/), a browser tool I ran in desktop Chromium 149, gave 10,487,546 bytes on clip A at 1080×608, 24 fps. That is 10.49 MB decimal, and also 1,786 bytes over 10 MiB. ImgIng has presets, not a target-size option, so landing near a number is luck there, not a setting.
The plain formula overshoots by 9 to 11 percent on clip A
Dividing 10,000,000 × 8 by 60 seconds gives 1333 kbps for video. I fed that to a single pass and a two-pass encode.
| Clip A, 10 MB target | Bytes | vs. target |
|---|---|---|
| Single pass, no audio deducted | 10,917,084 | +9.17% |
| Two pass, no audio deducted | 11,124,442 | +11.24% |
Most of the excess is the audio track. AAC 128 kbps over 60 seconds is about 0.97 MB, which is 9.7% of a 10 MB budget. The MP4 container added roughly 66 KB. In the single-pass run the video stream itself came out a little under budget on A, so the audio was the main thing the formula left out. Two-pass put the video about 86 KB over.
Deduct the audio and the error shrinks, but not evenly
Subtracting 128 kbps first is one line of arithmetic. With a 2% margin written in, it looks like this:
target, secs, margin = 10_000_000, 60, 0.02
audio = 128_000 // 8 * secs
print(round((target * (1 - margin) - audio) * 8 / secs / 1000))
It prints 1179 kbps. I did not re-encode with the margin included. The runs below used 1205 kbps, which is the same formula with margin 0.
| 10 MB target, audio deducted | Clip A | Clip B (synthetic) |
|---|---|---|
| Single pass | 9,965,325 (−0.35%) | 9,134,627 (−8.65%) |
| Two pass | 10,174,131 (+1.74%) | 11,195,510 (+12.0%) |
| CRF bisection | 9,854,805 (−1.45%) | 9,838,907 (−1.6%) |
Two-pass was the only one that went over on A. I had assumed it would be the most accurate, and on these clips it was not. On clip B it overshot by 1.2 MB, because a mostly static picture gives the rate controller very little to bite on, and it spent bits on the scroll jumps. That reason is my guess from the per-second bitrate curve. I did not isolate it.
CRF bisection never went over, but each round re-encodes the whole file. Seven rounds took 157 seconds on A and 59 seconds on B, against about 23 seconds for a single pass.
What margin I would leave
On clip A, 2% was enough to cover the worst case, which was two-pass at +1.74%. That is arithmetic, not a rerun. On clip B, 2% would not have saved the two-pass run, which was 12% over. So the margin depends on how predictable your footage is.
For a hard cap, I would take 3% off the target for busy camera-style footage like clip A, run single pass, and check the file size after the encode. If it is over, drop the bitrate by the overage and encode again. For screen recordings I would avoid two-pass until I had checked a real one: a single synthetic clip is a thin sample, but the miss was large.
Do the MiB check too. A 10,174,131-byte file fails a 10,000,000-byte cap and passes a 10 MiB one, so knowing which cap you have can matter more than the encoder choice.
Top comments (0)