编写 MP4 复用器以获得乐趣和收益

6个月前 (07-12)新闻资讯483

在 OBS 30.2 中,我引入了新的“混合 MP4”输出格式,它解决了用户在 OBS 存在期间遇到的许多抱怨;它像 MKV 一样能够抵御数据丢失,但又像常规 MP4 一样具有广泛兼容性。

到达这里是一个相当漫长的旅程,并且涉及修复 OBS 中的其他几个错误,这些错误只有深入了解音频和视频数据的存储方式后才会显现出来。

在这篇文章中,我将尝试解释 MP4 的工作原理、常规/碎片化 MP4 的缺点以及我如何尝试使用混合方法解决这些问题。

QTFF、BMFF、WTF

我们今天所熟知和喜爱的 MP4 文件格式是基于 Apple 的“QuickTime 文件格式”(QTFF) - 大多数人称之为“MOV” - 最初创建于 90 年代。国际标准化组织 (ISO) 于 2001 年对其进行了改编,创建了MP4 文件格式,后来又分为更通用的“基本媒体文件格式”(ISO BMFF)和包含 MPEG 特定功能的 MP4 扩展。

从那时起,MP4 多年来经历了多次更新和扩展,以支持新的编解码器和更专业的用例。基本格式的可扩展性还意味着 MP4 的各种用户(例如 Apple)已添加扩展以支持各种其他功能,例如 DRM、3D 视频等。

问题特里布尔斯MP4

虽然 MP4 非常普及,几乎任何产品都支持它,但在将实时视频录制到磁盘的用例中仍存在一些问题。为了解释这些问题,我们首先来了解一下 MP4 文件的基本结构,因为这将有助于理解混合 MP4 工作所需的条件。

MP4 文件的核心是由 ISO 中称为“盒子”或 Apple 术语中称为“原子”的对象组成的。每个盒子都包含一个包含其大小和四个字母的名称/类型的标题,后面是其数据。大多数盒子都包含 ISO/Apple 规范中定义的数据结构,但有些是其他盒子的容器。这允许文件具有层次结构,并且可以通过引入包含附加信息的新盒子来轻松扩展格式,而不会破坏与现有软件的向后兼容性。但是,出于本篇博文的目的,我们将仅研究所谓的“顶级盒子”,即直接写入文件且不包含在其他盒子中的盒子。

MP4 图表

OBS 或 FFmpeg 生成的典型 MP4 文件将包含四个顶层框:

  • ftyp- 文件类型框:包含有关文件中使用的标准版本的信息

  • free- 自由空间框:应忽略/跳过的占位符

  • mdat- 媒体数据盒:包含媒体轨道(音频、视频等)的数据

  • moov- 电影盒:包含其他带有文件和媒体轨道元数据的盒子

这里有两件事造成了我们在使用 MP4 时遇到的主要问题:moov位于文件末尾,在完成文件时写入,并且需要能够理解盒子中包含的数据mdat。这意味着,如果文件写入因任何原因(BSOD、磁盘已满、断电等)而中断,并且盒子moov未写入,则文件极难恢复 - 甚至不可能恢复。如果您刚刚录制了《反恐精英》中最好的关键时刻,但随后磁盘空间耗尽,现在您没有任何证据证明它曾经发生过,这显然非常糟糕™!

请注意free框,因为它稍后会变得很重要:框标头中的大小字段限制为 4 GiB。为了获得mdat大于该大小的框,需要使用扩展的大小字段,这会增加标头的大小。FFmpeg 和 OBS 将写入占位符,以便mdat在必要时可以覆盖它以写入更长的标头。

这将引出下一部分,详细介绍解决这个问题的第一次尝试......

碎片化的挫败感

不久前,ISO格式进行了扩展,支持将媒体数据分割成“片段”,这通常称为“碎片MP4”。这些片段也可以分割成单独的文件,这主要用于通过互联网流式传输视频,无论是Twitch 1上的直播还是Netflix上的电影。这方面的细节超出了本文的范围,但您可以通过阅读更多关于HLSDASH的内容来详细了解这样做的原因及其对流式传输用例的优势。

