2012年5月26日土曜日

mpegtsについて調査する必要がなくなりました。

ども、動画、音声パケットの解析を期待された方すみません。

なんか解析する必要がなくなっちゃいました。
というのも、xuggleの動作でコンテナ用のパケットを捕捉することができちゃったので、わざわざJavaで構築しなくてもちゃんとデータができました。
その上映像と音声の同期も勝手にやってくれたので、さらに楽でした。

注意点はいくつかあって、どうやらHandlerにデータを渡すときに、きちんとパケットとして満了してなくても飛んでくることがあるみたいですね。

もっと早くみつけたかった・・・。


今日わかったこと
・先日ブログの記事にしたmp3の無音のやつ。たしかに、同じ秒数の無音になるんですが、よくよくみるとlame3.98だかっていう文字がはいっている・・・無音のmp3というより、情報入力用のフレーム?

・flvのデータはhttp://osflash.org/flvここのデータを参考にすると動画や音声のデータタグは実はその後のデータもなにかしらあるみたいです。
[データタグ][生データ]という形かとおもっていたので、tsデータと比較していたところ
FLV [データタグ][不明なデータ][一致する生データ]
TS [TSヘッダ][不明なデータ][一致する生データ]という形になっていました。

mpegtsについて調査してみます。その3

さて、最期のとりで、音声パケットと映像パケットについて

一応の目星からするとおそらく音声が0x101映像が0x100となります。
とりあえず単純な0x101の方からいってみます。

mp3の音声のないデータはもともとこの動画から切り出しているので、コレがどこにあるかまず調べてみたいとおもいます。
とりあえずmp3のヘッダの部分のおさらいからすると、
ff fbですでに、mpeg1のlayer3、protectedなし
52 64は5はテーブルより64kbit、2の上位2ビットが00なので、テーブルよりサンプリングが44.1kHzとなります。
実データの方にでてくるのがff fb 90 64なのでこちらもしらべてみると・・・
ff fbからmpeg1 layer3 protectedなし
92 64から128kbps 44100Hz・・・あ
bps2倍にしてしまった・・・でもab 64いれてるのに・・・64kだったか!?


ff fb 52 64 a9 0f f00 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 4c 41 4d 45 33 2e 39 38 2e 34 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55
サイズは209バイトなので、188バイト1パケット分を超えています。

とりあえず、4101が発生してから、次の4101がでるまでを貼付けてみます。
101:47 4101 10 00 00 01 c0 0b 75 80 80 05 21 00 03 ec 31 ff fb 90 64 00 0f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

102:47 0101 11 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

103:47 0101 12 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 ff fb 92 64 be 8f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

104:47 0101 13 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 

105:47 0101 14 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 ff fb 92 64 ff 8f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

106:47 0101 15 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

107:47 0101 16 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 ff fb 92 64 ff 8f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 

108:47 0101 17 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

109:47 0101 18 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

110:47 0101 19 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 ff fb 92 64 ff 8f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

111:47 0101 1a 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

112:47 0101 1b 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 ff fb 92 64 ff 8f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

113:47 0101 1c 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

114:47 0101 1d 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 ff fb 92 64 ff 8f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

117:47 0101 1e 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

118:47 0101 3f 04 00 ff ff ff 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 4c 41 4d 45 33 2e 39 38 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

ここから次の4101
195:47 4101 10 00 00 01 c0 0b 76 80 80 05 21 00 05 6c c3 ff fb 92 64 ff 8f f0 00 00 69 00 00 00 08 00 00 0d 20 00 00 01 00 00 01 a4 00 00 00 20 00 00 34 80 00 00 04 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 

ここから推測するに7つはいっていますね。64kbpsのデータ209バイトなので128kbpsだとすると400kbyteくらい、そこからすると個数的には問題ないですね。

あーやっちまった。

追記
とりあえず、64kの音声のデータを準備して、PATとPMTを確認しましたが、同じでした。


mpegtsについて調査してみます。その2

mpegtsのデータの最小ユニットはトランスポートパケット(tsパケット)という188バイトのデータの連続になっている。

先頭の4バイトは内容が何であるかという記述がはいっているようです。
47 40 11 10
始めの47は固定
次の2バイトのうち下位13ビットがPIDというパケットがなにであるか判別するためのID
この場合は40 11 & 1F FFの結果で0x11になります。
最期の1バイトの下位4ビットが巡回カウンターらしいです。
たぶんそれ以外のビットはランダムではいっているものかなと想像しています。
(これについては、今後のパケットを読み込むときに明らかになるでしょう。)

PIDには固定されているものと、こちら側で自由につけれるものがあるみたいです。
とりあえず固定されているものを列挙しておきます。

PSI(基本情報)
0x0000 PAT 内包しているPID一覧を持たせることでこのtsストリームがどういう情報をもっているのか、わかるようにするものです。本の目次みたいなものですね。
0x0001 CAT 限定受信をサポートする情報を持つ(多分いらない)
SI(拡張情報)
0x0010 NIT チャンネル番号や変調方式、ガードインターバル等、送信するネットワークに関するもの
0x0024 BIT 放送局個別情報(多分いらない)
0x0011 SDT チャンネルの名称(冒頭であげたデータがこれに相当します。)ffmpegなんたらという情報がはいっていました。削除しても問題ないみたいなので、今回たぶん出力しない。
0x0012 EIT 番組の名称や放送日時、放送内容等、番組の内容・・・(多分いらない)
0x0014 TOT 現在の日付、時刻、サマータイム情報(いらないと思う。時間処理にかかわるならでてくるだろうか?)
0x0014 TDT 現在の日付、時刻に関する情報(同上)

