2013年11月28日木曜日

javaをつかったコンバートプログラムを書いていました。

表題どおり、javaで動くメディアデータの変換プログラムを書いていました。
といっても、pureJavaで書いているわけではなく、ffmpegとかつかって変換させるというものです。

概念的には次の図のようなやつです。

動画のデータのUnitを入れると、変換した後のデータがでてくるというもの。
xuggleを使う場合はこんな感じ。テストコード
avconv(ffmpeg)を使う場合はこんな感じ。テストコード
となりました。

やってることは同じで
1:TranscodeManagerを宣言。
2:MediaUnitを解釈するためのプログラム設置
xuggleならpacketizer、ffmpegならdeunitizerとunitizer
3:TrackManagerをTranscodeManagerに作らせる
4:TrackListenerをくっつけて出力を拾わせる。
となってます。

これをうまくつかって、高速、安定、字幕も使えて、複数トラック出力もできる、ライブ用のHttpLiveStreaming、はやいところつくってみたいですね。

2013年11月18日月曜日

jsegmenterのプログラム作り直しました。

最近myLib.chunk*というライブラリをつくりました。
というのも、いままでのmyLib.packetのmpegtsの切り方が気に入らなかったからです。

今日はその説明
ffmpegでmpegtsを出力すると音声の分割がbyte量単位になります。
よって無音部や音が弱い部分があると、音声の塊の長さが長くなることがあります。
また、音声と映像のデータがきちんと同期しているわけではありません。

概念図を書いてみます。
まずは定義、映像のキーフレーム、中間フレーム、音声のフレームの3つがあります。

本来のデータは次のようになります。

ファイルに格納されるデータ
これがffmpegが出力するファイルだと次のような感じになっています。(mpegtsの場合)

いままでの僕のプログラム(myLib.packet)での分割だと、単にキーフレームの位置のみをみて、分割しているので、次のようになっていました。
キーフレームをみつけたら、時間を確認して、必要な時間分経っていたらファイルを分割するという感じ。
これだと、1つ目は音声がない映像だけ、
2つ目は音声も映像もあるけど、映像のデータよりだいぶ前の音声データがはいっている形になっています。
実際によく使うマリオのプロモ動画ははじめの方で分割後のデータが音声抜けていたりしていました。

で、今回はこうした。(myLib.chunk)

これで1つ1つの分割単位が、その単位の再生に必要な動画の1セットと対応する音声データを保持する形になっています。
なお、この概念図は時間の長さのみを考慮しているので、実際の出力ファイルのサイズは乱高下します。

プログラム
https://github.com/taktod/jsegmenter/tree/new
利用しているライブラリは
https://github.com/taktod/myLib

一応型変換できるようになったので、flvやmp4、mkvのデータからコーデックさえ合えばHLS形式のデータ分割できるようにしたいですね。
一応下地はすでにつくってあるけど。

2013年11月16日土曜日

11/16すべきこと

誰かに見られてないとダレそうなのでブログに書いてみる。

とりあえずしたいこと。
・HLSのwebvttによるトラック動作の動作確認。
・rtmpのDL→xuggle変換→ULのプログラムをとりあえず完成させてどのくらいの早さがでるか見てみたい。
・HLSのシステムを作りたい。

とりあえずこの3つやりたい。
1つ目は会社での作業がかかわってくるのでやっときたいところ。
2、3は単なる趣味。まぁ、うまく動作したら会社でつかってもいいけど。

で、1つ目だ。
iOS6の後期からHttpLiveStreamingでwebvttを使った字幕動作が対応になっています。
具体的にiOS6の状態で動作するのを確認したことがないので個人的にはiOS7から対応と見ています。

わかっていること
サンプル:http://49.212.39.17/mario/index.m3u8

■レベル1
・iOSの古いバージョンでも動作可能。ただし字幕は見れない。
・日本語等も使える。
・m3u8を複数組み合わせた状態でしか実行できない。
上記のサンプルでは、index.m3u8がsubPlaylist.m3u8とmario.m3u8を内包している形になっている。

■レベル2(つかってみてわかったこと)
・字幕トラックの時間がかぶるのはあまりよろしくない。
00:00:07.500 --> 00:00:08.500 line:100% align:left position:0%
hello

00:00:08.000 --> 00:00:08.500 line:100% align:left position:0%

こんにちは

こういう字幕をつくると、互いの字幕が干渉する。

00:00:07.500 --> 00:00:08.000 line:100% align:left position:0%
hello

00:00:08.000 --> 00:00:08.500 line:100% align:left position:0%
hello

こんにちは

こうやった方が綺麗に字幕がでる。
干渉しすぎると字幕が極端に小さくなって見えなくなったりする。

・内部m3u8でエラーが発生したら動画が止まる。(一度でもm3u8を応答した場合)
→動画が読み込めませんでした。とでて前のページに戻されます。

・黒画面のまま固まる。(初出力の前に内部でredirectとかした場合)
→アクセス権限の問題でhttpのredirect等をやっているときで内部のm3u8でredirectやってしまうと、そのredirectは実行されますが、動画ではないのでそのまま表示待ちになります。

■知りたいこと
・字幕の出現時間をmpegtsのpcrに合わせるといい感じに動作するけど、どうやら
EXT-X-DISCONTINUITYを挟んで別の動画に切り替えると時間の概念がおかしくなるらしい。
字幕側もいったん切らないとだめなのか、pcrの時間ベースではなく、経過時間で時間を出力をしないとだめなのかがわからないところ。

・mpegtsのpcrはやく1日流し続けるとデータがoverflowしてカウンターが0にもどります。
この現象が発生したときにvttの記述をどうすべきか不明な点。

このあたり知っておきたい。
調査してなにかわかったら、この記事に追記しようと思う。

さぁ、今日もがんばろう。

■で、実際にやったこと。
とりあえず、rtypeDeltaのデータをmpegts化して動作させて、別トラックとして利用しようとおもいました。
で、avconvで変換しようとおもったら、動作しない・・・
仕方ないので、vlcで変換させてみました。aacに直接できなかったので、ac3にしてみた。(ac3のmpegts作成ははじめて)
変換してみたら、いつもと違うmpegtsができました。
違う点1:patやpmtのデータのデータがちょっと違う。
左がvlc出力、右がffmpeg出力(avconv)
vlcはヘッダ + adaptationField + PATデータになってますが
ffmpegはヘッダ + PATデータ + 埋めとなってます。

違う点2:pmtデータに見慣れないdescriptorがありました。
存在したのは、RegistrationDescriptorとISO639LanguageDescriptorの2つ
とりあえず解説しているサイトを参考に読み込みできるように調整。

違う点3:始めのデータが音声のみのpmtを出力していました。
途中から音声 + 映像のpmtに変わってました。
[このあたりは、vlcが再生データを変換していることに起因している気がします。(これは想像)]
このままだと、ffmpegにまわしたときに音声しか変換に拾われませんでした。
先頭部分の音声のみの部分を削ぎ落としたファイルをつくったら、音声 + 映像のデータとffmpegにも認識されました。

で、ライブラリも書き換え。
https://github.com/taktod/myLib
きちんと動作するようにしました。

本題に入る前に分割用のtsファイルを作るのに時間かかっちゃったw

■途中経過2
その1のhttpLiveStreamingの字幕入りのときに、中途で動画データが切り替わったらどうすればよい?というのに回答がでました。
(まぁ動作させた上での実験結果ですけど)

結論としては、字幕側のm3u8にも#EXT-X-DISCONTINUITYをいれること
字幕のtimestampも始めからやり直しにすること
この2点できればいけるみたいです。

サンプル:
字幕付きのストリーム
http://49.212.39.17/m3u8/index.m3u8
字幕のストリーム(中途切り替えあり)
http://49.212.39.17/m3u8/subPlaylist.m3u8
映像のみのストリーム(中途切り替えあり)
http://49.212.39.17/m3u8/movie.m3u8
マリオのデータ
http://49.212.39.17/m3u8/mario.m3u8
rtypeのデータ
http://49.212.39.17/m3u8/rtype.m3u8

