nend

Provide Meta data from a mp3 audio stream

1 post in this topic

Hi all, 

The last days I'm busy with trying to understand how a mp3 audio stream works.

I needed for a much bigger project the meta data from a streaminglink.

I found a lot of problems which I had to solve.

But with a bit of help from Trancexx I manage to make it work.

This program wil unpack playlist streams automaticly and shows the Artist - Title information (now playing) and al the icy headers.

Ontop there is a small example and 4 streaminglinks (link 2,3 are playlist links).

Please let me know what you think of it.

 

#include <Array.au3>

Local $link[4]

$link[0] = "http://icecast-qmusic.cdp.triple-it.nl/Qmusic_nl_fouteuur_96.mp3"
$link[1] = "http://www.radiostaddenhaag.com/stad.pls"
$link[2] = "http://www.hitz24.com/streams/listen.pls"
$link[3] = "http://candlelight.live-streams.nl/candlelight"

TCPStartUp()

Local $icy_array = _Get_ICY_data($link[0]); choose one of the link above
If @error Then
    ConsoleWrite("Nothing found!"); not a streaminglink or the streaminglink is not providing the data
Else
    _ArrayDisplay($icy_array) ; display the information
EndIf

TCPShutdown()


; #FUNCTION# ====================================================================================================================
; Name ..........: Get Meta data
; Description ...: Gets a array with icy header
; Syntax ........: _Get_ICY_data(streaming mp3 audio link)
; Return values .: Success      - 0 artist - title, rest are icy headers
;                  Failure      - Returns @ERROR
; Author ........: Nend
; Remarks .......:
; Related .......:
; Link ..........:
; Thanks to......: Trancexx for pointing me in the right direction
; ===============================================================================================================================

Func _Get_ICY_data($link)
    Local $meta_total
    Local $url_array = _Crack_URL($link); split link 0)domain   1)target    2)portnumber
    If Not @error Then
        Local $station_link_array = _Split_playlist_link($url_array[0], $url_array[1], $url_array[2]); if link is playlist return array with streaminglinks
        If Not @error Then
            For $i = 0 To UBound($station_link_array)-1
                $url_array = _Crack_URL($station_link_array[$i]); split link 0)domain   1)target    2)portnumber
                If Not @error Then
                    $meta_total = _Get_stream_info($url_array[0], $url_array[1], $url_array[2])
                    If Not @error Then
                        Return $meta_total
                    EndIf
                Else
                    Return SetError(1)
                EndIf
            Next
        Else
            $meta_total = _Get_stream_info($url_array[0], $url_array[1], $url_array[2]); if not playlist then get info from link
            If @error Then
                Return SetError(2)
            Else
                Return $meta_total
            EndIf
        EndIf
    Else
        Return SetError(1)
    EndIf
EndFunc