こんなところですね。

さて、解析を進めます。
先頭のSDTはとりあえず、無視します。
PATのパケットはすべて47 40 00 1*という形でした。なのでtsファイルを生成するときには、これに準拠します。
間隔はざっとみたところ50パケットずつくらいに挿入されていました。tsファイルの形でTCP転送するので、パケットロスはまぁないことを期待して、tsファイルそれぞれの先頭にいれる程度でもいい気がします。そのあたりはおいおい調整する方向でいきたいとおもいます。
内容は47 40 00 11 [00 00 b0 0d 00 01 c1 00 00 00 01 ef ff 36 90 e2 3d]
こんな感じ
http://nomanganolife.blogspot.jp/2009/12/mpeg2-ts.htmlこちらの図を参考にすると、先頭の00が気に入りませんが
[00]テーブル識別
[b0 0d]([1][011][0000 00001101])
セクションシンタクス指示 1
続く3ビット 011固定
セクション長は0x00d(13バイト)
[00 01]トランスポートストリーム識別 1
[c1]([11][00000][1])
2ビット11固定
バージョン番号5ビット 0
カレントネクスト指示 1
[00]セクション番号
[00]最終セクション番号
ここから含まれるpid情報
[00 01]放送番組番号識別
[ef ff]([111] [01111111111111111])
3ビットの111固定
ネットワークPID 0xFFF(4095番)
最期にこのパケットのCRC情報
[36 90 e2 3d]

というのが延々と続いていました。
番組的には音声と映像がわかれているということはないみたいです。
今後生成するtsファイルも1tsストリームにつき1放送しか扱わないのでこのデータをそのままコピーして成立しそうな予感です。

今後は0xFFF番のトラックにメディアデータが格納されているであろう・・・ということですね。

ここで0xFFF(映像データ)と0x000(PAT)をのぞいたデータをDumpしてみました。
ざっと目についたのが
47 41 00 n*
47 01 00 n*
(nの部分は1,2,3を確認、たぶんなんでもいいのかもしれない。)
という0x100のデータ

47 4101 n*
47 0101 n*
(nの部分は1,3のみ確認、こちらもnは好きな数字でいけるとかな気がする。)
という0x101のデータ

47 1f ff 10
(これはnullパケットと呼ばれる伝送ビットレートの調整用のパケットらしい。内容はffで埋められていました。)
この3つがありました。

0x100と101はいったいなんなんだろうか・・・
一覧化してみたところどうやら0x100が映像パケット0x101が音声パケットっぽいですね。
100側
47 41 00 1*
47 41 00 3*
47 01 00 1*
47 01 00 2*
47 01 00 3*

101側
47 41 01 1*
47 01 01 1*
47 01 01 3*

以上が確認できました。それ以外はないみたいです。

さて、映像と音声はとっておきにしたいので、先にfff番のパケットについて調査を進めます。
調べてみましたPMTというやつですね。
47 4f ff 1*のみで内容はすべて同じみたいです。
どうやらPATと同じく含まれている音声と映像のPID情報を保持しているようです。
47 4f ff 11 00 02 b0 17 00 01 c1 00 00 e1 00 f0 00 1b e1 00 f0 00 03 e1 01 f0 00 4e 59 3d 1e


http://allegro.dtiblog.com/blog-entry-187.html
http://nomanganolife.blogspot.jp/2009/12/mpeg2-ts_09.html
こちらの記事を参考にやってみると
47 4f ff 10(TSヘッダ)
00
[02] [b0 17] 00 01 c1 00
00 e1 00 f0 00 1b e1 00 f0 00 03 e1 01 f0 00
4e 59 3d 1e(CRC)

[02]まず先頭の02ですが、これがPMTの識別する値みたいです。
[b0 17]つづく上位4ビットbの部分が固定で残りの0x017が今回のPMTデータの長さになるらしい。
0x17がデータ長なので23バイトが今回のデータみたいです。
00 01 c1 00 00 e1 00 f0 00 1b e1 00 f0 00 03 e1 01 f0 00 4e 59 3d 1e
この部分がちょうど23バイトになっていますので、正解です。


47 4f ff 10(TSヘッダ)
00
02 b0 17 00 01 c1 00 00
e1 00 f0 00
1b e1 00 f0 00
03 e1 01 f0 00
4e 59 3d 1e(CRC)

つづく00 01 c1 00の部分はPATと同じみたいです。
PAT 47 40 00 11 00 00 b0 0d 00 01 c1 00 00 00 01 ef ff 36 90 e2 3d

e1 00 f0 00の部分ですが
e1 00の部分、先頭3ビットは111固定残り13ビットがPCR_PIDとのことです。今回は0x100になります。
f0 00の部分は先頭4ビットが1111固定(fの部分)残りの12ビットは今後の番組情報長の基底ですが、0なので情報はありません。
あとの残りは
1b e1 00 f0 00
03 e1 01 f0 00
緑がストリーム形式種別
青が上位3ビット 111固定と下位13ビットがエレメントのPID
fの部分は4ビット1111固定で
赤の部分がデータ長(中身がないので0固定)
となります。

