Firefox + DLNA (Net::UPnP) = Native Ogg Player

前に d:id:RobinEgg:20080517:p1 で書いた続きにあたる感じ。久しぶりの DLNA クライアントネタ。

春休みに DAAP じゃ Ogg の共有できないんだけど、という Ogg 使いの友人から質問を受け、時間があったのでコーディング。

Firefox 3.5 というか Gecko 1.9.1 からネイティブで Xiph/Ogg コーデック(vorbis/theora)に対応することになったので、フロントエンドは Javascript で、バックエンドを DLNA 使うために前も使った Net::UPnPPerl で実装。間は ajax で非同期にデータを取りに行くことで、フリーズ回避。前はクライアント側での処理に固執してたので、読み込み時のフリーズをどうしようかというのが結構大きかったけど、サーバーサイドと適切に切り分けたらそこまで大きい問題じゃなかった。

Opera も一部試験的に実装を始めてるけど、今のところそもそもの規格元の HTML 5 がまだ Working Draft な段階なので、動作時にプロパティ弄ったりするようなダイナミックな環境ではまだまだ安定していない感じ。スタティック再生でもちょっと怪しい気がする(汗

とりあえずプロトタイプが出来たので、あとはデザインとかその辺なんだけど意外に安定してて使い勝手が良い。一昔前までなら考えられなかったけど、最近じゃ JS の実行速度だけじゃなくて DOM の足回りとかも凄く軽快になってるので、大凡15000エレメントを含むソースを放り込んでも特に文句も言わず動いてくれる。Javascript のチューニングとかやってるわけじゃないし、 for 使う部分も多いので、時折固まりかけたりするのはご愛敬。プレイリストの読み込みには多少時間がかかるけどね。昔 IE6 (hta) で動かしたら1000エレメント程度で死んじゃったことを考えると凄い進歩。

再生するだけなら単に audio/video オブジェクトに URI 渡して、 load()/play() すればいいだけなので簡単なんだけど、プレイリストとかの実装のが厄介。再生ファイルのURIを配列に放り込もうかとか考え出すと時間がかかってしょうがないので、とりあえず前には戻らず次へ次へで垂れ流しなアプリで良いかなと。あと、WMP Plugin とか使えば MP3 も再生できるんだけど、再生中ファイルのURI取得ができないとかいうよく解らない仕様なので放置。

まぁ実際問題として、まともに Windows/CLI で使える DLNA 実装が非常に少ないし、クライアントになるともっと少ないので、Windows 上で DLNA アプリを使うのも作るのも難しいのがネックな現状、ある意味では結構良い選択肢なんじゃないかな > firefox。ネットに上がってる DLNA 関連のネタのほとんどが PS3Xbox360 関連だしねぇ。面白いし、使いようによっては便利な規格なのでもっと広まってもいいと思うんだけど。

DLNA サーバ

特にこだわりはない。Ogg/Vorbis がどちらかというとマイノリティーな規格なので、ushare とか MediaTomb とかのはっきり対応してますよ、という物を使う方が無難。gMediaServer はよく解らない。

ushare を使う場合、Ogg の Mimetypes が変なので、 src/mime.c の Ogg の項を書き換えます。

//{ "ogg",  UPNP_AUDIO, "http-get:*:audio/x-ogg:"},
{ "ogg",  UPNP_AUDIO, "http-get:*:application/ogg:"},

ここでは application/ogg にして動いてるけど、 audio/ogg でも行けるのかな?

Mediatomb では Transcode も行えるので、 mp3 ファイルを Ogg stream に変換してストリーミングすることも可能。なお、 Jpeg ファイル等のように全く非対応のファイルを指定しちゃうと Firefox もろともクラッシュしちゃうので config.xml で消しておいた方が無難。

(中略)
    <mappings>
      <extension-mimetype ignore-unknown="yes">
        <map from="mp3" to="audio/mpeg"/>
        <map from="flac" to="audio/x-flac"/>
        <map from="ogg" to="application/ogg"/>
        <map from="wma" to="audio/x-ms-wma"/>
        <map from="m3u" to="audio/x-mpegurl"/>
      </extension-mimetype>
      <mimetype-upnpclass>
        <map from="audio/*" to="object.item.audioItem.musicTrack"/>
      </mimetype-upnpclass>
      <mimetype-contenttype>
        <treat mimetype="audio/mpeg" as="mp3"/>
        <treat mimetype="application/ogg" as="ogg"/>
        <treat mimetype="audio/x-flac" as="flac"/>
      </mimetype-contenttype>
    </mappings>
  </import>
  <transcoding enabled="yes">
    <mimetype-profile-mappings>
      <transcode mimetype="audio/mpeg" using="2ogg"/>
      <transcode mimetype="audio/x-ms-wma" using="2ogg"/>
      <transcode mimetype="audio/x-flac" using="2ogg"/>
    </mimetype-profile-mappings>
    <profiles>
      <profile name="2ogg" enabled="yes" type="external">
        <mimetype>application/ogg</mimetype>
        <accept-url>yes</accept-url>
        <first-resource>yes</first-resource>
        <accept-ogg-theora>no</accept-ogg-theora>
        <agent command="/home/hogehoge/ffogg.sh" arguments="%in %out"/>
        <buffer size="1048576" chunk-size="131072" fill-size="262144"/>
      </profile>
    </profiles>
  </transcoding>
Mediatomb :: config.xml

ここで指定している /home/hogehoge/ffogg.sh の中身は、

#!/bin/bash
exec ffmpeg -i "$1" -acodec libvorbis -ac 2 -sameq -f ogg - > "$2"

というもの。単に ffmpeg で変換掛けてるだけ。ビットレート指定とかはあまり意味がない気がするので、とりあえず -sameq 指定で。

本体部分のソースはもう少しできあがったら公開するかも。