node.js - 如何从 RTMP 流(avc1 编码)中提取 SPS 和 PPS?
问题描述
我正在开发节点媒体服务器的扩展,以将传入的流作为 MP4 保存到磁盘。对于这种到 MP4 的转换,我非常依赖Apple QuickTime 电影规范、ISO/IEC 14496-14 规范(我在 Rust-MP4 GitHub 存储库中免费发现)和HLS.js 源代码
我目前正在测试一个视频。一旦成功,我将开始尝试其他视频。对于我的用例,我只需要支持 H.264 视频和 AAC 音频。
目前,当建立 RTMP 连接时,我收到的前 3 个数据包是一致的:
- 1 个
AMF metadata
数据包 (RTMP cid = 6),包含视频宽度、视频高度、比特率、音频采样率等信息 - 1 个
audio
数据包 (RMTP cid = 4),包含 7 个字节的数据。我假设这是 AAC 配置数据包 - 1 个
video
数据包 (RTMP cid = 5),包含 46 个字节的数据。我假设这是 AVC 配置数据包
在编写 MP4 moov
atom 时,我需要在两个地方使用不在 AMF 元数据中的附加信息(并且可能位于这两个配置数据包中):
- 在
esds
原子中,HLS.js 源附加了“配置”数据。我假设我只是在此处附加音频配置数据包中的整个 7 字节有效负载 - 在
avcC
原子中,HLS.js 源附加了“sps”和“pps”数据。这是我问题的根源
关于这 46 个字节的解析,我在Node Media Server和HLS.js中找到了似乎解析相同数据的代码。这两段代码之间的区别在于,Node Media Server 在数据包的开头需要额外的 13 个字节的数据。我收到的数据包似乎包含这些额外的 13 个字节,所以我只是按照他们的引导提取width
、height
、profile
、compat
和level
信息。特别是 46 个字节是:
[0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0xc0, 0x1f, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x42, 0xc0, 0x1f, 0xa6, 0x11, 0x02, 0x80, 0xbf, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xc2, 0x3c, 0x60, 0xc8, 0x46, 0x01, 0x00, 0x05, 0x68, 0xc8, 0x42, 0x32, 0xc8]
将其分解为我可以轻松解析的字节(在使用指数哥伦布编码之前):
[
0x17, // "frame type", specifies H.264 or HVEC
0x00, 0x00, 0x00, 0x00, 0x01, // ignored. Reserved?
0x42, // profile
0xc0, // compat
0x1f, // level
0xff, // "info.nalu" (per Node Media Server source)
0xe1, // "info.nb_sps" (per Node Media Server source)
0x00, 0x19, // "nal size"
// Above here are the bits exclusively seen by Node Media Server (specific to RTMP?)
// Below here are the bits passed to HLS.js as "unit.data" (common to all AVC1 streams?):
0x67, // "nal type"
0x42, // profile (again?)
0xc0, // compat (again?)
0x1f, // level (again?)
// Below here, data is not necessarily byte-aligned as Exponential Golomb encoding is used
// ...
]
现在我遇到的问题是在创建moov
原子(avcC
特别是原子)的过程中,我需要知道字节sps
和pps
字节。从 HLS.js 源来看,sps
可能只是这个视频配置数据包减去前 13 个字节。但是我如何找到pps
?实际上是pps
这个数据包的最后几个字节,我应该把它拆分到某个地方吗?这将在另一个数据包中交付吗?如果预期有两个视频数据包,是否有某种方法可以区分它们,以便我知道哪个是sps
哪个是哪个pps
?
如果我能弄清楚最后一点,那么我应该完全写完moov
数据包(之后我只需要找出mdat
数据包的正确格式,我应该有工作代码)
更新:为了记录,我刚刚检查了正在传递的第四个数据包,看它是否可能包含pps
数据。在重新连接到流约 20 次后,第四个数据包始终是一个video
数据包(RTMP cid = 5),但数据包的大小范围从 16000 字节到 21000 字节。我怀疑这是合法的视频数据。
第二次更新:当我完成 SPS 解析时,我刚刚检查了视频配置数据包中的偏移量,并且我在字节 23 ( 0x84
) 上。因此,PPS 实际上很可能位于该字节数组的末尾,但我不确定有多少字节是分隔符/标头(NAL 类型、NAL 长度等)以及实际 PPS 有多少字节。
解决方案
推荐阅读
- java - MacOS 上的 Java NNS 错误,找不到库
- docker - 自定义 WSL 配置文件破坏了 docker 桌面
- javascript - 提交ajax表单
- c# - 如何在 ASP .NET MVC 中将 @title.visible 更改为 false
- c++ - 如何确定一个 URL 是否指向服务器本身?
- java - 通过将更大的数组切成块来创建子数组数组(Java)
- coq - Coq 似乎拒绝承认用命题公式简单地替换命题变量?
- usb - USB设备是否可以检测到它连接到哪个主机?
- asp.net-mvc - 没有按钮小部件的亚马逊支付集成(从服务器端重定向)
- android - Expo / Android Simulator '错误打开模拟器'