Web

Получение информации о дорожке из аудиопотока с помощью PHP

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

Ответ 1

Это поток SHOUTcast, и да, это возможно. Это не имеет абсолютно никакого отношения к ID3-тегам. Некоторое время назад я написал сценарий для этого. В любом случае вот как сделать это самостоятельно:

Первое, что вам нужно сделать, это подключиться к серверу напрямую. Не используйте 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 на «;», но остерегайтесь заголовков, содержащих «;». Я не уверен в том, какой метод используется для символов эскейп-последовательности. Небольшие эксперименты должны помочь вам. Не забудьте отключиться от сервера, когда закончите работу с ним! Есть много ссылок на метаданные SHOUTcast. Вот одна из них: 

http://www.smackfu.com/stuff/programming/shoutcast.html

Ответ 2

Посмотрите: 

https://gist.github.com/fracasula/5781710.

Это небольшая статья с функцией PHP, которая позволяет извлекать метаданные MP3 (StreamTitle) из URL потоковой передачи. Обычно сервер потокового вещания помещает в ответ заголовок icy-metaint, который сообщает нам, как часто метаданные отправляются в потоке. Функция проверяет наличие этого заголовка ответа и, если он присутствует, заменяет им параметр interval. В противном случае функция вызывает URL потоковой передачи с соблюдением интервала и, если метаданные отсутствуют, повторяет попытку через рекурсию, начиная с параметра offset. Вот пример кода:

<?php

/**

 * Пожалуйста, обратите внимание. Для корректной работы этого gist требуется как минимум PHP 5.4.

 * В противном случае рассмотрите возможность понижения кода массива $opts до классического синтаксиса "массив".

 */

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));

Ответ 3

Вот немного упрощенная версия, работающая на PHP <= 5.3 (оригинал ориентирован на 5.4). Он также повторно использует тот же ресурс соединения. Я убрал исключение из-за собственных нужд, вместо этого возвращается 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;

    }

Ответ 4

Вот код на 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;

    }

Схожие статьи

Какой хостинг выбрать для сайта
Web

Какой хостинг выбрать для сайта

Web

Как восстановить сериализованную строку, которая была повреждена из-за неправильной длины счетчика байтов

Web

Отношения Laravel: попытка получить "описание" свойства не-объекта

Web

Что с точки зрения непрофессионала, является рекурсивной функцией в PHP