1

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

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

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

4

1 回答 1

2

您不能随意剪切媒体并期望播放器能够播放它。这适用于裸 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. 我们只在发生变化时发送字符串。

于 2015-07-18T05:45:30.480 回答