从流式广播中获取信息

get info from streaming radio

有一些标准的方法可以向流媒体广播服务询问当前正在播放的歌曲吗? 我目前以不同的方式为每个站做这件事,例如(SomaFM):

  $wg=join("\n",`wget -q -O - https://somafm.com/secretagent/songhistory.html`);
  $wg=~/\(Now\).*>([^<]*)<\/a><\/td><td>([^<]*)/s;  
  print "Secret Agent\n\n\n"

或(Radio Svizzera Classica):

$wg=join("\n",`wget -q -O - http://www.radioswissclassic.ch/en`);
$wg=~/On Air.*?titletag">([^<]*).*?artist">([^<]*)/s;
print "Radio Svizzera Classic\n\n\n"

...但我想知道是否有更标准的方法来做到这一点,而不是依赖于下载 html 迟早会改变的页面

对于具有 ICY 元数据的 SHOUTcast/Icecast 风格的电台(占互联网广播电台的大部分),最好的办法是从流本身获取这些数据。

首先,您需要 URL 到实际的流。如果您在 http://somafm.com/secretagent/, you'll see links to listen in other players. As an example, let's use the 128k AAC link, which points at http://somafm.com/secretagent130.pls 访问 SomaFM 的秘密特工页面。这不是实际的流……它是一个播放列表文件,其中包含指向实际流的链接。在您喜欢的文本或代码编辑器中打开它,看看我的意思:

[playlist]
numberofentries=2
File1=http://ice1.somafm.com/secretagent-128-aac
Title1=SomaFM: Secret Agent (#1  ): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!
Length1=-1
File2=http://ice2.somafm.com/secretagent-128-aac
Title2=SomaFM: Secret Agent (#2  ): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!
Length2=-1
Version=2

互联网电台通常包括多台服务器,用于故障转移。如果听众与一个断开连接,播放器通常会滚动到下一个项目。当一个服务器达到其侦听器限制时,这也很有用......玩家将(希望)最终击中另一个活动的服务器。

无论如何,启动 Wireshark 或其他一些数据包嗅探器的副本。在您的音频播放器中点击 URL 之一,然后检查流量。我们首先要看的是请求和响应。

GET /secretagent-128-aac HTTP/1.1
Host: ice1.somafm.com
User-Agent: VLC/2.2.4 LibVLC/2.2.4
Range: bytes=0-
Connection: close
Icy-MetaData: 1

HTTP/1.0 200 OK
Content-Type: audio/aacp
Date: Sat, 20 May 2017 20:43:56 GMT
icy-br:128
icy-genre:Various
icy-name:Secret Agent from SomaFM [SomaFM]
icy-notice1:<BR>This stream requires <a href="http://www.winamp.com/">Winamp</a><BR>
icy-notice2:SHOUTcast Distributed Network Audio Server/Linux v1.9.5<BR>
icy-pub:0
icy-url:http://SomaFM.com
Server: Icecast 2.4.0-kh3
Cache-Control: no-cache, no-store
Pragma: no-cache
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type
Access-Control-Allow-Methods: GET, OPTIONS, HEAD
Connection: Close
Expires: Mon, 26 Jul 1997 05:00:00 GMT
icy-metaint:45000

这些互联网广播服务器要么是 HTTP(在 Icecast 和其他情况下)要么非常接近它(旧版 SHOUTcast),并接受正常的 GET 请求。在这种情况下,我的播放器 (VLC) 发出 GET 请求 /secretagent-128-aac,这是实际流的路径。

我的播放器还包括一键请求header:

Icy-MetaData: 1

Icy-MetaData header 要求服务器将元数据与音频流数据混合。也就是说,"now playing"轨道信息将定期发送注入到流中。

在服务器响应 headers 中,还有另一个键 header:

icy-metaint:45000

这告诉我们两件事...首先是服务器同意发送元数据。二是元数据区间为45000字节。每 45,000 个字节,服务器将注入一块元数据。让我们回到我们的数据包嗅探器,看看它是什么样的:

元数据块的第一个字节 0x06 告诉我们元数据块有多长。取该字节的值,将其乘以 16,您将得到元数据块的长度(以字节为单位)。也就是说,第一个元数据块字节的 0x06 告诉我们接下来的 96 个字节将是元数据,然后 return 将成为常规流数据。请注意,这意味着整个元数据为 97 个字节...长度指示符为 1 个字节,其余为 96 个字节(在本例中)。

现在,让我们进入实际的文本元数据格式:

StreamTitle='Buscemi - First Flight To London';StreamUrl='http://SomaFM.com/secretagent/';

看起来很简单。 key='value',分号;分隔。不过,这有一些重要的收获。例如......没有真正标准的方法来转义单引号。如果元数据值需要包含单引号,有时是\',有时是'''。有时根本就没有转义!

此外,并非所有服务器都使用相同的字符编码。您可能可以安全地假设 UTF-8,但确实希望某些服务器可能不同,或者只是简单地破坏了它们自己的元数据编码。

无论如何,既然您知道所有这些是如何工作的,您就可以实施了。如果你愿意,我有一些你可以许可的代码。一个是 Node.js API 服务器,当给定流时 URL 将为您 return 元数据,进行所有缓冲和解析 server-side。另一个是基于 MSE 的 client-side 播放器...请注意,尽管这仅适用于支持 CORS 的服务器,而且据我所知,目前只有我自己的服务器 (AudioPump CDN) 可以这样做。如果您对这些代码中的任何一个感兴趣,请随时通过 brad@audiopump.co e-mail 联系我。如果您对我在 Stack Overflow 上的回答有疑问,请 post 在此处发表评论。