PS3 Media Server上の動画をiPhoneで見られるようにする

我が家ではX27DべアボーンにCentOSを入れてメディアサーバーとして運用している。クライアントにPlaystation3を使っているのでサーバーソフトにはPS3 Media Serverを使っているのだけれども同じサーバーからiPhone4で動画が再生できるDLNAクライアントがなくて困っていた。

Media Link Playerの接続確認済みデバイスリストでもPS3 Media Serverでの動画は非対応の注釈が。

1:PS3 Media Serverが特定の通信方式(keep-alive)に対応していないため、iOS4.2ではPS3 Media Server上の動画は再生できません。

そうは言われてもKeep-aliveに対応していないから再生できないというのが腑に落ちない。Keep-aliveは多分HTTP Keep-aliveのことだと思うが、コンテンツは複数のファイルに分かれている訳ではないから1回のGETリクエストでファイルは手に入るはず。仮にファイルサイズが大きいから複数のGETに分けるとしても当座の再生に必要なデータは1回のGETで取得すればいいはず。Keep-aliveの可否によるオーバーヘッドが問題になるとは考えづらい。それ以前にPS3 Media ServerがHTTP Keep-aliveに対応していないとも思えない。

と、言う訳でクライアントの作り方次第で何とかなるんじゃないの?という思いからiPhone用のPS3 Media Server向けクライアントを自作してみようと思った。

まずは目標と条件を以下のように設定する。

目標:iPhone4で撮影したH.264動画をPS3 Media Serverに配置し、それをiPhone上から再生する
おまじないというかお約束というか…
条件1:HTML + JavaScriptでWebアプリとして作成する
Appleにお布施を払いたくないのでWebアプリとして開発することにする。動画のデコードまで自力で行いたくはないのでHTML5のVideoタグを用いて再生するようにする。
条件2:可能な限りサーバー側には手を入れない
クライアントの作り方次第で〜と風呂敷を広げたのでサーバー側は設定変更でなんとかできるようにする。

実は結論から言うとPS3 Media Server側に手を入れないと再生できないことがわかり、ソースコードに手を入れてビルドし直した結果Media Link Player Liteで再生ができてしまった。なのでクライントを自作しようと言う試みは無用となったので中止した。

audioタグを用いたMobile Safari上での音声ファイル再生

まずはPS3 Media Server上にあるオーディオファイルをhtml上から再生させてみる(いきなりビデオ再生は問題のきり分けが難しいかもしれないのでまずはオーディオから)。

<html>
  <head>
    <meta charset="UTF-8"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="apple-touch-icon" href="../images/icon-256.png"/>
    <title>Title</title>
  </head>
  <body>
  <audio controls="controls">
  <source src="http://192.168.x.x:5001/get/0$0$0/xxxxxxxx.m4a"/>
  </audio>
  </body>
</html>

PS3 Media Server上におかれているコンテンツ名(audioタグのsrcに指定するところね)を確定させるにはUPnPのSOAPメッセージをやりとりして調べる必要がある(実際そこも苦労した。JavaScript用のSOAPクライアントライブラリの準備とかクロスドメイン問題の解決とか)のだけれどそこは省略。

ところがこのHTML、MacのSafariやChromeではオーディオの再生ができるのだがなぜかiPhone上ではエラーが表示される。もう少し調べたところMP3は再生できるのだがAACだとNG。同じようなHTMLをMacのApacheファイル共有機能を使って表示させると問題なし。パケットアナライザでパケットの内容を追ってみるとオーディオファイルに対するGETは発生しているしサーバー側も一応データは送っているようだ。だとするとこれはレスポンスヘッダの問題か?

ここまでくるとサーバーでデバッグしてみないとよくわからない。PS3 Media Serverの野良ビルドを検討することにした。

PS3 Media Serverのビルド

ビルド自体はとても簡単。PS3 Media ServerはJavaで実装されていてEclipseのプロジェクトファイルも用意されているのでGoogleコードのSVNリポジトリからEclipseのプロジェクトとして簡単にインポートができる。デバッグもデバッグボタンを押すだけでOK。Linux版に関してはbuild.xmlã‚’ant実行するだけで配布モジュールまで簡単に作れる親切さ。

早速レスポンスヘッダを見てみる

デバッガ上でオーディオファイルのGETリクエストに対するレスポンスを見てみるると。

DefaultHttpResponse(chunked: false)
HTTP/1.1 206 Partial Content
Content-Type: audio/x-m4a
Content-Range: bytes 0-1/16474173
Accept-Ranges: bytes
Connection: keep-alive
Server: Mac_OS_X-x86_64-10.6.8, UPnP/1.0, PMS/1.30.1
Content-Length: 16474173

怪しいと思うのは2カ所。Content-TypeとContent-Length。

  1. audio/x-m4aというMIME typeをSafariが認識しない
  2. Content-LengthがContent-Rangeで指定した範囲のサイズと異なる

後者だとするとMP3の場合に動作するのが解せないのでまずは前者を疑う。MIME typeは設定ファイルで上書きできるのでaudio/mp4-latmと変更してみるも効果なし。では後者か…これはソースコードを直すしかない。

Index: net/pms/network/RequestV2.java
===================================================================
--- net/pms/network/RequestV2.java	(revision 748)
+++ net/pms/network/RequestV2.java	(working copy)
@@ -522,7 +522,7 @@
 			}
 		} else if (inputStream != null) {
 			if (CLoverride > -1) {
-				if (lowRange > 0 && highRange > 0) {
+				if (lowRange >= 0 && highRange > 0) {
 					output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "" + (highRange - lowRange + 1));
 				} else if (CLoverride != DLNAMediaInfo.TRANS_SIZE) {
 					// since 2.50, it's wiser not to send an arbitrary Content length,
@@ -574,7 +574,7 @@
 			// logger.trace( "Sending stream: " + sendB + " bytes of " + argument);
 			// PMS.get().getFrame().setStatusLine(null);
 		} else {
-			if (lowRange > 0 && highRange > 0) {
+			if (lowRange >= 0 && highRange > 0) {
 				output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "" + (highRange - lowRange + 1));
 			} else {
 				output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "0");

ということでContent-Rangeで指定した範囲とContent-Lengthの値を合わせたところ今度はうまく行った。

同じように動画の表示をさせたところこれもうまく行く。もしやと思いML Playerで再生させようとするとこれまた表示できるようになった!
つまりはiOSのHTTPクライアントライブラリがContent-RangeヘッダとContent-Lengthヘッダの両方を見ていてPS3 Media Serverから届くレスポンスを不正なものと認識しているのが原因のようだ。iOSレベルの仕様によるものだとするとiOSのDLNAクライアントがことごとく動作しないのもうなずける。Keep-alive云々は関係ないけれどクライアント側ではどうにもできないというML Player側の言い分は正しい。

というわけでこの変更によってML PlayerからでもPS3 Media Server上の動画が見られるようになったのでクライアントの作成はやめてしまいました(本当はML Playerはオーディオ再生中もアプリを切り替えると停止するとか動画のシーンサーチがやりづらいとか不満もあるけれどそのためだけにクライアントを自作するほどのモチベーションはないしね!)。


PlayStation 3 (160GB) チャコール・ブラック (CECH-2500A)


Apple iPod touch 64GB MC547J/A 【最新モデル】