字幕のやり方:
再生する。
右下にある字幕ボタンを押してcommentを選択
シークバーを巻き戻して視聴する
すると左下に秒数カウントがでてきます。

iPadの場合はフルスクリーンにしてからやらないと字幕選択ボタンがでてきません。

端末による動作確認結果:
iOS7のiphone4S、問題なし。
Nexus7 android、映像はみれるけど、字幕は選択できず。シークしようとするとこの動画を再生できませんとでてくる。

備考:EXT-X-ENDLISTをいれると強制的に始めからにできるけど、VODではなくライブ用の動作検証なので、今回はあえていれてません。

というわけで#EXT-X-DISCONTINUITYを適当に挿入してやればよかったという落ちでした。
よかったよかった。

2013年11月2日土曜日

hlsのbyteRangeアクセスが気になったのでちょっとつくってみた。

今日書いた記事
http://poepoemix.blogspot.jp/2013/11/hlsmpegts.html

これを書いているときに、そういえばbyteRangeアクセスのやつつくったことないなとおもったので、ちょっと作ってみた。

mp4バージョン
http://49.212.39.17/rtype.mp4
hlsバージョン
http://49.212.39.17/rtype.m3u8

内容は同じです。
資料によるとiOS5以降なら対応らしいですね。

どうなんだろう・・・ちょっと早いな程度ですかね。

ちなみに作り方。
1:hls分割をavconvにやらせる。

$ avconv -i rtype.mp4 -acodec copy -vcodec copy -bsf h264_mp4toannexb -start_number 1 -f hls -hls_time 10 rtype.m3u8

2:rtype0.ts、rtype1.ts・・・とデータが326個できたので、全部のdurationとサイズを確認するし、m3u8ファイルを作成する。
$ avconv -i rtype0.ts
$ ls -l

3:分割したファイルを結合する。
$ cat rtype0.ts > rtype.ts
$ cat rtype1.ts >> rtype.ts
$ cat rtype2.ts >> rtype.ts
・・・
めんどくさかったので2と3の処理はプログラムを書いてさくっと終わらせました。

4:適当なサーバーにデータをアップロードすれば出来上がり。

ではでは

hls用のmpegts作成プログラムが進んだのでメモしておきます。

はじめにぶっちゃけて置きますが、最近はffmpegが優秀になっているので、面倒なことしなくてもよかった気がします。
まぁ、勉強になって楽しかったですが・・・

---- まずは基本 ----

hls(HttpLiveStreaming)がなにか?
 appleのiPhone等、iOSでバイスで大きめの動画を再生させる場合、もしくはライブ映像を流す場合、httpLiveStreamingという方式を使うのが推奨されてます。
 一応mp4でも再生は可能なのですがmp4の場合はファイルの先頭部にmoovという再生時の位置情報やキーフレームがどのようにはいっているか等、いろいろな情報がいっきに詰まっています。1時間の動画とかになった場合は、これが大きすぎてダウンロードがうまくいかなかったり、動作しても再生までに時間がかかったりします。
 hlsを使うとテキストでできた再生用のリストファイルと再生の実体動画がわかれているので、必要な動画データからダウンロードさせることでサクッと動作します。
 最近では、androidやWiiUにも実装されている規格となります。

利点は?
 まずhttpのやり取りだけで成立するので、たいていの環境で動作します。
 サイズの大きいデータの場合は再生まで速いです。
 ライブ映像も扱うことが可能です。
 必要に応じて転送サイズを変更することも可能です。
 iOS6以降の場合はsubtitleを挿入したりすることも可能です。
欠点は?
 mpegtsベースなので転送データサイズが増えます。
 基本動作では、ファイルが大量にできるので、管理が大変です。

どうやって使うの?
 VODの場合はVLCプレーヤーでコンバートできたはずです。
 avconvを使う場合は出力タイプをhls、出力ファイル拡張子をm3u8にすれば作成されます。
例:$ avconv -i mario.flv -acodec copy -vcodec copy -f hls output.m3u8
として実行した場合、output[n].tsが大量にできて、output.m3u8が生成されます。
(与太話:最近のプログラムは優秀ですね。以前はrtmpdump + ffmpeg + segmenterとか組み合わせてつくっていました。精度もよくなかった・・・)

---- 続いて変わった使い方 ----

ファイルが大量にできるのはヤダ
 そういう人向けに1つのmpegtsファイルの部分アクセスを実行することで動作させることも可能になっています。
0byte〜
500byte〜
1000byte〜
みたいなアクセスを実行します。
httpの206アクセスが可能なサーバーでかつ、そこそこあたらしいiOS端末の場合は動作可能になります。
詳しくは仕様をみてください。

ライブを扱いたい
 ffmpegやavconvはpipelineでデータの入力を受け付けることが可能なので
rtmpdump -> ffmpegで出力したデータにhttp経由でアクセスする
みたいなことを実施すればライブ映像を扱うことができます。

回線速度に合わせて映像のクオリティーを前後させたい
 appleの提供しているsegmentCreatorを使えば確かできるはずです。が、使ったことはありません。
仕様によると
・重ねる映像の比率が一致すること(16:9と4:3をまぜるということは不可)
・音声データは一致すること
の2点を守ればOKとのことで、実際に作成したところ動作できました。
切り替えはシームレスで、あ、急に映像が悪くなった・・・というのがわかる感じでした。

subtitleをいれたい。
 本来はCEA608でしたっけ?mpegtsのデータにclosed captionを織り込む必要があったのですが、日本語は扱えませんでした。
ところがiOS6から(僕が確認したのはiOS7からですが・・・)webVttのm3u8をつくることで任意の言語のsubtitleを挿入することが可能になった模様です。
 詳しくはappleのドキュメントをみるか、僕にコンタクトとってください。そんなに難しいものではないです。
 なお、iphone4Sでデバッグしていますが、ちょっとバグがあるみたいです。

---- 本題 ----

さて、本題ですが最近このmpegtsの動作について研究していました。
というのもffmpegやavconvで吐くmpegtsデータの音声と映像の吐き方の部分で、いくつかの要件があったためです。
1:映像と音声の吐き方に関連性がないため、分割したときに、片方のデータが欠如することがあった。
2:分割したときに先頭に必要なトラック情報がなかったため、トラック情報のデータに当たるまで再生が開始されない問題があった。
3:nullパケットや無駄に挿入された追加情報があるためそれらのデータを削除したかった。

ちなみにできてからわかったのですが、1,2に関してはavconvにhls出力を実行させると解決したデータが吐き出されることがわかっています。

で、やってみてできたデータがこちらです。
http://49.212.39.17/mario2/index.m3u8
iphoneあたりでみてみると再生できます。

やっていることはデータを受け取ってからトラック情報、映像、音声にデータを分解
音声データは一番細かいフレームデータ(0.03秒ごと程度)に分解しておく。
映像のキーフレームからキーフレームの間隔にその間に配布べき音声データを適宜挿入しつつファイルの先頭にだけ、トラック情報をいれるようにしておく。

先頭にトラック情報と必要なキーフレームが必ずくるようになっているので、どこから再生してもスパット動作するという形になります。

---- 苦労した点 ----

mpegtsのフォーマット変換に苦労しました。
・まずpcrの取り扱いの問題、単にavconvに変換させると同期処理のためかpcrの値がtimestamp値よりちょっと遅れていることがあるみたいです。これを調整するのに苦労しました。
・bufferの問題。vlcとvlc互換のライブラリをつかっている場合ですが、音声用のpesが大きいと後ろの方のデータを無視するという仕様だかバグだかわからない動作がありました。
回避策として、中間フレームでも音声データ量が大きくなった場合は、ファイルに書き出すようにしました。
pesは60kByteほどのデータを持たせることが可能なんですが、4kByte強の粒度で挿入するようにしました。
なお、ffmpegではこの問題はない模様ですが、出力データの粒度は4kByte強になっているみたいです。
・pesのデータの取り扱いの資料がなかったので、いくつかパラメーターの意味を誤解していた部分があったところ。