エレメントのPIDが0x100と0x101になったので、これは先ほどのデータと一致しますね。
1bと03のストリーム形式種別については、とりあえず1bを映像 03を音声と考えて処理しますが、いろんなファイル変換してみて、どうなるか確認するつもり。
PMTの情報も今回の出力結果をコピーしてつかって問題なさそうな予感です。
エレメントの内容は次回・・・

mpegtsについて調査してみます。その1

mpegtsについて調査してみたいと思います。

今回僕のやりたいことは、mpegtsのファイルを分割することではなく、mpegtsのファイルをコーデックで求められたバイトデータから生成することなので、ちょっと難易度高め?な気がします。

とりあえずgoogle先生に尋ねます。
[mpegts specification]でぐぐります。
http://en.wikipedia.org/wiki/MPEG_transport_stream
http://nomanganolife.blogspot.jp/2009/12/mpeg2-ts.html
http://allegro.dtiblog.com/blog-entry-185.html

とりあえず前知識としてこの3つくらいは読んでおきました。
わかったことは、最小単位は188バイトで構成されている。
いろんなヘッダが存在する。といったところ。

データを解析するにあたって、いつもつかっているマリオギャラクシー2のPVに対して次の変換を実行しました。

$ ffmpeg -i orgMario.flv -acodec libmp3lame -ab 64 -ar 44100 -vcodec libx264 -vpre default2 -s 320x240 -f mpegts mario.ts
$ ffmpeg -i orgMario.flv -acodec libmp3lame -ab 64 -ar 44100 -vcodec libx264 -vpre default2 -s 320x240 -f flv mario.flv

オリジナルデータもh264 + aacだったのでそのままvcodec copyとacodec copyでtsファイル化できるかなとおもったのですが、h264 bitstream malformatedというエラーがでてしまったので、今回red5 + xuggleで生成しようとおもっている h.264 + mp3の形式でとりあえず出力するようにしました。
なお、ffpresetのdefault2は前回の記事のxuggleで利用したもののsetProperty値の元ネタとなったものです。
2つつくった理由はflv側は動画データ、音声データの入り方を理解済みなのでデータがどのようにmpegtsに代入されているか理解するのに使える。
かつ同じ変換方法をつかえば出力される動画データ、音声データは一致する。
というのを期待しています。
あと、javaのプログラムをささっと書いて、188バイトずつのデータを取得し、文字列にしたテキストデータも準備しました。メモ書き用みたいなものです。
flvデータ
 tsデータ(幅を調節して、4行が1つのまとまりになるようにしてます。)
 txt形式に直したデータ

ではちまちま解析していくか・・・

2012年5月24日木曜日

xuggleにh.264変換させた。

あってるかはわからない。でも、一応それっぽいHexはDumpできた。


http://code.google.com/p/xuggle/source/browse/#svn%2Ftrunk%2Fjava%2Fxuggle-xuggler-red5
ベースはここのコードですが、変更点を羅列しておきます。
自分用メモ
VideoTranscoderDemo
220行目あたり動画コーデック指定を次のようにする。

outputStreamInfo.setVideoWidth(320);
outputStreamInfo.setVideoHeight(240);
outputStreamInfo.setVideoBitRate(300000);
outputStreamInfo.setVideoFrameRate(IRational.make(1, 25));
outputStreamInfo.setVideoCodec(ICodec.ID.CODEC_ID_H264);
outputStreamInfo.setVideoGlobalQuality(0);

560行目あたり動画コーダーの指定の部分。
outCoder.setCodec(outCodec);
outCoder.setWidth(mOutputInfo.getVideoWidth());
outCoder.setHeight(mOutputInfo.getVideoHeight());
outCoder.setPixelType(mOutputInfo.getVideoPixelFormat());
outCoder.setGlobalQuality(mOutputInfo.getVideoGlobalQuality());
outCoder.setBitRate(mOutputInfo.getVideoBitRate());
outCoder.setFrameRate(mOutputInfo.getVideoFrameRate());
outCoder.setNumPicturesInGroupOfPictures(mOutputInfo.getVideoNumPicturesInGroupOfPictures());
outCoder.setProperty("coder", "1");
// outCoder.setProperty("flags", "+loop");
outCoder.setProperty("cmp", "+chroma");
outCoder.setProperty("partitions", "+parti8x8+parti4x4+partp8x8+partb8x8");
outCoder.setProperty("me_method", "hex");
outCoder.setProperty("subq", "7");
outCoder.setProperty("me_range", "16");
outCoder.setProperty("g", "250");
outCoder.setProperty("keyint_min", "25");
outCoder.setProperty("sc_threshold", "40");
outCoder.setProperty("i_qfactor", "0.71");
outCoder.setProperty("b_strategy", "1");
outCoder.setProperty("qcomp", "0.6");
outCoder.setProperty("qmin", "10");
outCoder.setProperty("qmax", "21");
outCoder.setProperty("qdiff", "4");
outCoder.setProperty("bf", "3");
outCoder.setProperty("refs", "3");
outCoder.setProperty("directpred", "1");
outCoder.setProperty("trellis", "1");
// outCoder.setProperty("flags2", "+mixed_refs+wpred+dct8x8+fastpskip+mbtree");
outCoder.setProperty("wpredp", "2");
outCoder.setFlag(IStreamCoder.Flags.FLAG_LOOP_FILTER, true);
outCoder.setFlag(IStreamCoder.Flags.FLAG2_MIXED_REFS, true);
outCoder.setFlag(IStreamCoder.Flags.FLAG2_WPRED, true);
outCoder.setFlag(IStreamCoder.Flags.FLAG2_8X8DCT, true);
outCoder.setFlag(IStreamCoder.Flags.FLAG2_FASTPSKIP, true);
// outCoder.setFlag(IStreamCoder.Flags.FLAG, true);

