2013年2月17日日曜日

いままでつくったHttpLiveStreaming(iPhone用)のまとめ

 rtmpをつかったストリーミングサービスを会社で立ち上げ。その後、スマートフォンにも対応するかという話になったところから、iPhone用のストリーミングをつくるということに注力してきました。
 おかげで動画、音声コンテナについてかなり詳しくなったし、いろいろわかるようになってきました。その振り返りを残しておきたいとおもいます。

第0号:mp4の分割ファイルを順につくってhtml5のvideoタグで再生させるやり方。
 →一番はじめに上から提案されたやりかた。
ライブストリーミングとはいえないし、httpLiveStreamingという方式があることがわかったので見送り。

第1号:wowzaのnative変換、ここからhttpLiveStreamingを使い始める
 →どういうものかとりあえず触ってみたもの。
以下の弱点があった。
1:h264 + mp3もしくはaacのコーデックに適合しないとダメ
2:wowzaのプログラムとしてアクセス制御ができない。
3:安定性微妙。

注意:最新のwowzaには変換動作がついているので、そちらを使えば問題ない可能性あり

第2号:rtmpdump + ffmpeg + segmenter
 radikoの音声をこの3つの組み合わせで流しているという記事をみつけた
http://nrglog.net/blog/archives/2010/04/radiko-iphone.html
この記事だったかは不明、まぁでも似たないようだった。
動作させてみたところ、おおむねだいたいのチャンネルの放送が視聴できた。

第3号:(変換セット) + ftpで転送 + apacheで提供
 会社でftpとapacheによる提供プログラムを書いた。
単に生成ファイルをcronで拾ってftpで転送。
apacheをつかってユーザーにm3u8とtsを提供することで動作。
確実にコンバートできる自信はなかったため、コンバートできている部屋のみ提供という形だった。
 初の実践投入

第4号:rtmpClient もしくは flazr + ffmpeg + segmenter
 red5のrtmpClientの動作をライブストリームを別のサーバーに移して多人数配信させるという動作を別途研究していたので、その研究の流用。
放送者の接続、切断、リロードその他のコントロールをうまくしたかったため実験する。

第5号:flazr + avconv + segmenter
 特定のコーデックの場合変換起動をffmpegが実行できないバグがあったため、いろんなバージョンのffmpegとavconvを試してみた。結果としてavconvを選択。
rtmpのダウンロード動作はflazr一択になる。

第6号:flazr + avconv + segmenter(改造)
 3号の時点でリアルタイム処理にならなかったため、systemコマンド(だったと思う。)をつかってsegment作成時にphpをキックし、すぐにアップロードする形にした。
 実践投入第二弾

第7号:flazr + xuggle
 segmenterの吐き出すファイルをみていたところ、どうも出力がよろしくない感じがしたため、変換まわりにも口をだしたくなったため、ffmpegのjni実装であるxuggleに手をだす。
 こちらも4号と同じく、rtmpのビットレート変換やコーデック変換技術の研究の副産物。xuggle-xuggler-red5のプログラムをflazr化した。
 ちなみに出力がよくないとおもった理由は再生を開始したときに、音声だけの時間が微妙にできたり、すぐに開始しなかったりしたため。調査の結果として、どうやらmpegtsの出力ファイルの先頭に動画のキーフレームがきていないっぽいことがわかる。
 xuggleのファイル出力時に変換後データのタイプとフレーム情報がわかるので、擬似的にキーフレームを選択して、分割候補にすることに成功した。
(このときにはmpegtsのファイルの内部構造にまでは口をだしてなかった。)

第8号:flazr + xuggle(マルチ出力)
 xuggleの出力コンテナをいくつか準備することで変換自体は1度しかおこなわないが、いろんなコンテナのファイルを出力できるというものを書いてみた。
https://github.com/taktod/streaming/tree/mediastreaming
いろいろ拡張したところこうなった。
動作はそこそこだが、ここでいくつか問題があることがわかった。
 xuggleの変換だと、3つほど同時に変換を実行したら急に動作がわるくなった。
よってxuggle-xuggler-red5みたいにアクセスのあるストリームを個別に変換するという処理はありえなくなった。(1プロセスで複数変換を走らせることになるため)
また、プロセスを別にしても動作の改善がみられなかったのでJNIとマルチプロセスの相性がわるいような気がしました。
 大量のコンテナ出力を実行したところやはり負荷があがった。
どうやらコンテナ変換動作もあまり得意ではなさそう。
(rtmp inputでflv中低、mpegts中低)という出力をやってみたところ動作が芳しくなくなった。
 遅延を抑制するために、javaのプログラムをマルチスレッド化して動作するようにしてみたんですが、今度はxuggleの動作が微妙になってきた。(コンバートがおかしくなったりもした。)ここでだいぶxuggle熱が下がりました。
 ちなみにwebM出力をつくるためstream-mとmatroskaのドキュメントを解析し、webMについて詳しくなった。

第9号:flazr + avconv + jsegmenter
 変換動作をいろいろいじってみたところ結果的にavconvやffmpegをそのままプロセスとして実行してもらった方がコストがいいことがわかりました。
 仕方ないのでmpegtsの分割を自力でやってみることにしました。で、mpegtsがどういうものであるか学習していった。副産物としてmp3もHttpLiveStreamingでながせることがわかった。で、mp3がどういうものであるかも学習した。
その結果:
1:キーフレームで分割できるようになった。
2:mpegts出力ファイルのtimepositionがわかるようになった。
3:mp3でもHttpLiveStreamingできることがわかった。(appleとしては非公式サポートフォーマットかもしれませんね。)

https://github.com/taktod/jsegmenter
で、こんなものができた。
まぁ、mediaStreamingでつかっていたパケット解析の部分の流用であります。

HttpLiveStreamingを「変換して生成する」場合にはキーフレームの挿入タイミングをffmpegのコマンドで調整して、生成mpegtsをキーフレームごとに分割して配置するということをやると一番ライブストリームに適合した動作になることがわかりました。
また、mpegts出力側のtimepositionがわかるので、ffmpegの変換遅延とCPUの利用状況を確認できるようになりました。

このころのプログラムでは、この3つのプログラムをパイプでつないで動作させてました。入力データのcodecやフレーム状況がかわった場合にプロセスごと作成しなおすという動作をやっていました。

第10号:flazr + avconv
 第9号の動作をつくったあとに、まだたまに変換動作がうまくいかない放送があることに気づきました。問題点は以下2点
1:プロセスの作成停止を繰り返すのはサーバーにやさしくない。(ゾンビプロセス等ができる可能性が高い。)
2:rtmpの入力データは放送している環境に依存して、ffmpegが再生プレーヤーで扱いにくいものになる可能性がある。またそのおかげでコンバート待ちが発生したりする。

 まず1点目、6号でsegmenterからsegment作成時にphpをキックするという動作を書いていましたが、このやり方だと、大量のプロセスが使い捨てになります。また中途で放送がとまった場合や、動作開始後一定時間音声しかこない場合は、あきらめて音声のみのコンバートにまわすなど、いろいろな要素でなにかある度にプロセスを立ち上げ直すという動作を実行していました。
 →結果としてたまにCPUを占領してしまうプロセスができたりします。
 CPUのおおいサーバーをつかっている場合はこのままでも問題ないのですが、CPUがそれほどない場合は結構致命的になります。
 この対策として、10号では、flazrのプロセスは基本的に止めない形にしました。
flazrのプロセスが転送データの状態を確認して、必要があれば、avconvのプロセスをProcessBuilderで制御します。

 次に2点目、入力データがよろしくないとコンバートに影響がでる件。
まずは、silenceLevel問題
http://livedocs.adobe.com/flash/9.0_jp/ActionScriptLangRefV3/flash/media/Microphone.html#setSilenceLevel()
途中で入力音量が小さくなったら音がないと判定する件。余計な音を拾わないので放送する側にとってはあると助かる機能ですが、ffmpegにとってはあってもらったらこまる機能になります。というのも音声パケットが欠損するとffmpegのコンバートは停止してしまいます。停止するということは、視聴が止まるしどんどん実際の放送との遅れがどんどんのびていきます。
 続いて映像がない場合の問題。rtmpでライブストリームしているときにUSBカメラを物理的に抜いてしまうと映像側の転送データがなくなります。こうなると音声同様コンバートが止まる訳です。さらにsegmentの作成方法が基本映像データのキーフレームにしてあるので、そもそもsegmentができない(もしくは切れずに大きなものになっていく。)という状況になります。
というわけでこちらも対処してあります。
以前の記事で何回か無音mp3をつくるという記事をつくっていますが、flvは構造が単純なため、無音mp3や適当な動画データを織り込んでやることが可能です。

 10号では、さらにリアルタイム性の確認方法も織り込んであります。

というわけで10号は結果的に次のようになっています。
1:コンバート安定化対策
 ・音声がない場合は無音mp3を挿入する。
 ・映像がない場合は代替flvを挿入する。
 ・音声が中途で欠損した場合は最終音声を1秒ごとに挿入してコンバートが中途でとまらないようにしてある。
 ・コーデック情報を常に監視して変更した場合は速やかにコンバート書け直しを実施する。
2:動作安定化対策
 ・segmenterやアップロードすべてFlazrにやらせることでプロセスはすくなくしておく。
 ・変換入力データと出力データのtimestampを把握することで変換効率をリアルタイム監視
 ・生成データの転送効率も監視しておく。
 ・プロセス管理の方法をredis等外部kvsに依存させることで他のプロセスで動作レジュームが可能にした。
 ・復旧までにかかるタイミングを2秒以内に設定
という形に固まりました。

で、この魔改造を施したシステムですが、実際にはこうなりました。
https://github.com/taktod/TakStreaming2/commits/master
また、入力するデータにまで口を出せば遅延10秒以内はいけるみたいです。

実践投入の方も去年の終わりぎりぎりに会社のシステムに織り込めて、売り上げもあがりました。
(注:ちなみにコンバート安定化対策はそのままffmpegのコンバートコスト低減対策になります。)

で次の予定ですが、
第11号:flazr + avconv(予定)
 10号で音声が欠如したときに、最終音声をいれてコンバートを促すという手法をとったわけですが、この方法、最終音声が無音ではなかった場合、その音が繰り返されるという問題があります。今のところ無音mp3については研究してあるので、他の無音speexとかをつくれば同じことができるのですが、いろんなコーデックが選択可能だし、bitrateとかの兼ね合いもあるので、その辺りどうすべきか調査して折り込みたいところです。一応、無パケットメディアタグというのがあるみたいなので、そちらをいれて動作するなら、すごくうれしいですね。簡単だし。
 あとは、再変換要求すべき状態を見極めて、プロセスの再起動が必要ないところがあれば、再起動せず、あたらしいメディアデータを注入したいところ。
 https://github.com/taktod/stdinFlazr
こちらでrtmpによるpublish操作を書いてみましたが、変換したデータをサーバーにあげ直すという動作もやってみたいところです。wowzaでコンバート動作がサポートされてるし、ffmpegで入力出力両方にrtmpdumpプラグインを指定できるので、なくてもよさそうですが、前者はコストが高い、後者は接続の安定性が微妙という問題があるので、まぁ意味はありそうです。

 MusicTubeの件でmp4の音声部分のみ抜き出しプログラムを書いていますがmp4のmoofというボックスを使えば、再生可能端末が限られますがmp4でもliveStreamingできるみたいですね。また、webMのストリーミングも同じ手法でできるみたいです。
このあたりのことにも最近は興味あります。webMとmp4とm3u8でライブストリーミングできるようにしておけば、html5だけでライブストリーミングできるようになると思いますね。

 なお、技術協力ほしいという方おりましたら、連絡いただけるとうれしいです。
上記にあげている内容はほぼすべてこのブログとgithubにあげてますが、細かい微妙な部分での差異等は伝えきれていないと思っています。
 その辺りどういう情報が必要なのかわかりましたら、ブログの記事にしてどんどん情報公開していきたいですね。

2013年2月11日月曜日

MusicTubeというwebアプリをつくってみました。

先日mp4から映像をOFFにするという記事を書いたわけですが、それを応用したjettyで書いたプロキシーサーバーと適当なwebアプリを準備してみました。

このwebアプリを使えばyoutubeのデータをiOS6.1の端末でもBGMとして流しつつ別のアプリに移動することができます。

http://taktodtools.appspot.com/musicTube/index.html?#main
iOS 6.1以降のiPhoneで音楽を聴くためにつくったわけですが、上記のようにChromeやSafariでも動作し、ちゃんと音楽聴けるみたいです。

まずは使い方
検索ボックスに適当な検索キーワードをいれて、実行するとyoutubeの検索を実施して、結果が表示されます。
ここでSearchのボタンをクリックすれば検索されます。
結果はこんな感じ。この状態で左の画像かタイトルをタップすると音楽の再生がはじまります。


右の(+)をタップするとマイリストに登録され、次回からは検索しなくても再生することができます。

また、右上の[三]みたいなところをタップすると左にメニューを出すことができます。
メニューでは、上から順にいくつか項目があります。

musicTube
・home:はじめの画面および、検索結果画面に移動します。
・[search youtube...]:youtubeの動画検索を実施する簡易検索ボックス
・my list:検索結果から、リストを保存した場合のリストを参照できます。
playing
・:再生中のタイトルが表示されます。
setting
・---:リピートをするかのフラグです。---リピートしない、repeat終わったらサイド再生する。
・one track:再生の方式を変更します。one trackその曲のみ流す all track my list上の全曲を流す。
・clear my list:my listのデータを削除します。
・skin:webアプリのスキンを変更します。
information
・version:現在のプレーヤーのバージョンです。
・contact author:作者へのコンタクト情報です。

スキンを変更するとこんな感じになります。

MyListでは(ー)で指定トラックの削除 ▲▼でトラックの入れ替えが実行できます。

このように順番を変えたり、必要のないトラックを削除できます。

iPhoneの下のところにあるコントローラーでも
再生、停止と次のトラックへ移動がコントロールできます。

こんな感じです。
まだちょっとバグがあるみたいで、(主にjqmobiのバグ)、左のメニューでmyListを選択したのに、home側に強制的に移動してしまったりすることがあるみたいです。そのときは、ブラウザのリロードを実行してみてください。

とりあえずは、こんなところです。
ではでは

2013年2月9日土曜日

とりあえずそれっぽいjetty開発環境ができた。

jettyの開発をeclipse上でうまい感じにする方法をさがしていたところ次のようになりました。

まず、m2eclipseをインストールします。

新規作成から、その他を選択して、wizardの選択肢から、Maven Projectを選択します。

次の選択はプロジェクトをどこに置くかなので、そのまま進みます。

次の選択でprojectのテンプレートを選択します。
とりあえずjetty用のwebappを作りたいので一番デフォルトと思われる
maven-archetype-webappを選択します。

続いてプロジェクトの詳細を入力していきます。
GroupIDは出力ファイルに直結するみたいです。

とりあえずプロジェクトができました。

必要なものを追加します。
まず、servletを書くのでservlet用の依存関係を追加します。


つづいて、pluginとしてjettyを追加 eclipse側の9.0.0を追加したかったんですが、java7必須っぽいので、mortbayのjetty8でいってみることにします。

とりあえずコンパイル起動してみたいので、Run Configurationをいじります。
Maven Buildに新規追加します。

で実行する。
ダウンロードがいくつか進んだりしてから、jettyサーバーが起動します。

アクセスするとこうなります。
これで一応開発できるんですが、ちょっとだけpom.xmlをいじっておきます。
設定を追加しておきました。
こうしておくと、自動的にwebAppのプログラムを書き換えたら、読み込み直しが走るようにできるみたいです。

では、servletのプログラムでも書き書きしていこう。

