ShoutcastStream Class






4.73/5 (10 votes)
A ShoutcastStream class for receiving Shoutcast media streams.
Introduction
This article describes a class for receiving Shoutcast streams. For some background information, look at this short protocol description, or visit the Shoutcast website.
Background
In my last project, I had to receive, capture, and play Shoutcast streams. I looked for some ready to use implementations, but didn't find any, so I decided to write my own.
Using the Code
Because it's a relatively simple protocol, the code is also quite simple, but it does what it should. :)
For the best interoperability to other classes (e.g., for DirectSound playing it), I derived from the abstract Stream
, so other classes that can handle streams can handle my stream too.
/// <summary>
/// Creates a new ShoutcastStream and connects to the specified Url
/// </summary>
/// <param name="url">Url of the Shoutcast stream</param>
public ShoutcastStream(string url)
{
HttpWebResponse response;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Headers.Clear();
request.Headers.Add("Icy-MetaData", "1");
request.KeepAlive = false;
request.UserAgent = "VLC media player";
response = (HttpWebResponse)request.GetResponse();
metaInt = int.Parse(response.Headers["Icy-MetaInt"]);
receivedBytes = 0;
netStream = response.GetResponseStream();
connected = true;
}
In the constructor, the connection is established. Therefore, we have to put a new field into the HTTP request, Icy-MetaData:1
. We also set the UserAgent
to "VLC Media Player" (we can also use WinAmp etc.) because some Shoutcast servers behave different with different UserAgent
s.
After we get the response, we read out the Icy-MetaInt
value. This is the value that shows how many data bytes we receive before the server will send a metainfo block.
/// <summary>
/// Reads data from the ShoutcastStream.
/// </summary>
/// <param name="buffer">An array of bytes to store
/// the received data from the ShoutcastStream.</param>
/// <param name="offset">The location in the buffer
/// to begin storing the data to.</param>
/// <param name="count">The number of bytes
/// to read from the ShoutcastStream.</param>
/// <returns>The number of bytes read from the ShoutcastStream.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
try
{
if (receivedBytes == metaInt)
{
int metaLen = netStream.ReadByte();
if (metaLen > 0)
{
byte[] metaInfo = new byte[metaLen * 16];
int len = 0;
while ((len += netStream.Read(metaInfo, len,
metaInfo.Length - len)) < metaInfo.Length) ;
ParseMetaInfo(metaInfo);
}
receivedBytes = 0;
}
int bytesLeft = ((metaInt - receivedBytes) > count) ?
count : (metaInt - receivedBytes);
int result = netStream.Read(buffer, offset, bytesLeft);
receivedBytes += result;
return result;
}
catch (Exception e)
{
connected = false;
Console.WriteLine(e.Message);
return -1;
}
}
If the requested count of bytes is less than the remaining bytes before the next metainfo block, we will read as much bytes as requested. But if there are less bytes before the next metainfo block as the user has requested, we will only read the remaining bytes and return. When the user calls Read
the next time, we fill first receive the metainfo block and then continue reading the media data from the stream.
Every time a metainfo block is received, the current stream title is extracted. If the stream title changes, the corresponding event is fired.
Note
Before using this in your application, you have to extend your App.config by the following:
<configuration>
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing ="true"/>
</settings>
</system.net>
</configuration>
Points of Interest
If someone is interested in playing a Shoutcast stream using Managed DirectSound (pure .NET), ask for it. Perhaps, I'm going to write an article about that.
History
- 1.0 - Initial revision (10 June 2007)