こんな感じにする。
ffpresetが適応できなかったので、要は設定データをすべてsetPropertyで記述。ただしsetFlagだけは、別途記述があるみたいなので、そっちを利用する。
mbtreeだけ設定方法がみつからなかったので削除。

で、上図のようにpictureエンコードが走りデータが出力されるようになった。
あー厳しい戦いだった・・・これでmpegtsのコンテナ方式さえ解読できれば、自力でtsパケットつくれそう。


2012年5月23日水曜日

iphone上でのライブストリーミングデモうごきました。

iphone上で、Canvasとmp3のm3u8ストリーミングを利用してライブストリーミングをする。
そんな動作のデモができたので、動画にとってYoutubeにあげてみました。


動作の概要は次のとおり。
1:rtmpで映像+音声をred5に送る。
2:red5の内部で映像をjpegにする。生成jpegファイルはwebSocketでリアルタイム共有
3:red5の内部で音声をmp3のストリーミングにする。
4:アクセスユーザーはmp3のストリーミングを実行しつつ、Audioの位置から画像をJavascriptでアニメーションさせる。

3の動作の高速化を実施するために、以前つくったRed5のWebSocketプラグインを利用しています。

いまのところの課題は・・・
1:画像の転送料の軽量化とfpsの改善。
2:プログラムを修正して、room動作で問題がでないように修正。
3:iOS4でも動作するようにしたい。すくなくともiPhone4あたりでも動作してほしい。
(まだ未検証)

今回のデモは320x240の画像で実行していますが、実際にサービスとして利用するとしたら、160x120の小さな画面とコメント等の機能がある通常プレーヤーと従来のmpegtsによる動画だけ、高画質のフルスクリーンモードを準備して、任意で切り替えられる、的な感じですかね。


駄文その1:
ここ数日やっているこのライブストリーミングですが、一応HttpLiveStreamingの動作遅延を極限までなくすと、どこまでつめることができるのか?という検証もかねています。

・今回のストリーミングは、6〜7秒ほどのずれになっていますね。
・rtmpdump + ffmpeg + segmenterの組み合わせでがんばって出せる限界がだいたい20秒〜40秒
・今回のストリーミングの方式のmp3のみフルスクリーン動作でのベンチマーク最高が4秒程度(1、mp3の長さは2秒指定にしてあります。)

という結果になっています。

ffmpegをプログラムとして使う→ファイルとして成立するものをつくる。
xuggleにred5でパケットデータを送る→パケットの出力しかしない。
というわけで、後者の方が有利なのだろうか?と想像しています。
mpegtsのフォーマットを理解して自力でred5 + xuggleでtsパケットをつくれば映像のストリーミングでも8秒以内のずれに押さえ込める気がしますね。

駄文その2:
今回、今後の解決しないといけないものとして、3つあげました。
2はやっつけてつくったプログラムを修正すればあっというまに出来上がります。
3はさっさと公開して、だれかがiOS4の端末で検証してくれるのを待てばOKです。たぶん動作するはず。

で、問題は1ですね。
普通のネット環境で、すいすいみれる状況にしようかとおもったら転送量はできれば500kbps以下にしたいところ。
今回の動作の目標は10fpsに定めていますので
500kbps / 8(byteに変換) / 10 = 6.25 Kbyteが一枚のjpegのサイズということになります。正直これはきついです。ものすごいダメ画質になるでしょう。

ここででてくるのが、Canvasによるピクセル描画ですが、手持ちのiPhone4Sでためしたところ、160x120くらいのサイズでないと厳しいみたいです。
ここまで画像が小さいと、結局jpegを送るのとたいしてかわらないかもしれません。

ま、もっともAppleが考えを切り替えてVideoタグでの表示がフルスクリーンonlyという状況じゃなくなれば、こんな面倒なことしなくてもいいんですけどね。

2012年5月21日月曜日

xuggleのspeex_uwb_modeのリンクエラー対策

xuggleを利用して、iPhone用のライブストリーミング(フルスクリーンではない動作)をやりたいわけです。
iphone上でコメントの流れるながれる生放送を作りたいその1で書きましたが今回は・・・

音声:mp3ベースのm3u8ストリーミング
映像:jpgベースの画像をcanvasに書き込んでやりくりする。
この2本で攻めます。
これらのデータですが、xuggleとred5でやりくりで構築する予定です。

今回はこのxuggleのインストールで詰まった話。
インストールターゲットは、自分のPCであるMacOSX 10.6.8とサーバーとしてもっているサクラのVPS(CentOS)の2カ所です。

