Click here to Skip to main content
15,879,239 members
Articles / Programming Languages / Visual Basic
Article

Read raw SHOUTcast Stream

Rate me:
Please Sign up or sign in to vote.
2.68/5 (7 votes)
25 Jul 2005 54.2K   367   20   5
How to read raw SHOUTcast stream.

Introduction

This is only a raw solution to grab a SHOUTcast stream from a server.

Using the code

VB
'Imports System.Net
'Imports System.Text

Private Sub Button1_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Button1.Click
    SHOUTcast_Save("http://192.168.168.97:80/stream/2001", "c:\song_raw.mp3")
End Sub

Public Function SHOUTcast_Save(ByVal sURL As String, _
                ByVal sFileName As String) As Boolean

    Dim webreq As System.Net.HttpWebRequest = _
                  CType(System.Net.WebRequest.Create(sURL), _
                  System.Net.HttpWebRequest)

    webreq.Headers.Clear()
    webreq.Headers.Add("GET", "/stream/2001 HTTP/1.0")
    webreq.UserAgent = "WinampMPEG/5.09"
    webreq.Headers.Add("Icy-MetaData", "1")

    Dim webres As System.Net.WebResponse = webreq.GetResponse()

    Dim oReader As System.IO.Stream = webres.GetResponseStream()
    Dim oFile As System.IO.Stream = New System.IO.FileStream(sFileName, _
                 System.IO.FileMode.OpenOrCreate, _
                 System.IO.FileAccess.ReadWrite, System.IO.FileShare.None)

    For Each oHeader As System.Net.WebHeaderCollection In webres.Headers

        '(0)    "icy-notice1"    String
        '(1)    "icy-notice2"    String
        '(2)    "icy-name"       String
        '(3)    "icy-genre"      String
        '(4)    "icy-url"        String
        '(5)    "icy-pub"        String
        '(6)    "icy-metaint"    String  'how often the metadata 
        '                                    is sent in the stream
        '(7)    "icy-br"         String
        '(8)    "icy-irc"        String
        '(9)    "icy-icq"        String
        '(10)    "icy-aim"       String

        'see http://ample.sourceforge.net/developers.shtml
        'icy-notice1 - An informational message. 
        'icy-notice2 - Another informational message, 
        '              probably "icy-notice3", "icy-notice4" etc.
        '              can also be used. 
        'icy-name - The name of the stream that the server is sending,
        '           this is usually displayed along with
        '           the current song title in clients. 
        'icy-genre - The genre of the music served. 
        'icy-url - An URL associated with the stream, 
        '          usually the homepage of the "webradio" or similar. 
        'icy-pub - Not sure, believe it indicates 
        '          if the stream is public or private 
        'icy-br - BitRate, seems mostly informational as most
        '         clients encountered seem to support VBR (Variable BitRate). 
    Next

    Dim b As Integer
    While True
        b = oReader.ReadByte()
        If b = -1 Then Exit While
        oFile.WriteByte(Convert.ToByte(b))

        'ToDo: Get the metadata (Thanks to SmackFU 
        'http://www.smackfu.com/stuff/programming/)
        'Read the data stream as you normally would, 
        'keeping a byte count as you go. 
        'When the number of bytes equals the metadata 
        'interval, you will get a metadata block.
        'The first part of the block is a length specifier, 
        'which is the next byte in the stream. 
        'This byte will equal the metadata length / 16. 
        'Multiply by 16 to get the actual metadata length. 
        '(Max byte size = 255 so metadata max length = 4080.) 
        'Now read that many bytes and you will 
        'have a string containing the metadata. 
        'Restart your byte count, and repeat. Yay!
        'Note that the metadata length is set to 0 most 
        'of the time, meaning there is no metadata. 
        'The metadata is normally sent at two particular 
        'places: immediately after connecting and when 
        'the song changes. It seems as if some servers 
        'aren't real diligent about the former, 
        'so it might take a while to get your first block.
        'Also, be sure you don't count the bytes in the metadata 
        'length field or the metadata when you're figuring 
        'out whether you've hit the interval. Only the MP3 
        'data counts. So you can't be lazy and just keep 
        'a total byte count and mod it.

        'ToDo: Interpret the metadata
        'Part of the metadata string should look like this:
        'StreamTitle='title of the song';

    End While

    oReader.Close()
    oFile.Close()

End Function
//

Points of Interest

You see, that the metadata is not parsed. (This means you must not write all bytes.) Good Luck... :)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThe server committed a protocol violation. Section=ResponseStatusLine Pin
chrsarly044-Oct-07 12:40
chrsarly044-Oct-07 12:40 
AnswerRe: The server committed a protocol violation. Section=ResponseStatusLine Pin
dfrede5-Oct-07 6:28
dfrede5-Oct-07 6:28 
Shared Sub Main()
ReadMP3("http://scfire-chi-aa03.stream.aol.com:80/stream/1040", _
"C:\Users\dani\Downloads\mp3", True)
End Sub

