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で作業ツリーを作成し直すことで問題のない状態にしておく。