---- で研究してなにしたいの? ----

まずはプログラムの精度をあげたい点
・ffmpegのrtmpdumpを利用している場合もしくは、rtmpdumpからpipeしてつかっている場合は、映像配信がunpublishされた場合にプロセスごと止まってしまう点がある件。
できたら、unpublishしたら待機しておいて、publish再開したときにすぐに動作再開してもらえばいい感じの動作になる。
・flashのrtmp配信では、soundLevelによる音声欠損や中途でのコーデック切り替えやサイズ切り替えが可能だが、きちんと対応したい。音声欠損は回復するまでコンバートがとまってしまう。映像切り替えはその後ffmpegがエラー吐いて動作しなくなる。

続いてちまたにあふれるmp4のデータをmpegtsで視聴できるようにするproxy servletを書きたい。
できたら高速アクセスできるようになるはず。

変換の高速化を図りたい。
・ffmpegで変換していると変換に3秒ほどかかる形になっているが、実は型に合わせるために、待っている時間が存在しているみたいです。実際にxuggleの変換部分だけ抜き出して動作させてやると変換はそこまで時間がかからない。
よって、自力でmpegts出力ができるようになっておくと高速コンバートが実装できる可能性が高い。

あとは、単なる趣味。

2013年9月20日金曜日

メディアデータの高速変換に挑戦してみたい。

この記事は今週末ちょっとやってみようと思っていることへの先駆けです。
ブログを書いた時点ではきっちりした検証はしていませんことを先に書かせていただきます。

https://github.com/taktod/myLib/blob/217bc0acb2d5165f4c0941d7dfd2534526156b0f/myLib.xuggle.flv/src/test/java/com/ttProject/xuggle/flv/test/MultiCoderTest.java
こちらのプログラムのh263avcMultiThreadTest2の動作・・・
実行すると5秒だけ、h263のflvとavcのflvを並行して作成します。
できるデータは黒字に白で時間が表示されるflvになります。

実際に実行してみると、avcの方だけわずかに短いデータになります。

内部処理でsleep値を5にしてありますが、始めは100にしていました。
100にするとavcの方だけめちゃくちゃ短いデータになります。(2秒くらい?)

このことから推測するに、どうやらavcの変換でははじめにフレームが大量に送られてこないと変換が始まらない可能性があります。

さて、100ミリ秒に設定してあったということは、10fps程度のデータを流し込んでいたことになります。
5ミリ秒に設定したということは(たぶん実際はそこまで速度でていないはずですが・・・) 200fpsでていたことになります。

10fpsを素直に変換すると3秒ほど遅延する。
200fpsを変換するとほぼ遅延しない。

さて、会社でやっているhttpLiveStreamingへの変換ですが、入力flvと出力mpegtsのtimestampを比較するとおおよそ2秒〜3秒遅延します。
rtmpのfpsですが、最高で15fps程度と・・・
ん?どこかで聞いたことあるような遅延秒数ですね。


というわけで、もしかするとh264(avc)への変換ですが、入力ソースが仮に10fpsだとしても、パケットをねつ造して(同じ画像を重ねがけして)100fpsとしてコンバートに回してやればもしかしたら変換遅延を0に近づけることが可能なのではないでしょうか?

まぁ、そんなことするとCPUが異常に必要になったり変換が崩れたりするかもしれませんが、ちょっと週末時間があるときに試したいな・・・と思う次第です。

つくったら誰か買ってくれないかなぁ。
ではでは〜

2013年9月13日金曜日

mavenのxuggleについて

xuggleというライブラリがあります。
javaでのffmpegを扱うためのライブラリです。
xuggleそのものは2、3年前から知っていて、僕が借りているsakuraのvpsにもxuggleをソースコードからインストールしてつかっていたりしてます。

このxuggle、インストールするのが非常に厄介なので配信ツールみたいな一般のユーザーに配るプログラムには向いていないなぁと思っていたのですが、なんと、mavenで公開されているjarファイルには、JNIで読み込むライブラリが同梱されています。(40MByteほどサイズあるけど)

しかも同梱されているライブラリはwindows用、mac用、linux用と3つきちんと入っています。(40MByteほどサイズあるけど)

一応、iMac、windowsXP、CentOSで動作することを確認してあります。

ちなみに同梱されているライブラリはGPLバージョンでh264やmp3、aacも扱うことが可能みたいです。

変換用のライブラリがわざわざコンパイルしなくても利用できるのは非常にありがたいことですね。


ついでにいうと、変換プログラムを動作させるのにシステムライブラリを汚さないところもGoodだとおもいます。


惜しいところはどうもxuggleは最近下火になっているんですよね。

では、今日もはりきってプログラムしていこう。

2013年7月31日水曜日

h264についてメモ書き

正しくない可能性がありますので、h.264について知る上での手がかり程度にしてください。

■構造について
まずはnal構造
nal構造というのは、次のような形になっています。
00 00 01 [h264のフレームデータ] 00 00 01 [h264の別のデータ]・・・
mpegtsの中身とかはコレになっています。

その他の構造もあります。
[4バイトでフレームデータサイズ] [h264のフレームデータ][4バイトのサイズ][h264...]・・・
flvやmp4はこちら側になっているみたいです。

その他の構造があるってことを知っておかないと混乱します。

■フレームデータについて
とりあえず1バイト目をみれば、どういうデータであるか知ることができます。
bitにすると
ABBC CCCC
という形になっていて
Aは0固定
Bは数値が大きいほど変換に必要なデータ、0の場合はなくてもいいデータ
Cの部分はフレームタイプになっています。
↓ボクのプログラムでつかっている定義はこんな感じ
https://github.com/taktod/myLib/blob/master/myLib.media.h264/src/main/java/com/ttProject/media/h264/Type.java

とりあえず、以下のデータを押さえてあります。
0x01:slice いわゆるinnerFrame、High Profileのファイルをみたら、優先度が0になっているデータがあってびっくりした。
0x05:sliceIDR いわゆるkeyFrame
0x07:sequenceParameterSet 略すとsps
0x08:pictureParameterSet 略すとpps spsとppsを合わせたデータがflvではmediaSequenceHeaderとなっています。mp4ならavcCのタグの中身がこの2つ合わせた物になっている。
0x09:accessUnitDelimiter 単なる区切りです。もちろん優先度は0

とりあえずこんなことになっているらしいです。

2013年6月25日火曜日

matroskaを理解していく。

webmのストリーミングをつくっておきたいので、とりあえずコンテナであるmatroskaを理解していくことにしています。

で、とりあえず、lacingというのがわかりにくかった。

matroskaでは、EBMLに従ったデータを扱うことで、容量を節約しています。
http://matroska.org/technical/specs/index.html#EBML_ex
データを表現するときに、始めのビットがでてくる部分で、データ長がきまるみたいな感じです。
たとえば、5を表現する場合はビットにすると
0000 0101
1バイトで表現できるので、始めのビットを初っ端にいれてやって
1000 0101→16進数にすると0x85となります。
800を表現する場合、ビットにすると
0000 0011 0010 0000となります。
2ビット必要になるので・・・
0100 0011 0010 0000→16進数で0x4320となります。

matroskaでは、各データはmp4みたいにelementという単位で取り扱いします。
http://matroska.org/technical/specs/index.html#LevelEBML
[タグ][データの長さ][データ]という構成でできています。
タグはEBMLでつくられたデータで1バイト〜4バイトでできています。
データの長さは、1バイト〜8バイトでできているEBMLで表現した数値になっています。
データの部分は各タグ次第という感じです。
とりあえずCodecPrivateの中にFlvでいうところのMediaSequenceHeaderのデータがあったこと。
BlockもしくはSimpleBlockの中に各データが入っていること。
Seek関連の部分が、Segmentの中の各要素の位置情報がはいっていることを確認してあります。

で、問題の中身で注意しないといけなさそうなのは・・・
1:Seek関連のSeekPositionのデータの中身ですが、数値でSegment領域の内容データの先頭からの相対位置で格納されているみたいです。
[タグ][サイズ][データ....]のデータの始まりの位置からの相対位置です。

