Click here to Skip to main content
Click here to Skip to main content

ShoutcastStream Class

By , 10 Jun 2007
 

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 UserAgents.

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)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dirk Reske
Software Developer
Germany Germany
Member
After I worked for 12 Months as Java developer in a litte company, I worked at a global directoy publishing company (also as Java developer).
 
At the moment I'm studying Information Technologies.
 
My favorite areas are network and internet programming.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionProblem with Read methodmemberredreggae16 Jan '13 - 11:55 
Hi, I had a problem with the Read method. I use it with the NAudio library.
Your method doesn't ensure that the byte[] buffer is always filled completely.
 
So I changed it to:
        public override int Read(byte[] buffer, int offset, int count)
        {
            try
            {
                read = 0;
                leftToRead = count;
                thisOffset = offset;
                bytesRead = 0;
                bytesLeftToMeta = ((metaInt - receivedBytes) > count) ? count : (metaInt - receivedBytes);
 
                while(bytesLeftToMeta > 0 && (read = netStream.Read(buffer, thisOffset, bytesLeftToMeta)) > 0)
                {
                    leftToRead -= read;
                    thisOffset += read;
                    bytesRead += read;
                    receivedBytes += read;
                    bytesLeftToMeta -= read;
                }
 
                // read metadata
                if(receivedBytes == metaInt)
                {
                    readMetaData();
                }
 
                while(leftToRead > 0 && (read = netStream.Read(buffer, thisOffset, leftToRead)) > 0)
                {
                    leftToRead -= read;
                    thisOffset += read;
                    bytesRead += read;
                    receivedBytes += read;
                }
 
                return bytesRead;
            }
            catch(Exception)
            {
                return -1;
            }
        }
 
        private void readMetaData()
        {
            metaLen = netStream.ReadByte();
            if(metaLen > 0)
            {
                metaInfo = new byte[metaLen * 16];
                int len = 0;
                while((len += netStream.Read(metaInfo, len, metaInfo.Length - len)) < metaInfo.Length) ;
                ParseMetaInfo(metaInfo);
            }
            receivedBytes = 0;
        }
 
The private fields I have after change are:
private int metaInt;
private int receivedBytes;
private Stream netStream;
private bool connected = false;
private string streamTitle;
 
private int read;
private int leftToRead;
private int thisOffset;
private int bytesRead;
private int bytesLeftToMeta;
private int metaLen;
private byte[] metaInfo;

QuestionImplementationmemberSaksham Ghimire16 Aug '12 - 16:30 
Hi,
 
I know its been a long time since you wrote this but how do you actually implement it in a windows form part?
 
All I am trying to do is have a shoutcast stream play through my program.
GeneralGreat articlememberarinhere8 Dec '08 - 2:50 
Hello,
 
First of all I would like to thank you for this wonderful article. Its really helped a lot. I have simply one question.
 
Actually I have used this class in my application in which I have to fetch the song history from Shoutcast server. I have seen that you used a function Read(byte[] buffer, int offset, int count), in this class.
 
I didn't able to pass data into it (As I didn't understand what to send). First of all i have created an object of the class and called "ShoutcastStream(string url)" function and it worked fine, but after that when I tried to get the songs history or metadata i didn't able to manage that. If you got my problem then please do reply.
 
Its very important. Kind of code which has used the class will be very helpful.
 
Thanks and Regards - Arin
 
Arin
Mail: arinhere@gmail.com
http://blogs.msdn.com/coding4fun/archive/2008/04/16/8399645.aspx

GeneralRe: Great articlememberDirk Reske8 Dec '08 - 23:47 
You don't have to pass data to the read method.
You just have to pass an empty array, where the received audio data should be stored in.
 
I will have a look at the code the next days (Its a long time, since I've written it)
 
For fetching the song history, just read from the stream in a loop and check the StreamTitle property regularly.
 
Dirk
QuestionHow link a Stream object ('Shout cast stream") to WMPlib.dll in C#memberThe Courier1 Nov '07 - 10:11 
Hi, thanks 4 the source, very interesting.
 
I'd like to play your stream obect into Windows media Player object (WMP.dll), it's possible?
I know if you put and url in to Windows media player URL object works, but I'd like to take some additional info which only shoutcast class can provide.
 
thanks
 
the courier
QuestionUsing DirectSound to playback from a mic.memberKitty Carlyle 3144 Aug '07 - 8:06 
Hello,
I was wondering if there is a way to use the capture buffer for the mic. and then playing it back by streaming? I already got to capture data but I am not certain how to output it.

 
Meow

NewsOpenSource Audio library and Server Shoutcast [modified]memberDAXweb_IT31 Jul '07 - 6:34 
Developed in c#
 
Mp3 Decode (MadX)
Mp3 Encode (Lame)
Shoutcast Server
 
http://www.codeplex.com/AudioAndShoutcast
 
it's an Alpha version...
i need help for testing and make it stable version...
 

-- modified at 12:45 Tuesday 31st July, 2007
GeneralManaged .NETmemberthund3rstruck13 Jun '07 - 17:51 
I'm looking for a way to provide shoutcast streams playback in pure .NET code for my web-based open source mp3 player
http://www.nealosis.com/demo
GeneralRe: Managed .NETmemberDirk Reske13 Jun '07 - 22:46 
Hello,
 
have you looked at http://www.devhost.de/index.php/2007/06/11/playing-shoutcast-streams-using-directsound/
You can provide the managed directx assemblys with you app, if you don't want the user to install managed directx.
 
Dirk
GeneralCan't wait to try itmembermerlin98111 Jun '07 - 4:46 
Looking forward to trying this. Thanx
 


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I theme/skin my controls and apps with Themer

I get my developer tools from Merlin A.I. Soft

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

GeneralI'm intrested in Managed DirectSoundmemberystoffman11 Jun '07 - 4:22 
Or even better how can I record the sound and play it later?
GeneralRe: I'm intrested in Managed DirectSoundmemberFreak.2k11 Jun '07 - 4:40 
How to capture the stream should be clear !?
To play it using DirectSound, I've taken the c# mp3 decoder from here.
 
You have to make some changes to the decoder in order to get DirectSound working with it....otherwise you will get a bunch of exceptions...
 
I will post an example this evening Smile | :)
GeneralRe: I'm intrested in Managed DirectSoundmemberDirk Reske11 Jun '07 - 8:32 
look at my (new) blog at http://www.devhost.de/

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 10 Jun 2007
Article Copyright 2007 by Dirk Reske
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid