いろいろ作業してとうとうこうなりました。
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秒くらいでできるようになったら、どこかの人導入しませんか?
プログラムしてみようか
プログラミングを週末にぼちぼちやります。 twitterはこちらhttp://twitter.com/#!/search/taktod
2013年6月2日日曜日
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の一部だけ応答する動作にも対応させていません。
前回のしめくくりで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です。
myLibとmediaMp4を入手して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は汎用性がかなり高いはずなのでがんばってつくりたいところです。
ではでは〜
ここ数日、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です。
myLibとmediaMp4を入手して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変換にも取りかかりたいですね。
こちらの話の続き
■今回は実際に動作するプログラムをつくって、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で作業ツリーを作成し直すことで問題のない状態にしておく。
会社でも家でも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終わるし
とりあえず、いろいろ試していけそうな予感です。
で、既に読み込みが完了している部分ではシークができるんですが、読み込みが完了していない部分ではシークができません。
これは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の値には他にも
というのがあるみたいです。
http://download.eclipse.org/jetty/stable-7/apidocs/org/eclipse/jetty/websocket/WebSocketServlet.html
とりあえず、いまのところ僕が扱っているwebSocketの動作ではtextデータのやり取りができたらそれでいいので、minVersion -1だけをいれておけばいいかなと思っています。
で、古い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だけをいれておけばいいかなと思っています。
登録:
投稿 (Atom)




