Отслеживание информации из аудиопотока с помощью PHP
Можно ли извлекать информацию трека из аудиопотока с помощью PHP? Я сделал кое-что, и ближайшая функция, которую я могу найти, - stream_get_transports, но мой хост не поддерживает HTTP-перенос через fsockopen(), поэтому мне нужно будет немного поработать, чтобы узнать, что еще возвращает эта функция.
В настоящее время я пытаюсь вытащить метаданные исполнителя и отслеживать их из потока AOL.
Ответы
Ответ 1
Это поток SHOUTcast, и да, это возможно. Это абсолютно не связано с тегами ID3. Некоторое время назад я написал script для этого, но больше не могу его найти. Только на прошлой неделе я помог другому парню, у которого был довольно полный script, сделать то же самое, но я не могу просто опубликовать его источник, поскольку он не мой. Однако я свяжусь с ним, если вы напишите мне по электронной почте [email protected].
В любом случае, вот как это сделать самому:
Первое, что вам нужно сделать, - это напрямую подключиться к серверу. Не используйте 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 MetaData. Это хороший вариант: http://www.smackfu.com/stuff/programming/shoutcast.html
Ответ 2
Здесь вы можете найти готовый код, полезный для вашей проблемы: https://gist.github.com/fracasula/5781710
Этот маленький gist предоставляет простую функцию PHP, которая позволяет извлекать метаданные MP3 (StreamTitle) из потокового URL.
Обычно в заголовках ответов потоковый сервер помещает заголовок "ледяной метчик", который сообщает нам, как часто метаданные отправляются в потоке. Таким образом, функция проверяет заголовки ответов и, если присутствует "ледяная метка", параметр интервала будет заменен.
В другом месте функция вызывает URL-адрес потоковой передачи, соответствующий вашему интервалу, и, если метаданных нет, функция повторит попытку через рекурсию, начиная с параметра смещения.
<?php
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
Большое спасибо за код fra_casula. Вот немного упрощенная версия, работающая на 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
Это код С# для получения метаданных с использованием 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;
}