2013年2月7日木曜日

ffmpegやavconvのパフォーマンス改善案ってどんなのあったっけ?

 ffmpegやavconvは動画を扱うサイトなら、だいたいつかっているopenSource界の重鎮です。で、会社でこのコンバートまわりのチューニングする必要がでてきそうなので、その前にいままでを振り返ってなにができるのか、ブログにまとめておきたいと思います。いままでも何回か書いたことあるので、重複にはなりますが、まぁ自分用のまとめってことで・・・
 僕の領分はrtmpとflv、mp4、mp3、mpegts、mkvこのあたりです。あとはmidiってのも扱ったことがあります。

 つづいてターゲットとなるソース。rtmpに流れているデータが対象になります。特徴としては、rtmp→flvの変換は非常に容易になっていること。ただし、flvはtimestampが絶対値で書かれているのに対し、rtmpでは相対かつ映像と音声で扱いが独立していること。
中途で片方のメディアデータが欠損することも別のコーデックや設定に切り替わることも許可していること。こんな感じです。
 出力周りの要件としては、mp4、webm(mkv)、flv、httpLiveStreaming(mpegts、mp3)といったところです。
 動作の要件としては、httpLiveStreamingに変換してライブで映像を流すなら、なるべくリアルタイムな動作ができるように調整する必要あるし、同じhttpLiveStreamingにするとしてもVOD用のファイルをつくるなら、セグメントの大きさとかに注意して帯域が乱高下するようなデバイスでも必要なデータバッファが保持できるような構成にするとか考慮できます。

 で、パフォーマンスをあげましょう。
1:flvデータを事前に修正してやったら負荷が下がる。
データの欠損や並び順がおかしいことがあるのでcodec copyで一度変換に通してやることでそのあたりの修正をいれることが可能です。
flvtoolをつかったり、codec copyをつかった変換プログラムに通したり、自力で修正したりすればいいです。
codec copyを使う場合の注意ですが、できたらコンテナ変換した方が修正できる可能性があがります。(そうでない場合は中途で変換がとまったりする。)
自力で修正する件に関してはやった実績があります。

2:pthreadを有効にせず負荷をさげる。
pthreadを使うと、マルチスレッド動作になってパフォーマンスがあがります。
言い換えると、マルチスレッドになる分、処理の間にthreadごとの動作を調停するための処理が追加されたりするわけです。
手持ちの動画を最高の画質でコンバートするという要件だったら、つけておくと高速化できていいわけですが、1つのサーバーで複数のデータの変換を実施するみたいなことをやる場合はむしろ余計な処理が増えて邪魔です。
なので、pthreadを抜いてffmpegを使うのも「あり」だと思っています。
ただし、目立った改善を目にしたことはありません。xuggleの動作で元々はいっているffmpeg動作側にpthreadはいっているみたいですが、むしろコレが足を引っ張っていたことがあります。

3:ffmpegのパラメーターの順番をかえることで負荷を下げる。
ffmpegのパラメーター配置では、変換の開始位置を変更したり、入力ソースのフォーマットをあらかじめ指定したりすることで、動作効率をあげることができます。
-ssパラメーターをいじるのが一番わかりやすい例だと思います。

こんなもんだっけ?なんかもっとあった気がするんだが・・・思い出したら追記していこう。

2013年2月6日水曜日

jettyやってみる。

jettyに手をだしてみます。

基本的には、red5と同じtomcat系列のサーバープログラムみたいです。
http://www.eclipse.org/jetty/
ここからDownloads - jetty downloads - stable 9のdownloadで
http://download.eclipse.org/jetty/stable-9/dist/
ここまでたどり着き、tar.gzのデータをdownloadしました。

解凍したら、webappsがあってred5と似た感じだなとおもいつつ
$ java -jar start.jar
で実行

http://localhost:8080/でトップにアクセスできて、とりあえず問題なしに動作することがわかりました。

