Получение информации о дорожке из аудиопотока с помощью PHP - PullRequest
19 голосов
/ 06 февраля 2011

Можно ли извлечь информацию о дорожке из аудиопотока с помощью PHP? Я провел некоторое копание, и ближайшая функция, которую я могу найти, - stream_get_transports, но мой хост не поддерживает транспорт http через fsockopen (), поэтому мне придется еще немного поработать, чтобы увидеть, что еще возвращает эта функция.

В настоящее время я пытаюсь извлечь метаданные исполнителя и отслеживать из потока AOL.

Ответы [ 4 ]

49 голосов
/ 06 февраля 2011

Это поток SHOUTcast, и да, это возможно. Это никак не связано с тегами ID3. Недавно я написал сценарий для этого, но больше не могу его найти. Буквально на прошлой неделе я помог другому парню, у которого был довольно полный сценарий, сделать то же самое, но я не могу просто опубликовать исходный код, поскольку он не мой. Однако я свяжусь с ним, если вы напишите мне по адресу brad@musatcha.com.

В любом случае, вот как это сделать самостоятельно:

Первое, что вам нужно сделать, это подключиться к серверу напрямую. Не используйте HTTP. Ну, вы, вероятно, могли бы использовать cURL, но это, скорее всего, будет намного сложнее, чем стоит. Вы подключаетесь к нему с помощью fsockopen() ( doc ). Убедитесь, что вы используете правильный порт. Также обратите внимание, что многие веб-хосты блокируют много портов, но обычно вы можете использовать порт 80. К счастью, все потоки SHOUTcast, размещенные на AOL, используют порт 80.

Теперь сделайте ваш запрос так же, как ваш клиент.

GET /whatever HTTP/1.0

Но перед отправкой <CrLf><CrLf> включите следующий заголовок!

Icy-MetaData:1

Это говорит серверу, что вы хотите метаданные. Теперь отправьте вашу пару <CrLf>.

Хорошо, сервер ответит несколькими заголовками, а затем начнет отправлять вам данные. В этих заголовках будет icy-metaint:8192 или аналогичный. Этот 8192 является мета-интервалом . Это важно и действительно единственное значение, которое вам нужно. Обычно это 8192, но не всегда, поэтому обязательно прочитайте это значение!

В основном это означает, что вы получите 8192 байта данных MP3, а затем кусок мета, затем 8192 байта данных MP3, а затем кусок мета.

Считайте 8192 байта данных (убедитесь, что вы не включили заголовок в это число), отбросьте их, а затем прочитайте следующий байт. Этот байт является первым байтом метаданных и указывает, как долго метаданные. Возьмите значение этого байта (фактический байт с ord() ( doc )) и умножьте его на 16. Результатом является число байтов, которые нужно прочитать для метаданных. Считайте это количество байтов в строковую переменную для работы с вами.

Затем обрежьте значение этой переменной. Зачем? Потому что строка дополняется 0x0 в конце (чтобы она равномерно помещалась в кратные 16 байтов), а trim() ( doc ) позаботится об этом за нас.

У вас останется что-то вроде этого:

StreamTitle='Awesome Trance Mix - DI.fm';StreamUrl=''

Я позволю вам выбрать ваш метод выбора для анализа этого. Лично я, вероятно, просто разделю с пределом 2 на ;, но остерегайтесь заголовков, которые содержат ;. Я не уверен, что такое метод escape-символа. Небольшой эксперимент должен помочь вам.

Не забудьте отключиться от сервера, когда закончите!

Существует множество ссылок на метаданные SHOUTcast. Это хорошо: http://www.smackfu.com/stuff/programming/shoutcast.html

14 голосов
/ 14 июня 2013

Проверьте это: https://gist.github.com/fracasula/5781710

Это небольшая сущность с функцией PHP, которая позволяет извлекать метаданные MP3 (StreamTitle) из потокового URL.

Обычно сервер потоковой передачи помещаетicy-metaint заголовок в ответе, который говорит нам, как часто метаданные отправляются в потоке.Функция проверяет этот заголовок ответа и, если он присутствует, заменяет параметр интервала им.

В противном случае функция вызывает потоковый URL-адрес, соответствующий вашему интервалу, и, если какие-либо метаданные отсутствуют, она снова пытается выполнить рекурсию, начиная с параметра смещения.

<?php

/**
 * Please be aware. This gist requires at least PHP 5.4 to run correctly.
 * Otherwise consider downgrading the $opts array code to the classic "array" syntax.
 */