2:BlockとSimpleBlockにあるLaceについて
http://matroska.org/technical/specs/index.html#lacing
読んでもはじめなんのこっちゃわかりませんでした。
とりあえずサンプルデータ
これはmp3を格納しているblockのデータです。
まずlacingというのは、ブロックにいれるにしては、データが細かすぎて何度もブロックをつくるのはもったいないというときに利用します。
1つのブロックの中に複数のデータがはいっているという状況を作り出します。

 まずタグの部分、先頭の0xA1がそうです。これはBlockの方です。
 続く0x4ADAの部分がデータサイズになります。EBMLの数値なので0x0ADAになります。
 続く0x82がトラックIDを示しています。これもEBMLの数値なので0x02になります。
このデータでは、Track:0x01がh.264の映像データ Track:0x02がmp3の音声データになっています。
 続く2バイトがこのブロックのtimestamp情報になります。このBlockが含まれているClusterのtimecodeからの差分として、記録されています。
0x0000になっているので、timestampは0になります。この数値はEBMLではないです。
 続く0x06の部分が、各種フラグになっています。0x06は0000 0110となりこのBlockがEBMLLacingを利用していることがわかります。
 次の0x07の部分がこのブロックが保持しているlacingされているデータの数-1になっています。この数値はEBMLではないみたいです。
7なので、MP3のデータが8個はいっていることになります。
 この後の数値7つ(EBML表記)が各lacingされているデータのサイズになっています。
 このサンプルの場合は
0x41A1  0x01A1
0x5F97  0x1F97
0xBF  0x3F
0xF3  0x73
0x8B  0x0B
0xF3  0x73
0x8B  0x0B
となっています。
この数値ですが、どうやら始めのデータは単にデータ量なんですが、その後のデータは前のデータとの差分量になっているみたいです。
で、その差分量の計算方法が結構ややこしいです。

 まず先頭の0x41A1ですが、EBMLの数値にしたがって、0x01A1が始めのデータになります。
 以降のデータですが、各バイトの中間値を0とする数値になるみたいです。
0x5F97に対応する数値は0x1F97
2バイトのデータは0x0000〜0x3FFFですが、中間値は0x1FFFになります。
0x1F97 - 0x1FFF = -0x68が差分になります。
よって0x01A1 - 0x68を計算してやって0x139が次のデータ量になります。
 次のデータは0xBF・・・EBMLを解除すると0x3F
1バイトのデータは0x00 〜 0x7Fで中間値は0x3Fになります。
0x3F - 0x3F = 0x00これが差分になるため、0x139が次のデータ量になります。
 続くデータは0xF3(0x73)、先ほどと同じく中間値は0x3Fなので
0x73 - 0x3F = +0x34
よって0x139 + 0x34 = 0x16Dがデータ量になります。
こうして計算していくと
0x01A1
0x0139
0x0139
0x016D
0x0139
0x016D
0x0139
最後の8個目のデータはこのブロックの残りデータすべてとなります。
0x016D

という風に計算して導きだすことができます。

本来はここまでしなくても、単にffmpegにデータを出力させてwebmのストリーミングするだけならできるのですが・・・
解析に利用しているrtypeDeltaの動画がちょっと壊れているらしく、その原因を知りたいなとおもっているため、回り道しています。(また、可能だったら正しいデータに復元してやって、他のところで利用したいと思っています。)
ちなみに解析に利用しているデータはこちらのr-typeDeltaの動画です。(再生は可能ですが、30分あたりで止まるっぽいです。続きにシークすれば、続きの再生も可能です。
matroskaのデータが壊れているのは確認済みです。)

では、また〜

2013年6月2日日曜日

audioPlayerで使っているmp4の話 その5

いろいろ作業してとうとうこうなりました。

flowplayerによるflashのデモとhtml5によるデモ
合計4つあります。

flashの動作はお約束の読み込んでいないデータの部分でもシークできます。

やり方
1: java1.6以降とmaven3を入手する
2:myLibを入手する。
https://github.com/taktod/myLib
$ git clone git://github.com/taktod/myLib.git
$ cd myLib
$ mvn installでOK
3:mediaMp4を入手する。
https://github.com/taktod/mediaMp4
$ git clone git://github.com/taktod/mediaMp4.git
$ cd mediaMp4
$ mvn jetty:run
4:chromeあたりでアクセスする。
http://localhost:8080/index.jsp

で上記のサイトにアクセスできると思います。ちなみに上記のスナップショットは都合上少々htmlをいじってあります。

で、やってみた感想ですが応答が遅いです。
だいたい平均して1秒くらい?
たまにfileの読み込みに問題がでて、5秒とかかかるときがあるみたいです。
個人的には実践で利用するには、0.1秒くらいの応答にしたいです。(シークがどこであってもそうしたい。)
どうもネックになっているのが、ローカルファイル上にcacheしているデータっぽいので、これをcacheシステム(ehcacheか?)あたりに移設したり、前から順にシークしていって問題の場所を見つけている部分もあるので、その処理の高速化をやればまだなんとかできそうです。
同じことはmp4の動作にもいえそうですね。

なお、応答ができるようになったあとは、DLしながら動作するだけなので、そこは適当にDL待ちがおきない程度の動作でうごけばいいと思っています。

応答がだいたい0.1秒くらいでできるようになったら、どこかの人導入しませんか?

2013年5月30日木曜日

audioPlayerでつかっているmp4の話 その4

前回の続きです。

前回のしめくくりでflvで応答するバージョン、その音声onlyとmpegtsによるhttpLiveStreamingの応答を作りたいとして締めくくりましたが、1つめのflvで応答するバージョンがとりあえずできました。

ソースコードは以下の2つです。
https://github.com/taktod/myLib (ライブラリ補助)
https://github.com/taktod/mediaMp4 (mp4用のサーブレットあれこれ)

使いかたはmyLibをcloneしてきて
$ mvn installでmavenのローカルリポジトリに登録
mediaMp4をcloneしてきて
$ mvn jetty:run
でサーバーが立ち上がるので
http://localhost:8080/test.flvでアクセスすればflvファイルとしてhttp://49.212.39.17/mario.mp4のファイルにアクセスできます。

やっていることは次のとおり。
mp4のデータ参照用タグを一時ファイルにコピーします。
あとは映像や音声のフレームを読み込みつつ、flvのデータとして応答を返すだけです。

うーん。文字列にするとすこぶる簡単なことをやっているように見えますね。

いまのところmp4の応答とは違い、httpのレスポンスヘッダをきちんとつくっていません。また、206の一部だけ応答する動作にも対応させていません。

2013年5月14日火曜日

audioPlayerでつかっているmp4の話 その3

前の記事の続きです。

ここ数日、myLibのリポジトリを複数のモジュールプロジェクトに分割する作業とversion4の動作を作成してました。
複数に分割した理由は、必要なプログラムのみつまみ食いするためです。
今回myLibのプログラムには
FileRead用のchannel動作
https://github.com/taktod/myLib/tree/master/myLib.channels
プログラム作成補助のutil動作
https://github.com/taktod/myLib/tree/master/myLib.util
mp4解析補助
https://github.com/taktod/myLib/tree/master/myLib.mp4
flv解析補助
https://github.com/taktod/myLib/tree/master/myLib.flv
の4つのプログラムをとりあえずいれてありますが、
mediaMp4の現在までの動作では、flvは必要ないです。

なので分割しているmyLib.channels myLib.util myLib.mp4だけ取り込んでます。
まぁ、flvにコンバートしてDLさせるversion5の動作をつくったらflvも必須になりますけど・・・

さて、今回の更新の目玉は映像取り除きservlet proxyです。
myLibmediaMp4を入手してservletを立ち上げるとボクのsakuraのvpsにアップロードしてある。http://49.212.39.17/mario.mp4のデータをベースにデモ動作します。

まずは基本の動作
元データへのアクセスversion1
http://49.212.39.17/mario.mp4

