按「目标体积×8÷时长」算码率,是上传前压视频最常见的第一步。我跑了一段 60 秒的 1080p 动作片(下称 A,来自维基共享资源,Isak Zvegelj 拍摄,CC BY 3.0)。目标 25 MB 时算出约 3333 kbps,单遍编码后文件是 25 785 503 字节并超了 3.14%。目标换成 10 MB 时算出约 1333 kbps,文件是 10 917 084 字节并超了 9.17%。
这篇只回答一件事:多出来的这几十万字节是谁拿走的。数字都来自同一台 M4 Mac。编码用 x264 medium 和 AAC 128 kbps 音轨,工具是 FFmpeg 7.1 且每项只跑了一次。MB 一律按 1 000 000 字节算(数字里的空格只是千分位)。界面里的 MB 可能是 1 048 576 字节。同样写 25 MB 两边差 4.86%,比码率控制本身的误差还大。动手前先确认对方的限制按哪种口径。
多出来的字节先被音轨和容器吃掉
我把 A 单遍的超出额拆开,单位是字节。
| 目标 | 总超出 | 视频码率控制误差 | 音轨 | 容器 |
|---|---|---|---|---|
| 25 MB | +785 503 | −253 166 | +971 835 | +66 834 |
| 10 MB | +917 084 | −121 585 | +971 835 | +66 834 |
公式只管视频,文件里还装着音轨。128 kbps 乘 60 秒是 960 000 字节而实测音轨样本是 971 835 字节。多出的 11 835 字节我没拆,推断是音频帧头和取整。这 0.97 MB 占 10 MB 目标的 9.7%,占 25 MB 的 3.9%。容器是 moov 这类索引。A 上容器合计 66 834 字节(其中 moov 占 66 786 字节),占 10 MB 目标的 0.67%。量小但不会是零。
反直觉的是第三列。视频部分的码率控制误差是负的。A 上单遍比预算少用了约 1.0% 到 1.2%。音轨和容器扣掉再算,单遍反而略低于目标:25 MB 档是 −0.66% 而 10 MB 档是 −0.35%。
码率控制在编码前就得猜帧有多大
视频部分为什么还会有偏差,哪怕偏的是负数?下面是推断,我没有逐行核对 x264 的源码。编码器一帧一帧往下编,量化参数必须在这一帧编完之前定下来。可这一帧最终多大要编完才知道。单遍只能靠已编完的帧和一个复杂度模型去猜。猜偏了就在后面的帧里补。两遍编码的第一遍先收集每帧复杂度,第二遍按统计分配码率。但第二遍定参数时依旧是预测。字节数还是事后才出。顺序决定了误差躲不掉:先定参数,后出字节。
一个侧面证据是逐秒码率。10 MB 档的平均目标是 1205 kbps,A 的逐秒码率在 790 到 1950 kbps 之间晃。平均值对得上并不等于每一秒都对得上。总字节只在整段结束时结算。最后几秒多出来的部分已经没有时间去补,这一段同样是推断。
两遍编码在这批数据里没有更贴目标
三种做法都扣了音轨且偏差按目标折算,括号里是单次耗时。
| 做法 | 25 MB 档 | 10 MB 档 |
|---|---|---|
| 单遍 | −0.66%(23.1 s) | −0.35%(22.7 s) |
| 两遍 | +0.28%(34.2 s) | +1.74%(25.8 s) |
| CRF 二分 | −0.86%(217.1 s) | −1.45%(157.3 s) |
两遍最贴目标这个说法,这份数据不支持。三种里只有两遍超了目标,25 MB 档超 69 942 字节而 10 MB 档超 1.74%。原因我没查出来。推断和第二遍的码率分配有关但没有证据。CRF 二分每一轮都是整片重编码,7 轮要跑 157 到 217 秒,是单遍的 7 到 9 倍而换来的是两个档都落在目标以内。这只是 A 上的结果所以不写成哪种做法更好。
同一段 A 我也在浏览器里的现成工具上压了一次。用的是图映 ImgIng(imging.cn/)的视频压缩页。它只能... 10 487 546 字节且画面降到了 1080×608 和 24 fps,高清均衡加最长边 1280 是 25 938 497 字节。想贴着 10 MB 或 25 MB 只能自己换档位去碰,落点不由你定。它内部怎么分配码率我不清楚。
换成录屏误差差了一个量级
B 是逐帧画出来的合成录屏,同样 60 秒 1080p。内容比真实录屏更静更干净,下面的结论只属于这一段。10 MB 档扣掉音轨后,单遍是 −8.65% 而两遍是 +12.0%(11 195 510 字节),CRF 二分是 −1.6%。同样的两遍在 A 上是 +1.74%。B 的视频码率控制误差有 ±1.1 MB。逐秒码率在 40 到 5000 kbps 之间跳,两遍在场景切换的地方出现尖峰。
25 MB 档是另一种情况。单遍是 −43.9% 而两遍是 −42.7%。CRF 二分把范围放到 0 到 51,也只做到 11 053 804 字节(−55.8%)。这里不是码率控制失灵,而是内容太简单,预算用不满。
目标体积要留多少余量
我现在算码率的写法是:视频码率 =(目标字节×8÷时长)− 音频码率 − 容器摊销。66 KB 的容器摊到 60 秒大约是 9 kbps 可以忽略但音轨那一项必须扣。余量没有通用值。A 上两遍最大超 1.74% 而留 2% 够用。B 上两遍超了 12.0% 所以 2% 远远不够。同一套参数换了素材后误差就差一个量级。余量跟着素材走,别写成常量。
编完以后量一次实际字节,超了就按超出的比例下调码率重压,这一步我没测收敛几次。更稳的做法是拿你自己的素材按扣音轨的公式压一遍。看实际字节和目标差多少再决定留几个百分点。