2009年12月19日土曜日

Java 使いの人向け ActionScript3 のクラス仕様まとめ

仕事の都合でJavaからActionScript3(FlexじゃなくてFlash 10みたいです)に乗り換えることになったので、クラスの仕様に親しむためまとめを作ってみました。


■参考文献
http://www.tom.sfc.keio.ac.jp/~fjedi/wiki/index.php?%A5%AF%A5%E9%A5%B9%BC%FE%A4%EA%A4%CE%BB%C5%CD%CD%28ActionScript3%29
こちらのWikiに完璧にまとまっていますので、ぶっちゃけこのWikiを見ればすべて解決すると思います。


■大原則
迷ったらJavaと同じ。
以下のJavaと違う点に掲載のない事項はすべてJavaと同じ。多重継承が無くてinterfaceが用意されている点など。


■Javaと違う点
  1. package宣言は.NETやC++のように{}で囲む必要がある。{}で囲んだ中はJavaとほぼ同じ。publicクラスが一つしか持てない等。
    package {
        //以下javaと同じ
        import flash.text.TextField;
        public class Hogehoge extends TextField { /*...*/ }
    }
  2. package{}の外側にファイルローカルな関数や変数やクラスを宣言できる。この領域はpackage{}の内側でインポートしたクラスやメソッドが使えない。別途インポートし直す必要がある。
    package {
        import flash.text.TextField;
        public class Hogehoge extends TextField { /*...*/ }
    }
    // 別途インポートが必要
    import flash.text.TextField;
    var abesi:TextField = new TextField();
  3. dynamicクラスがある。
  4. abstractクラスやabstractメソッドが無い。
  5. その他、native, synchronized, transientなどが無い。
  6. namespaceがある。(が、正直使い道がよく分からない)
  7. enum型はない。
  8. コンストラクタにもfunctionを付ける必要がある。
  9. コンストラクタは必ずpublicになる。アクセス制御修飾子を指定しないとpublicとして扱われる。
  10. オーバーロードが一切使用できない。コンストラクタもオーバーロードできない。
  11. オーバーライドの際には必ずoverride修飾子を付ける必要がある。
  12. プロパティのgetter/setterを作るために特別な仕様がある(get修飾子, set修飾子)

その他、クラス以外の違いとして、
  1. String型が参照渡しではなく値渡し(プリミティブ扱いなので)
  2. 関数の引数にデフォルト引数が利用できる(オーバーロードの代わりに使う)
    function abesi(a:String="abesi", b:String="hidebu") { /*...*/ }
    abesi(); //自動的にデフォルト引数が使用されるのでこういう呼び出しが可能
  3. 関数の可変長引数の指定の仕方が異なる
    //Javaでは
    public void abesi(String... args) { /*...*/ }
    //AS3では
    public function abesi(...args) { /*...*/ }


■サンプル
以上を踏まえて、練習がてらにプレイスホルダー付きテキストフィールドクラスを作ってみました。

Javaではコンストラクタのオーバーロードが必要になったところ、AS3ではデフォルト引数のおかげですっきり書けました。やっぱりデフォルト引数はいいですね。あと試しにtextColorプロパティのsetterをオーバーライドしてみたのですが、そのせいでthis.textColor = 0x999999とすると意図しないタイミングでdefaultTextColorが書き換わってしまいはまりました。super.textColorとしてスーパークラスのsetterを呼びだすようにして回避。getter/setterのオーバーライドは強力ですが上手く使わないと混乱を招く諸刃の剣になりそうです。

iMovie 08 でニコニコ動画にアップロードするための動画をエンコードする設定まとめ

皆さんご存じニコニコ動画。Windows環境で動画を作ってアップするための情報はたくさんあるのですが、Mac環境用のネタが少ないので、メモしておきます。

参考にしたページは以下の通り。
http://d.hatena.ne.jp/KZE/20091106/p1

http://www.smilevideo.jp/static/www/help/#000562

http://nicowiki.com/encode.html


■環境編
Mac OS X 10.6.2 Snow Leopard
iMovie 08
QuickTime Player X (Snow Leopard付属)

iMovieは08を使用します。各所でお勧めされているiMovie HDであれば高性能でflvも扱えるらしいのですが、新しいソフトを入れて管理するのが面倒という理由により却下です。また、iMovie 09からは動画クリップごとの再生速度管理もできるらしいので、kskとかスローモーションの演出もできるためそっちのほうがお勧めです。