でやったことですが、http://www.xuggle.comから
https://github.com/xuggle/xuggle-xuggler/commits/master
こちらのgitのデータを入手。antでプログラムのコンパイルインストールを実行したのですが、speex_uwb_modeのリンクエラーがでました。動作しねぇ・・・

でいろいろすったもんだがあったあげく。
http://code.google.com/p/xuggle/
こちらのgoogle Code上にあがっているデータをCheckoutしてコンパイルしたら動作しました。
こちらでは問題なく動作しました。
リンクエラーで困った方は、google code側のデータも試してみてください。


さて、以下駄文。
今回xuggleを選んだ理由です。

・xuggleとred5は相性がいいです。過去にはxuggle-xuggler-red5というred5のストリームをリアルタイム変換するライブラリを公開されてました。これを利用すれば本当にリアルタイム画像縮小とかできて便利だし、リアルタイム画像データ加工とかもできます。
しかもパケットベースでデータにアクセスできます。

・一応rtmpdump(librtmp)とffmpegの組み合わせでもリアルタイム変換は実行可能ですが、次のような弱点があります。
1:放送ストリームがとぎれるとrtmpdumpがとまってしまう。
2:rtmpの仕様上、中途で映像を止めずに画質やコーデックデータを変更することが可能ですが、それを実行するとffmpegの動作が狂う(フレーム不正扱いになって動作しなくなる。)
3:rtmp上のflvでは、メディアストリームがシームレスであるという保証がない。
基本的にメディアデータにgapがあると動作がおかしくなってしまうのですが、データの構造上、間をあけて何秒から次のパケットを再生するということが可能になる。
またこの状態になったときにffmpegがきちんとすぐ動作する保証はない。

以上の点をふまえて次のような戦略でせめて見ます。
・音声データを始めからシームレスにつないだデータをリアルタイムに出力しつつ、音声の位置をベースに表示すべき画像を取得しそれで動画を作り上げる。
・音声データにgapがある場合は無音mp3のデータを挟むことで埋めてやる。(mp3の特性上キーフレームというのが存在しないので、プチっというノイズが入る可能性はあるがストリームが壊れることはないため、無音mp3は挟むたい放題です。長さは約0.2秒なのでうまくやれば人の気にならないくらいのずれに押さえることが可能。また、xuggleとred5の組み合わせでは1パケット情報ごとの管理ができるので、その点もgood)
・映像データはxuggle画像デコード時のVideoPictureをHookしてJavaのBufferedImageに変換。それをjpgにして出力することで準備する。

というやり方でせめる予定です。問題点であげたものに大しては
1:Red5サーバーへのパケット転送は放送プレーヤーが何しようが途切れることはない。
2:xuggle+red5で攻めた場合、コーデックデータ等が変わっても変換に利用する対象が1パケットの短いものなので影響はでない。仮にでても、そのパケットを捨てて次のパケットで復活させればOK
3:gapが仮にあっても無音mp3で埋めるので問題なし。
ということになります。

駄文その2
xuggle-xuggler-red5の簡単な解説。
xuggle-xuggler-red5では次のような手順で動作します。
1:Xuggle動作用のクラスの準備
Xuggleの動作用のIURLProtocolHandlerFactoryを拡張したRed5のパケット用クラスを準備する。Red5HandlerFactory.java
パケットを解析して送り出す。IURLProtocolHandlerを拡張したRed5用のクラスを準備する。Red5Handler.java
2:IBroadcastStreamのパケットデータを監視する。(red5の動作 IMediaStream.addStreamListenerで監視できます。)
受け取ったデータはqueueの形にして、コンバート待ちにする。
3:Transcoderという処理を実行するスレッドを立ち上げてそこでパケットにQueueがある限り変換を実行しまくる。
この部分に例では、listenerを仕込んで
変換前→デコード後→変換後の3カ所でデータに手がだせるようになっています。
4:変換後のパケットをRed5用のメッセージに戻してBroadcastStreamに流す(すると放送ストリームとして認識されて動作します。)
という一連の動作をものすごい早さで繰り返します。

今回の動作では、変換の目標をflv1への映像エンコードとmp3への音声エンコードを実行すると定義し、3のデコード後の画像を横取りしてjpg化、3の変換後の音声を横取りしてmp3化します。
ちなみにすでにjpg化と音声の内部データの確認は済んでいます。

駄文その3
xuggle-xugglerで作成されるライブラリの話。
xuggle-xugglerをコンパイルするとdistというディレクトリの中に動作用ライブラリ.soとかができ、さらにjava用のjarファイル等もできあがります。
僕の場合
[DLしたxuggle]/java/xuggle-xuggler/dist以下にできあがりました。
[DLしたxuggle]/java/xuggle-xuggler/dist/libにxuggle-xuggler.jarがあったわけですが、実はこれがコンパイルされたものではなかったという衝撃の事実が・・・
本体はなんと
[DLしたxuggle]/java/xuggle-xuggler/dist/stage/usr/local/share/java/jars/の中に別のxuggle-xuggler.jarが・・・
こちらをリンクしないとリンカーエラーがでるという事態に陥りました。
xuggleに手を出す方で、リンクがうまくいかないという場合は、PC内のどこにxuggle-xuggler.jarができているか再度確認したら幸せになれるかもしれません。


さて、あとはgapを埋めるロジックを考えて実際に動作するデモをつくるのみ・・・
いっちょやりますか。

2012年5月20日日曜日

