2012年7月28日土曜日

冷やしデザイン,はじめました ~in 2012夏~

プロジェクト V8 の続き.

で,VSD for GPS に Google V8 を組み込んで結局何がしたかったのかというと,今 VSD の画面デザインはハードコーディングされていてユーザにはいじる余地がないので,画面描画を JavaScript で制御できるようにしておけば,ユーザが自由にデザインを作れて,要はスキン機能のようなものが実現できる.

で,あらかた動き始めたので,テストがてらめっちゃイケてるメーターつくってみたよ!!

シブい…我ながらシブ過ぎちゃうぜ.

ちなみにこの時の JavaScript はこんな感じ.
今までは円とか線とかプログラムで描いてメーター画像を生成してたけど,画像ファイルを読み込めるようにしておけば,かなりデザインの自由度が広がるはず.
var vsd = new Vsd;
var img = new Image( "d:\\dds\\vsd\\vsd_filter\\meter.png" );
var font = new Font( "Impact", 50 );

function Draw(){
    // メーター画像描画
    vsd.PutImage( vsd.Width - img.Width, vsd.Height - img.Height, img );
    
    // メーター針描画
    vsd.DrawNeedle( 981, 613, 130, 150, 30, vsd.Speed / 180, 0xFF0000, 3 );
    
    // 適当に文字列描画
    vsd.DrawString( 920, 650, ~~vsd.Speed, font, 0x00FF00 );
    vsd.DrawString( 200, 300, "hoge fuga piyo", font, 0x00FF00, 0x0000FF );
}

2012年7月22日日曜日

