使用 PHP 从音频流中提取曲目信息

2022-08-30 19:24:25

是否可以使用 PHP 从音频流中提取曲目信息?我已经做了一些挖掘,我能找到的最接近的函数是stream_get_transports但我的主机不支持通过fsockopen()进行http传输,所以我必须做更多的修补,看看该函数还返回了什么。

目前,我正在尝试从AOL流中提取艺术家和跟踪元数据。


答案 1

这是一个SHOUTcast流,是的,这是可能的。它与ID3标签完全无关。不久前,我写了一个脚本来做这件事,但再也找不到了。就在上周,我帮助另一个拥有相当完整的脚本的人做同样的事情,但我不能只是将源代码发布到它上面,因为它不是我的。但是,如果您通过电子邮件向我发送 brad@musatcha.com,我会让您与他取得联系。

无论如何,这是自己动手的方法:

您需要做的第一件事是直接连接到服务器。不要使用 HTTP。好吧,您可能可以使用cURL,但它可能比其价值更麻烦。您可以使用 (doc) 连接到它。确保使用正确的端口。另请注意,许多Web主机会阻止很多端口,但您通常可以使用端口80。幸运的是,所有 AOL 托管的 SHOUTcast 流都使用端口 80。fsockopen()

现在,像您的客户一样提出您的请求。

GET /whatever HTTP/1.0

但是,在发送之前,请包含下一个标头!<CrLf><CrLf>

Icy-MetaData:1

这告诉服务器您需要元数据。现在,发送您的一对 .<CrLf>

好的,服务器将使用一堆标头进行响应,然后开始向您发送数据。在这些标头中将是一个或类似的。8192 是元区间。这很重要,也是您唯一需要的价值。它通常是8192,但并非总是如此,因此请确保实际读取此值!icy-metaint:8192

基本上,这意味着,您将获得8192字节的MP3数据,然后是一块meta,然后是8192字节的MP3数据,然后是一块meta。

读取 8192 字节的数据(确保在此计数中未包含标头),丢弃它们,然后读取下一个字节。此字节是元数据的第一个字节,指示元数据的长度。取此字节的值(实际字节与 (doc)),并将其乘以 16。结果是要读取元数据的字节数。将这些字节数读入字符串变量中,以便您使用。ord()

接下来,修剪此变量的值。为什么?因为字符串在末尾填充(以使其均匀地适合16字节的倍数),并且(doc)为我们处理了这个问题。0x0trim()

你将剩下这样的东西:

StreamTitle='Awesome Trance Mix - DI.fm';StreamUrl=''

我会让你选择你选择的方法来解析它。就我个人而言,我可能只是在 上以 2 的限制进行拆分,但要小心包含 .我不确定转义字符方法是什么。一些实验应该对你有所帮助。;;

完成后,不要忘记断开与服务器的连接!

有很多SHOUTcast MetaData引用。这是一个好主意:http://www.smackfu.com/stuff/programming/shoutcast.html


答案 2

检查这个: https://gist.github.com/fracasula/5781710

这是PHP函数的一个小要点,可让您从流URL中提取MP3元数据(StreamTitle)。

通常,流式处理服务器会在响应中放置一个标头,该标头会告诉我们元数据在流中发送的频率。该函数检查该响应标头,如果存在,它将用它替换 interval 参数。icy-metaint

否则,该函数将根据你的间隔调用流式处理 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));

我希望这有帮助!


推荐