function getMp3StreamTitle($streamingUrl, $interval, $offset = 0, $headers = true)
{
    $needle = 'StreamTitle=';
    $ua = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36';

    $opts = [
            'http' => [
            'method' => 'GET',
            'header' => 'Icy-MetaData: 1',
            'user_agent' => $ua
        ]
    ];

    if (($headers = get_headers($streamingUrl))) {
        foreach ($headers as $h) {
            if (strpos(strtolower($h), 'icy-metaint') !== false && ($interval = explode(':', $h)[1])) {
                break;
            }
        }
    }

    $context = stream_context_create($opts);

    if ($stream = fopen($streamingUrl, 'r', false, $context)) {
        $buffer = stream_get_contents($stream, $interval, $offset);
        fclose($stream);

        if (strpos($buffer, $needle) !== false) {
            $title = explode($needle, $buffer)[1];
            return substr($title, 1, strpos($title, ';') - 2);
        } else {
            return getMp3StreamTitle($streamingUrl, $interval, $offset + $interval, false);
        }
    } else {
        throw new Exception("Unable to open stream [{$streamingUrl}]");
    }
}

var_dump(getMp3StreamTitle('http://str30.creacast.com/r101_thema6', 19200));

Я надеюсьпомогает!

1 голос
/ 27 января 2017

Это код C # для получения метаданных с использованием HttpClient:

public async Task<string> GetMetaDataFromIceCastStream(string url)
    {
        m_httpClient.DefaultRequestHeaders.Add("Icy-MetaData", "1");
        var response = await m_httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
        m_httpClient.DefaultRequestHeaders.Remove("Icy-MetaData");
        if (response.IsSuccessStatusCode)
        {
            IEnumerable<string> headerValues;
            if (response.Headers.TryGetValues("icy-metaint", out headerValues))
            {
                string metaIntString = headerValues.First();
                if (!string.IsNullOrEmpty(metaIntString))
                {
                    int metadataInterval = int.Parse(metaIntString);
                    byte[] buffer = new byte[metadataInterval];
                    using (var stream = await response.Content.ReadAsStreamAsync())
                    {
                        int numBytesRead = 0;
                        int numBytesToRead = metadataInterval;
                        do
                        {
                            int n = stream.Read(buffer, numBytesRead, 10);
                            numBytesRead += n;
                            numBytesToRead -= n;
                        } while (numBytesToRead > 0);

                        int lengthOfMetaData = stream.ReadByte();
                        int metaBytesToRead = lengthOfMetaData * 16;
                        byte[] metadataBytes = new byte[metaBytesToRead];
                        var bytesRead = await stream.ReadAsync(metadataBytes, 0, metaBytesToRead);
                        var metaDataString = System.Text.Encoding.UTF8.GetString(metadataBytes);
                        return metaDataString;
                    }
                }
            }
        }

        return null;
    }
1 голос
/ 15 октября 2013

Большое спасибо за код fra_casula. Вот немного упрощенная версия, работающая на PHP <= 5.3 (оригинал нацелен на 5.4). Он также использует тот же ресурс подключения. </p>

Я удалил исключение из-за своих собственных потребностей, вернув false, если ничего не найдено.

    private function getMp3StreamTitle($steam_url)
    {
        $result = false;
        $icy_metaint = -1;
        $needle = 'StreamTitle=';
        $ua = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36';

        $opts = array(
            'http' => array(
                'method' => 'GET',
                'header' => 'Icy-MetaData: 1',
                'user_agent' => $ua
            )
        );

        $default = stream_context_set_default($opts);

        $stream = fopen($steam_url, 'r');

        if($stream && ($meta_data = stream_get_meta_data($stream)) && isset($meta_data['wrapper_data'])){
            foreach ($meta_data['wrapper_data'] as $header){
                if (strpos(strtolower($header), 'icy-metaint') !== false){
                    $tmp = explode(":", $header);
                    $icy_metaint = trim($tmp[1]);
                    break;
                }
            }
        }

        if($icy_metaint != -1)
        {
            $buffer = stream_get_contents($stream, 300, $icy_metaint);

            if(strpos($buffer, $needle) !== false)
            {
                $title = explode($needle, $buffer);
                $title = trim($title[1]);
                $result = substr($title, 1, strpos($title, ';') - 2);
            }
        }

        if($stream)
            fclose($stream);                

        return $result;
    }
...