V8 エンジン搭載完了ヽ(´ー`)ノ

Google V8 の「初期化・コンパイル」と「スクリプト実行」の関数スコープを分離する,がうまくいった.ただし正しいやり方なのかどうかは不明(;´д⊂)

まず,クラスを新規に作成し,初期化・コンパイルルーチンである Load() とスクリプト実行ルーチンである Run() に分ける.
Load() で構築されかつ保持しておかなければならないオブジェクトは,メンバ変数に保存しておく.ただし,Script::New() したハンドルは ( Local<> なハンドルなので ) メンバ変数に保持しただけでは×なので,handle_scope.Close( script ) とすることで,現在のハンドルスコープを破棄しつつ script はその上位のハンドルスコープにコピーされる.
あと Run() で Context::Scope context_scope( ... ); でコンテキストに入っているけど,コンパイル時にも何らかのコンテキストに入っておかなければならないらしいので,ダミーで Context::Scope context_scope( ... ); やっている.
一番やばそうなのは,一番最初の v8::HandleScope を関数ローカル変数としてではなくメンバ変数で (すなわち new で) 生成していること.Google のドキュメント
Note: The handle stack is not part of the C++ call stack, but the handle scopes are embedded in the C++ stack. Handle scopes can only be stack-allocated, not allocated with new.
とあるように,HandleScope は new で作ってはいけないのだが,いわば First-new-Last-delete さえ守れば多分大丈夫と自分を信じることにする(笑)

以下はコードの断片.足りない関数とかは v8/samples/shell.cc とかから持ってきてくらさい.
class CScript {
  public:
    CScript( void );
    ~CScript( void );
    
    BOOL Load( char *szFileName );
    BOOL Run( void );
    
  private:
    v8::Persistent<v8::Context> m_context;
    v8::Handle<v8::Script> m_script;
    v8::HandleScope m_handle_scope;
};

/*** print ******************************************************************/

// 関数オブジェクト print の実体 
Handle<Value> Func_print(const Arguments& args) {
  String::AsciiValue str(args[0]);
  DebugMsgD("%s\n", *str);
  return Undefined();
}

/*** コンストラクタ *********************************************************/

CScript::CScript(){
    m_context.Clear();
    m_script.Clear();
}

/*** デストラクタ ***********************************************************/

CScript::~CScript(){
    m_context.Dispose();
}

/*** ロード・コンパイル *****************************************************/

#define SCRIPT_SIZE ( 64 * 1024 )

BOOL CScript::Load( char *szFileName ){
    // 準備
    HandleScope handle_scope;
    
    // グローバルオブジェクトの生成
    Handle<ObjectTemplate> global = ObjectTemplate::New();
    
    global->Set(
        String::New( print ),
        FunctionTemplate::New( Func_name )
    );
    
    // グローバルオブジェクトから環境を生成
    m_context = Context::New( NULL, global );
    
    TryCatch try_catch;
    
    // ダミーのスコープを生成  Script::New するときは
    // 何らかのコンテキストに Enter しておかなければならないらしい
    Context::Scope context_scope( m_context );
    
    char *szBuf = new char[ SCRIPT_SIZE ];
    
    // スクリプト ロード
    FILE *fp;
    if(( fp = fopen( szFileName, "r" )) == NULL ){
        // エラー処理
        return FALSE;
    }
    
    int iReadSize = fread( szBuf, 1, SCRIPT_SIZE, fp );
    fclose( fp );
    szBuf[ iReadSize ] = '\0';
    
    Handle<String> ScriptBody = String::New( szBuf );
    
    delete [] szBuf;
    
    Handle<Script> script = Script::New(
        ScriptBody, String::New( szFileName )
    );
    if( script.IsEmpty()){
        // Print errors that happened during compilation.
        ReportException( &try_catch );
        return false;
    }
    
    m_script = handle_scope.Close( script );
    return TRUE;
}

/*** Run ********************************************************************/

BOOL CScript::Run( void ){
    HandleScope handle_scope;
    TryCatch try_catch;
    
    // 環境からスコープを生成
    Context::Scope context_scope( m_context );
    
    Handle<Value> result = m_script->Run();
    
    if( result.IsEmpty()){
        assert( try_catch.HasCaught());
        // Print errors that happened during execution.
        ReportException( &try_catch );
        return FALSE;
    }else{
        assert( !try_catch.HasCaught());
        if( !result->IsUndefined()) {
            // If all went well and the result wasn't undefined then print
            // the returned value.
            String::Utf8Value str( result );
            const char* cstr = ToCString( str );
            DebugMsgD( "%s\n", cstr );
        }
        return TRUE;
    }
    
    return TRUE;
}

-----
以下自分用メモ
・v8 を shared library でコンパイルするには,gyp の実行時以下のコマンドラインで実行.
third_party\python_26\python build\gyp_v8 -Dcomponent=shared_library

・pdb 不要な形式で作成 (/Z7)
standalone.gypi の 'DebugInformationFormat': '3'→'1',

2012年7月21日土曜日

V8 エンジン搭載

# 以下,「できない」と書いてるのは「ぼくにはとてもできない」の意味

VSD for GPS に Google V8 を搭載することにした.
で,色々なサンプルをみてると,だいたい次のような流れになっている.
main(){
  V8初期化処理;
  スクリプトコンパイル;
  while( ... ){
    スクリプト実行;
  }
}
ここで注意しなければならないのは,V8 の作りとして,初期化・コンパイルで構築されたオブジェクトが生存していることが前提で,すなわち
init(){
  V8初期化処理;
  スクリプトコンパイル;
}

run(){
  スクリプト実行;
}

main(){
  init();
  while( ... ){
    run();
  }
}
みたいなことが単純にはできない.なぜなら,init() を抜けた時点で init() 内で構築されたオブジェクトが破棄されてしまうから.
ちょっと C に詳しい人なら (゚Д゚)ハァ? 何言ってんだコイツは,と思う人がいるかもしれない.でも俺が理解する限り V8 がそうなってるんです.特に HandleScope とかが独自のスタック構造になっていて,init() 内のオブジェクトを生かしたまま init() を抜けるとかができない.

で,ここで問題になってくるのが,VSD は AviUtl のプラグインだということ.大雑把に言ってプラグインとしてユーザが定義するのは 1フレーム分の画像を処理する関数で,1フレーム処理するごとにプラグイン関数を抜けて AviUtl に制御を返してやらなければならない.
ということは,プラグイン関数の中で毎回 初期化・スクリプトコンパイル・スクリプト実行 を行なってやらなければならない.数千フレームある動画をエンコすると数千回コンパイルが起こるわけでwww あほらしすぎる(;´д⊂)

んー,どうしたものか.

2012年7月1日日曜日

Win7 の avisynth DirectShowSource() でハマる

昔入れた WindowsServer2008R2,どうもマルチメディア系の動作が変で使いにくかったので,放置状態だった.で,ひょんなことから Win7 Ultimate を \4,600 で購入www 中国からの通販でもなんか平気になってきた自分…恐ろしい子!

んで,Win7 に本格的に乗り換えようといろいろ設定してたら,avisynth の DirectShowSource() で mp4 を読もうとすると,「Media Player Classic はグラフ中のピンのいくつかを再生できませんでした (云々かんぬん)」で怒られる.
avisynth は 1.5 倍再生とか VSD for GPS とかで多用しているので,これが動かないと痛すぎる.(;´Д⊂)
こういう時はたいてい必要なコーデックが足りてないと怒られるけど,ffdshow + MP4 splitter ちゃんと入れてるけどなぁ.

でいろいろもがいているうちに,なぜか avs 再生した時は ffdshow が使われてなさそうなことがわかった.
Win7 では OS 標準で mp4 デコーダとかを持ってるはずだけど,インストールされている DirectShowFilter のどれを使うかっていう優先度はどこで決めてるんだろ? と思ってたどり着いたのがここ
なるほど,メリット値ってので優先度が決まるのね.で DirectShow Filter Tool で ffdshow の優先度見てみたら,ほぼ最優先じゃん orz

んーと思って,さらにググって見つけたのがここ
ここをみると,Win7 からはメリット値を無視して (!!!) マイクロソフト謹製のデコーダしか使わず,ffdshow とかは使わないらしい.
そう聞くと,MPC で mp4 は問題なく再生できたこと (MPC はメリット値を無視しない) も,avisynth の DirectShowSource() で問題がでたことも,WinServer2008R2 ではこの問題が起こらなかったこと (2008R2 には MS 謹製コーデックは入ってない) も,全てに合点がいく.

で,Win7DSFilterTweaker というツールで Win7 でもデコーダの優先順位を設定できて,avisynth の DirectShowSource() でも無事 ffdshow が使用された.

てか,中途半端なウンコデコーダを強制的に使わせてエラー吐くとか,ヤメテクレ(;´Д⊂)