录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%

用 ffmpeg 转录屏时,关键帧间隔(-g)这一项很容易被放过去,x264 默认 250 帧看着也挺合理。这次把同一段录屏按 1 秒、2 秒、5 秒、10 秒、30 秒和不限六档重新编码,别的参数一个不动。按这次的数据,间隔从 2 秒拉到 10 秒,录屏体积小了 75.6%。同样的操作放到摄像头拍的视频上只小了 5.0%。文字清晰度在几档之间几乎没差。真正变的是跳转时要多解码多少帧。

样本是自制的模拟网课录屏

录屏样本是一段自制的 4 分钟模拟网课录屏,里面有课件翻页、鼠标、打字和讲义滚动。规格是 1920×1080、30 fps。它按 12 Mbps 恒定码率生成,原文件 360.0 MB,没有音轨。它是拿课件页面截图合成的,不是哪个录屏软件录出来的。整段约 78% 的时间画面完全不动,也没有噪点。它比真实录屏干净也更好压,读下面的数字时要记着这一点。对照组是维基共享资源上两段 CC 协议的摄像头视频:一段 30 秒 1080p25,另一段 7.87 秒 1080p30。编码都在一台 Apple M4 的 Mac 上用 ffmpeg 加 x264 跑。参数是 preset medium、CRF 23 并去掉音频,每档只跑一次。动命令行之前同一段录屏先在浏览器里过了一遍。我用的是图映 ImgIng imging.cn/ 的视频压缩,智能推荐档 H.264 出来 20.2 MB。它的小字清晰度和后面 ffmpeg 1080p 那几档同档。下文的 MB 一律按 1 MB = 1,000,000 字节算。

录屏体积随间隔拉长一路往下掉

五条命令只有 -g 那一处不同。「不限」用的是 x264 的 keyint=infinite,整段只在开头留一个关键帧。行尾注释是这段样本实际输出的字节数:

bash 复制代码
ffmpeg -i input.mp4 -an -c:v libx264 -preset medium -crf 23 -g 30  gop1s.mp4   # 34,550,226 字节
ffmpeg -i input.mp4 -an -c:v libx264 -preset medium -crf 23 -g 60  gop2s.mp4   # 18,065,145
ffmpeg -i input.mp4 -an -c:v libx264 -preset medium -crf 23 -g 150 gop5s.mp4   #  7,835,468
ffmpeg -i input.mp4 -an -c:v libx264 -preset medium -crf 23 -g 300 gop10s.mp4  #  4,402,121
ffmpeg -i input.mp4 -an -c:v libx264 -preset medium -crf 23 -x264-params keyint=infinite gopinf.mp4  # 1,299,050

30 秒那档是 -g 900,出来 2,167,967 字节。六档加上 x264 默认值放进一张表。摄像头那列用的是 30 秒那段对照视频:

关键帧间隔 录屏字节 录屏相对 1 秒 录屏平均需解码帧数 摄像头相对 1 秒
1 秒 34,550,226 100% 15.5 100%
2 秒 18,065,145 52% 30.5 94%
5 秒 7,835,468 23% 75.5 90%
10 秒 4,402,121 13% 150.5 89%
默认 250 帧(8.3 秒) 5,222,157 15% 124.8 与 10 秒相同
30 秒 2,167,967 6.3% 450.5 未测(片长只有 30 秒)
不限 1,299,050 3.8% 3,600.5 89%

这张图看两条线和灰柱。蓝线是录屏体积,它从 1 秒档的 100% 一路掉到不限档的 4%。橙线是摄像头视频。2 秒以后它就趴在 89%~94% 之间。灰柱是录屏跳到任意位置平均要解码的帧数,看右侧的对数轴,从 16 涨到 3600。摄像头那条线的素材取自维基共享资源(CC BY-SA),重新编码过。

从 2 秒到 10 秒录屏从 18,065,145 字节降到 4,402,121 字节,一共小了 75.6%。摄像头那段从 19,159,323 字节降到 18,202,432 字节只小了 5.0%。另一段 7.87 秒的摄像头视频相对 1 秒档在 2 秒档是 85%,在 10 秒档是 72%。它太短了,10 秒和不限两档对它来说都只剩一个关键帧。同一个参数在两类视频上的反应差了十几倍。