で、eclipseで簡単に起動テストしたいので、やってみました。
http://chitan.hateblo.jp/entry/2012/06/30/150604
このサイトによるとjetty wtpというのがつかえるみたいです。
http://download.eclipse.org/jetty/updates/jetty-wtp
このアドレスをinstall softwareの部分で指定すればいいみたいですね。
で、いつものようにDLします。

すると、新規作成のその他の項目のserverを選択すると、jettyの項目が増えています。


こんな感じ。

v9のserverはないみたいなので、とりあえず、v8.1でいってみたいと思います。
http://download.eclipse.org/jetty/stable-8/dist/
こちらからtar.gzのデータをダウンロードし、解凍します。

あとは起動時のサーバー指定の部分をいじります。
RuntimeEnvironment:という部分があるのでクリックして
 JettyServerの設定の部分で先ほどダウンロードして解凍したJetty v8.1を指定します。
 起動したらこんな感じで動作します。
これでおしまい。あとは、webappを書いていけばよいはず。さぁ、やってくか。

mavenに手をだしてみる、その2eclipseでやる。

とりあえずコマンドラインのmavenをやってみたわけですが、eclipseで開発しているので、そちらでもやりたいわけです。

というわけで入手してみます。
調べてみたところよさそうなのは、m2eclipse
http://www.eclipse.org/m2e/download/
こちらのサイトによるとeclipseのhelp - Install new softwareで設定するURLが指定されていますね。
http://download.eclipse.org/technology/m2e/releases
あとはこれを設定して、ソフトウェアを入手するだけ。

再起動が要求されるので、再起動を実施。
その後新規プロジェクトでmavenのプロジェクトが選択できるようになりました。

つくったら、mvnコマンドでつくったのと同じような感じになりました。
依存関係のライブラリが自動ではいったりするところがすてきですね。

さて、ではこれでjettyを取り込んでプロキシープログラムをさくっとつくっちゃおう。

mavenに手をだしてみる。

red5もmavenをつかうとかいうフォーラム上の話あったし、jettyを使うにもmavenいるっぽいみたいなこと書いてあるので、とりあえずmavenというものにさわってみようと思う。

とりあえず、いままでプログラムを取得するときにつかったことがあるのでmvnのプログラム事態はすでにインストール済みでした。

http://www.limy.org/program/java/maven/first.html
とりあえず、このmaven事始めというのをみつつ、コマンドをいくつか動かしてみた。


$ mvn archetype:create -DgroupId=com.ttProject.app -DartifactId=my-app
とりあえずこれでプロジェクトが生成されるらしいので、なにも考えずに実行してみた。
たしかに、いろいろとDLが進んでいくつかのファイルが生成されました。

これを実行するとmy-appというディレクトリに適当なプロジェクトのテンプレートができるみたいですね。

$ cd my-app
my-appに移動すると、中にはsrcとpom.xmlがありました。
あとはpom.xmlの中身をいじっていけばいいのか?よくわからないんですが
とりあえずコンパイルいってみます。

$ mvn compile
実行すると
targetというのができました。
中身はclassがはいっているのでjavaのclassファイルが生成されたみたいです。

$ mvn package
パッケージ化が進むみたいです。snapshotのjarファイルとかその他いろいろ

$ mvn test
これでjunitのテストができるみたいです。

とりあえずmavenというのは、デフォルトのプロジェクトをつくるのに使うっぽいですね。
いままでmvnでデータを取り込んでくれ的なサイトがいくつかあって、xmlの記述がpom.xmlの一部になっていて、どうやってつかうのかよくわからなかったんですが、これでちょっとわかった気がしました。

とりあえず、xuggleからデータをパチってきてみます。

なんのこっちゃ分けわからんとおもっていました。

先ほどつくったmvnのテンプレートプロジェクトのpom.xmlに記述を増やしました。
どんな感じになったかというと、こんな感じ

で次のコマンドを実行。
$ mvn dependency:copy-dependencies
するとDLがはじまってガンガンデータが落ちてきました。


どこにデータがどんな風に追加されたのかはちょっと不明ですが、とりあえずこうなった。
xuggle.comのDLのところにあったmavenの説明ってこういうことだったんですね。