続いてproxy経由のアクセスversion2(データはそのまま)
http://localhost:8080/test.mp4
さらに映像タグ無効化version3
http://localhost:8080/test.m4
今回公開した映像タグ削除version4
http://localhost:8080/test.m4a
特筆すべきところはversion4のダウンロードサイズです。
映像の部分をきれいに落としてあるので、元データは14.2MBなんですがproxy経由のDLサイズはたった1.7MB
これが現状のaudioPlayerで利用しているプログラムとほぼ同等のプログラムになります。
(現状利用しているプログラムよりさらによい動作になっています。)

あと作りたいのは
version5 flvとして応答する
version6 flvとして応答する(音声のみバージョン)
とmpegtsとしてHttpLiveStreamingで応答する
の3バージョンつくりたいですね。バージョン5は汎用性がかなり高いはずなのでがんばってつくりたいところです。

ではでは〜

2013年5月4日土曜日

audioPlayerでつかっているmp4の話 その2

mp4のコンテナをいじりまわす。
こちらの話の続き

■今回は実際に動作するプログラムをつくって、githubにあげてみました。
とりあえず以下の2つ
library: http://github.com/taktod/myLib/
servlet:http://github.com/taktod/mediaMp4/

■とりあえず触ってみたい方は以下の手順で動作できます。

java6(7でもいいと思う)とmaven2、gitを準備します。

myLibを取得します。
$ git clone git://github.com/taktod/myLib.git
$ cd myLib/
とりあえずテスト
$ mvn test
http://49.212.39.17/mario.mp4のデータをリモート解析します。
特に問題なかったらローカルのリポジトリにインストール
$ mvn install

続いてmediaMp4を取得します。
$ cd ..
$ git clone git://github.com/taktod/mediaMp4.git
$ cd mediaMp4/
そのまま実行
$ mvn jetty:run
servletが立ち上がったらgoogle chromeで
http://localhost:8080/test.mp4
にアクセスしてhttp://49.212.39.17/mario.mp4のデータにproxyアクセスできているのを確認して
http://localhost:8080/test.m4a
にアクセスして同データのproxyアクセス(映像off)できているのを確認してもらえればいいと思います。


前回の話であげた作成プログラム2号がmp4アクセス
作成プログラム3号がm4aアクセスとなります。

■これでなにができるか

・musicTubeをつくるきっかけになった。映像offにすることでBGM再生できるようにできます。
・必要のないboxを削除して応答することで、転送データ量を減らすこともできます。
・CDNサービスのデータソースを割り当てることで1つのソースでいろんなコンテナを提供できるようになります。(*)

その他httpのmediaデータproxyで手を出すのはいろいろな物に応用が効きそうです。

■今後

とりあえずversion4をさっさと公開してflv変換にも取りかかりたいですね。


2013年5月3日金曜日

gitコマンドメモ

俺的 git コマンドメモ

会社でも家でもgitをつかってソースの管理をやっています。
で、よく使うgitコマンドメモ

$ git init
今いるディレクトリをgitのリポジトリにするコマンド。

$ git remote add [名前] [リポジトリアドレス]
リモートリポジトリを適当な名前に割り当てるコマンド
例:git remote add origin git@github.com:taktod/testProject

$ git pull [リモート指定] [リモートブランチ]:[ローカルブランチ]
リモートリポジトリのデータを持ってくるコマンド
[リモート指定]の[リモートブランチ]のデータを、[ローカルブランチ]に割り当てる
基本:git pull origin master
[origin]の[master]のデータを、[現在のブランチ]に割り当てる。
例:git pull git@github.com:taktod/testProject master:test
[git@github.com:taktod/testProject]の[master]のデータを、testに割り当てる

$ git push [リモート指定] [ローカルブランチ]:[リモートブランチ]
リモートリポジトリにデータを割り当てるコマンド
[ローカルブランチ]のデータを、[リモート指定]の[リモートブランチ]に割り当てる。
基本:git push origin master
[master]のデータを、[origin]の[master]に割り当てる
例:git push origin master:test
[master]のデータを、[origin]の[test]に割り当てる
例:git push origin :test
[空]のデータを、[origin]の[test]に割り当てる(空なので削除になる)
例:git push git@github.com:taktod/testProject master:tmp
[master]のデータを、[git@github.com:taktod/testProject]の[tmp]に割り当てる

$ git log
gitのログを参照する。
例:git log --graph --pretty=oneline
このコマンドを使うとtreeの形で確認できるので重宝してます。

$ git branch
ローカルブランチを確認する。

$ git checkout [ローカルブランチ]
任意のブランチに切り替える。コミットIDを指定した場合はそのコミットIDのデータにする。

$ git reset --hard [コミットID]
現在のブランチがさしている状態をログごと前の状態に戻すコマンド

とりあえずこの辺り押させておけばいいかなと思っています。
あるある例:
公開データで利用しているgitリポジトリがマージで壊れた!というときには
1:サーバーに入り
2:git log --graph --pretty=onelineで過去の大丈夫そうなcommitIDを見つける
3:git reset --hardで見つけたcommitIDの状態に戻す
4:git pullで作業ツリーを作成し直すことで問題のない状態にしておく。

2013年4月25日木曜日

audioPlayerでつかっているmp4の話

 最近注力しているaudioPlayerのhtml5のプログラムですがaudioタグでmp4が再生できない場合に、Flashをつかって再生させるようにしてあります。(firefoxとかIEとか)
 で、既に読み込みが完了している部分ではシークができるんですが、読み込みが完了していない部分ではシークができません。
これはFlashの仕様に依存しています。
 で、これをなんとかしてやりたいなぁと思っているわけです。

nginxのモジュールには似たような問題の解決方法があります。
http://nginx.org/en/docs/http/ngx_http_mp4_module.html
何をやっているかというと、getパラメーターで与えた値以降のみで構成されているmp4を生成して応答してやるというもの。
10秒からを選択した場合に、10秒の部分から始まるmp4をhtmlの応答として返せばいいじゃね?というわけです。
ただこのnginxのモジュールはローカルにあるファイルに対して実行する修正になります。
 僕があつかっているデータはあくまで元データはyoutubeにあり、データ転送のproxyを実行することで転送途中で速度を損ねることなくデータ調整するところがミソになっています。というわけでnginxのモジュールでハイ解決とはいきません。


 で、自作する必要があるわけなのでいままでのおさらいということでメモしておきます。


■作成プログラム第1号:(単なるproxyサーバー:nettyバージョン)
 はじめにつくったプログラムはnettyから起こしたサーバーでした。
なぜnettyかというとflazrつながりでnettyにハマっていたからです。
確かにnettyでhttpサーバーを書くことができたのですが1つ問題がありました。
nettyで動作させたところ、応答データの追記書き込みができないみたいです。
httpのpartial Content(206応答)の肝はデータを少しずつ応答する・・・なんですが、この少しずつというのがnettyにとっては苦手みたいです。(すくなくともhttpサーバー動作としてつかったクラスは苦手だった模様です。)
しょっちゅうtimeoutとか起こして使いにくいなぁと思っていました。
 このころはまずは動作を・・・ということでテストでつくってみたproxyサーバーになります。

■作成プログラム第2号:(単なるproxyサーバー:jettyバージョン)
 上記のnettyバージョンのproxyサーバーを書いているときに、jettyのproxyServletの動作を参考にしていました。proxyServletを使うと手軽にproxyサーバーをつくることができます。
ここでいうproxyサーバーというのは、ネットワーク設定にいれて、全部のhttpの通信をこのproxyサーバーを通して動作させるというたぐいのproxyです。
 mp4ファイルへのアクセスがどのようになっているのか調査するのにちょうどいい感じだったので重宝していました。
 で、nettyでつくっていた動作の応答が芳しくなかったし、動作の調査してるときに、ちょうどjettyのプログラムにいろいろとログを挟んだりしていたので、じゃぁ、jettyに乗り換えるか・・・ということでjettyベースのプログラムを構築しました。
もちろんつくったのは、特定のファイルの通信にのみ間に入って手を加えるproxyです。
 これははっきりいってあっさりできました。だって、アクセスパスから目標mp4のアドレスを調べてしまえばあとはproxyServletのコピペで終わっちゃいましたからね。

