无法从某些 icecast 流中提取元数据
Can't extract metdata from some icecast streams
我正在尝试从流中提取 icecast 元数据。
我有适用于某些流而不适用于其他流的代码。
问题是有些流没有 return icymetaint 值,这就是代码丢失的地方。
我无法从此流中获取 icymetaint header:
http://radio.hbr1.com:19800/tronic.ogg
但是当我把它放在 VLC 媒体播放器中时,它显示元数据就好了。
那么我到底错过了什么? icecast 流传输元数据还有哪些其他方式?流版本为 Icecast 2.3.3
这是 class 中的代码,用于检索元数据和 headers:
public function GetDataFromStream($parsedUrl)
{
$returnData = array();
$addr = $parsedUrl['host'];
$addr = gethostbyname($addr);
$sock = fsockopen($addr, $parsedUrl['port'], $errno, $errstr, 5);
$path = isset($parsedUrl['path'])?$parsedUrl['path']:'/';
if ($sock)
{
$request = 'GET '. $path .' HTTP/1.0' . CRLF .
'Host: ' . $parsedUrl['host'] . CRLF .
'Connection: Close' . CRLF .
'User-Agent: ' . $this->useragent . CRLF .
'Accept: */*' . CRLF .
'icy-metadata: 1'.CRLF.
'icy-prebuffer: 65536'.CRLF.
(isset($parsedUrl['user']) ? 'Authorization: Basic ' .
base64_encode($parsedUrl['user'] . ':' . $parsedUrl['pass']) . CRLF : '').
'X-TipOfTheDay: Winamp "Classic" rulez all of them.' . CRLF . CRLF;
if (fwrite($sock, $request))
{
$theaders = $line = '';
while (!feof($sock))
{
$line = fgets($sock, 4096);
if('' == trim($line))
break;
$theaders .= $line;
}
$theaders = explode(CRLF, $theaders);
foreach ($theaders as $header)
{
$t = explode(':', $header);
if (isset($t[0]) && trim($t[0]) != '')
{
$name = preg_replace('/[^a-z][^a-z0-9]*/i','', strtolower(trim($t[0])));
array_shift($t);
$value = trim(implode(':', $t));
if ($value != '')
{
if (is_numeric($value))
$this->headers[$name] = (int)$value;
else
$this->headers[$name] = $value;
}
}
}
if (isset($this->headers['icymetaint']))
{
$metainterval = $this->headers['icymetaint'];
$intervals = 0;
$metadata = '';
while(1)
{
$data = '';
while(!feof($sock))
{
$data .= fgetc($sock);
if (strlen($data) >= $metainterval)
break;
}
$len = join(unpack('c', fgetc($sock))) * 16;
if ($len > 0)
{
$metadata = str_replace("[=10=]", '', fread($sock, $len));
break;
}
else
{
$intervals++;
if ($intervals > 100) break;
}
}
$metarr = explode(';', $metadata);
foreach ($metarr as $meta)
{
$t = explode('=', $meta);
if (isset($t[0]) && trim($t[0]) != '')
{
$name = preg_replace('/[^a-z][^a-z0-9]*/i','', strtolower(trim($t[0])));
array_shift($t);
$value = trim(implode('=', $t));
if (substr($value, 0, 1) == '"' || substr($value, 0, 1) == "'")
$value = substr($value, 1);
if (substr($value, -1) == '"' || substr($value, -1) == "'")
$value = substr($value, 0, -1);
if ($value != '')
{
$tmp = &$this->metadata;
$tmp[$name] = $value;
}
}
}
$this->valid = true;
}
else
{
$this->valid = false;
}
fclose($sock);
}
else
echo 'unable to write.';
}
else
//echo 'no socket '.$errno.' - '.$errstr.'.';
;
}
那是因为你只是想解析由 Nullsoft 在 Shoutcast 中引入的脑残的古代元数据滑流。
正确 流使用容器(例如 Ogg 或 WebM)而不是丢弃原始数据。
较新的 Icecast 服务器提供 JSON API(版本 2.4.1 及更高版本)。这比只为元数据拉入整个流更有用。
如果您正在解码流,那么您应该查看用于解析流的适当库,想到 libogg、libopus、libvorbis。
您可以使用 .xspf
挂载点扩展,获取 XML 并解析它:
<?php
$stream_url = "http://radio.hbr1.com:19800/tronic.ogg";
$xspf_url = $stream_url . ".xspf";
$xml = file_get_contents($xspf_url);
if($xml){
$data = simplexml_load_string($xml);
// Track artist
print $data->trackList->track->creator;
// Track title
print $data->trackList->track->title;
}
?>
这是 .xspf
数据的样子(我使用 lynx
阅读 URL 内容):
$ lynx -mime_header http://radio.hbr1.com:19800/tronic.ogg.xspf
HTTP/1.0 200 OK
Content-Type: application/xspf+xml
Content-Length: 615
<?xml version="1.0" encoding="UTF-8"?>
<playlist xmlns="http://xspf.org/ns/0/" version="1">
<title/>
<creator/>
<trackList>
<track>
<location>http://radio.hbr1.com:19800/tronic.ogg</location>
<creator>Res Q</creator>
<title>Fakesleep (2012)</title>
<annotation>Stream Title: HBR1 - Tronic Lounge
Stream Description: Music on Futurenet
Content Type:application/ogg
Bitrate: Quality 0,00
Current Listeners: 28
Peak Listeners: 45
Stream Genre: Tech House, Progressive House, Electro, Minimal</annotation>
<info>http://www.hbr1.com</info>
</track>
</trackList>
</playlist>
如你所见/playlist/trackList/track/title
XML节点是你的歌名,/playlist/trackList/track/creator
通常是艺术家。
我正在尝试从流中提取 icecast 元数据。 我有适用于某些流而不适用于其他流的代码。 问题是有些流没有 return icymetaint 值,这就是代码丢失的地方。
我无法从此流中获取 icymetaint header: http://radio.hbr1.com:19800/tronic.ogg
但是当我把它放在 VLC 媒体播放器中时,它显示元数据就好了。 那么我到底错过了什么? icecast 流传输元数据还有哪些其他方式?流版本为 Icecast 2.3.3
这是 class 中的代码,用于检索元数据和 headers:
public function GetDataFromStream($parsedUrl)
{
$returnData = array();
$addr = $parsedUrl['host'];
$addr = gethostbyname($addr);
$sock = fsockopen($addr, $parsedUrl['port'], $errno, $errstr, 5);
$path = isset($parsedUrl['path'])?$parsedUrl['path']:'/';
if ($sock)
{
$request = 'GET '. $path .' HTTP/1.0' . CRLF .
'Host: ' . $parsedUrl['host'] . CRLF .
'Connection: Close' . CRLF .
'User-Agent: ' . $this->useragent . CRLF .
'Accept: */*' . CRLF .
'icy-metadata: 1'.CRLF.
'icy-prebuffer: 65536'.CRLF.
(isset($parsedUrl['user']) ? 'Authorization: Basic ' .
base64_encode($parsedUrl['user'] . ':' . $parsedUrl['pass']) . CRLF : '').
'X-TipOfTheDay: Winamp "Classic" rulez all of them.' . CRLF . CRLF;
if (fwrite($sock, $request))
{
$theaders = $line = '';
while (!feof($sock))
{
$line = fgets($sock, 4096);
if('' == trim($line))
break;
$theaders .= $line;
}
$theaders = explode(CRLF, $theaders);
foreach ($theaders as $header)
{
$t = explode(':', $header);
if (isset($t[0]) && trim($t[0]) != '')
{
$name = preg_replace('/[^a-z][^a-z0-9]*/i','', strtolower(trim($t[0])));
array_shift($t);
$value = trim(implode(':', $t));
if ($value != '')
{
if (is_numeric($value))
$this->headers[$name] = (int)$value;
else
$this->headers[$name] = $value;
}
}
}
if (isset($this->headers['icymetaint']))
{
$metainterval = $this->headers['icymetaint'];
$intervals = 0;
$metadata = '';
while(1)
{
$data = '';
while(!feof($sock))
{
$data .= fgetc($sock);
if (strlen($data) >= $metainterval)
break;
}
$len = join(unpack('c', fgetc($sock))) * 16;
if ($len > 0)
{
$metadata = str_replace("[=10=]", '', fread($sock, $len));
break;
}
else
{
$intervals++;
if ($intervals > 100) break;
}
}
$metarr = explode(';', $metadata);
foreach ($metarr as $meta)
{
$t = explode('=', $meta);
if (isset($t[0]) && trim($t[0]) != '')
{
$name = preg_replace('/[^a-z][^a-z0-9]*/i','', strtolower(trim($t[0])));
array_shift($t);
$value = trim(implode('=', $t));
if (substr($value, 0, 1) == '"' || substr($value, 0, 1) == "'")
$value = substr($value, 1);
if (substr($value, -1) == '"' || substr($value, -1) == "'")
$value = substr($value, 0, -1);
if ($value != '')
{
$tmp = &$this->metadata;
$tmp[$name] = $value;
}
}
}
$this->valid = true;
}
else
{
$this->valid = false;
}
fclose($sock);
}
else
echo 'unable to write.';
}
else
//echo 'no socket '.$errno.' - '.$errstr.'.';
;
}
那是因为你只是想解析由 Nullsoft 在 Shoutcast 中引入的脑残的古代元数据滑流。
正确 流使用容器(例如 Ogg 或 WebM)而不是丢弃原始数据。
较新的 Icecast 服务器提供 JSON API(版本 2.4.1 及更高版本)。这比只为元数据拉入整个流更有用。
如果您正在解码流,那么您应该查看用于解析流的适当库,想到 libogg、libopus、libvorbis。
您可以使用 .xspf
挂载点扩展,获取 XML 并解析它:
<?php
$stream_url = "http://radio.hbr1.com:19800/tronic.ogg";
$xspf_url = $stream_url . ".xspf";
$xml = file_get_contents($xspf_url);
if($xml){
$data = simplexml_load_string($xml);
// Track artist
print $data->trackList->track->creator;
// Track title
print $data->trackList->track->title;
}
?>
这是 .xspf
数据的样子(我使用 lynx
阅读 URL 内容):
$ lynx -mime_header http://radio.hbr1.com:19800/tronic.ogg.xspf
HTTP/1.0 200 OK
Content-Type: application/xspf+xml
Content-Length: 615
<?xml version="1.0" encoding="UTF-8"?>
<playlist xmlns="http://xspf.org/ns/0/" version="1">
<title/>
<creator/>
<trackList>
<track>
<location>http://radio.hbr1.com:19800/tronic.ogg</location>
<creator>Res Q</creator>
<title>Fakesleep (2012)</title>
<annotation>Stream Title: HBR1 - Tronic Lounge
Stream Description: Music on Futurenet
Content Type:application/ogg
Bitrate: Quality 0,00
Current Listeners: 28
Peak Listeners: 45
Stream Genre: Tech House, Progressive House, Electro, Minimal</annotation>
<info>http://www.hbr1.com</info>
</track>
</trackList>
</playlist>
如你所见/playlist/trackList/track/title
XML节点是你的歌名,/playlist/trackList/track/creator
通常是艺术家。