与我们相关的是,碎片文件的“不完整”部分moov仅包含解码每个轨道所需的基本信息,而碎片中包含的特定样本(视频帧、音频片段)的信息存储在moof每个碎片的开头的(电影碎片框)中。

MP4 碎片图

这对于 OBS 录制用例非常有用,因为这意味着文件不再依赖于moov包含文件中有关媒体数据的所有信息的单个文件。每个片段只需要自己的moof框 + 开头的不完整部分moov即可正确播放。这意味着如果文件的写入被中断(例如由于电源故障),直到最后一个片段的所有内容仍将可读,从而解决了常规 MP4 文件的数据丢失问题。

听起来好得令人难以置信,不是吗?好吧,有一些重大的缺点最终导致我们很快停止使用碎片化 MP4 作为默认设置:

  1. 各种应用程序(包括编辑器和播放器)仍然无法很好地支持碎片文件

  2. 它们在硬盘或网络驱动器上的访问速度很慢,因为需要读取每个片段的标头才能获取文件的完整元数据并开始播放

  3. 由于 2,Windows 资源管理器等文件浏览器不显示文件持续时间。

  4. 某些播放器(例如默认的 Windows Media Player)由于 2. 不允许在文件中搜索,而是像直播一样播放

当然,可以通过重新混合文件来解决这个问题,但这只会让我们回到使用 MKV 的起点。一定有更好的方法……

混合和谐

很久以前,我有一个简单的想法:如果我们只是用一个完整的文件“完成”一个碎片文件,moov让它像一个普通的 MP4 一样工作,会怎么样?几个月前,我开始真正探索这个想法,它演变成我们现在所知道的“混合 MP4”。

在录制过程中,混合文件实际上只是一个碎片化的 MP4,保留了对数据丢失的抵御能力,但当录制停止时,它会被快速修改为看起来像普通的 MP4。我将这个过程称为“软重混”,因为它只需要覆盖文件的一小部分即可实现与完全重混文件类似的结果。

为此,moov在文件末尾写入一个完整的索引,该索引与普通 MP4 完全一样,开头的占位符free框被一个标头覆盖,mdat该标头将整个文件(直到新写入的框)变成一个巨大的媒体数据框,从而有效地向读取应用程序隐藏碎片。这意味着我们现在得到的是一个看起来moov像普通 MP4的文件,但实际上它内部有碎片!

混合 MP4 图

混合方法最终解决了我们遇到的所有问题,结合了两种方法的优点。如果文件未完成,您仍然拥有一个有效的碎片化 MP4,可以在必要时重新混合;如果它已完成,那么从各方面来看,它都只是一个普通的旧 MP4。

差不多就是这样了。这个想法经过了几轮迭代和改进,这篇文章只详细介绍了 OBS 中发布的最终版本。几天的工作和研究只能用几段话来总结,这有点让人难受,但这就是副标题中的“痛苦”部分的目的。

大雄兔

构建此实现的过程让我陷入了不少困境,因为它要求我学习大量有关音频和视频数据如何存储在文件中的底层细节,有时我会想知道为什么我的结果与我使用的参考不同。本节包含我在处理混合 MP4 输出时遇到的一些有趣和一些不那么有趣的示例。

章节标记

标记是 Hybrid MP4 在现有的基于 FFmpeg 的输出基础上添加的主要功能之一。别误会,FFmpeg也支持这些功能,但我们从未实现过。但在我这样做的时候,我觉得这可能是完成它的好时机,并且会给用户一个很好的激励去实际使用和测试它。

MP4 标准本身实际上并没有为章节标记定义任何内容,它们完全基于 Apple 的 QuickTime 规范,而且即使如此,它似乎也只是大部分文档化。OBS 中的实现直接改编自 FFmpeg,应该可以在所有相同的软件中使用,例如视频播放器和一些编辑套件,例如 DaVinci Resolve。遗憾的是,这不包括 Adobe Premiere 或 Final Cut Pro,但可能会有工具让这些用户更容易使用!

附加元数据

当时我想,我可以为每个媒体轨道和文件本身添加一些额外的元数据,比如编码器配置2,所以我就这么做了!当您测试不同的设置并希望稍后进行比较,但又不想在每次测试后都重命名文件时,这尤其有用。文件现在还包含正确的创建/编码日期,因此即使您重命名它们,您仍然可以跟踪文件的最初录制时间。