無音mp3、1フレーム分

base64変換後のデータですが

無音mp3、1フレーム分つくってみました。

64kbps 44.1kHz 2ch 0.026122...秒分のデータです。
秒数を正確に計算する場合は
(nフレーム) x 144 / 44100(Hz)で算出でき、単位は秒になります。

BASE64の場合
//tSZKkP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVMQU1FMy45OC40VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=

プログラムで書き込むならこんな感じ
byte[] noSoundMp3 = {
(byte)0xff, (byte)0xfb, (byte)0x52, (byte)0x64, (byte)0xa9, (byte)0x0f, (byte)0xf0, (byte)0x00, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x0d, (byte)0x20, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0xa4, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00, (byte)0x34, (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x4c, (byte)0x41, (byte)0x4d, (byte)0x45, (byte)0x33, (byte)0x2e, (byte)0x39, (byte)0x38, (byte)0x2e, (byte)0x34, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55};


iphone上でコメントが流れる生放送を作りたい、その1

できそうな目処がたったので、実行してみたいと思います。

動作的には次のような感じ

Flashのrtmpをつかって放送する

Red5でうけとる。

ストリームをxuggleに渡してリアルタイム変換を実行する。
・jpgでコマ画像を12〜15fps程度で実行
・mp3はm3u拡張として流せるように準備

iPhone用のストリームはhttp経由で適当にアクセス
Flash用のストリームはrtmpでそのままアクセス

といった感じ

今のところ可能っぽいこと。
・xuggleの変換後のwriterPacketのデータの中身は生メディアデータになるみたい。
(単純にmp3のフレームデータになっていたので、前回のmp3 splitterと同じようなことが可能。)フレームの長さを計算しつつ、いくつのパケットを結合するか計算すれば、問題なく組めそう。
・jpgにして書き出す操作は、xuggleにやらせてもいいし、xuggle-xuggler-red5の動作で以前各フレームの画像の抜き出しに成功したので問題ないはず。
・mp3フレームのデータには、キーフレームという概念がないので、どこできってもきちんと動作する。tsの場合は(たぶんh.264を動画コーデックに利用しているからだと思うが)終了時のフレームの位置によっては再生が強制的にとまったりする。

解決しないといけなさそうなこと。
・音声のないストリーム、もしくは無音なデータが含まれるストリームの場合、単純に結合すると音声がない間のmp3フレームが抜け落ちる。よってその間をうめるなにかを用意しないと、mp3のm3u拡張がめちゃくちゃになる。(無音部が抜け落ちるため)
→確認として、音声のあるflvデータ→音声のないflvデータ→mp3に変換を実行すると、mp3にはストリームがない状態になってしまう。

いまのところの解決案
1:ダメ画質の映像をもったtsファイルのストリームにして、audioタグにつっこんでおく。
→tsファイルにしたら問題なく動作することはわかっていますが、tsファイルにすると放送がとぎれたときのコントロールがややこしいかもしれぬ。あと、tsファイルベースのaudioタグ再生だと、挙動がかわる。(別アプリに移動したときとか一旦停止してしまって、放送者との同期がずれるかもしれない。)
2:無音のmp3フレームを別途保持しておいて、音がないときには、そのデータでうめておく。これならうまくいけるかもしれないが、xuggleの変換処理が別スレッド動作になるから同期が面倒か?

できたら2で解決したいですね。

あとは作ってみてどのくらいずれが生じるか?
フレームのデータをwebsocketでやり取りするとして、先読みとかうまく動作するか?
そんなところでしょうか?

会社でやってるtsファイルのストリーミングは、現状3〜5分遅延、アップデート後目標30秒〜1分遅延なんですが、今回は技術検証の意味とxuggleをつかったred5上でのリアルタイムエンコードなのでできたら10秒遅延以内を目指したいですね。

果たして、妙な技術エラーにぶちあたらないといいけど・・・どうなることやら。
以上意味不明な駄文でした。

2012年5月19日土曜日

xuggleではまりました。

xuggleを利用して、java上でコーデック変換をやろうと必死になっていました。

ちょっと長いですが・・・

Exception in thread "Launcher:/test" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'web.scope' defined in ServletContext resource [/WEB-INF/red5-web.xml]: Cannot resolve reference to bean 'web.handler' while setting bean property 'handler'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'web.handler' defined in ServletContext resource [/WEB-INF/red5-web.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ttProject.Application]: Constructor threw exception; nested exception is java.lang.UnsatisfiedLinkError: com.xuggle.xuggler.XugglerJNI.ICodec_CODEC_ID_FFMETADATA_get()I
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1325)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1086)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
at org.red5.server.tomcat.TomcatLoader$1.run(TomcatLoader.java:594)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'web.handler' defined in ServletContext resource [/WEB-INF/red5-web.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ttProject.Application]: Constructor threw exception; nested exception is java.lang.UnsatisfiedLinkError: com.xuggle.xuggler.XugglerJNI.ICodec_CODEC_ID_FFMETADATA_get()I
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:965)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:911)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:322)
... 13 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ttProject.Application]: Constructor threw exception; nested exception is java.lang.UnsatisfiedLinkError: com.xuggle.xuggler.XugglerJNI.ICodec_CODEC_ID_FFMETADATA_get()I
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:141)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:74)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:958)
... 21 more
Caused by: java.lang.UnsatisfiedLinkError: com.xuggle.xuggler.XugglerJNI.ICodec_CODEC_ID_FFMETADATA_get()I
at com.xuggle.xuggler.XugglerJNI.ICodec_CODEC_ID_FFMETADATA_get(Native Method)
at com.xuggle.xuggler.ICodec$ID.<clinit>(ICodec.java:877)
at com.xuggle.xuggler.SimpleMediaFile.<init>(SimpleMediaFile.java:46)
at com.ttProject.Application.<init>(Application.java:20)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:126)
... 23 more

