【问题标题】:Can't extract metdata from some icecast streams无法从某些 icecast 流中提取元数据
【发布时间】:2016-10-21 10:23:52
【问题描述】:

我正在尝试从流中提取 icecast 元数据。 我有适用于某些流而不适用于其他流的代码。 问题是某些流不返回 icymetaint 值,这就是代码丢失的地方。

我无法从此流中获取 icymetaint 标头: http://radio.hbr1.com:19800/tronic.ogg

但是当我把它放在 VLC 媒体播放器中时,它可以很好地显示元数据。 那么我到底错过了什么? icecast 流传输元数据还有哪些其他方式?流版本是 Icecast 2.3.3

这是类中用于检索元数据和标头的代码:

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.'.';
        ;

}

【问题讨论】:

    标签: php stream metadata icecast


    【解决方案1】:

    你可以使用.xspf挂载点扩展,获取XML并解析它:

    <?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;
    }
    ?>
    

    .xspf 数据如下所示(我使用lynx 读取 URL 内容):

    $ 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>
    

    您可以看到/playlist/trackList/track/title XML 节点是您的歌曲名称,/playlist/trackList/track/creator 通常是艺术家。

    【讨论】:

    • 我检查了 .xspf,它确实列出了听众数量、听众峰值、质量。它没有列出对我来说最重要的事情,那就是歌名。我错过了什么吗?
    • 我现在意识到支持 json 版本 2.4 及更高版本的流是不返回 .xspf 文件中的标题的流。其他人会。非常感谢。标记为正确答案。
    • 你是说 Icecast 2.4 吗?奇怪,我的解决方案也应该适用于 2.4,你能给我一个指向 Icecast 2.4 流的链接,其中.xspf 中缺少标题吗?
    【解决方案2】:

    那是因为您只是试图解析 Nullsoft 在 Shoutcast 中引入的脑死亡的古代元数据滑流。

    正确的流使用容器(例如 Ogg 或 WebM)而不是丢弃原始数据。

    较新的 Icecast 服务器提供 JSON API(2.4.1 及更高版本)。这比只为元数据拉入整个流更有用。

    如果您无论如何都在解码流,那么您应该寻找合适的库来解析流,想到 libogg、libopus、libvorbis。

    【讨论】:

    • 感谢您的回答。问题是我无法控制流的版本,所以我需要能够解析它们。我尝试使用 php 关键字搜索 Ogg 和 WebM、libogg、libopus、libvorbis,但在使用 php 解析 sterams 时没有发现任何有用的东西。
    猜你喜欢
    • 2015-07-19
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    相关资源
    最近更新 更多