2012年11月25日日曜日

red5コンパクトにしてみようか・・・

red5の1.0.0RC2をダウンロードした・・・
42Mもありますね。サイズでかすぎる。こんなにいらないよ。
というわけでコンパクトにしてみようと思います。
正直必要のないライブラリが入りすぎてると思うんですよね。

というわけで、スリム化させてみたいと思います。
目標
・サイズをちっちゃくする。
・最低配信と視聴はできるようにして、独自アプリケーションも動作するようにする。
・jrubyだのgroovyだのといったいらないライブラリは削除
・rtmp以外のプロトコルもいらないので削除
・デフォルトアプリケーションもいらないものはどんどん削除
・tomcatの動作もいらないので削除

といったところでしょうか。

やったこと1:
tar.gzバージョンをダウンロードして、解凍。
red5.shを起動して動作確認。
→当然OK

やったこと2:
いらないアプリケーション削除
とりあえずliveだけあればよし。
vodとinstallerは削除した。

やったこと3:
docいらね。
削除したら71M→41Mになった。

やったこと4:
red5-highperf
red5-debug
red5-shutdown
はいらないので削除

やったこと5:
Makefile削除build.xmlも削除
makeを実行すると、/usr/lib/red5にインストールするらしい。ちょっと・・・

やったこと6:
webapps/root/の中のhtmlファイル特にdemosを削除
demos/publisher.htmlは便利ですが・・・とりあえず容量を削減したいので、削除
41M→38Mになった。

やったこと7:
conf/red5-core.xmlからrtmp以外の設定を排除

やったこと8:
jrubyとjythonのライブラリを削除
27Mまで落ちた。

やったこと9:
tar.gzに圧縮した
24Mになった。

というわけで24Mまで削減してみた。
http://49.212.39.17/red5-1.0.0-min1.tar.gz

うーん。普通の人に配布するなら、やっぱきついな。

2012年11月23日金曜日

Flazrの配信をいじってみたいと思う。

では、実際に改造していくことにします。

とりあえず、ReadableByteChannelをつかってSystem.inのデータを取得していくというプログラムをつくるのだが、はじめからSystem.inをつかっているとデバッグがしにくいのでとりあえず、FileChannel経由でファイルのデータを取得するプログラムをつくることにします。

もともと、ファイルの時間を指定してデータを抜き出すことができなくなるので心配していたFlvLiveReader.next()の動作も、LInkedBlockingQueue.take()をつかってデータが到達するまで処理を止めることにしました。

https://github.com/taktod/stdinFlazr/commit/a429295bbaf0b01c8f0e1aeeb659849f93989a5c
で、できたのがこちらのコード
いまのところrtmpPublisherをいじったりはしていないので、ClientHandlerEx、ClientPipelineFactoryEx、RtmpPublisherEx等は利用していない形になっています。

実際に放送を流してみたところ、たまーにちょっとwaitが入る感じになりました。
たぶんLinkedBlockingQueueによるwait時間分とrtmpPublisherが管理している再生時間とのずれがおこって、そのデータ待ち分だけ中途でとまるといったことがおこるのでしょう。

んで、FileChannel経由をやめて、System.inから取り出すプログラムも組んでみた。
https://github.com/taktod/stdinFlazr/commit/385ea597bdc7849f02ab8e42258c060fc5c20a3a

実際にflvファイルをcatしてからpipeでつないで放送させてみたところきちんと動作しました。

僕としては、これで一段落したつもりです。
あとやることとしては
・役割ごとに動作を変更していって、クラスの整備をする。
・実際にリアルタイム出力をつかってながせるかやってみる。
・rtmpPublisherをいじって動作を安定させる
ということがあるのですが、とりあえずパイプ接続のデータをrtmpでpublishするという件の調査はできたので、もういいかなと思っています。

Flazrの配信をいじってみたいと思う。

では実際にプログラムをつくっていきます。

まず、com.ttProject.flazrにFlvReader、FileChannelReader、RtmpPublisherに対応したクラスを準備してやる必要があります。
com.ttProject.flazr.io.SydinChannelReader
com.ttProject.flazr.io.flv.FlvLiveReader
を作成しました。
RtmpClientのコネクト前のところで、独自のクラスに組み替えが可能です。
とりあえず-が入力されたときに動作を変更できるように改造
https://github.com/taktod/stdinFlazr/commit/b74bc103de4a43e99337f04cc6fab24e69af11d1


んで、独自クラスも追加ただし中身はからっぽ

つづいて、FlvLiveReaderの中身をFlvReaderの丸コピーにして、こちらのクラスを元に動作させてみたいと思います。

09:59:59,177 [main] INFO [FlvReader] - FlvLiveReader
コンストラクタのところに仕込んでおいたログ出力がきちんとでてきたのを確認した。

