SHOUTcast server in Java

2019-09-03 07:11发布

I'm making a shoutcast server in Java and I've a hit a bump in the road. My server can stream data well if I send the client (VLC/WINAMP) an HTTP response:

HTTP/1.1 200 OK\r\nContent-Type:audio/mpeg\r\n\r\n\

and then start streaming the files, However, when I send the client an ICY response, the sound starts getting a little bit choppy and squeaky and sometimes plays a little bit fast. Sometimes it plays little bits of chunks from all the songs in the playlist at the same time but if I disconnect the client and connect again it plays properly, properly in this case being a little bit choppy and squeaky. I've tried to connect to shoutcast servers on the internet and they seem flawless. (I'm also sending my data in 24kb chunks)

Here's my ICY response

String respond = "ICY 200 OK\r\n"
                     +"icy-notice1: <BR> This stream requires"
                     + "<a href=\"http://www.videolan.org/\">VLC</a><BR>\r\n"
                     + "icy-notice2: Lee Shoutcast<BR>\r\n"
                     + "icy-name: Lee's Mix\r\n"
                     + "icy-genre: Rock\r\n"
                     + "icy-url: http://localhost:9025\r\n"
                     + "content-type: audio/mpeg\r\n"
                     + "icy-pub: 1\r\n"
                     + "icy-metaint: 24576\r\n"
                     + "icy-br: 96\r\n\r\n";

I've done a little bit of reading and found out that I have to send the client data in the format Header|DataChunk|Header..... so I looked up how to do the header and came up with this as a test run to see if it would remedy the choppiness.

String header = "";

    String heading ="StreamTitle='The year of the ram';"
            + "StreamUrl='someaddress:9025';";

    byte []headingBytes = heading.getBytes();

    int NumberOfBlocks = ((headingBytes.length - 1) / 16) + 1;

    int toPad = NumberOfBlocks*16 - headingBytes.length;


    header =  NumberOfBlocks + "StreamTitle='The year of the ram';"
            + "StreamUrl='http://someaddress:9025';";


    String finalStr = header + padding(toPad);

    System.out.println(finalStr);

    byte []finalByte = finalStr.getBytes();

    return finalByte;

Padding method just adds zeros to the right of the string:

public String padding(int numberOfPads)
{
    String ret = "";

    for(int i = 1; i <= numberOfPads; i++)
    {
        ret += "0";
    }

    return ret;
}

The resulting string is:

5StreamTitle='The year of the ram';StreamUrl='http://someaddress:9025';000000000000

I then convert the String to bytes and write those header bytes to the stream then write my 24kb chunk and then header and so on. This doesn't seem to help the situation at all. Is there something I'm doing wrong with SHOUTcast since it works perfectly well with ICEcast ?

P.S I know the code looks a bit messy and could be better but this was just a test. I am also sending this constant string as a test to see if it helps. The plan was to do it properly If it worked.

Any help is appreciated.

My information was acquired from

EDIT:

This is how I am determining when to send the bytes (this code is in a while(true) loop)

            buffer = new byte[24576];

            //read the file into the buffer
            bytesRead = song.read(buffer);

            //start streaming the file.
            outputStream.write(buffer);

24KB of the song are read into the buffer then written to the output stream. I deleted the bit of code that wrote the metadata but I was writing the metadata before outputStream.write(buffer).

VLC and wimamp do send icy-metadata = 1.

1条回答
欢心
2楼-- · 2019-09-03 07:37

First off, I recommend not emulating SHOUTcast's status line of ICY 200 OK. Use HTTP/1.0 200 OK instead. You will have much better compatibility with clients this way.

Now, the reason your server is behaving different is due to the metadata, as you have suspected. The icy-metaint response header controls the interval between meta blocks. You should only insert this header and return metadata if requested by the client. The client must send icy-metadata: 1 to request metadata. Otherwise, you must only send stream data.

You don't show how you are determining when to insert metadata, but I suspect you are running into a common misconception. The first data to be sent to the client should be the stream data, not the metadata. The stream data needs to be sent in increments specified by your meta interval response header.

[24,576 bytes of stream] [metablock] [24,576 bytes of stream] [metablock] etc.

Finally, you are padding your metablock incorrectly. Don't use a literal 0, use a NUL byte. 0x00.

查看更多
登录 后发表回答