【问题标题】:Real-time audio streaming using HTTP - choosing the protocol and Java implementation使用 HTTP 的实时音频流 - 选择协议和 Java 实现
【发布时间】:2015-10-07 05:34:38
【问题描述】:

我正在尝试为实时音频(在 Java 中)实现简单的 HTTP 服务器。假设有一个网站,您可以在其中看到正在播放的歌曲列表。当客户端连接到服务器时 - 让我们说在歌曲中间 - 我正在考虑使用“Range”HTTP标头并从歌曲的那部分开始发送数据范围。但是,如果在下载期间连接暂时丢失(并且歌曲完成) - 服务器应该发送之前的歌曲部分并完成它 - 还是服务器应该发送当时正在播放的歌曲的那些部分?什么是最佳实践/原则?

PS - 我不是在寻找用于音频流的 3rd 方软件。

编辑:
现在,在对可用的实时流技术进行了一些研究之后, 我看到了这些目标:
1. 为简单的实时音频流选择协议
2. Java中的协议实现(服务器端)

【问题讨论】:

  • 请回答问题或如果有重复链接,请显示,但如果您不知道答案,请不要关闭它。

标签: java http audio-streaming internet-radio


【解决方案1】:

您不能随意剪切媒体并期望播放器能够播放它。这适用于裸 MPEG 流,但其他容器和编解码器可能会遇到问题。因此,除非客户端已经拥有其余部分,否则不要发送部分文件。

当歌曲结束并进入下一首时,您还存在如何做的问题。

有两种方法可以实现这一点。其中之一是让您的客户可以使用静态媒体,然后在音频客户端寻找正确的时间。

我会选择的方式是真正创建一个互联网广播流,每个人都可以同时听到相同的内容,因为您实际上有一个公共缓冲区,可以在同一时间从其中复制块并将其发送到所有客户端。现在,如果您这样做,您将需要使用支持任意拼接(MP3 或 AAC)的编解码器/容器,或者在将流发送到客户端时使用容器重新包装流。这是一个复杂的问题,所以最好使用现成的东西,比如 Icecast。我知道您说您不是在寻找第三方解决方案,但这是最好的方法。如果您想自己完成这一切,则必须重新实现所有这些,或者仅支持 MPEG 流。

编辑:来自您的评论:

您能否详细解释一下数据流格式,即[24,576 bytes of stream] [metablock] [24,576 bytes of stream] [metablock] 等。如何分隔块,元块的内容是什么?

如果您愿意,您可以将 SHOUTcast 样式的元数据混合到您的流中。并非所有客户都支持这一点。如果他们这样做,他们将在请求中向您发送以下标头:

Icy-MetaData: 1

如果您看到该标头和值,您可以选择在流中包含元数据。元数据只是在每个流数据块之后注入。要包含元数据,首先您需要确定您的流块有多大。相距太远,元数据将无法与流很好地对齐。靠得太近,理论上你会浪费带宽(但不会浪费太多,因为不变的元数据块只有一个字节长)。我通常坚持使用 8KB。 16KB 有时 32KB 的情况并不少见。在响应标头中输出该块大小,元数据间隔:

Icy-MetaInt: 8192

首先,向客户端发送 8192 字节 (8KB) 的音频流数据。

现在是元数据块的时候了。以字符串开头,如下所示:

StreamTitle='This is my stream title';StreamUrl='';

您可以传入 StreamUrl 甚至其他字段,但现在只有 StreamTitle 真正被客户使用。 (StreamUrl 曾经有能力通过大写一些字母或其他东西来弹出浏览器,我不记得确切的触发器是什么。它不再使用。)然后将此字符串转换为缓冲区并用空字节填充(0x00) 到最近的可整除块 16。也就是说,如果您的元数据块的字符串版本是 51 字节长,您需要它是 64 字节长,因此您将添加 13 字节的 NUL 填充。

关于字符集的快速说明。许多客户端在其元数据中支持 UTF-8。有些没有。此外,如果您必须在元数据中使用撇号 ',则需要对其进行转义。不幸的是,似乎没有真正标准的方法来做到这一点。反斜杠有时会起作用。重复角色有时会奏效。不同的玩家工作方式不同。尝试使用 Winamp,看看它喜欢什么,因为那将是你能得到的“官方”。其他一切可能只是一个坏掉的客户。 (如果你想变得非常狡猾,你可以从User-Agent请求头中确定客户端并相应地调整你的转义。)

现在您有了元数据块,您只需在它的前面添加一个字节,表示它的长度,除以16。因此,如果我们现在有一个 64 字节的元数据,我们将在它的前面添加字节 0x04,这表明我们的元数据是 64 字节长。这总共提供了一个 65 字节的元数据块,我们现在将其发送给客户端。发送。

从这里,我们再次进入循环,在插入元数据之前再发送 8KB 的流数据。这一次,由于我们不想更改元数据,我们只发送0x00 作为我们的元数据块。同样,由于第一个字节表示块的长度,并且我们没有更新标题,所以告诉客户端长度是0。我们只在发生变化时发送字符串。

【讨论】:

  • 确实感谢您的解释。我还找到了您的答案here。您能否详细解释一下数据流格式,即[24,576 bytes of stream] [metablock] [24,576 bytes of stream] [metablock] etc.。如何分隔块,元块的内容是什么?有官方解释吗?
  • 我还没有测试/实现它,但我怀疑是为了内容可以在所有设备上播放,RTMP 通过 HTTP 不是更好吗?据我了解,它是专有协议和Adobe allows to implement it for free。并且可以使用流行的 Adob​​e Flash 播放器简单地播放流。
  • 我注意到Sunshine radio 使用RTMP - 在听了很多台之后 - 我可以说服务质量非常好。
  • @ErnestasGruodis 渐进式 HTTP 流比 RTMP 更兼容。事实上,这就是为什么我将它用于我工作的所有电台。我可以从 10 年前取出我的 Palm Pilot 并播放 HTTP 流,但 RTMP 将是不行的。另外,请记住,Flash 即将被淘汰,RTMP 流通常不能在普通的 HTML5 <audio> 标记中工作。通过 HTTP 流式传输可以实现这一点。如果您想向前看,请查看 HLS 或 MPEG DASH,但这些都不像普通 HTTP 那样兼容。
  • 好的,明白了。您能否将完整的客户端请求和服务器响应标头(它们如何交互)写到上面的解释中?
猜你喜欢
  • 2015-03-23
  • 1970-01-01
  • 2016-08-09
  • 1970-01-01
  • 2011-11-17
  • 1970-01-01
  • 1970-01-01
  • 2020-03-22
  • 1970-01-01
相关资源
最近更新 更多