SHOUTcastについて調べてみる
このページの趣旨
SecondLife内で、今聴いているネットラジオの曲情報(曲名やアーティスト名)を取得する方法を模索することです。現在1)、LSLとPHPを連携させ取得することには成功していますが、外部サーバにかかる負荷が大きく実用的ではないことから、もっと別の方法があるのではないかと試行錯誤中です。
このページでは、「ネットラジオ = SHOUTcast」という前提で話を進めています。ちなみに、SHOUTcastとは、世界中で最も広く使われているオンデマンド・ライブストリーミング配信アプリケーションの1つです。
SHOUTcastに関する日本語情報は「あたまにきたどっとこむ-超図解!誰でも始められるネットラジオ SHOUTcast編」が解りやすいと思いますのでそちらを参考にしてください(arz NitelyはSHOUTcastを導入したことはありませんので質問にはお答えできません…)。
SHOUTcastにおけるメタデータ送受信
メタデータとは?
一言で言うと「データについてのデータ」ということになります。そのデータがどういうものであるのかを表すインデックス的な情報で、データの作者、作成日時、ファイル形式などであることが一般的です。ココで言うメタデータとは、「今聴いているネットラジオの曲名(とアーティスト名)」を指します。
メタデータを受信するには?
メタデータを受信するには「Icy-MetaData: 1」という値をHTTPリクエストヘッダーで送信する必要があります。
メタデータはどうやって送信される?
曲データ(MP3データ)の間に割り込む形でメタデータが送信されてきます。前述の「Icy-MetaData: 1」を受け、ホストは「icy-metaint: ※※※」という値をレスポンスヘッダで送り返してきます。例えば「icy-metaint: 8192」であれば、8192byteごとにメタデータが割り込んで送られるわけです。
通常のMP3ファイルにはID3タグによるメタ情報が埋め込まれているのですが、ストリーミング配信においてはID3情報は欠落するということです。従って、PHPのID3関数などを使ってメタ情報を取得する方法は使えません。
また、曲と曲の境目に特別な信号が送られることがないため、厳密に曲が変わる瞬間に曲名を切り替えることは不可能です。経験的に言うと、曲の境目より前に、既に次の曲のメタ情報の送信が開始されているようです。
ホストへの接続
HTTPリクエストヘッダー
ホストへの接続はHTTP/1.0でOK。User-Agentは何であっても問題ないようです。
GET / HTTP/1.0 Host: (ホスト名) User-Agent: (ソフト名など) Accept: */* Icy-MetaData:1
PHPでの接続例
$sc_host = "192.168.0.1"; // <- Modify as you need $sc_port = "8080"; // <- Modify as you need $server = fsockopen($sc_host, $sc_port, $errorno, $errormsg, 20); if (is_resource($server)){ $out = "GET / HTTP/1.0\r\n"; $out .= "Host: gw\r\n"; $out .= "Accept: */*\r\n"; $out .= "User-Agent: PHP-".phpversion()."\r\n"; $out .= "Icy-MetaData: 1\r\n"; $out .= "Connection: close\r\n\r\n"; fwrite($server, $out); }
レスポンスヘッダー
下記は実際にPHPで接続したときに受信したレスポンスヘッダーです。
ICY 200 OK icy-notice1: <BR>This stream requires <a href="http://www.winamp.com/">Winamp</a><BR> icy-notice2: Firehose Ultravox/SHOUTcast Relay Server/Linux v2.6.0<BR> icy-name: S K Y . F M - Absolutely Smooth Jazz - the world's smoothest jazz 24 hours a day icy-genre: Soft Smooth Jazz icy-url: http://www.sky.fm/smoothjazz/ content-type: audio/mpeg icy-pub: 1 icy-metaint: 16384 icy-br: 96
メタデータだけを取得したい場合
上記「ホストへの接続」で接続した場合は、レスポンスヘッダーの後、実際にMP3データの受信が始まります。これをPHPで受けつつ、icy-metaintごとにメタデータを抽出しようとすると、かなりの無駄が生じてしまいます…(これが負荷増大の原因ですね)。また、LSLから直接接続したとしても、受け取れるレスポンスボディーのサイズが大きすぎて処理不能となってしまいます…(しかも、LSLからではIcy-MetaData: 1を送信できないし…)。そこで、今流れている曲名だけを取得する方法を紹介します。
7.htmlに接続
7.htmlに接続すると、今流れている曲名だけを取得することが可能です。メタデータを取得し、整形し、LSLに返すには、この方法が最も有効だと言えます。また、現在はまだ成功していませんが、もっと研究を続けるとPHPを使わずにLSLから直接メタデータを抽出できるかもしれません。
PHPからの接続は次のようにすると可能です。
$sc_host = "192.168.0.1"; // <- Modify as you need $sc_port = "8080"; // <- Modify as you need $server = fsockopen($sc_host, $sc_port, $errorno, $errormsg, 20); if (is_resource($server)){ $out = "GET /7.html HTTP/1.0\r\n"; $out .= "Host: \r\n"; $out .= "User-Agent: Mozilla\r\n"; $out .= "Connection: close\r\n\r\n"; fwrite($server, $out); }
さて、お気づきと思いますが、「ホストへの接続」のときと異なり、「User-Agent」は「Mozilla」になっています。今のところ「Mozilla」以外では成功していません。具体的に言いますと、Mozilla以外のUAで接続すると、「method = HEAD」として接続したと見なされるようで、「ホストへの接続」のときと同じレスポンスヘッダーが返されてしまいます(HEAD接続なので、もちろんBODYは空っぽです)。
LSLから直接UAを偽装して接続する方法が現在のところわかりませんので(…というか、あるの??)、そこでつまずいているわけです…。
さてさて、PHPから7.htmlに接続すると、次のようなレスポンスが返ってきます。
HTTP/1.0 200 OK content-type:text/html <HTML><meta http-equiv="Pragma" content="no-cache"></head><body>178,1,265,300,175,96,Miles Davis - The Ghetto Walk</body></html>
はじめの2行はレスポンスヘッダで、3行目は空白、4行目のレスポンスボディーはHTMLとして返ってきていますね。body内部は左から順番に次のような情報を表しているようです。
- 現在のリスナー数
- (不明…)
- これまでのリスナー数の最大値
- 同時接続可能数
- (不明…)
- ビットレート
- アーティスト名 - 曲名
というわけで…
- PHPで7.htmlに接続
- レスポンスの4行目からHTMLタグを削除し変数に代入
- その値を半角コンマで分割
- 7番目の値をLSLに返す
でOKですよねw
played.htmlに接続
played.htmlに接続すると、最近流れた曲リストを取得することができます。これは、ウェブブラウザでストリーミングホストに接続しようとした場合に表示されるページと同じものです。HTMLソースが「7.html」に比べて複雑なので、こちらにアクセスしても処理が面倒なだけですね…。
接続方法は7.htmlのときと同じです。「7.html」のところを「played.html」に変えるとPHPからアクセスすることが可能です。
現時点での問題点と今後の課題
LSL単独で可能か?
LSLとPHPを連携させ、7.htmlからメタデータを取得し、それをSecondLife内で表示させることには成功しましたが、できればLSL単独で取得できた方が何かと無駄がありません。しかし、LSLから「UserAgentをMozillaと偽装」して接続することができないため、今のところ成功には至っていません。
URLの表記型によってはNG...
SHOUTcastのホストに接続するためのURLの表記型には2種類あるようです。
- http://192.168.0.1:8080
- http://example.com:80/path/to/stream/file/stream-ID
1の型であれば、7.htmlに接続しメタデータを取得できるのですが、2の型ではどうも7.htmlに接続できないようです。今のところやったことは…
- 2の型のまま直接7.htmlにアクセス … NG
- ドメインからホストIP情報を取得 → 2の型を1の型に変換して7.htmlにアクセス … NG
という感じです…。ポート番号とstream-IDのあたりが謎です…。このあたりをさらに研究し、まずは、両方のURL型において、PHPから7.htmlにアクセスし、メタデータを取得できるようになりたいものです(そのあと、LSL単独に挑戦かな〜w)。