摄像头视频的字节压在P帧上

差别出在字节落在哪类帧上。默认参数下,录屏视频流有约八成压在那二十几个关键帧上,另外七千多帧 P、B 帧合起来才占两成;摄像头视频正好倒过来,30 秒那段有 59.4% 的字节在 P 帧上,7.87 秒那段是 77.7%。

静止帧为什么能小成这样?下面是推断,没有单独测。P、B 帧记的是和参考帧的差。画面不动的宏块大概率走了跳过模式。几个比特就能说清「照抄参考帧」。摄像头就算机位不动也有传感器噪点和细小晃动。每个宏块都有残差要记。我是中文系转行的,编码器内部怎么判跳过讲不深,但这个推断和上面的数对得上。录屏里每多一个关键帧就是整屏重画一遍,平均 140,557 字节实打实加上去。1 秒档在 240 秒里要放 240 个关键帧。按每个约 140 KB 粗算就是三十多 MB。这和表里 1 秒档的总量在一个量级。

文字清晰度不随间隔变化

体积少了这么多,字会不会糊?按这次的数据并没有。拿第 60 秒同一块英文小字和无损原始画面比 SSIM,1 秒档是 0.9686,10 秒档 0.9689,不限档 0.9677。整段和原文件比的话是 1 秒档 0.99932、默认档 0.99934、不限档 0.99899。CRF 23 管的是每一帧画多细,关键帧间隔管的是隔多久整屏重画一次,两件事在这段样本上基本是分开的。不限档那个 0.99899 低了一点点,原因没查。

代价是跳转要多解码很多帧

关键帧一稀拖进度条就得从前一个关键帧一路解到目标位置。表里第四列就是这个代价的代理指标。算法是对每一帧数从前一个关键帧解到它要多少帧再取全片平均。2 秒档是 30.5 帧,10 秒档涨到 150.5 帧,30 秒档到了 450.5 帧。不限档 3,600.5 帧。这相当于平均要从半段之前开始解。单线程精确跳转的中位耗时只能看相对大小:10 秒档是 163.4 ms,不限档是 2,623.5 ms。两档差了十几倍。

要拖着看的课程录屏,可以停在 10 秒这一档。体积已经是 1 秒档的 13%,比 x264 默认的 250 帧那档还小一些。平均要解的 150 帧换成单线程跳转中位也就 163.4 ms。30 秒档又省一半。可平均要解 450 帧。值不值我还拿不准,得看播放器和观看习惯。不限档更适合只从头放到尾的存档,谁要在里面来回拖会很难受。

关掉场景切换检测几乎没差

不限档还顺带对了一下场景切换检测:关掉(scenecut=0)是 1,299,049 字节,开着是 1,299,050 字节,只差 1 字节。原因是这段样本第 40、120、180 秒的三次翻页本来就没触发关键帧,新页面都走了 P 帧,所以开关它都一样。真实录屏要是从 PPT 整屏切到浏览器,结果可能不一样。这种情况这次没测。

这段样本只有一段 4 分钟,单次跑完。它没有 60 fps 版本,也不是 Retina 那种更高的分辨率。真实录屏的噪点和抗锯齿抖动都比它多。HLS、DASH 切片时关键帧间隔要和分片时长对齐,这一点我也只是推断,没测过。想在自己的录屏上核对,可以挑一段 5 分钟左右的各跑一次 -g 60 和 -g 300 比体积。再拖几下进度条看卡不卡。

相关推荐
Lank_M18 分钟前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写20 分钟前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李22 分钟前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易36 分钟前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马1 小时前
前沿视界
前端
kyriewen1 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易1 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员
IT_陈寒1 小时前
Vue的这个响应式陷阱,差点让我加班到天亮
前端·人工智能·后端
Flynt3 小时前
别写拖拽行为,只声明"能拖到哪":UIST 最佳论文的拖拽方案我实测了
前端·svg·preact