■作成プログラム第3号:(映像のないmp4をつくるバージョン)
 単なるproxyがうまくいったので、そろそろ転送しているmp4のデータに口をだそうと思いました。とりあえず目標は通常の動画から映像部分を取り除いて音楽のみのデータに変えてやってiOS6.1のiPhoneでもBGM再生させること。これが目標です。
でやったこと
・音声用trakデータ、映像用trakデータの2種類がある。
・tag名をfreeに変更すると無視するtagに変更できる
以上の2点がmp4をいじっていてわかったのでproxy転送しているデータのtrakの部分をfreeに書き換えておくってやれば音声のみのmp4にできそうということになりました。
で、つくったところ、いい感じの動作をしてくれたのでさくさくっとjqmobiバージョンのmusicTubeをつくってベータリリースしました。

■作成プログラム第4号:(映像部のデータ転送やめましたバージョン)
 上記のプログラムでもうまく動作していたんですが、映像の部分を無視するようにしたとはいえ、なくなったわけではないので使わないデータ転送がある状態でした。
使わないなら消しちまえということで、削除したかったのですが、そうなってくると、4バイト書き換えるだけでは済まなくなります。
 で、やったこと
・映像用trakデータは必要ないので、削除する。
・削除にあわせてヘッダデータも書き換える。
・mdatの実データ内容もうまく変更してやる必要がある。
という3つを満たしつつ
・データ実体は持たない、http proxyとしてきちんと成立させる。
(転送速度に影響がでないようにする)
というちと無謀なことに挑戦してみました。まぁ、実際にできちゃったわけですが
 まず1つ目の大問題
httpの応答では、これから応答するデータがどういう大きさであるか応答しなければいけません。初回アクセス時にこのサイズがどの程度になるかは誰にもわかりませんし、さすがにこの計算を実施してから応答を開始する・・・では遅すぎます。
たまたまiPhoneのアクセスでは何度が分けてアクセスが来ることが先のproxyServletの動作ログからわかっていたので、なにもわからなければデータ元のサーバーが応答したサイズをとりいそぎ返すことにしました。
 続いて2つ目の問題
httpの全体のサイズはごまかしましたが、mp4の内部データでごまかしを使うわけにはいきません。(応答データが中途で変わることはまずありえないし)
というわけで、moovのタグの開始部(先頭から30バイト程度の部分があるやつ)の大きさは正しい値にする必要があります。この計算を高速に実施しないとhttp proxyとしては成立できなくなっちゃいます。
幸い次の2点のおかげで解決することができました。
・partialContentの要求を使うことで、originalデータの問い合わせ時に必要な部分の問い合わせに狙い撃ちできたこと。
・mp4の内部データがサイズ + タグ + 内容という構成だったので、あらかじめ大きさが把握できたこと。
いくつかのタグは非常に大きいので頭から律儀に読み込みを実施しているとタイムアウトになりそうなくらい時間がかかってしまうのですが、そのあたりはすっぱりあきらめサイズだけに重点を置いたところ解決できました。
 3つ目の問題
moovの内容の応答を勧めていくことになるのですが、先ほどスキップしたデータの非常に大きなタグが次の問題になります。具体的には、stco(co64)のタグが問題です。
このタグは、mp4ファイルの内部のどこに目的のデータがあるか記述しているタグです。
データを書き換えつつ応答しなければいけないので、以下のような手法をとりました。
データ元のサーバーに2本httpUrlConnectionの読み込みストリームを作成して、映像と音声のstcoのデータを並列で取得しつつ、計算してたたき出した値をiPhoneに返していくという動作にしました。
後述のmdatの編集もそうなんですが、前から順にデータが並んでいるという構造であるため、逐次データをつくって応答していくということができて助かりました。
このときに平行してサーバー上のデータとして、mdatのデータのどの部分を抜き出して応答すべきかというデータを保持するようにしています。
 あとは、mdatの部分の応答ですがデータ元サーバーからは全部DLしつつもクライアントには、計算して出した必要な部分のみ応答を返していくという動作をすることで、映像のデータを完全に除外したデータ転送を実施することに成功しました。
 一応まだmetaデータ部(udat)がまぁなくてもいい命令なんですが、データ元サーバーのサインみたいなものだと思うので、そこには手を出してません。
あと、html5のaudioタグの動作仕様だと思うのですが、プレーヤー側でシークした場合に新しくparticalContentsでサーバー側にDLの要求が飛んでくるので、その場合は対応するデータ元サーバーへの問い合わせ位置を計算したあと、その場所からのDLを行いつつクライアントには必要なデータのみ応答するという動作に仕上げることができました。
これが現状のproxyサーバーの動作となります。


■これからつくるやつ案
とりあえず、Flashの動作のために、moovをもっと改造して中途から始まるmp4をつくる必要があります。
で、さくっとmp4ファイルをいくつか確認して関係ありそうなタグをあつめてみました。
trakの中のstbl以下の次のデータが関係ありそうです。
stsd (Sample descriptions)
stts (Map decoding time to sample)
stsc (Map sample to chunk)
stsz (Sample sizes)
stco (Chunk offsets)
stss (Sync sample table) (これは映像onlyっぽいです。もともと映像と音声を同期させるためのデータみたい)

まずstsd
ざっと調べたところ手持ちのmp4のデータではどれも1つのデータのみはいっているみたいです。とりあえずそのままコピーでいいかも
続いてstts
サンプルの時間の記述みたいです。中途で変わることがあるデータみたいなので、中途からはじめるためには、それにあわせて変更しないとだめみたいです。
stsc
Chunkの構成とそのデコード方法?が記述されているみたいです。
audioのデータをみるとそれほどデータ数がおおいわけではなさそうです。単なるサンプルとデコード方法の対応表だったら変更なしでもとりあえずいけるのだろうか?
stsz
サンプル1つ1つのサイズ定義
mp4の解説からするとまぁ、なくてもいいよというデータっぽいですが、いまのところ消せたらラッキーくらいに思っています。DL済みデータのシークができなくなるかもしれませんね。
stco
前回修正したmdat上のどこにデータがあるか指定
moovが先頭にきているデータなので、変更にあわせて書き換えが必要。
stss
映像と音声の同期用、今回は音声のみの抽出なのでばっさり削除でいいはず。(もともと映像側にしかないデータなので切ってるし)

http://d.hatena.ne.jp/SofiyaCat/20080430
こちらを今日は参考にしました。


まじめにmp4をつくるなら上記のデータの修正が必要になりそうです。

で、そんなことをしていると大変なので、proxy用に不真面目にやる方法も考えてみました。
1:DL済みデータのシークをすっぱりあきらめて参照用のindexがmdatの先頭にしかないデータをつくるやり方。
これが可能なら結構楽です。中途の位置を計算する方法がそもそも必要なくなりますので、moovの内容もとってもコンパクトになりそうです。
ただ、きちんと成立するデータがつくれるかちょっと疑問ですが・・・
 問題点は、サイドシークした場合にDL済みの位置だとしても再DLを強制されるところ。
3G回線のandroid端末だと何度もmediaデータの転送が走ってイライラするかもしれません。
2:あたまの方にあるデータをすべて0byteだったことにするやり方。
これも可能なら非常に楽です。stcoのoffsetデータを目標の時間のデータまですべて0にしてしまって全部DLしているが見かけ上は中途再生にみえるみたいなことができるかもしれません。
 問題点は、うごかない端末が出てくる可能性があることくらいでしょうか。
3:FLVにしちゃう。
osmfのプラグインにHLS対応のがあるんですが、mpegts→flv変換をactionScriptでやってるみたいです。同じようにmp4→flvのコンテナ変換を自力でやっちゃってflashに送るデータをflvにしてしまえば中途データにするのは非常に楽です。
これができたら汎用性かなり高くなりますね。flashをつかっている限り・・・はですが。
需要としては、flash→mp4変換がproxyでできてしまえばすばらしく良いということになりますが、そちらはおそらく無理でしょう。mp4の作成に必要なデータがflvの先頭だけで収集完了できるとは思えないです。
4:結局つくらない。
 wifi環境(使えるところが光回線ばっかりだからだと思うのですが)で何度かFlashの動作を確認しているのですが、4時間ほどあるffのmp4でもDL完了までにかかる時間が50秒程度
