Не удается извлечь метданные из некоторых потоков icecast

Я пытаюсь извлечь метаданные icecast из потоков. У меня есть код, который работает для одних потоков, а не для других. Проблема в том, что некоторые потоки не возвращают значение icymetaint, и именно здесь код теряется.

Я не могу получить заголовок icymetaint из этого потока: http://radio.hbr1.com:19800/tronic.ogg

Но когда я вставляю его в медиаплеер VLC, он отлично показывает мета. Так что именно мне здесь не хватает? Какие еще есть способы передачи метданных ледяным потоком? Потоковая версия - 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.'.';
        ;

}

person Not Amused    schedule 21.10.2016    source источник


Ответы (2)


Вы можете использовать .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 обычно - исполнитель.

person Alex Paramonov    schedule 24.10.2016
comment
Я проверил .xspf, и он показывает количество слушателей, пик слушателей, качество. Но в нем не перечислено самое главное для меня, это название песни. Я что-то упускаю? - person Not Amused; 25.10.2016
comment
Теперь я понял, что потоки, поддерживающие json версии 2.4 и выше, не возвращают заголовок в файле .xspf. Другие делают. Большое Вам спасибо. Отмечен как правильный ответ. - person Not Amused; 26.10.2016
comment
Вы имеете в виду Icecast 2.4? Странно, мое решение должно работать и с 2.4, не могли бы вы прислать мне ссылку на поток Icecast 2.4, где заголовок отсутствует в .xspf? - person Alex Paramonov; 26.10.2016

Это потому, что вы всего лишь пытаетесь проанализировать бессмысленный поток древних метаданных, представленный Nullsoft в Shoutcast.

Правильные потоки используют контейнер (например, Ogg или WebM) вместо того, чтобы выбрасывать необработанные данные.

Новые серверы Icecast предлагают JSON API (версия 2.4.1 и выше). Это более полезно, чем извлечение всего потока только для метаданных.

Если вы все равно декодируете поток, вам следует поискать подходящие библиотеки для анализа потоков, на ум приходят libogg, libopus, libvorbis.

person TBR    schedule 21.10.2016
comment
Спасибо за Ваш ответ. Проблема в том, что у меня нет контроля над версией потока, поэтому мне нужно иметь возможность анализировать их все. Я пробовал использовать Ogg и WebM, libogg, libopus, libvorbis с ключевым словом php, но не нашел ничего полезного при парсинге стерам с помощью php. - person Not Amused; 22.10.2016