多轨视频

新的 MP4 输出现在还支持多个视频轨道和多个音轨,这对于调试 Twitch增强广播等功能非常有用,因为它拥有一个包含所有视频流的文件,可以在 MPC-HC 等播放器中轻松切换。

因此,对于普通用户来说,它还不是非常有用,但希望我们将来可以利用它来进行诸如 ISO 录制之类的操作(好吧,如果我们也能说服视频编辑器支持它......)。

音频编码器延迟以及为什么你的音频在过去几年可能不同步

AAC 和 Opus 音频都具有所谓的“启动”样本,这是音频流开始时的几毫秒静默,用于“预热”编码器,播放文件时应跳过该静默。包含启动样本的音频数据包具有负时间戳,表示在播放期间应跳过它们所包含的(部分)音频。OBS 中有两个与此相关的独立问题:

  1. 时间戳 < 0 的音频数据包将被丢弃,直到找到时间戳最接近且小于 0 的音频数据包,然后其时间戳将被“纠正”,以使流始终从 0 开始,从而包括输出中的延迟并导致其略微不同步

  2. CoreAudio AAC 编码器实现根本没有从时间戳中减去延迟,因此音频总是延迟 40-44 毫秒(在 48/44.1 kHz 时)

问题 1. 恰好不会影响默认音频编码器 (FFmpeg AAC),因为它使用相当于数据包持续时间的延迟,这意味着第一个包含实际音频的数据包已经从 0 开始。另一方面,Opus 会产生第一个时间戳为 -312 的数据包,下一个数据包为 648,然后 OBS 会分别将数据包“更正”为 0 和 960,并导致包含 312 个静音样本,音频延迟约 6.5 毫秒。

偶然的合作

就在我们将混合 MP4 功能合并到 OBS 几天后,FFmpeg 维护者 Martin提交了一个补丁,为 FFmpeg 的 MOV/MP4 复用器添加了类似的功能。这完全是巧合,并附有一条注释,指出 Apple 显然已经做了类似的事情,这证明了英雄所见略同 😛。

现在它也已合并到 FFmpeg 中,这意味着它已经在 git 构建中可用,并且希望很快就能进入稳定版本!

注意:FFmpeg 的实现略有不同,OBS 版本为了以防万一还增加了一些安全网。不过,有这些还是很棒的!

向前迈进

虽然 MP4 在许多方面都很棒,但它缺少其祖先 QTFF(或 MOV)中的一些功能和编解码器支持。添加“混合 MOV”模式是真正使其成为 OBS 中的新默认模式的下一步。这主要需要处理 MOV 和 MP4 之间的一些差异,例如 PCM 音频、不同实现的元数据结构,以及对 ProRes 编解码器的支持。如果一切顺利,我希望这可以在 OBS 31.0 之前完成!

为了将来的改进,我还想再做一些其他的事情:

  1. 章节标记时间戳更准确(需要 OBS 后端更新)

  2. 支持写入时间码(tmcd)轨道(需要与 1. 相同的后端更新)

  3. 支持可配置的片段持续时间,而不是始终在关键帧上进行分段(对于 ProRes 等仅限内部的编解码器尤其重要)

  4. (可能)内置实用程序用于以不同的格式导出章节标记,以便在 Premiere 等中更轻松地使用。

  5. (大概率)支持 Premiere 中嵌入XMP 元数据用于标记

致谢

  • FFmpeg 及其贡献者记录了未记录的内容

  • mp4box.js 的 GPAC,对于调试我的多路复用器和检查其输出非常有用

  • Apple 的旧 QTFF 文档确实非常好,并且对诸如启动样本等概念有很好的解释

  • ISO不是为这些规范设置付费墙,而是让它变成一个该死的纸上谈兵,每次你得到一个文档,它都会引用另外三个同样是付费的文档


此软件“仅限学习交流,不能用于商业用途”如用于商业用途,请到官方购买正版软件,追究法律责任与本站无关!

我们每月需支付高额服务器费用,捐赠将保证服务器有更好的配置和稳定运行;非常感谢您的捐赠支持。
(资源收集整理维护不易,敬请珍惜并感谢开发者。)