DL済みの部分はシークできるので、まぁストレスはあるものの使えなくはない程度かなぁという感じです。通常の動画だと数秒でDL終わるし

とりあえず、いろいろ試していけそうな予感です。

jettyのwebSocketServletで古いWebSocketにも対応してみる。

webSocketとbookmarkletを利用して、iPhoneみたいな端末上でもwebのデバッグができるプログラムを実は書いて使っています。(chromeのconsoleや要素解析みたいなことができます。簡易的なものだけど)
で、古いiOSの端末でサーバーへの接続がうまくいかなかったのでなんとかする方法を見つけました。

なんとかする方法はgithubにあげました。
https://github.com/taktod/websocketJetty/commit/bb9502d4040daccae498d805e5e22d40987f9c30
web.xmlの記述にinit-paramのデータを追加して、minVersionの値を-1にすれば、全部のwebSocketに対応するみたいです。(0じゃだめ)
デフォルトでは、RFC6455以降に対応するみたいになっているみたいですね。

これは推測ですが、webSocketのバージョンによっては、binaryの転送はサポートされていないとかあるので、minVersionの値によっては使えない動作がでてくると思います。
その点だけは注意しなければいけないです。


init-paramの値には他にも

  • bufferSize
  • maxIdleTime
  • maxTextMessagesSize
  • maxBinaryMessagesSize
  • minVersion

というのがあるみたいです。

http://download.eclipse.org/jetty/stable-7/apidocs/org/eclipse/jetty/websocket/WebSocketServlet.html


とりあえず、いまのところ僕が扱っているwebSocketの動作ではtextデータのやり取りができたらそれでいいので、minVersion -1だけをいれておけばいいかなと思っています。

2013年4月21日日曜日

MusicTubeのプログラムをbootstrapで書き直してみた。ただしまだ全機能ではないけど


MusicTubeの音楽を聴くプログラムをbootstrapで書き直してみました。

senchaよりあっさりしてるけど、まぁそこそこな動作になったと思う。
上の部分にプレーヤーを持ってきて、動作詳細を隠しておくのはいいかもしれないですね。sencha側のプログラムもコレに合わせてしまった方がいいかも。


いまのところの仕様は
http://taktodtools.appspot.com/audioPlayer/index.html#list/(youtubeの動画ID),(youtubeの動画ID)....という感じに並べればそれが再生リストになるというものです。

今回はwebkit以外のブラウザでもきちんと動作するように、audioタグでmp4が再生不能なブラウザかどうか判定して無理な場合はFlashで再生するようにしてみました。

bootstrapをつかったとはいえ、やっぱりIEが鬼門ですね。
とりあえず手元にあるwinXPのIE8での動作確認はやっておきましたが、幅が小さくなったときの動作が気に入らないですね。

上記のiframe内のリストはbeatmaniaの曲で固めてみました。個人的な趣味です。
ではでは

2013年4月16日火曜日

HttpLiveStreamingとmp4の動作の違いについて

youtubeの動画をiphoneでみると、mp4のデータが視聴できます。

今回はこのmp4と僕が好きなmpegtsベースのHttpLiveStreamingという形式の違いについてです。

まずはmp4
mp4はxmlみたいなタグ+データの形式のコンテナです。webmのmatroskaも似た感じです。
たいていのデータは詰め込むことができますが、一番メジャーなのは、h.264 + aacの組み合わせです。3gpというちょっと前の携帯電話で扱える動画なんかも基本mp4だし、adobeが出しているhttpDynamicStreamingやf4vもベースはmp4です。

データの中身は大きくわけでmoovとmdatに分かれます。moovは本でいうところの目次にあたる部分で、シークしたりするときにどこにデータがあるかという情報が書かれています。mdatはメディアデータの実データ部分になります。
ffmpegでコンバートした場合、mdatの後にmoovが出力されますが、moovが先頭にある方が、シーク等に有利なので通常はMP4Boxあたりを利用して、入れ替えます。
例:Flashで再生させるとき、moovが後にあると、全部読み込まないと再生が始まりませんが、moovが前にある場合はすぐに再生が始まります。

iPhoneでmp4にアクセスした場合は、はじめにmoovのデータをダウンロードしようとします。それが完了すると、再生がはじまります。なお、3Gの状態でこのmoovの部分が基底時間以内にDLできない場合timeoutになります。サーバーが原因のtimeoutかクライアントが原因のtimeoutかはまだわかっていません。


続いてHttpLiveStreaming(略してHLS)
こちらはmpegをベースにしたストリーミング規格です。内部データとしては、mp3やmpegtsが対応しています。以前mp4でつくろうとしたことがありますが、mp4では残念ながら無理でした。
こちらはmpegデータを複数のファイルに分割し、それを順番にDLしては再生させることで動作します。また、その複数ファイルを管理するindexファイルが必要になります。

index用のファイルがm3u8ファイル、実データは*.tsもしくは*.mp3となります。
appleの公式ではたしか、30秒ごとに区切ったデータがよいみたいなことが書いてあったかと思うのですが、別に1秒で区切ってもOKです。
ライブストリームを配信する場合にはなるべく短くした方がよりリアルタイムになりますが、逆にデータの複合に失敗しやすくなるという問題もあります。(僕がつくったjsegmenterとかの分割プログラムでは、keyFrameに注意してそのあたり対処してあります。)

mp4のmoovと違い、m3u8ファイルは非常にサイズが小さいので、再生開始時のオーバーヘッドが小さく、3Gでもさくさくっと再生開始ができるという利点があります。


で、どの両者の違いについて・・・特にmusicTubeをやっているのでaudioの場合の違いについて。です。
1:Flashでの再生
mp4は再生できて、hlsは再生できない。
単に対応していないだけです。ただし、iOSアプリをAirから起こした場合は再生可能になります。
2:iPhoneでの再生
aacベースのmp4は普通に再生できます。aacのmpegtsベースのhlsは再生できません。
mp3ベースのhlsは再生できました。もちろんaudio扱いになるので、両者とも、別のアプリに移動しても流しっぱなしにできます。
前は確かaacのmpegtsベースのhls再生可能だったはずです。動画のBGM再生つぶされたときについでにつぶされたのかもしれません。
確認したブラウザはchrome Mercury Sleipnir Safariについて調べてあります。operaはたぶん、safariで再生させられることになると思います。
3:androidでの再生
nexus7のchrome, opera, firefoxで調べてみました。
まずmp4
chrome、OK
opera、DLになる。
firefox、OK
続いてHLS
chrome、OKだが、accのmpegtsベースのみ、かつ再生中に別のアプリには移動できない。mp3ベースはエラーになる。
operaとfirefoxはm3u8ファイルのDLになる。
4:3G状態での再生について(iPhone)
 mp4は始めのmoovのDLがきついときがある、ただし、それがDLできてしまえばメモリー上に乗るので、その後はメディアデータに専念できる。
よって、wifi状態でmoovだけ落としてからでかければ比較的快適に視聴できる。
HLSより先読みがしっかりされるので、地下鉄とか乗っていてもつっかかりにくい感じがする。
 HLSは開始時の動作がスムーズ。ただし、内部の分割データにも余計な情報が乗っているのであとで落とすデータが少々大きめ。(あまり違いないけど。)
ファイルベースで先読みするのでmp4より先読みは弱め。(mp4は細かくDL、hlsは大雑把にDL)よって地下鉄等でDLがとまると、つっかかることあり。