というエラーがどうしても解決できない。jarファイルの取得にも特に問題なさそうだが・・・はて・・・とおもっていました。

原因は・・・
xuggle/java/xuggle-xuggler/dist/libにあるjarファイルをつかっていたのですが、これは環境にインストールされた正しいjarファイルではなく本来は
xuggle/java/xuggle-xuggler/dist/stage/usr/local/share/java/jars/に「生成された」jarファイルをつかわないとだめだったようです。

似たような名前のライブラリをそれっぽいところに生成(もしくは配置)しないでもらいたいものである。気づかねーよ。

iPhone用のライブストリーミングの話、canvas+jpeg+mp3ならいけそう。

Iphone上でライブストリーミングする件

Canvas + jpeg + mp3の組み合わせならいけそうな予感です。
http://jsdo.it/poepoemix/evA9

一応河川敷でちゃりをとめて見てみたら動作できたので、3Gでも動作は可能みたいですね。
Iphone4S(AU)です。

いまは、red5 + xuggleでストリーム変換するプログラムを書き込んでいる途中。

さてさて、どうなることやら。

2012年5月17日木曜日

iPhone用のライブストリーミングの話、mp4は無理でした。

iphone用のライブストリーミングでコメント付き(字幕付き)の映像を流せないか?という件ですが、いろいろ調べた結果。そもそもmp4でHttpLiveStreamingができないことがわかりました。

まずできない根拠ですが
iPhoneやiPad等で再生できる普通のmp4ファイルをつくります。

以下のような拡張m3u8を作成します。


#EXTM3U
#EXT-X-TARGETDURATION:105
#EXTINF:105,
http://適当なドメイン/hogehoge.mp4
#EXT-X-ENDLIST

で、このファイルを再生してみます。
すると、うまく動作しませんでした。

正確には、音だけ再生できるデバイスがたまにあったのですが、まともな動作はしませんでした。同様に、aiffファイルやwavファイルもやってみましたがどれもだめでした。

HttpLiveStreamingで利用できるのは、mpeg互換なデータのみな気がしますね。(個人的な意見で確証はありません。)

というわけで現状のiPhoneデバイスでニコニコ動画風の流れるコメントを表示するなら、
mp3準拠httpLiveStreamingとアニメーションgif?あたりを駆使してやるくらいしか方法なさそうな予感です。

2012年5月16日水曜日

iPhone用のライブストリーミングの話、コメントを流したい

iPhone用のライブストリーミングに、Flashでやり取りしているコメントデータを流したい・・・

いまのところ想定しているやり方は・・・
・音声をmp3で流しつつ劣化した画像をhtml5で描きつつその上にコメントをJavascriptで流す。
この方法は昨日つくってみたmp3のHttpLiveStreamingをうまく使えばできそうです。

・字幕付きmp4のHttpLiveStreamingをつくる。
今日はこっちについて調べてみました。
mp4で字幕をつける場合はidx+sub?やsrt、ttxtがありますが、今回はいろいろできるttxtを選択してみました。
というのもttxtにはスクロールが定義されており、ニコニコ動画みたいなコメントのスクロールができるかな?とおもったためです。
結果としては、できませんでした。
iPhone4Sを利用しているのですが、字幕としてttxtにも対応していましたが、対応している命令はStyleタグによる色やフォントタイプの変更くらい。
文字数がおおすぎる場合は、枠(2段幅26文字程度)だけ表示し、そとにはみ出るみたいです。

つ、つかえない・・・
フォーマットの仕様書を読んだときには、いろいろできそうでワクワクしたのに、ちきしょう。

2012年5月15日火曜日

iPhone用のライブストリーミングの話、実際にやってみた。

javaでmp3を分割するプログラムを書いてみて、実際にmp3を元にしたHttpLiveStreamingを書いてみたらどうなるかやってみました。

下記のアドレスにiPhoneでアクセスし、Audioタグの再生を実施すると音楽が流れるかとおもいます。
http://jsrun.it/poepoemix/m3utest

思った通り、mp3ベースのHttpLiveStreamingが構築できているのがわかるかとおもいます。

これやっぱり、mp4もHttpLiveStreamingに載せることが可能なんでしょうか・・・
また時間があったらやってみよう。

2012年5月12日土曜日

iPhone用のライブストリーミングの話

iphoneではFlashが利用できません。
なのでrtmpをつかったストリーミングでは対応できません。
最近のFlashMediaServerやWowzaMediaServerでは、m3u8の拡張子でうごくHttpLiveStreamingで一応Flashで放送したデータを視聴できるようにできます。

WowzaMediaServerの場合内部コーデック情報が合致する場合にのみ流せるオプションが一応ありますが、動作の安定性とストリームの詳細コントロール(途中でサーバーから切ったり等)がうまくできません。
今日はこのHttpLiveStreamingの話。

