使用HTTP进行实时音频流传输-选择协议和Java实现 [英] Real-time audio streaming using HTTP - choosing the protocol and Java implementation

查看:642
本文介绍了使用HTTP进行实时音频流传输-选择协议和Java实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

I'm trying to implement simple HTTP server for real-time audio (in Java). Suppose there is a website where you can see a list of songs which are playing one after another. When client connects to server - lets say in the middle of the song - I'm thinking to use "Range" HTTP header and send the data range starting from that part of song. But if during download connection is temporary lost (and song finished) - should the server send previous song part and finish it - or should the server send those parts of song which is playing at that moment? What are best practices/principles?

PS - I'm not looking for 3rd party software for audio streaming.


Now after some research in available real-time streaming technologies, I see these goals:
1. Choosing protocol for simple real-time audio streaming
2. Protocol implementation in Java (server side)

推荐答案

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

You cannot arbitrarily cut media and expect a player to be able to play it. This works with bare MPEG streams, but other containers and codecs can have trouble. Because of this don't send partial files unless the client already has the rest of it.

您还遇到了当歌曲结束并进入下一首歌时该怎么做的问题.

You also have the problem of what to do when the song ends and you're on to the next.

有两种方法可以实现此目的.一种方法是让客户端可以使用静态媒体,然后在音频客户端中寻找正确的时间.

There are two ways to implement this. One of which is to have static media available to your clients and then seek to the correct time in the audio client-side.

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

The way I would choose is to truly create an internet radio stream where everyone hears the same thing at the same time because you effectively have a common buffer which chunks are copied from and sent to all clients about the same time. Now, if you do this you will either need to use codecs/containers that support arbitrary splicing (MP3 or AAC) or re-wrap streams with the container as they are sent to the client. This is a complicated problem, so it's best if you use something off-the-shelf that does this, like Icecast. I know you say you're not looking for third-party solutions, but that's the best way. If you want to do it all yourself, you'll have to reimplement all of it, or support MPEG streams only.

根据您的评论:

您能解释一下有关数据流格式的更多信息吗,它是[24,576字节的流] [元块] [24,576字节的流] [元块]等.如何分离块,元块的内容是什么?

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

If you wish, you can mux SHOUTcast-style metadata into your stream. Not all clients support this. If they do, they will send you the following header in the request:

Icy-MetaData: 1

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

If you see that header and value, you can optionally include metadata in the stream. The metadata is simply injected after every chunk of stream data. To include metadata, first you need to decide how big your stream chunks are. Too far apart, and the metadata won't align well to the stream. Too close together and in theory you are wasting bandwidth (but not much, since an unchanging metadata block is only a byte long). I usually stick with 8KB. It's not uncommon to see 16KB and sometimes 32KB. Output that chunk size, the metadata interval, in the response headers:

Icy-MetaInt: 8192

要开始工作,请将8192字节(8KB)的音频流数据发送到客户端.

To get things started, send 8192 bytes (8KB) of audio stream data to the client.

现在是时候处理元数据块了.以这样的字符串开头:

Now it's time for a metadata block. Start with a string, like this:

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

您可以在StreamUrl甚至其他字段中传递,但是这些天客户实际上只使用StreamTitle.( StreamUrl 曾经能够通过大写一些字母或其他内容来弹出浏览器,但我不确定是否知道触发器是什么.不再使用它.)然后将此字符串转换为缓冲区并使用空字节( 0x00 )填充到最接近的均匀可分割块16.也就是说,如果元数据块的字符串版本长51字节,则需要将其长64字节,这样您就可以将添加13个字节的 NUL 填充.

You can pass in StreamUrl or even other fields, but only StreamTitle is really used by clients these days. (StreamUrl used to have the ability to popup a browser by capitalizing some letters or something, I don't remember for certainty what the trigger was. It's no longer used.) Then convert this string to a buffer and pad with null bytes (0x00) to the nearest evenly divisible block of 16. That is, if the string version of your metadata block is 51 bytes long, you need it to be 64 bytes long so you will add 13 bytes of NUL padding.

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

A quick note on character set. Many clients support UTF-8 in their metadata. Some don't. Also, if you have to use an apostrophe ' in your metadata, it needs to be escaped. Unfortunately there doesn't seem to be a truly standard way of doing this. Backslash sometimes works. Repeating the character sometimes works. Different players work differently. Experiment with Winamp and see what it likes, as that would be about as "official" as you can get. Everything else is probably just a broken client. (If you wanted to get really crafty, you could determine the client from the User-Agent request header and adjust your escaping accordingly.)

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

Now that you have the metadata block, you just need to add one byte to the front of it that indicates how long it is, divided by 16. So if we now have a 64-byte metadata, we will add the byte 0x04 to the front of it which indicates that our metadata is 64 bytes long. This gives in total a 65-byte metadata block that we now send to the client. Send it.

从这里开始,我们再次进入循环,在插入元数据之前发送了另外8KB的流数据.但这一次,因为我们不想更改元数据,我们只发送 0x00 作为我们的元数据块.同样,由于第一个字节指示块的长度,并且我们不更新标题,所以请告诉客户端该长度为 0 .我们仅在发生变化时才发送字符串.

From here, we just enter the loop again, sending another 8KB of stream data before inserting metadata. This time around though since we don't want to change the metadata, we just send 0x00 as our metadata block. Again, since the first byte indicates the length of the chunk, and we're not updating the title, tell the client that the length is 0. We only send the strings when something is changing.

这篇关于使用HTTP进行实时音频流传输-选择协议和Java实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