Hatena::Groupsubtech

NaN days

ブログを移転しています。最新の記事は motemen.hatenablog.com へどうぞ

2010-05-23

Plack::Middleware::Icecast::StatusUpdate というのを書きました

02:25 | Plack::Middleware::Icecast::StatusUpdate というのを書きました - NaN days を含むブックマーク はてなブックマーク - Plack::Middleware::Icecast::StatusUpdate というのを書きました - NaN days

http://github.com/motemen/Plack-Middleware-Icecast-StatusUpdate これです。

音声をストリーミング配信するときに、サーバが Icecast プロトコルに対応していれば、クライアント (foobar2000 とか iTunes とか) 側でトラック情報の更新を受け取ることができます。そのためには音声データにメタデータを織りまぜないといけないのですが、それを自動でやってくれる middleware です。

使い方は example/streamer.pl を見てもらえれば分かるのですが、データを書き込んでいる最中に、Plack が寄越している $env$env->{'icy.status'} にハッシュリファレンスを設定してやります。

$env->{'icy.status'}->{title} = $title;

あとは曲が変わるたびにこれを更新してやればよいです。クライアントが ICY-Metadata ヘッダを送信したとき、かつサーバがレスポンスに Content-Length ヘッダを含めない (= ストリーミングの) 場合にしか機能しないので、enable 'Plack::Middleware::Icecast::StatusUpdate' するだけで万事うまくいくはずです。

これで Plack を使ってストリーミングアプリを作ろうと思ったんだけど、example/streamer.pl を動かしてみたらすごい勢いで書き込みバッファに溜め込むのでメモリが食われる。このへんを気にしながらちゃんと動くものを作るにはサーバの実装と密結合せざるを得ないのかな。トホホ。

Icecast についてはなんかまとまった仕様がなくて、いろいろ調べたところ

を読み、実験して、以下のようなものだと理解しました。

  • データの配信は HTTP
  • クライアントがステータスの逐次更新に対応している場合、リクエストヘッダに ICY-Metadata: 1 を含める
  • サーバがステータスの逐次更新を行う場合、レスポンスヘッダに例えば ICY-Metaint: 16000 を含める。この 16000 というのは好きな値でよくて、このバイト数ごとにデータ中にメタデータが現れる。
  • metaint バイト分音声データを送信したら、サーバは以下のようなデータ列をメタデータとして送信する。
    • (後続するメタデータ長 / 16) (1バイト)
    • 指定されただけの長さのメタデータ文字列 (バイト長が 16 の倍数になるまで "\0" で埋める)
    • メタデータは "StreamTitle='stream-title';" というフォーマットになっている。StreamDescription とかもありそうな感じがする。
  • また metaint バイト分音声を送信し、あとは繰り返し。

SHOUTcast では "HTTP/1.0 200 OK" の代わりに "ICY 200 OK" としてたみたいですね。へえ…。