Can't extract metdata from some icecast streams

走远了吗. 提交于 2019-12-25 08:46:15

问题


I'm trying to extract icecast metadata from streams. I have code that works for some streams and not for others. The issue is that some streams don't return the icymetaint value and that's where the code gets lost.

I can't get the icymetaint header from this stream: http://radio.hbr1.com:19800/tronic.ogg

But when I put it in VLC media player it shows the meta just fine. So what exactly am I missing here? What other ways are there for an icecast stream to transmit metdata? The stream version is Icecast 2.3.3

This is code inside a class to retrieve the metadata and headers:

public function GetDataFromStream($parsedUrl)
{
    $returnData = array();
    $addr = $parsedUrl['host'];

    $addr = gethostbyname($addr);

    $sock = fsockopen($addr, $parsedUrl['port'], $errno, $errstr, 5);
    $path = isset($parsedUrl['path'])?$parsedUrl['path']:'/';

    if ($sock)
    {
        $request = 'GET '. $path .' HTTP/1.0' . CRLF .
            'Host: ' . $parsedUrl['host'] . CRLF .
            'Connection: Close' . CRLF .
            'User-Agent: ' . $this->useragent . CRLF .
            'Accept: */*' . CRLF .
            'icy-metadata: 1'.CRLF.
            'icy-prebuffer: 65536'.CRLF.
            (isset($parsedUrl['user']) ? 'Authorization: Basic ' .
            base64_encode($parsedUrl['user'] . ':' . $parsedUrl['pass']) . CRLF : '').
            'X-TipOfTheDay: Winamp "Classic" rulez all of them.' . CRLF . CRLF;

        if (fwrite($sock, $request))
        {
            $theaders = $line = '';

            while (!feof($sock))
            {
                $line = fgets($sock, 4096);

                if('' == trim($line))
                    break;
                $theaders .= $line;
            }

            $theaders = explode(CRLF, $theaders);

            foreach ($theaders as $header)
            {
                $t = explode(':', $header);


                if (isset($t[0]) && trim($t[0]) != '')
                {
                    $name = preg_replace('/[^a-z][^a-z0-9]*/i','', strtolower(trim($t[0])));
                    array_shift($t);
                    $value = trim(implode(':', $t));

                    if ($value != '')
                    {
                        if (is_numeric($value))
                            $this->headers[$name] = (int)$value;
                        else
                            $this->headers[$name] = $value;
                    }
                }
            }

            if (isset($this->headers['icymetaint']))
            {
                $metainterval = $this->headers['icymetaint'];
                $intervals = 0;
                $metadata = '';

                while(1)
                {
                    $data = '';

                    while(!feof($sock))
                    {
                        $data .= fgetc($sock);

                        if (strlen($data) >= $metainterval)
                            break;
                    }

                    $len = join(unpack('c', fgetc($sock))) * 16;

                    if ($len > 0)
                    {
                        $metadata = str_replace("\0", '', fread($sock, $len));
                        break;
                    }
                    else
                    {
                        $intervals++;
                        if ($intervals > 100) break;
                    }
                }

                $metarr = explode(';', $metadata);

                foreach ($metarr as $meta)
                {
                    $t = explode('=', $meta);

                    if (isset($t[0]) && trim($t[0]) != '')
                    {
                        $name = preg_replace('/[^a-z][^a-z0-9]*/i','', strtolower(trim($t[0])));

                        array_shift($t);

                        $value = trim(implode('=', $t));

                        if (substr($value, 0, 1) == '"' || substr($value, 0, 1) == "'")
                            $value = substr($value, 1);

                        if (substr($value, -1) == '"' || substr($value, -1) == "'")
                            $value = substr($value, 0, -1);

                        if ($value != '')
                        {
                            $tmp = &$this->metadata;
                            $tmp[$name] = $value;
                        }
                    }
                }
                $this->valid = true;
            }
            else
            {
                $this->valid = false;
            }

            fclose($sock);
        }
        else
            echo 'unable to write.';
    }
    else
        //echo 'no socket '.$errno.' - '.$errstr.'.';
        ;

}

回答1:


You can use .xspf mountpoint extension, get XML and parse it:

<?php
$stream_url = "http://radio.hbr1.com:19800/tronic.ogg";
$xspf_url = $stream_url . ".xspf";
$xml = file_get_contents($xspf_url);
if($xml){
    $data = simplexml_load_string($xml);
    // Track artist
    print $data->trackList->track->creator;
    // Track title
    print $data->trackList->track->title;
}
?>

Here is how .xspf data looks like (I use lynx to read the URL content):

$ lynx -mime_header http://radio.hbr1.com:19800/tronic.ogg.xspf
HTTP/1.0 200 OK
Content-Type: application/xspf+xml
Content-Length: 615

<?xml version="1.0" encoding="UTF-8"?>
<playlist xmlns="http://xspf.org/ns/0/" version="1">
  <title/>
  <creator/>
  <trackList>
    <track>
      <location>http://radio.hbr1.com:19800/tronic.ogg</location>
      <creator>Res Q</creator>
      <title>Fakesleep (2012)</title>
      <annotation>Stream Title: HBR1 - Tronic Lounge
Stream Description: Music on Futurenet
Content Type:application/ogg
Bitrate: Quality 0,00
Current Listeners: 28
Peak Listeners: 45
Stream Genre: Tech House, Progressive House, Electro, Minimal</annotation>
      <info>http://www.hbr1.com</info>
    </track>
  </trackList>
</playlist>

As you can see /playlist/trackList/track/title XML node is your song title, /playlist/trackList/track/creator is usually an artist.




回答2:


That's because you are only trying to parse the braindead ancient metadata slipstreaming introduced by Nullsoft in Shoutcast.

Proper streams use a container (e.g. Ogg or WebM) instead of throwing raw data out.

Newer Icecast servers offer a JSON API (Version 2.4.1 and above). This is more useful than pulling in a whole stream just for the metadata.

If you are decoding the stream anyway, then you should look into proper libraries for parsing streams, libogg, libopus, libvorbis come to mind.



来源:https://stackoverflow.com/questions/40174150/cant-extract-metdata-from-some-icecast-streams

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!