Private Shared m_bExitRead As Boolean = False
Private Shared m_sCurrentName As String = ""
Private Shared m_MP3file As FileStream = Nothing
Private Shared m_sPath As String = ""

Public Shared Sub [Stop]()
m_bExitRead = True
End Sub



Public Shared Sub ReadMP3(ByVal sURL As String, _
Optional ByVal sRecordPath As String = "", _
Optional ByVal bDecode As Boolean = False)


Dim th As New Threading.Thread(AddressOf player)
th.Start()

m_bExitRead = False
m_sPath = sRecordPath
Dim s As New cShoutcast(sURL)
AddHandler s.StreamTitleChanged, AddressOf StreamTitleChanged

Dim ios As System.IO.Stream = New MemoryStream
Dim b(s.MetaInt) As Byte
Do
Dim iRead As Integer = s.Read(b, 0, b.Length)

If Not IsNothing(m_MP3file) Then m_MP3file.Write(b, 0, iRead)

ios.Flush()
ios.Write(b, 0, iRead)
ios.Position = 0

If m_bExitRead Then Exit Do


'Decoder
If bDecode Then

Dim mp3stream As New MP3Decoder.Mp3Stream(ios)

Dim numberOfPcmBytesToReadPerChunk As Integer = 256 * 32768 '512
Dim buffer() As Byte = New Byte(numberOfPcmBytesToReadPerChunk) {}

Dim bytesReturned As Integer = -1
Dim totalBytes As Integer = 0
Dim bFirst As Boolean = True

While bytesReturned <> 0

bytesReturned = mp3stream.Read(buffer, 0, numberOfPcmBytesToReadPerChunk)
If bytesReturned = 0 Then Exit While



End While


ios.Position = 0

End If


Loop

'oWaveFile.Close()
If m_sCurrentName <> "" Then m_MP3file.Close()

End Sub

Shared Sub player()
While Not m_bExitRead
If m_alSound.Count > 0 Then

Dim wav As Media.SoundPlayer = CType(m_alSound(0), Media.SoundPlayer)
wav.PlaySync()
m_alSound_ms(0).Close()

wav.Dispose()
m_alSound.RemoveAt(0)
m_alSound_ms.RemoveAt(0)

End If
End While
End Sub

Private Shared m_alSound As New ArrayList
Private Shared m_alSound_ms As New ArrayList