Func _Get_stream_info($_Host, $link, $_Port=80)
    Local $Total_header, $metaint, $icy_array[1]

    Local $_Timer = TimerInit()
    For $i = 0 To 4; try 5 times to connect to host
        Local $socket = TCPConnect(TCPNameToIP($_Host), $_Port)
        If Not @error Then
            ExitLoop
        EndIf
        Sleep(200)
    next
    If $socket <> -1 Then
        TCPSend($socket, "GET /" & $link & " HTTP/1.1" & @CRLF & _
                         "Icy-MetaData:1"  & @CRLF & _
                         "User-Agent: TR" & @CRLF & _
                         "Host: " & $_Host & @CRLF & _
                         "Connection: Close" & @CRLF & _
                         "Cache-Control: no-cache" & @CRLF & @CRLF); send options

        While 1
            $tcp_recieve = TCPRecv($socket, 800, 1)
            $Total_header = Binary($Total_header) & $tcp_recieve
            If StringInStr($tcp_recieve, "0d0a0d") Then; if end header then
                Local $Header_length = StringInStr(BinaryToString($Total_header), @CRLF & @CRLF)
                Local $header = BinaryToString(BinaryMid($Total_header, 1, $Header_length-1))
                Local $metaint_array = StringRegExp($header, "(?<=icy-metaint:).*",3)
                If Not @error Then
                    $metaint = $metaint_array[0]; icy meta int to know where the meta data is in the binary code
                EndIf
            EndIf
            If $metaint <> "" Then; If Meta int is known
                If BinaryLen($Total_header) > $metaint + 500 Then
                    Local $strip_binary = BinaryToString(BinaryMid($Total_header, $Header_length + 5 + $metaint, 500), 4); strip the binary data from meta int
                    Local $split_meta_array = StringRegExp($strip_binary, "(?<=StreamTitle=').*?(?=';)", 3); strip Streamtitle from binary data
                    If Not @error Then; if found
                        TCPCloseSocket($socket); close socket
                        $icy_array[0] = $split_meta_array[0]
                        $icy_header_array = StringRegExp($header, "icy-.*", 3)
                        ReDim $icy_array[UBound($icy_header_array) + 1]
                        For $i = 1 To UBound($icy_header_array); put al the icy info in a array
                            $icy_array[$i] =  $icy_header_array[$i-1]
                        Next

                        Return $icy_array; return 0 artist - title the rest of the array is the icy data
                        ExitLoop
                    EndIf
                EndIf
            EndIf

            If TimerDiff($_Timer) > 1000 Then; timeout if more then 1000 milsec
                TCPCloseSocket($socket)
                Return SetError(2)
                ExitLoop
            EndIf
        WEnd
    Else
        Return SetError(1)
    EndIf
EndFunc

Func _Split_playlist_link($_Host, $link, $_Port=80)
    Local $total_string
    Local $_Timer = TimerInit()
    For $i = 0 To 4; try 5 times to connect to host
        Local $socket = TCPConnect(TCPNameToIP($_Host), $_Port)
        If Not @error Then
            ExitLoop
        EndIf
        Sleep(200)
    next
    If $socket <> -1 Then
        TCPSend($socket, "GET " & $link & " HTTP/1.1" & @CRLF & _
                         "User-Agent: TR" & @CRLF & _
                         "Host: " & $_Host & @CRLF & _
                         "Connection: keep-alive" & @CRLF & _
                         "Cache-Control: no-cache" & @CRLF & @CRLF); send options

        While 1
            $tcp_recieve = TCPRecv($socket, 4000, 1)
            $total_string = Binary($total_string) & $tcp_recieve

            If BinaryLen($tcp_recieve) = 0 Then
                TCPCloseSocket($socket)
                ExitLoop
            EndIf

            $_Recv = BinaryToString($tcp_recieve); concat binary string

            If TimerDiff($_Timer) > 1000 Then; timeout if more then 1000 milsec
                TCPCloseSocket($socket)
                ExitLoop
            EndIf
        WEnd
        $total_string = BinaryToString($total_string)
        If StringInStr($total_string, "pls+xml") Or StringInStr($total_string, "x-scpls") Or StringInStr($total_string, "x-mpegurl") Then; Check for Content-Type
            Return StringRegExp($total_string, "http://.*", 3); return array with all links
        Else
            Return SetError(1)
        EndIf
    Else
        SetError(1)
        Return 0
    EndIf
EndFunc


Func _Crack_URL($Domain_total); Crack URL and return array 0)domain 1)target 2)pornumber
    Local $aResult, $splitarray, $return[3], $port, $Domain, $link

    StringRegExpReplace($Domain_total, "/", "/", 0)
    If @extended > 2 Then
        $Domain = StringLeft($Domain_total, StringInStr($Domain_total, "/", Default, 3)-1)
        $link = StringReplace($Domain_total, $Domain, "")
    Else
        $Domain = $Domain_total
    EndIf

    $Domain = StringReplace($Domain, "http://", "")
    $Domain = StringReplace($Domain, "www.", "")

    If StringInStr($Domain, ":") Then
        $splitarray = StringSplit($Domain, ":", 2)
        If Not @error Then
            $Domain = $splitarray[0]
            $port = $splitarray[1]
        EndIf
    EndIf

    If $port = "" Then
        $port  = 80
    EndIf

    $return[0] = $Domain
    $return[1] = $link
    $return[2] = $port

    Return $return
EndFunc

 

Share this post


Link to post
Share on other sites



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now