简短回答: 压缩后变大,是因为 CRF 设的是"画质目标",不是"文件大小目标"。如果原视频本来就压得很狠(码率低),用一个比它画质更高的 CRF 重新编码,编码器就会花更多字节去保住画质,文件自然比原来大。解决办法是改成限制码率,或者干脆不重新编码。
如果你只想拖进去就压好,QuickVid Compress(我做的 Mac 应用)按目标大小和场景给了预设;下面是用 ffmpeg 自己动手的做法。
先搞清楚 CRF 到底管什么
CRF 越小,画质越高,文件越大;CRF 越大,画质越低,文件越小。这一点没变,详见 CRF 是什么意思。
关键是:CRF 不看你原来的文件有多大。 它只问"这一帧要达到什么画质"。原视频已经被压得很糊时,编码器为了在新文件里保住那些已经存在的压缩痕迹,要花很多字节。
实测:一个会变大的例子
我用 ffmpeg 生成了一段 10 秒、1280×720 的测试视频,用 1.5 Mbps 编码,文件 1.95 MB。再用 libx264 以不同 CRF 重新编码:
| 重新编码设置 | 文件大小 | 相对原文件 |
|---|---|---|
| 原视频(1.5 Mbps) | 1.95 MB | – |
| x264 CRF 18 | 6.16 MB | 约 3.2 倍 |
| x264 CRF 23 | 3.91 MB | 约 2.0 倍 |
| x264 CRF 28 | 2.38 MB | 约 1.2 倍 |
| x265 CRF 28 | 2.17 MB | 约 1.1 倍 |
连 CRF 28 也没有比原文件小。测试内容是合成的分形动画,画面细节很多,真实素材的数字会不同,但规律一样:原视频码率越低,用低 CRF 越容易变大。
办法 1:限制最大码率(最直接)
在 CRF 之外加一个码率上限。编码器仍按 CRF 追求画质,但不允许超过上限:
ffmpeg -i in.mp4 -c:v libx264 -crf 23 -maxrate 1200k -bufsize 2400k -preset medium -c:a copy out.mp4
同一段测试视频,这条命令输出 1.55 MB,比原文件小。-bufsize 一般设为 maxrate 的 2 倍左右。
办法 2:两遍编码,直接命中目标码率
想要确定的大小,用目标码率两遍编码:
ffmpeg -y -i in.mp4 -c:v libx264 -b:v 1200k -pass 1 -an -f null /dev/null
ffmpeg -i in.mp4 -c:v libx264 -b:v 1200k -pass 2 -c:a copy out.mp4
测试里得到 1.55 MB。码率 ≈ 目标大小 ÷ 时长:目标 8 MB、时长 60 秒,约为 8×8192 kbit ÷ 60 ≈ 1090 kbps,再给音频留一点。
办法 3:别重新编码
如果原视频已经够小,或者你只是想换容器、剪头尾,用拷贝,文件大小不会变大,画质也不会再损失:
ffmpeg -i in.mp4 -c copy out.mp4
先用 ffprobe 看看原视频码率,再决定要不要重编码:
ffprobe -v error -show_entries format=bit_rate -of default=nw=1:nk=1 in.mp4
代价:别指望免费午餐
码率上限会降画质。同一段测试视频对比原视频的 PSNR(数值越高越接近原片):CRF 18 约 46.7 dB,CRF 23 约 43.0 dB,加了 1200k 上限的版本约 35.8 dB。文件越小,离原片越远,这没有办法绕开。PSNR 只是粗略指标,对真实素材最终还是要用眼睛看。
另外两个常见原因
- 从 HEVC 转到 H.264。 同样画质下 HEVC 通常比 H.264 小很多。iPhone 默认拍 HEVC,转成 H.264 再用低 CRF,文件容易变大。要小就继续用 HEVC(
libx265,苹果设备加-tag:v hvc1)。 - 分辨率没变,只改了编码器。 想真正变小,往往还要降分辨率或帧率,比如
-vf scale=-2:720。
常见问题
CRF 越大压缩越小吗? 是的,CRF 越大,画质越低,文件越小;每增加约 6,文件大致减半(H.264 经验值)。但前提是原视频没有被压得很狠。
CRF 18 能保证比原文件小吗? 不能。CRF 18 追求很高的画质,原视频码率低时,输出常常比原文件大。
我的手机视频压了反而大了,怎么办? 先用 ffprobe 看码率,再用办法 1 的码率上限,或保持 HEVC,不要转成 H.264。