由于旧版本的x264存在bug,h.264视频流具有以下三个属性:
- 使用 x264 build 150 或更早版本编码
- 使用 4:4:4 色度子采样
- 比特流不包含 x264 版本信息
许多视频播放器无法正常播放。新版本的视频播放器mpv
有一个专门的选项
--vd-lavc-assume-old-x264
专门针对此问题(参见:https://mpv.io/manual/master/)。
在FFmpeg 错误追踪器建议将正确的 SEI.h264 添加到视频流中(我猜包含 x264 版本信息)。我不喜欢依赖这样的黑客,所以我的问题是:有没有一种“正确”的方法(最好使用 ffmpeg)来修复文件,就好像它们是用新(修复)版本的 x264 编码的一样?
显然我希望保留(或多或少)视频质量和文件大小。如果需要重新编码,那么它应该不会改变任何东西,但会修复旧 x264 实现的错误行为。(更多信息:错误报告给出了一个损坏的文件示例。据推测,旧 x264 中的错误可能是这里介绍。
答案1
感谢您发布这个问题,它帮助我了解了我所遇到的问题的性质。我有一个似乎有效的解决方案。
就我而言,我使用的是来自 Ubuntu 存储库的 ffmpeg。能够解码我的 libx264 文件的最后一个版本是 2.8.6。升级到 2.8.14 或 2.8.15 后,我遇到了您描述的解码问题。我不想重新编码我的旧视频,我只想修复标头,以便 ffmpeg 可以正确识别原始编码过程中引入的错误,并正确播放它们。
所以首先我下载了包含最新版本 ffmpeg v4 的静态二进制文件。我将此二进制文件链接到ffmpeg4
我的系统上,以便我可以控制我正在使用的版本。我们需要 2.8 之后引入的一些新功能(不确定具体时间)。如果您已经安装了较新版本的 ffmpeg,只需使用该版本并在以下命令中替换ffmpeg4
为。ffmpeg
现在,从损坏的视频中提取原始比特流(称之为 BROKEN.mkv)。
ffmpeg4 -i BROKEN.mkv -vcodec copy -an -bsf:v h264_mp4toannexb raw.h264
我不确定 h264_mp4toannexb 标志是否必要,它可能会自动插入对于这种格式。
现在,将比特流放入一个新的 mp4 容器中,并修复 SEI 标头中有关旧的 x264 构建的信息。
ffmpeg4 -r 30 -i raw.h264 -avoid_negative_ts 1 -bsf:v h264_metadata='sei_user_data=dc45e9bde6d948b7962cd820d923eeef+x264 - core 150' -c copy FIXED.mp4
比特流不包含时间戳信息,因此您将在此处收到大量警告。我还发现我必须手动将帧速率设置为 30fps(-r 30
),否则它会猜测 25 到 30fps 之间的某个可变帧速率。我不知道如何正确提取时间戳或将它们正确地混合到新容器中。如果您有解决方案,请告诉我!很多人都推荐这样做,-fflags +genpts
但这种方法行不通似乎什么都做对我来说。最后,我添加了-avoid_negative_ts 1
以使第一帧的时间戳为非负数。
最后,这是可选的,如果你想将结果放入 MKV 容器中,你可以这样做
ffmpeg4 -i FIXED.mp4 -c copy FIXED.mkv
为什么先转换为 MP4,然后转换为 MKV? 似乎 MKV 容器在没有时间戳的情况下会拒绝继续,但 MP4 会这样做并发出警告。然后您可以转换为 MKV 而不会发出警告。
所以经过所有这些,我得到了可以正常工作的 MP4 和 MKV 文件。但是,我检查了一些帧,发现有一些细微的变化(亮度变化约为 2 个级别)。我不明白为什么会发生这种情况,因为这应该是无损的。如果您对如何做得更好有任何建议,请告诉我。
编辑:我注意到我的一些时间戳在 MP4 容器中是负数,从 -0.066667 秒开始。移至 MKV 容器后,所有负时间戳都变为零。添加-output_ts_offset 0.066667
到命令中解决了这个问题,并让它们从零开始。但我不明白为什么它从 -0.066667 开始。
编辑2:删除负时间戳的更好方法是在编码mp4时使用“-avoid_negative_ts 1”。