FFmpeg 压缩视频花屏排查
技术文章

FFmpeg 压缩视频花屏排查

一个奇怪的视频压缩导致的花屏问题。

3.1万次点击10分钟阅读

最近在开发我的新产品 OrbitSpace

为了制作官网,我需要录制一段产品演示视频,放在首页作为 Showcase,因此整个流程其实非常简单:

录屏 → FFmpeg 压缩 → 上传 Cloudflare R2 → 官网在线播放

本以为这是整个发布流程中最轻松的一步,没想到却让我踩到了一个非常隐蔽的坑,也顺带学到了不少关于 MP4 时间轴、FFmpeg 和浏览器视频解码的知识。

这篇文章记录一下整个排查过程,希望以后有人遇到类似问题时,能少走一些弯路。

事情的开端

这段产品演示视频是使用录屏软件直接录制的,录制完成后导出为:origin.mp4。为了确认没有问题,我先在本地检查了一遍:

  • ✅ QuickTime 正常
  • ✅ IINA 正常
  • ✅ 浏览器本地打开也正常

整个视频从头到尾没有任何异常,于是开始准备压缩,使用的是一条非常常见的 FFmpeg 命令:

整个压缩过程没有任何报错,直到我播放生成的视频。

一个诡异的现象

由于视频最终需要放到官网,我把压缩好的视频上传到了 Cloudflare R2,结果浏览器里完全无法播放,在播放到第 6 秒之后,直接停住了,整个流程看起来像这样:

第一反应:Cloudflare R2 把视频弄坏了?

因为问题是在上传之后暴露的,所以我的第一反应就是:

难道是 Cloudflare R2?

于是开始各种排查:

  • 删除重新上传
  • 新建 Bucket
  • 换域名
  • 清缓存
  • 下载回来重新播放

结果发现下载回来的视频依然会在第 6 秒花屏,也就是说 R2 保存的是已经有问题的视频,真正的问题其实发生在上传之前。

第二个怀疑对象:FFmpeg

既然不是 R2,那自然开始怀疑 FFmpeg,于是我开始疯狂试参数,包括:

  • CRF
  • Preset
  • Profile
  • Level
  • GOP
  • Keyframe
  • B Frame

几乎能试的都试了一遍,结果没有任何改善,依然是:

第 6 秒卡顿,本地播放花屏

这时候我才开始意识到一个问题,如果每次都坏在完全相同的位置,那它大概率不是编码质量的问题。

真正的突破:开始怀疑时间轴

后来查资料的时候,看到一句话让我突然意识到方向可能错了。

播放器能正常播放,不代表视频完全符合规范。

很多播放器都会自动帮用户做容错,例如:

  • PTS 不连续
  • DTS 跳变
  • 可变帧率(VFR)
  • 少量损坏帧

很多播放器会默默修复,所以用户根本感觉不到,但是 FFmpeg 在重新编码时,会继续使用这些时间信息。于是,原本隐藏的问题,被重新编码放大了。

最终解决方案

后来尝试重新整理整个视频时间轴,而不是单纯重新编码。

命令如下:

重新生成之后:

  • ✅ QuickTime 正常
  • ✅ Safari 正常
  • ✅ Chrome 正常
  • ✅ 上传 R2 正常
  • ✅ 官网播放正常

整个问题彻底消失,而且实际上真正关键的是下面几个:

重新生成视频时间戳(PTS),如果录屏软件导出的时间信息有问题,这一步会重新计算。

把视频统一转换成固定帧率,很多录屏软件导出的是 VFR(Variable Frame Rate,可变帧率),统一之后,整个时间轴都会重新整理。

明确告诉 FFmpeg:输出必须是恒定帧率(CFR),这一步和上一条通常需要一起使用。

重新同步音频时间轴,虽然表面看是画面出了问题,但浏览器播放视频时,音视频时间轴其实是一起推进的。

重新同步之后,很多奇怪的问题都会一起消失。

保证浏览器和硬件解码兼容,它不是导致第 6 秒花屏的元凶,但 Web 视频几乎都会加上这一项。

为什么上传后更容易暴露问题?

首先要明确的一点是,Cloudflare R2 不会修改视频内容。对象存储保存的是原始字节,真正导致不同的是浏览器播放方式,浏览器不会一次下载完整视频,而是:

如果视频存在:

  • 时间轴异常
  • Sample Table 异常
  • GOP 无法恢复
  • 时间戳跳变

浏览器硬件解码器可能直接停止继续解码,于是表现出来就是:播放到第 6 秒,视频彻底停住,而 QuickTime 的容错能力明显更强,所以还能继续播放。

问题复盘

现在回头看,其实整个问题一直都存在,只不过原始的视频文件中,播放器帮我隐藏了问题。

真正的流程应该是这样:

所以真正的问题是:录屏软件导出的视频包含了一些播放器能够自动容错,但重新编码和浏览器硬件解码不会替你修正的时间轴信息。

也正因为如此,它直到压缩/上传 CDN 之后才第一次真正暴露出来。

给 Web 视频的一份推荐参数

如果视频最终需要部署到:

  • 官网
  • Cloudflare R2
  • S3
  • CDN
  • Safari
  • Chrome
  • Edge

我现在直接使用下面这一套参数:

虽然参数比默认多了一些,但能够规避不少由于时间轴、可变帧率和播放器兼容性导致的隐蔽问题。

写在最后

在处理录屏软件生成的视频的时候,播放器为了保证用户体验,会默默帮我们处理很多问题。

只有当视频经历了:

  • 重新编码
  • 上传对象存储
  • 浏览器在线播放

这一整套流程时,那些原本隐藏得很深的问题,才会真正暴露出来。

如果你也遇到了:

  • FFmpeg 压缩后固定时间花屏
  • 上传 Cloudflare R2 后播放卡住
  • Safari 播放到一半停止
  • 浏览器在线播放异常

建议第一时间检查:

  • 视频时间戳(PTS / DTS)
  • 是否是 VFR(可变帧率)
  • 是否需要重新生成时间轴

相关文章