エンコの際にはQuickTimeを利用してエンコするため、一応QuickTimeのバージョンも載せておきました。たぶんLeopard付属のQuickTime Player 9とかでもうまくいくと思います。


■エンコ設定編
まず注意点として、私はプレミアム会員なので、以下の設定はプレミアム会員用に合わせています(ビットレート最大1000Kbps程度、ファイルサイズ最大100MB)。一般会員の方はビットレートを落とせばそのまま利用できるかと思います。

さて肝心の設定内容ですが、http://d.hatena.ne.jp/KZE/20091106/p1で解説されている内容とほとんど同じです。iMovie 08からはそのままflvを書き出すことができませんので、mpeg-4 H.264でエンコードします。具体的には以下の通り。
  1. メニューの「共有」から、「QuickTime を使用して書き出す」を選択する
    Cmd+Eの「ムービーを書き出す」では上手くエンコできませんので注意。
  2. 「書き出し」欄は「ムービーからMPEG-4」を選択する
    ここで「ムービーからQuickTimeムービー」を選択するとどんなに上手くエンコしてもニコ動側で再エンコされてしまいます。絶対に「ムービーからMPEG-4」を選択すること。
  3. 以下の画像のように設定する



    一番上のファイルフォーマットを「MP4(ISMA)」から「MP4」にしないとH.264でエンコできませんので、まず最初に変えておくこと。
    一般会員の方はムービーのデータレートをもっと落としてください。300ぐらいかな?逆に600より上げる必要はよっぽど画質が気になるか短い動画でもないかぎり無いと思います。ビットレートについては私なんぞよりもまとめWikiなどのほうが詳しいのでそちらをご覧ください。
  4. 「ストリーミング」の「ストリーミングを使用する」チェックボックスは入れない
    入れても大丈夫かどうかは未確認ですが、余計な物を入れると変なことになるのではと思い外してます。入れた方がいいのかな・・・

■仕上がり編
上記の設定でエンコしてみた結果がこちら。


特に問題なく見られていると思います。ただしエコノミー回避はしていないのでエコノミーが発動すると悲惨なことになりそうですが・・・


■FLVでエンコしたい編
FLVでエンコしたい場合や、もっと詳細な設定がしたい場合には、ffmpegXというツールを使うと良いようです。試していないのでここでは割愛します。
その他、iMovieから直接FLVでエンコできるようにする方法もあるらしいです。しかしH.264で直接アップロードできるようになった今となっては別にFLVにこだわるメリットも無いかも。

2009年12月16日水曜日

bulkloader.py を使って Google App Engine の本番サーバーから開発サーバーにデータを移す

Google App Engine SDKの開発サーバーのデータストアはtmpディレクトリにデータを保存するため、マシンを再起動するとデータの中身が全部消えてしまいます。毎回テストデータを用意するのが面倒なので、本番サーバーからデータを移してくるための方法を調べてみました。するとbulkloader.pyというユーティリティを使うと、簡単に本番サーバーのデータをダウンロードして保存し後からリストアすることができることがわかりました。ということで早速試してみます。
参考にしたのは以下のサイト。
http://code.google.com/intl/en/appengine/docs/python/tools/uploadingdata.html

※2010/01/31追記:--db_filenameオプションの使い方を掲載しました。


■前準備
app.yamlに以下の設定を追加してから、appcfg.py updateで本番デプロイを行います。
- url: /remote_api
script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
login: admin
これだけで準備ばっちりです。余談ですが、app-engine-patchを使うと、最初からこの設定がapp.yamlに含まれています。


■使用方法
本番サーバーからローカルにデータをダンプするときは以下のコマンドを実行します。
bulkloader.py --dump --kind=<kind> --url=http://<appname>.appspot.com/remote_api --filename=<data-filename>
たとえば私のアプリからhon_heroという名前のModelのデータをダンプしたいときには、
bulkloader.py --dump --kind=hon_hero --url=http://akisutesama.appspot.com/remote_api --filename=hon_hero.dump
などとするとうまくいきます。

ダンプしたデータを本番サーバーにリストアするときは以下のコマンドを実行します。
bulkloader.py --restore --kind=<kind> --url=http://<appname>.appspot.com/remote_api --filename=<data-filename>
--dumpが--restoreになっただけです。本番障害でデータが失われたときにリストアするためのコマンドなので、実行すると本番サーバーのデータがすべてダンプした地点のデータに置き換えられます。ご利用の際には細心の注意を!

