批量转码前用 ffprobe 筛一遍,36 个文件能跳过 26 个

同一个文件夹里五段视频,我用 HandBrake 的 Fast 1080p30 预设整批压了一遍。有一段 2.03 MB 的压完变成 5.85 MB,是原来的 288%。另一段 22.26 MB 的压完是 21.77 MB,只省了 2%。这是 5 段样本在一台 M4 Mac 上单次跑出的结果。它不能当成 HandBrake 的普遍表现。但它让我换了个顺序做事:批量转码之前先筛一遍,看哪些文件根本不该再碰。

码率低的文件再压会变大

那段变大的视频是 1080p,整体码率 2.06 Mbps 左右。本来就压得很狠。我把这个现象拆开测了一次。一共 4 组画面。3 段取自维基共享资源的真实视频,加 1 组合成测试图。每组先用 x264 的 CRF 12 到 48 共 9 档各压一遍当作已经压过的文件。再统一用 CRF 23、preset medium 压第二次,总共 36 个文件。低码率那一头的结果很干脆。bpp 低于 0.08 的 24 个点再压一次体积全在涨,涨幅是 1.34 倍到 6.67 倍。画质也没有变好,这里只算了 SSIM 数值,没有逐帧用眼睛看。驼鹿那组的预压文件对母版的 SSIM 是 0.9205,再压后是 0.9180。体积却成了 3.76 倍。我的理解是,再压一次只是用更多比特去保存已经丢掉的细节。

这张图要看什么:左右两栏纵轴都是再压后的体积变化。红线是 0,红线以上就是变大。左栏横轴是原码率。右栏横轴是每像素每帧比特数,还有一条标着 0.10 的虚线。黑色空心圆是 5 段真实视频,紫色菱形是合成测试图。样本视频:维基共享资源,作者与许可见文末;经重新编码、截取,已改动。

对比两栏就能看出,只看码率为什么不行。1080p 的驼鹿在 2.74 Mbps 再压后是 1.48 倍,720p 的人群在 1.95 Mbps 已经是 1.34 倍。按码率对齐时「不再变大」的位置散在 3.1 到 5.5 Mbps 之间。换成右栏的每像素每帧比特数,几组内容的转折点都落在 0.09 到 0.13 附近。

筛选只看三项数字

要读的是分辨率、帧率和码率三项。用 ffprobe 一条命令就能拿到。再算一个数:码率 ÷(宽 × 高 × 帧率),也就是每像素每帧的比特数,下文叫它 bpp。下面是一段 WebM 的读数。

arduino 复制代码
$ ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,r_frame_rate,bit_rate:format=duration,size,bit_rate -of default=nw=1 input.webm

同一条命令在驼鹿那段 1080p 的 VP9 WebM 上,视频流这一栏读出来是 N/A,整体码率那一栏有值,约 2.06 Mbps。

这里有个坑。WebM 的视频流读不到码率,是 N/A。只能退回下面 format 里那个整体码率。或者拿文件大小除以时长自己算。整体码率带着音轨,所以这类文件的 bpp 会略偏高。编码名我也看了,只能当参考。同样是 H.264 的文件,高码率和低码率的结局完全两样。光靠它判断不了。

算出来以后只比一次:bpp 低于 0.10 就跳过。我把这条套到 36 个文件上,低于 0.10 的有 26 个。其中 24 个再压后变大,另外 2 个只省了 9% 和 4%。没有一个能省 10% 以上的文件被误跳过。高于 0.10 的 10 个里有 8 个省了 10% 以上,剩下 2 个省 4% 到 7%。

筛完剩多少

我写了个小脚本,读 ffprobe 再算 bpp 最后和 0.10 比大小。我跑了一遍,测了四个文件。1280×720 的 6450 kbps 讲座视频 bpp 是 0.2333,放行。733 kbps 的低码率讲座视频是 0.0265,跳过。1080p 的 2735 kbps 片段是 0.0440,跳过。刚才那个 WebM 是 0.0332,也跳过。四个剩一个。我又真压了一遍来对账。放行的那个从 8,066,510 字节压到 4,513,680 字节,省了 44%。被跳过的另外三个里,我真压了两个:一个从 920,021 变成 2,183,646,是 2.37 倍。另一个从 2,670,854 变成 3,949,234,是 1.48 倍。WebM 那个没再压。