youtubeにiphoneでアクセスするとmp4で落ちてくるのでmusicTubeのサーバーサイドアプリでは、iphoneとyoutubeの間に入ってproxyサーバーとしてデータの転送をしつつmp4から映像データを取り去ることで、audioデータとして動作し、bgmとして再生できるようにしています。
で、いま最大の不満は外出中の3Gの状態でトラック遷移がうまくいかなかったり、長いデータの再生ができないことがある・・・ということです。
hlsにすると再生のオーバーヘッドが減りますからね。一度試してみたいんですが調査した結果mp3onlyになってしまったのでちょっと難しい話になってきました。
どうするかなぁ・・・

2013年4月11日木曜日

htmlの状態を簡単に確認するbookmarkletつくってみた。

別件でwebsocket経由で見ているページの内容をdumpするプログラムを書いたので、個人的にはそっちで事足りるのですが、iphoneでページをみているときに、ページのhtmlがどうなっているのか、確認したいことがあるので、調べることができるようにするbookmarkletを書いてみました。


javascript:var d=document,s=d.createElement('script');s.src="http://taktodtools.appspot.com/idump.js";d.body.appendChild(s);void(0);


正直websocketで動作するPCとかでdumpと実行ができるプログラムの方が便利。
ただ、自分のwebsocketサーバーがないと動作できないので、なかなか公開できないため、とりあえずつくってみたのが今回のbookmarkletとなります。


2013年4月4日木曜日

jettyサーバーでwebsocket動作を組んでみました。

会社でやろうとおもったので、mavenとjettyについていろいろと学習しています。
でいろいろとハマりました。
今回はそのはまった話。

https://github.com/taktod/websocketJetty/commits/master

例によってgithubにあげています。
今回はmaven2をつかってつくっていますので、動作確認したければ、websocketJettyのディレクトリでmvn jetty:run
を実行してもらえればjettyサーバーが勝手に起動して、確認できるかと思います。

mvn経由でjettyを実行する場合、webアプリケーションのrootは/になります。
よって、昨日の時点では、web.xmlの記述で設定しているpathをsocket/にしてあります。

で、jettyのtarballをダウンロードしてきて展開した場合の動作ですが、
warファイルの名前がrootになります。
コンパイル時には、websocketという名前で実行しているので、rootがwebsocket/になります。
実験はしていませんが、元のプログラムのままだと、pathにsocketをいれてあるので
websocket/socket/でアクセスしないとservletのクラスにコネクトしないことになります。

ところが、web.xmlに設定した値でいけると思い込んでしまったため、socket/でずっとアクセステストをしていました。
で、うごかなかったというわけです。

さて、ハマっていた状態だったわけでそこから脱出できた理由について書いておきます。
jettyサーバー8.1.10を利用しているのですが、通常モードでつかっているとログがなにもでてきませんが、ログレベルをDEBUGに変更すると、詳細動作ログがでてきます。
普通にjettyを起動する場合は
$ java -jar start.jar
というコマンドになりますが、ログの詳細を出したい場合は
$ java -Dorg.eclipse.jetty.LEVEL=DEBUG -jar start.jar
という形で出すことができます。
この状態でログを出したところ


2013-04-04 21:29:37.650:DBUG:oeji.nio:created SCEP@5d295c59{l(/0:0:0:0:0:0:0:1:50573)<->r(/0:0:0:0:0:0:0:1:8080),d=false,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0}-{AsyncHttpConnection@466e06d7,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-14,l=0,c=0},r=0}
2013-04-04 21:29:37.653:DBUG:oejh.HttpParser:filled 303/303
2013-04-04 21:29:37.659:DBUG:oejs.Server:REQUEST /socket/ on AsyncHttpConnection@466e06d7,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-5,l=22,c=0},r=1
2013-04-04 21:29:37.659:DBUG:oejsh.ContextHandler:scope null||/sockettt/ @ o.e.j.w.WebAppContext{/,null},/Users/todatakahiko/Downloads/jetty-distribution-8.1.10.v20130312/webapps/test.war

こんなログがでてきました。
追加しているwarファイルがwebsocket.warなのに、参照しているのがtest.warになっています。
このおかげで、見ているservletプログラムが自作したものと違う状態になっているというのに気づけました。

ちなみに、http://www.websocket.org/echo.htmlこちらのwebsocketのechoアクセステストのページで実行テストをするとエラーだった場合に以下のようなログがでてきます。

あーそれにしてもなんとかなってよかった・・・
mvn jetty:runのときとアクセスパスがかわるのは、musicTubeM4aのyoutubeのmp4のコンバートプロキシ書いたときにわかっていたのにハマるとは・・・

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の説明ってこういうことだったんですね。

2013年1月31日木曜日

mp4から映像データをoffにする。

youtubeのデータがmp4であることが(たぶん)確定したので、映像データを取り去りたいとおもいます。

まず、大前提として、mp4データなんですが次のような書式になっています。
1:データはボックスという形のまとまりになっている。
2:ボックスは子ボックスを内部に持つことができるが、それはボックスのタイプ次第
3:ボックスの形式は次のとおり。
[4バイト(ボックスのデータ量)][4バイト(ヘッダ文字列)][内容]
となっています。

で、大まかな構成ですが・・・
[ftyp]
[moov]
[mdat]
これらが一番ベースとなるボックス。
でmoovはコーデック情報として、trakという子ボックスを持っています。

さて、ここから映像データを取り去るかというと、映像用のtrakデータをoffにしてやればいいみたいです。とりあえず動作しました。
で、offのやり方。
[ftyp]
[moov]
  [trak(映像用)]
  [trak(音声用)]
[mdat]
という形になっていたので、映像用のtrakをfreeに書き換えます。するとあら不思議、映像用のtrakの内容がそっくりそのままskip用の除外データになっちゃいます。


[ftyp]
[moov]
  [free(映像用だがキャンセル)]
  [trak(音声用)]
[mdat]

で、再生してみたところ、音楽のみのデータになりました。
もちろんiOS6.1(iPhone4S)で、バックグラウンド音楽再生もできました。
シーク等もばっちり。

あとはyoutubeのデータをプロキシするサーバーをぱぱっと書けば、なんとかなりそうです。

youtubeのデータについて調べる。

youtubeをBGMにして流したい。でもiOS6.1でできなくなった・・・

というわけで、どうやればいいか調べます。

まず、youtubeでつかっているデータについて調べます。
とりあえず、iPhoneとiMacのsafariを接続して、データアクセスについて調べました。


http://r11---sn-3pm7en7k.c.youtube.com/videoplayback?id=01e3e19fd.....
というURLであることがわかりました。
以前の調査から、youtubeのアドレスは一度アクセス可になった場合、同じIPアドレスからのアクセスは許可されることになるみたいです。

というわけで、同じLANネットワークにあるところからこのURLをたたいてみます。
手持ちのiMacにはwgetコマンドがはいっていないみたいだったので、ぱぱっとjavaのプログラムを書いてみました。

ものすごい乱暴ですが、こんなコード

String url = "http://r11---sn-3pm7en7k.c.youtube.com/videoplayback?id=01e3e19fd......youtube_mobile";
URL urlObj;
HttpURLConnection urlCon;
urlObj = new URL(url);
urlCon = (HttpURLConnection)urlObj.openConnection();
urlCon.setRequestMethod("GET");
ReadableByteChannel rbc = Channels.newChannel(urlCon.getInputStream());
FileChannel fc = new FileOutputStream("test.mp4").getChannel();
while(true) {
  ByteBuffer buffer = ByteBuffer.allocate(65536);
  rbc.read(buffer);
  buffer.flip();
  if(buffer.remaining() == 0) {
    break;
  }
  fc.write(buffer);
}
fc.close();
rbc.close();
urlCon.disconnect();

これでとりいそぎ、test.mp4が取得でき、vlcで再生してみたところ、問題なく再生できました。

内容を確認してみたところ、やっぱりmp4でしたね。
Video: mpeg4 (Simple Profile) 映像データがh.264じゃなかったのが少々気になりますが・・・

一応、万一httpLiveStreamingでiPhoneがやりとりしていたら困るので(そっちをとった方が音声のみにできる公算がたかかった)、wiresharkで確認してみましたが、やっぱりmp4のようでした。

ここまでで、youtubeのデータはmp4であることがほぼ決定的になりました。