ダンプしたデータをローカルサーバーに入れるときは以下のコマンドを実行します。
bulkloader.py --restore --kind=<kind> --url=http://localhost:8080/remote_api --filename=<data-filename> --app_id=<appname>
本番だけではなくてローカルサーバーにもリストアができます。ローカルサーバーにリストアする際の注意点として、
  • dev_appserver.pyでアプリケーションが動いている必要があります。
  • --urlオプションをローカルホストに書き直す必要があります。
  • --app_idオプションで、自分のアプリケーションのapp_idを指定してやる必要があります。
これでローカルテストがだいぶ楽になりました。

同様にして、本番からデータをダンプしてローカルに入れるだけでなく、ローカルからデータをダンプして本番に入れることもできます。新しく実装した箇所に最初からデータを入れたい場合などに使えると思います。


■log, progress, resultを賢く使う
bulkloader.pyを実行すると、デフォルトでは以下のような名前のファイルが自動的に生成されると思います。
bulkloader-log-20091211_145622.log
bulkloader-progress-20091211_145622.sql3
bulkloader-result-20091211_145622.sql3
これらはそれぞれ「ログファイル」「現在どのエンティティまでダンプされたかを格納するデータベース」「実際にダンプした内容を格納するデータベース」となっており、上手く使えば2回目以降のダンプ実行時間を劇的に短くすることができます。

ログファイルを指定する際には--log_fileオプションを、データベースを指定する際には--db_filenameと--result_db_filenameを、それぞれ指定してください。たとえば、以下のようなシェルスクリプトを用意しておくと便利です。
#!/bin/sh

# 前回の実行結果が残っているとエラーになる(上書きしてくれない)ので、まずいったん消す
if [ -e hon_hero.dump ]; then
rm hon_hero.dump
fi
# bulkloader.pyを実行する
# db_filenameとresult_db_filenameは一緒のファイルを指定してもかまいません。
# (その場合は、一つのsqlite3データベースに一緒に格納されます)
# db_filenameとresult_db_filenameは、db_fileやresult_db_fileのように短縮して書いても動作するみたいです
bulkloader.py --dump \
--url=http://akisutesama.appspot.com/remote_api \
--kind=hon_hero \
--filename=hon_hero.dump \
--log_file=bulkloader-log-hon_hero.log \
--db_file=bulkloader-progress-hon_hero.sql3 \
--result_db_file=bulkloader-progress-hon_hero.sql3
こうすることで、2回目以降はbulkloader-progress-hon_hero.sql3の中身とリモートのdatastoreの中身を比較して、追加更新のあったもののみをダウンロードし、それ以外はローカルに保存してあるデータベースの中身を利用するので、劇的に処理が速くなります。結果はこちら。
[INFO    ] Connecting to akisutesama.appspot.com/remote_api

[INFO ] Have 2610 entities, 2610 previously transferred
[INFO ] 2610 entities (974 bytes) transferred in 0.4 seconds
1回目のロードには130秒かかったのですが、2回目はすべてローカルに保存されているデータを使ったので、なんと0.4秒で済んでいます。

こうしてダンプしたデータをローカルサーバーでリストアするときは、たとえば以下のようなシェルスクリプトを使います。
#!/bin/sh

# --db_fileと--result_db_fileには先ほどのダンプ時に指定したものとは別のファイルが必要になります
# (先ほどのデータベースはappspot.com用として設定されるので、localhostで使用するとエラーになってしまいます)
# --db_fileと--result_db_fileには特別な名前としてskipを指定することができます
# この名前を使用するとデータベースへの書き込み/読み込みを行いません
bulkloader.py --restore \
--url=http://localhost:8000/remote_api \
--app_id=akisutesama \
--kind=hon_hero \
--filename=hon_hero.dump \
--log_file=bulkloader-log-restore.log \
--db_file=skip \
--result_db_file=skip


■注意点
appspot.com以外のサーバー(たとえばlocalhostなど)からデータをdumpしたりrestoreしたりする際には、必ず--app_idオプションを指定する必要があります。これを忘れていて詰まりました><

もし特定の条件を満たすデータのみをダウンロードしたいという場合などは、設定ファイルをPythonで書く必要がありますが、appcfg.py download_dataを使うと良いと思います。