我正在尝试为实时音频实现简单的HTTP服务器(用Java)。假设有一个网站,您可以看到一个接一个播放的歌曲列表。当客户端连接到服务器时 - 让我们说在歌曲的中间 - 我正在考虑使用“Range”HTTP标头并从歌曲的那一部分开始发送数据范围。但是如果在下载连接期间暂时丢失(并且歌曲完成) - 服务器是否应该发送前一首歌曲部分并完成它 - 或者服务器是否应该发送当时播放的那些歌曲部分?什么是最佳做法/原则?
PS - 我不是在寻找用于音频流的第三方软件。
修改
现在,在对可用的实时流技术进行一些研究之后,
我看到了这些目标:
1.为简单的实时音频流选择协议
2. Java中的协议实现(服务器端)
答案 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
。我们只在发生变化时发送字符串。