Pat*_*ick 5 javascript metadata icecast web-audio-api
我一直试图找出实现我已经有一段时间的想法的最佳方法。
目前,我有一个无线电扫描仪的 icecast mp3 流,其中“正在播放”元数据会根据扫描仪登陆的频道实时更新。当使用 VLC 等专用媒体播放器时,元数据与接收到的音频完美对齐,它的功能完全符合我的要求——本质上是一个远程无线电扫描仪。我想通过网页实现类似的东西,从表面上看,这似乎是一项简单的任务。
如果我只想流式传输音频,使用简单的<audio>标签就足够了。然而,HTML5 音频播放器没有嵌入的 in-stream 元数据的概念,icecast 与 mp3 音频数据一起编码。虽然我可以从 icecast 服务器状态 json 查询当前的“正在播放”元数据,但由于客户端和服务器端缓冲,以这种方式完成时,音频和元数据之间可能会有超过 20 秒的延迟。在某些情况下,当扫描仪每秒都在更改其“正在播放”元数据时,这完全不适合我的应用程序。
有一个非常有趣的 Node.JS 解决方案就是为了这个确切的目标而开发的 - 无线电扫描仪应用程序中的实时元数据:icecast-metadata-js。这表明确实可以处理来自单个 icecast 流的音频和元数据。现场演示特别令人印象深刻:https : //eshaz.github.io/icecast-metadata-js/
但是,我正在寻找一种可以完全在客户端运行而无需安装 Node.JS 的解决方案,而且看起来这应该是相对简单的。
今天搜索了一天的大部分时间,似乎在这个站点和其他地方有几个类似的问题,没有任何有凝聚力、精心设计的答案或建议。从我到目前为止能够收集到的信息来看,我相信我的解决方案是使用 Javascript 流功能(例如fetch)从 icecast 服务器中提取原始 mp3 和元数据,通过 Web Audio API 播放音频并处理元数据当他们到达时阻止。类似于下图:

我想知道是否有人有任何好的阅读和/或示例来通过 Web Audio API 播放 mp3 流。我在大多数 JS 方面仍然是一个相对新手,但我了解 API 的基本概念以及它如何处理音频数据。我正在努力实现的正确方法是实现 a) 实时处理来自 mp3 流的数据,以及 b) 检测嵌入在流中的元数据块并相应地处理它们。
如果这是一个冗长的问题,我深表歉意,但我想提供足够的背景故事来解释为什么我想以我所做的特定方式处理事情。
在此先感谢您的建议和帮助!
我很高兴您找到了我的库icecast-metadata-js!该库实际上可以在客户端和 NodeJS 中使用。完全在客户端运行的实时演示的所有源代码都位于存储库中: https: //github.com/eshaz/icecast-metadata-js/tree/master/src/demo。演示中的流没有改变,只是服务器端的普通 Icecast 流。
你的图表中的内容基本上是正确的。ICY 元数据交错在实际 MP3“流”数据中。ICY 元数据更新发生的元数据间隔或频率可以在 Icecast 服务器配置 XML 中配置。此外,这可能取决于您的来源向 Icecast 发送元数据更新的频率/准确度。我的演示页面上的警察扫描仪中使用的软件几乎与音频完全同步更新。
通常,默认元数据间隔为 16,000 字节,这意味着每 16,000 个流 (mp3) 字节,将从 Icecast 发送一次元数据更新。元数据更新始终包含一个长度字节。如果长度字节大于0,则元数据更新的长度为元数据长度字节*16。
ICY 元数据是一串由分号分隔的 key='value' 对。元数据更新中任何未使用的长度都用空填充。
IE"StreamTitle='The Stream Title';StreamUrl='https://example.com';\0\0\0\0\0\0"
read [metadataInterval bytes] -> Stream data
read [1 byte] -> Metadata Length
if [Metadata Length > 0]
read [Metadata Length * 16 bytes] -> Metadata
Run Code Online (Sandbox Code Playgroud)
| 字节长度 | 响应数据 | 行动 |
|---|---|---|
| ICY元数据间隔 | 流数据 | 发送到您的音频解码器 |
| 1 | 元数据长度字节 | 用于确定元数据字符串的长度(不发送到音频解码器) |
| 元数据长度 * 16 | 元数据字符串 | 解码并更新您的“正在播放”(不发送到音频解码器) |
对 Icecast 服务器的初始GET请求需要包含Icy-MetaData: 1标头,该标头告诉 Icecast 提供隔行元数据。响应标头将包含 ICY 元数据间隔Icy-MetaInt,应捕获该元数据间隔(如果可能)并用于确定元数据间隔。
在演示中,我使用客户端获取 API 发出 GET 请求,并将响应数据提供到一个实例中,该实例IcecastReadableStream拆分流和元数据,并通过回调使每个数据可用。我使用媒体源 API 来播放流数据,并获取计时数据以正确同步元数据更新。
这是读取 ICY 元数据所需的最低 CORS 配置:
Access-Control-Allow-Origin: '*' // this can be scoped further down to your domain also
Access-Control-Allow-Methods: 'GET, OPTIONS'
Access-Control-Allow-Headers: 'Content-Type, Icy-Metadata'
Run Code Online (Sandbox Code Playgroud)
如果需要, icecast-metadata-js可以检测 ICY 元数据间隔,但最好允许客户端使用此附加 CORS 配置从标头读取它:
Access-Control-Expose-Headers: 'Icy-MetaInt'
Run Code Online (Sandbox Code Playgroud)
另外,我计划发布一个新功能(在完成 Ogg 元数据之后),该功能封装了fetchapi 逻辑,以便用户所需要做的就是提供 Icecast 端点,并获取音频/元数据。