使用PHP从audiostream中提取曲目信息
是否有可能使用PHP从audiostream中提取音轨信息? 我已经做了一些挖掘,我能find的最接近的函数是stream_get_transports,但是我的主机不支持通过fsockopen()的http传输,所以我将不得不做一些修补,以查看该函数返回的内容。
目前,我正试图从AOLstream中提取艺术家和跟踪元数据。
这是一个SHOUTcaststream,是的,这是可能的。 它与ID3标签完全没有关系。 我前一段时间写了一个脚本来做这个,但是再也找不到了。 就在上个星期,我帮助了另一个有相当完整脚本的人做同样的事情,但是我不能把源发布到它,因为它不是我的。 但是,如果您通过电子邮件brad@musatcha.com联系我,我会帮您联系他。
无论如何,这是如何自己做的:
你需要做的第一件事是直接连接到服务器。 不要使用HTTP。 那么,你可以使用curl,但它可能会比它的价值更多的麻烦。 你用fsockopen()
( doc )连接到它。 确保使用正确的端口。 另外请注意,许多networking主机将阻塞很多端口,但通常可以使用端口80.幸运的是,所有AOL托pipe的SHOUTcaststream都使用端口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.结果是元数据读取的字节数。 将这些字节数读入一个stringvariables,以供您使用。
接下来,修剪这个variables的值。 为什么? 因为string在末尾填充了0x0
(为了使它平均适应16个字节的倍数),并且trim()
( doc )为我们处理了这个问题。
你会留下这样的事情:
StreamTitle='Awesome Trance Mix - DI.fm';StreamUrl=''
我会让你selectparsing这个select的方法。 就我个人而言,我可能只是分裂2的限制;
,但要小心包含的标题;
。 我不确定转义字符方法是什么。 一点实验应该可以帮到你。
完成后请不要忘记与服务器断开连接!
那里有很多的SHOUTcast MetaData参考。 这是一个很好的: http : //www.smackfu.com/stuff/programming/shoutcast.html
在这里你可以find一些准备好的代码,对你的问题有用: https : //gist.github.com/fracasula/5781710
这个小小的要点提供了一个简单的PHP函数,可以让您从stream式URL中提取MP3元数据(StreamTitle)。
通常在响应标题中,stream媒体服务器将放置“icy-metaint”标题,告诉我们元数据在stream中被发送的频率。 所以,函数检查响应头,如果存在“icy-metaint”,interval参数将被replace。
在其他地方,函数根据你的时间间隔调用stream媒体URL,如果元数据不存在,函数将通过从offset参数开始的recursion再次尝试。
<?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));
我希望这有帮助!
非常感谢代码fra_casula。 这是一个稍微简化的版本,运行在PHP <= 5.3(原始目标是5.4)。 它也重用相同的连接资源。
我根据自己的需要删除了exception,如果没有find,则返回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; }
这是使用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; }