ここでRtmpPublisherを書き換えたい場合は、ClientHandlerも変更しなければいかなさそうな感じになってきた・・・やだなぁ
RtmpPublisherはどこで使われている?→ClientHandler
ClientHandlerはどこで使われている?→ClientPipelineFactory
ClientPipelineFactoryはどこで使われている?→RtmpClient
という図式になるので、3つクラスを追加
とりあえず変更を大量に実施して、RtmpPublisherExという独自クラスがつかわれるように改造。

10:24:23,203 [New I/O client worker #1-1] INFO [RtmpPublisherEx] - RtmpPublisherExがよばれたよん。
コンストラクタでログを出力するように変更して、呼ばれていることを確認。
もちろん実際にサーバーにデータをおくって、動作していることも確認。
これで元のFlazrに迷惑をかけずに、独自に動作を改造する下準備はおわり。



Flazrの配信をいじってみたいと思う。

前回に引き続き、Flazrの配信をいじります。
これができれば標準入力をベースにrtmpにpushできるので他のプログラムからpipeでつなぐこともできる上に、xuggle等で変換したデータをそのまま別のサーバーにpushすることもできます。

前回もかきましたがrtmpdumpとか使えばすでにできます。
でもFlazrを使えば内部のデータとかやり取りの方法(放送がとまったときの細かいコントロールとか)ができるようになります。

とりあえず、eclipseにプロジェクトとして取り込みます。
Flazrはこんな感じ。ライブラリのパスもきちんと通しておきました。src/main/javaにある。
com.flazr.rtmp.client.RtmpClientを実行すればプログラムが動作します。
オリジナルプロジェクトはこんな感じ。とりあえず、flazrのlibディレクトリの中身のライブラリを参照として取得し、自分のパッケージをつくりました。
com.ttProject.flazr.RtmpClientを実行すれば動作するというものにします。







ここでいったんgithubにプロジェクトをつくって、登録しておきます。

とりあえずは動作のどういう動作をするか確認していきます。
大本のflazrのプログラムに手をくわえてどういう動作をするか確認していきます。
logger.info("")で適当なメッセージをいれていくと・・・
対象は
com.flazr.io.flv.FlvReader
com.flazr.io.FileChannelReader
com.flazr.rtmp.RtmpPublisher
このあたりです。
というわけでFlvReaderの各関数のあたまにlogger.info("関数名");
のログをはさんでいきます。
んで、実行

09:33:42,584 [Hashed wheel timer #1] INFO [RtmpPublisher] - here...
09:33:42,584 [Hashed wheel timer #1] INFO [FlvReader] - hasNext
09:33:42,584 [Hashed wheel timer #1] INFO [FlvReader] - next
09:33:42,584 [Hashed wheel timer #1] INFO [FlvReader] - setAggregateDuration0
09:33:42,585 [New I/O client worker #1-1] INFO [RtmpPublisher] - fireNext1:100
09:33:42,585 [New I/O client worker #1-1] INFO [RtmpPublisher] - here...
09:33:42,585 [New I/O client worker #1-1] INFO [FlvReader] - hasNext
09:33:42,585 [New I/O client worker #1-1] INFO [FlvReader] - next
09:33:42,585 [New I/O client worker #1-1] INFO [FlvReader] - setAggregateDuration0
09:33:42,585 [New I/O client worker #1-1] INFO [RtmpPublisher] - fireNext77:100
09:33:42,585 [New I/O client worker #1-1] INFO [RtmpPublisher] - here...

こんなログが大量にでてきました。
どうやらタイマーである程度時間をスキップさせて、その間の命令を送信しているという感じですね。

注意しないといけない部分は、FlvReaderの動作の内部にデータのシークまわりの記述があります。
標準入力でうけとったデータをリアルタイムにストリーミングするので、ファイルのシークはできないことになります。
よってこの部分の処理の変更が必要になると予想されます。
また、RtmpPublisherで次の命令の発行を実行しているみたいですが、どうやらFlvReaderからrtmpMessageが取得できない場合は、ファイルが終了したと判定して止まってしまうようになっているようです。
標準入力のデータはなんらかの問題でデータが送られてこなくなるということも想定されますので、そのあたり注意しておかないといけないと思われます。
FileChannelReaderに関してはfileのByteBufferのデータをChannelBufferに変換してFlvReaderの読み込みの補助をやっているみたいなんですが、この部分もSystem.inに対応したなにかしらをつくらないとだめですね。

というわけでだいたいどこを変更すべきかという見当はついたつもりです。


Flazrの配信をいじってみたいと思う。

僕にとって結構ひいきにしているプログラムがあります。
Flazrというrtmpのjava実装です。

rtmp関連の経緯は次のような感じです。
3年前くらい。
会社でストリーミングサービスをやるのに、fms wowza red5の動作検証中にred5のrtmpClientという実装をみつける。
とりあえずred5同士でストリームの中継ができることがわかったんで、apache-mina実装のこのrtmpClientにハマってました。

ところがこのrtmpClient、fms4.5(だったか?)からFlashMediaServerに接続できなくなるという問題点を発見。またストリーミングではなくファイルデータをベースにしてrtmpサーバーにメディア転送を実施するとはやおくりみたいなことが発生することもわかりちょっとへこみました。

そんなときにこのFlazrをみつけましたね。
こちらは早送りも発生せず、FlashMediaServerにも無事に接続可能。jbossのnettyベースだったので乗り換えるのに手間取ったけど、最近ではflazrのデータソースをxuggleに回す動作も問題なくできたし、と、なかなかの動作を見せてくれてます。

で、いままではFlazrをベースにrtmpサーバーからデータをひっぱってきて、コンバートかけてiOS用のストリーミングに変換したり、Flvに落とし込んだりしていたわけですが今回ちょっとrtmpサーバーに映像をpushすることに挑戦したいとおもっています。

Flazr
Flazrは2009年の年末に開発がとまったみたいです。
RC2のアーカイブデータが公開されていますが、僕はソースコードからコンパイルしなおしたデータを利用しています。(ソースコードも同梱されてたと思います。)
理由は、なぜかアーカイブのコンパイル済みデータが古くてちょっとバグがあるためです。

基本的には次のようなコマンドで利用します。
flvにダウンロードするとき
$ ./client.sh -host (サーバー) -app (アプリ名) (ストリーム名) (保存先ファイル名)
flvを放送するとき
$ ./client.sh -host (サーバー) -app (アプリ名) -live (ストリーム名) (配信元のflvファイル)
-liveの記述をわすれると配信元のデータが消えるのでその部分要注意です。

で、ファイルをベースにしたrtmpの配信はできるわけだが、今回やってみたいのはパイプで接続した標準入力をベースにrtmpの配信をするという件。
(ぶっちゃけるとrtmpdumpでも十分できるわけだが・・・)


2012年11月14日水曜日

複数m3u8から成立するHttpLiveStreamingのサンプルつくってみた。

せっかくjsegmenterつくってm3u8がつくれるようになったので、いっぱいつくってみました。

データはすべて以前もつかったことがある、スーパーマリオギャラクシー2のプロモーションビデオです。


高画質29.98fps
http://49.212.39.17/mario/mario.m3u8
中画質29.98fps
http://49.212.39.17/mario/middle.m3u8
低画質29.98fps
http://49.212.39.17/mario/low.m3u8
上記3つのうち最適なデータが選択されるやつ

http://49.212.39.17/mario/low_15.m3u8
上記3つのうち最適なデータが選択されるやつ

mp3の場合だけちょっと挙動が違います。アプリ切り替えてもとまりません。
あとは電車で移動中とか、画質の変化がどうなるか、あたり確認しておきたいところ。
なお、3G回線(AU)でたまに低画質15fpsでもきついときがあるみたいです。
動画はやっぱりしかたないのかな?

2012年11月10日土曜日

nioの実力

jsegmenterというjavaで書いたiOSのHttpLiveStreaming用のメディアデータ分割プログラムを書いていました。
https://github.com/taktod/jsegmenter

その中でffmpegの出力を直接パイプでうけとって処理をするというプログラムもいれていたのですが、どうも動作がダメダメでした。
MarioGalaxy2のプロモーション動画132秒をffmpegで変換してやった場合だいたい30秒強くらいで変換がおわります。

もともとSystem.inからbyte配列にデータを読み込む形でプログラムを書いていたのですが、これで動作するsegmenterをパイプでつないだところ、変換のクオリティーにもよるんですが、処理に150秒ほどかかる結果になりました。
ライブ映像をその場変換していたら、変換が追いつかない状態で遅延が酷くなる一方という悲しい結果になりました。
ソースはこんな感じです。
140行目あたりでSystem.inからbyte配列にデータを渡しています。

調べてみたところどうやらSystem.inから直接byteデータを読み込む形だと動作が遅いみたいです。

で、nio(new io)を使うように変更してみました。
ソースはこんな感じになりました。

130行目あたりで、ReadableByteChannelからByteBufferの形でデータを抜き出してます。


変換させてみたところ、32.5秒・・・
パイプなしでコンバートだけやったときには、31.6秒ほどで同じ変換がおわったので、めちゃくちゃ速くなりましたね・・・

xuggleの内部処理でffmpegとデータをやりとりするところとか、byte配列でやり取りしてるので、こういう部分全部nioに書き換えてやれば、いろいろと動作が良くなるのかな?とおもいました。

nioのファンになりそうw