まずはすでに検証ずみな話。
1つ目
HttpLiveStreamingでは、m3u8拡張子のファイル(中身はm3u拡張)、利用する動画はtsファイル形式で動作する。
内部のTSファイルは任意の秒数のファイルのつらなりにしておき、最終であるタグまで、延々と再生しようとする。
最終タグがないm3u8ファイルをうけとった場合はリスト上の未取得なtsファイルデータを取得していき、取得完了したら、m3u8ファイルを再度読み直すという動作をする。(なおこの動作はiOSのバージョンや端末によってまちまちである。)

2つ目
よってtsファイルを細かくしてリアルタイムのデータを順次準備して端末に送り続ければ、完了していない動画データ(要するにライブデータのこと)も多少の遅延で送ることができる。
例えばrtmpの場合
[放送プレーヤー]→[red5]→[rtmpdumpでデータをflvにする]→[ffmpegでtsファイルにコンバートする。]→[segmenterでtsファイルを分割する]→端末にデータを届ける
http://svn.assembla.com/svn/legend/segmenter/
(このプログラムはffmpegのバージョンによっては、少々プログラムを書き換えないと動作しないです。)
という一連の流れをつくってやればiphoneで視聴することができるようになる。

3つ目
実はiPhoneはm3uファイルにも対応している。
m3uファイルというのは、単にメディアファイルを並べただけのものです。
http://www.interq.or.jp/blue/inside/mp3/m3u-streaming.html
ここが一番わかりやすかった。
実際にこのフォーマットで動作させると、順繰りにmp3ファイルが再生されました。
mp3cut等をつかってmp3を小さなファイルに変換し、それをベースにm3uにした場合も動作しましたが、途中できれる部分が目立ちました。

4つ目
では、先ほどつくったファイルをm3u8と同じm3u拡張の定義にしたらどうなるかやってみました。
すると、再生できました。3つ目と同じく、中途がきれている感がでますが、どうにか再生できます。ただし、3つ目とは切れ方が若干違う印象を受けました。

ここで疑問点があります。
もしかしたらm3u8の内部ファイルってtsファイルじゃなくてもいいじゃない?

iPhoneのストリーミングをAudioタグに指定した場合mp3等オーディオデータとmp4等動画データでは若干挙動がかわります。
オーディオデータの場合はアプリを切り替えたり、safariのタブを切り替えても音楽は止まりません。
ですが、mp4やtsファイルをベースにした場合は、切り替え時にいったん切れます。
今回4つ目のやり方をやってみたときに内部データをmp3にしたところ、切り替え時に切れませんでした。
もしradiko等で音声を聴く場合には、mp3ベースのm3u8にした方がいい感じの動作になるかもしれません。

さらにmp4フォーマットでもいけるかもしれません。
tsファイルの動画を利用した場合、字幕を載せるのはフォーマット上では可能なんですが、うまい方法がみつかりませんでした。
ですがmp4形式なら簡単に字幕付き動画がつくれました。
mp4形式のm3u8ストリーミングがもしできるなら、字幕選択でコメントデータのON OFFが制御できるかも・・・という淡い期待が・・・

まぁこんなこと考えるのは、会社でストリーミングサービスのiphone視聴担当してるからなんですけどねw

2012年5月4日金曜日

簡単にwebSocketを試せるJavaプログラムをつくってみました。

WebSocketをやるには、node.jsをいれなくちゃいけない等初心者には、非常に厄介です。

というわけで、初心者でも簡単にWebSocketを体験できるようなアプリをつくりました。
javaアプリなので、JavaRuntimeが必要ですが、最近のPCならまずはいっているでしょう。

やり方
https://sites.google.com/site/javataktod/app
こちらにeasyWebSocketGUI.jarというプログラムがあるので、ダウンロードする。
落ちてきたらダブルクリックすればOK
起動するとこんなウィンドウがでてくる。
これでサーバーの準備はOK
ws://localhost:8080でアクセスできます。

とりあえず起動したら、Google ChromeかSafariで(*1)
http://jsrun.it/poepoemix/rIoc
にアクセス。
すると、webSocket経由でデータの共有が実行されます。

こんな感じでデータ共有できます。
GUIバージョンではないですが、CUIバージョンのプログラムはgithubにあげてありますので興味がありましたら、どうぞ。


注1、ChromeかSafariと指定したのは、jsdo.itのデータがwebkitのみに対応させてあるため。
動作確認そのものは・・
MacOSX 10.6.8の
Google Chrome 18.0.1025.168
Safari 5.1.5
Firefox 11.0
WindowXPの
Google Chrome 18.0.1025.168 m
Firefox 12.0
について動作確認を実施してあります。

2012年5月3日木曜日

websocketについて再度やってみる。その6(windowsのchromeでの動作不良対処しました。)

Windowsのchromeで動作がおかしかった件、原因がわかったので、対処しておきました。



BTW, please take a look at the section 5.1 of RFC 6455.
   A server MUST NOT mask any frames that it sends to
   the client.


とあります。Serverからの送信データはMaskつけちゃだめだよという・・・

chromeのコンソールの部分にもA server must not mask any frames that it sends to the client.
ってでていましたね。

というわけで、webSocketプラグインの方もこれにあわせて修正しておきました。