Shared Sub StreamTitleChanged(ByVal sTitle As String)
If m_sCurrentName <> "" And m_sCurrentName <> sTitle Then
m_MP3file.Close()
End If
m_sCurrentName = sTitle
If Not Directory.Exists(m_sPath) Then
Directory.CreateDirectory(m_sPath)
End If
If Directory.Exists(m_sPath) Then
m_MP3file = New System.IO.FileStream(m_sPath & "\" & PrepareFileName(sTitle) & ".mp3", System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite)
End If
End Sub

Private Shared Function PrepareFileName(ByVal sFileName As String) As String
Dim sRet As String = ""
Dim sPattern As String = "äöü-!()abcdefghijklmnopqrstuvwxyz1234567890 _."
For n As Integer = 1 To Len(sFileName)
Dim sChar As String = Mid(sFileName, n, 1)
If InStr(sPattern, sChar) > 0 Or InStr(sPattern.ToUpper, sChar) > 0 Then
sRet += sChar
End If
Next
Return sRet
End Function



and here the cShoutcast:


Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.IO
Imports System.Net
Imports System.Text.RegularExpressions

'/
'/ Provides the functionality to receive a shoutcast media stream
'/

Public Class cShoutcast
Inherits Stream

Private m_metaInt As Integer
Private receivedBytes As Integer
Private netStream As Stream
Private connected As Boolean = False

Private m_streamTitle As String

'/
'/ Is fired, when a new StreamTitle is received
'/

Public Event StreamTitleChanged(ByVal sTitle As String)

'/
'/ Creates a new ShoutcastStream and connects to the specified Url
'/

'/ <param name="url" />Url of the Shoutcast stream
Public Sub New(ByVal url As String)
Dim response As HttpWebResponse

Dim request As HttpWebRequest = CType(HttpWebRequest.Create(url), HttpWebRequest)
request.Headers.Clear()
request.Headers.Add("Icy-MetaData", "1")
request.KeepAlive = False
request.UserAgent = "VLC media player"


response = CType(request.GetResponse(), HttpWebResponse)
m_metaInt = Integer.Parse(response.Headers("Icy-MetaInt"))
receivedBytes = 0

netStream = response.GetResponseStream()

connected = True
End Sub

ReadOnly Property MetaInt() As Integer
Get
Return m_metaInt
End Get
End Property

'/
'/ Parses the received Meta Info
'/

'/ <param name="metaInfo" />
Private Sub ParseMetaInfo(ByVal metaInfo() As Byte)
Dim metaString As String = Encoding.ASCII.GetString(metaInfo)

Dim NewStreamTitle As String = Regex.Match(metaString, "(StreamTitle=')(.*)(';StreamUrl)").Groups(2).Value.Trim()
If Not NewStreamTitle.Equals(StreamTitle) Then
m_streamTitle = NewStreamTitle
OnStreamTitleChanged()
End If
End Sub

'/
'/ Fires the StreamTitleChanged event
'/

Protected Overridable Sub OnStreamTitleChanged()
If m_streamTitle <> "" Then
RaiseEvent StreamTitleChanged(m_streamTitle)
End If
End Sub

'/
'/ Gets a value that indicates whether the ShoutcastStream supports reading.
'/

Public Overrides ReadOnly Property CanRead() As Boolean
Get
Return connected
End Get
End Property

'/
'/ Gets a value that indicates whether the ShoutcastStream supports seeking.
'/ This property will always be false.
'/

Public Overrides ReadOnly Property CanSeek() As Boolean
Get
Return False
End Get
End Property

'/
'/ Gets a value that indicates whether the ShoutcastStream supports writing.
'/ This property will always be false.
'/

Public Overrides ReadOnly Property CanWrite() As Boolean
Get
Return False
End Get
End Property

'/
'/ Gets the title of the stream
'/

Public ReadOnly Property StreamTitle() As String
Get
Return m_streamTitle
End Get
End Property

'/
'/ Flushes data from the stream.
'/ This method is currently not supported
'/

Public Overrides Sub Flush()
Return
End Sub

'/
'/ Gets the length of the data available on the Stream.
'/ This property is not currently supported and always thows a <see cref="NotSupportedException">.
'/

Public Overrides ReadOnly Property Length() As Long
Get
Throw New NotSupportedException()
End Get
End Property

'/
'/ Gets or sets the current position in the stream.
'/ This property is not currently supported and always thows a <see cref="NotSupportedException">.
'/

Public Overrides Property Position() As Long
Get
Throw New NotSupportedException()
End Get
Set(ByVal Value As Long)
Throw New NotSupportedException()
End Set
End Property

''' <param name="buffer" />An array of bytes to store the received data from the ShoutcastStream.
''' <param name="offset" />The location in the buffer to begin storing the data to.
''' <param name="count" />The number of bytes to read from the ShoutcastStream.
''' <returns>The number of bytes read from the ShoutcastStream.
Public Overloads Overrides Function Read(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer) As Integer
Try
If receivedBytes = metaInt Then
Dim metaLen As Integer = netStream.ReadByte()

If metaLen > 0 Then
Dim metaInfo As Byte() = New Byte(metaLen * 16 - 1) {}
Dim len As Integer = netStream.Read(metaInfo, len, metaInfo.Length - len)
While (len) < metaInfo.Length
len += netStream.Read(metaInfo, len, metaInfo.Length - len)
End While

ParseMetaInfo(metaInfo)

End If

receivedBytes = 0
End If
Dim bytesLeft As Integer = IIf(((metaInt - receivedBytes) > count), count, (metaInt - receivedBytes))
Dim result As Integer = netStream.Read(buffer, offset, bytesLeft)
receivedBytes += result
Return result

Catch e As Exception
connected = False
Console.WriteLine(e.Message)
Return -1
End Try
End Function

'/
'/ Closes the ShoutcastStream.
'/

Public Overrides Sub Close()
connected = False
netStream.Close()
End Sub

'/
'/ Sets the current position of the stream to the given value.
'/ This Method is not currently supported and always throws a <see cref="NotSupportedException">.
'/

'/ <param name="offset" />
'/ <param name="origin" />
'/ <returns>
Public Overrides Function Seek(ByVal offset As Long, ByVal origin As SeekOrigin) As Long
Throw New NotSupportedException()
End Function

'/
'/ Sets the length of the stream.
'/ This Method always throws a <see cref="NotSupportedException">.
'/

'/ <param name="value" />
Public Overrides Sub SetLength(ByVal value As Long)
Throw New NotSupportedException()
End Sub

'/
'/ Writes data to the ShoutcastStream.
'/ This method is not currently supported and always throws a <see cref="NotSupportedException">.
'/

'/ <param name="buffer" />
'/ <param name="offset" />
'/ <param name="count" />
Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
Throw New NotSupportedException()
End Sub
End Class
GeneralRe: The server committed a protocol violation. Section=ResponseStatusLine Pin
Martin Garmendia12-Jan-08 10:51
Martin Garmendia12-Jan-08 10:51 
GeneralRe: The server committed a protocol violation. Section=ResponseStatusLine Pin
Ducain25-Jun-08 7:59
Ducain25-Jun-08 7:59 
GeneralRe: The server committed a protocol violation. Section=ResponseStatusLine Pin
George Cristian10-May-13 4:44
George Cristian10-May-13 4:44 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.