使用 PHP 从音频流中提取音轨信息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4911062/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pulling Track Info From an Audio Stream Using PHP
提问by Levi Hackwith
Is it possible to pull track info from an audio stream using PHP? I've done some digging and the closest function I can find is stream_get_transports but my host doesn't support http transports via fsockopen() so I'll have to do some more tinkering to see what else that function returns.
是否可以使用 PHP 从音频流中提取曲目信息?我已经做了一些挖掘,我能找到的最接近的函数是 stream_get_transsports,但我的主机不支持通过 fsockopen() 进行 http 传输,所以我必须做更多的修改以查看该函数返回的其他内容。
Currently, I'm trying to pull artist and track metadata from an AOL stream.
目前,我正在尝试从 AOL 流中提取艺术家和曲目元数据。
回答by Brad
This is a SHOUTcast stream, and yes it is possible. It has absolutely nothing to do with ID3 tags. I wrote a script awhile ago to do this, but can't find it anymore. Just last week I helped another guy who had a fairly complete script to do the same thing, but I can't just post the source to it, as it isn't mine. I will however get you in touch with him, if you e-mail me at [email protected].
这是一个 SHOUTcast 流,是的,这是可能的。它与 ID3 标签完全无关。我前段时间写了一个脚本来做到这一点,但再也找不到了。就在上周,我帮助另一个拥有相当完整脚本的人做同样的事情,但我不能只是发布源代码,因为它不是我的。但是,如果您通过[email protected]给我发送电子邮件,我会让您与他取得联系。
Anyway, here's how to do it yourself:
无论如何,这是自己做的方法:
The first thing you need to do is connect to the server directly. Don't use HTTP. Well, you could probably use cURL, but it will likely be much more hassle than its worth. You connect to it with fsockopen()
(doc). Make sure to use the correct port. Also note that many web hosts will block a lot of ports, but you can usually use port 80. Fortunately, all of the AOL-hosted SHOUTcast streams use port 80.
您需要做的第一件事是直接连接到服务器。不要使用 HTTP。好吧,您可能可以使用 cURL,但它可能比它的价值要麻烦得多。您使用fsockopen()
( doc)连接到它。确保使用正确的端口。另请注意,许多 Web 主机会阻塞很多端口,但您通常可以使用端口 80。幸运的是,所有 AOL 托管的 SHOUTcast 流都使用端口 80。
Now, make your request just like your client would.
现在,像您的客户一样提出您的要求。
GET /whatever HTTP/1.0
GET /whatever HTTP/1.0
But, before sending <CrLf><CrLf>
, include this next header!
但是,在发送之前<CrLf><CrLf>
,包含下一个标题!
Icy-MetaData:1
Icy-MetaData:1
That tells the server that you want metadata. Now, send your pair of <CrLf>
.
这告诉服务器您需要元数据。现在,发送您的一对<CrLf>
。
Ok, the server will respond with a bunch of headers and then start sending you data. In those headers will be an icy-metaint:8192
or similar. That 8192 is the meta interval. This is important, and really the only value you need. It is usually 8192, but not always, so make sure to actually read this value!
好的,服务器将用一堆标头响应,然后开始向您发送数据。在这些标题中将是一个icy-metaint:8192
或类似的。那个 8192 是元间隔。这很重要,而且确实是您需要的唯一值。它通常是 8192,但并非总是如此,因此请确保实际读取此值!
Basically it means, you will get 8192 bytes of MP3 data and then a chunk of meta, followed by 8192 bytes of MP3 data, followed by a chunk of meta.
基本上这意味着,您将获得 8192 字节的 MP3 数据,然后是一大块元数据,然后是 8192 字节的 MP3 数据,再是一大块元数据。
Read 8192 bytes of data (make sure you are not including the header in this count), discard them, and then read the next byte. This byte is the first byte of meta data, and indicates how long the meta data is. Take the value of this byte (the actual byte with ord()
(doc)), and multiply it by 16. The result is the number of bytes to read for metadata. Read those number of bytes into a string variable for you to work with.
读取 8192 字节的数据(确保此计数中不包括标头),丢弃它们,然后读取下一个字节。该字节是元数据的第一个字节,表示元数据的长度。取这个字节的值(带有ord()
( doc)的实际字节),然后乘以 16。结果是要读取元数据的字节数。将这些字节数读入一个字符串变量供您使用。
Next, trim the value of this variable. Why? Because the string is padded with 0x0
at the end (to make it fit evenly into a multiple of 16 bytes), and trim()
(doc) takes care of that for us.
接下来,修剪此变量的值。为什么?因为字符串0x0
在末尾填充(以使其均匀地适应 16 字节的倍数),并且trim()
( doc) 为我们处理了这个问题。
You will be left with something like this:
你会得到这样的东西:
StreamTitle='Awesome Trance Mix - DI.fm';StreamUrl=''
StreamTitle='Awesome Trance Mix - DI.fm';StreamUrl=''
I'll let you pick your method of choice for parsing this. Personally I'd probably just split with a limit of 2 on ;
, but beware of titles that contain ;
. I'm not sure what the escape character method is. A little experimentation should help you.
我会让你选择你选择的解析方法。就我个人而言,我可能只是以 2 的限制拆分;
,但要注意包含;
. 我不确定转义字符方法是什么。一些实验应该会对你有所帮助。
Don't forget to disconnect from the server when you're done with it!
完成后不要忘记与服务器断开连接!
There are lots of SHOUTcast MetaData references out there. This is a good one: http://www.smackfu.com/stuff/programming/shoutcast.html
有很多 SHOUTcast MetaData 引用。这是一个很好的:http: //www.smackfu.com/stuff/programming/shoutcast.html
回答by Francesco Casula
Check this out: https://gist.github.com/fracasula/5781710
看看这个:https: //gist.github.com/fracasula/5781710
It's a little gist with a PHP function that lets you extract MP3 metadata (StreamTitle) from a streaming URL.
这是一个带有 PHP 函数的小要点,可让您从流 URL 中提取 MP3 元数据 (StreamTitle)。
Usually the streaming server puts an icy-metaint
header in the response which tells us how often the metadata is sent in the stream. The function checks for that response header and, if present, it replaces the interval parameter with it.
通常流媒体服务器icy-metaint
在响应中放置一个标头,它告诉我们元数据在流中发送的频率。该函数检查该响应标头,如果存在,它将用它替换间隔参数。
Otherwise the function calls the streaming URL respecting your interval and, if any metadata isn't present, then it tries again through recursion starting from the offset parameter.
否则,该函数会根据您的时间间隔调用流 URL,如果不存在任何元数据,则它会通过从 offset 参数开始的递归再次尝试。
<?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));
I hope this helps!
我希望这有帮助!
回答by Simon
Thanks a lot for the code fra_casula. Here is a slightly simplified version running on PHP <= 5.3 (the original is targeted at 5.4). It also reuses the same connection resource.
非常感谢代码 fra_casula。这是一个在 PHP <= 5.3 上运行的稍微简化的版本(原始版本针对 5.4)。它还重用相同的连接资源。
I removed the exception because of my own needs, returning false if nothing is found instead.
由于我自己的需要,我删除了异常,如果没有找到则返回 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;
}
回答by Tommy Ovesen
This is the C# code for getting the metadata using HttpClient:
这是使用 HttpClient 获取元数据的 C# 代码:
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;
}