再换前面那 5 段维基共享资源的真实视频验证。去掉音轨,CRF 23 加 medium,单次,结果如下表。

样本 bpp 再压后体积 ÷ 再压前 筛选结果
驼鹿 1080p 0.032 2.805 倍 跳过
人群 1080p 0.113 0.831 倍 压
人群 720p(VP9) 0.239 0.560 倍 压
人群 360p 0.264 0.664 倍 压
人群 720p(VP8) 0.279 0.348 倍 压

5 段剩 4 段。跳过的恰好是开头被 HandBrake 压成 288% 的那段。0.113 那段压了也只省 17%。它贴着阈值,我会再看一眼要不要压。

阈值跟着目标设置变

我的推断是,0.10 不是视频自己的属性,它对应的是目标设置本来会产生的 bpp。我把目标换成更狠的 CRF 28 又测了一轮。低于 0.05 的 21 个文件里有 20 个变大。阈值大约要降到 0.06。这是对照数据给出的推断,我没有单独再验证。换成 H.265 或 AV1 或固定目标码率的模式。阈值怎么变我没测。真实样本都是维基共享资源转码过的 WebM,预压文件由 x264 生成,只测了 H.264 到 H.264。没有手机原片,也没有录屏素材。

不想自己写脚本的话,我用的是图映 ImgIng imging.cn/ 的视频压缩。它在电脑浏览器里支持多文件批量。档位自己选。官网写智能推荐档只在至少节省 12% 且画质复核通过时才采用。我拿开头那 5 段按默认设置跑了一次,有 4 段保留了原片。手机和平板上只能做图片压缩,视频要到电脑上做。

样本视频署名(文件 / 作者 / 协议):Moose by the road.webm / Forza1000 / CC BY-SA 4.0;A Demonstration of Disabled People June 2018 1.webm / DGW / CC BY-SA 3.0;Worldwide Demonstration -- March 2021 Amsterdam.webm / Karen Eliot / CC BY-SA 2.0;Demonstration of Ethiopian residents in Israel, July 2019.webm / Israel Police / CC BY-SA 3.0;Demonstration in Al-Zabadani, 23 March 2012.webm / Allfor Syria / CC BY 3.0。均取自维基共享资源,经重新编码、截取,已改动。

你可以自己拿最大的那个文件夹测一遍:先用 ffprobe 读出每个文件的 bpp,把低于 0.10 的挑出来单独放着,再把剩下的交给转码软件排队。0.10 套到自己的素材前,先抽几个文件按同样的办法试一次。

相关推荐
费曼学习法1 小时前
从 0 到 1 上手 Super-Agent 架构:任务拆解、工具路由与失败回滚的三段实现
前端
2026小鱼AI1 小时前
当 LLM 生成的教学设计“不合格”:结构化校验、局部重生成与并发下的质量漂移
前端
疯华整猫1 小时前
借助 Trae 复刻百度网盘“无权限摇一摇”检测:Android 传感器 AI 辅助实战
前端
deli0071 小时前
流式输出为什么一段一段蹦?拆开 SSE 分块实测 20094 个乱码
前端
不一样的少年_1 小时前
为什么你写的 Agent 一报错就死循环?很多人都漏掉了这层“错误自救与熔断”机制
前端·后端·agent
857god1 小时前
做了个匿名聊天站,我把 7 个前端坑踩了一遍
前端
deli0071 小时前
GIF 太大怎么压?调帧率、尺寸、颜色数,10 组参数体积对照实测
前端
Momo__2 小时前
你写的 import 重试,从来没生效过
前端·javascript·typescript
鶴哥只手遮天2 小时前
Angular 22 源码级深度解析-第 13 章 依赖注入(一):Injector 抽象、`R3Injector` 与 bloom filter
前端·开源·angular.js