使用HTTP实时音频流 - 选择协议和Java实现

时间:2015-07-17 13:20:11

标签: java http audio-streaming internet-radio

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

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

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

1 个答案:

答案 0 :(得分:2)

你不能随意剪切媒体并期望玩家能够播放它。这适用于裸MPEG流,但其他容器和编解码器可能会遇到麻烦。因为这不会发送部分文件,除非客户已经拥有其余文件。

你还有一个问题,即当歌曲结束时你该做什么,然后再继续下一个。

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

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

编辑:来自您的评论:

  

你能解释一下有关数据流格式的更多信息吗,这是[24,576字节的流] [metablock] [24,576字节的流] [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。我们只在发生变化时发送字符串。