AfterEffectsにPythonを組み込む
初めに
たまには外に情報を発信しないとね!ということで、AfterEffectsにPythonを組み込むという企画を行ってみようかと思います。
用意するもの
- Python2.6.8 x86-64
- AfterEffects CS5
- Bridge CS5 SDK
- Visual Studio 2005 (x64コンパイラをカスタムインストールしたもの)
今回はAfterEffectsのスクリプトからPython 2.6を呼び出しその結果を返すという実装を行ってみます。 はじめにAfterEffectsのスクリプトからC++のコードを呼び出す部分です。 これに関してはJavaScript Tools GuideのIntegrating External Librariesの章に書いてあります。
Pythonの初期化
Pythonを初期化する際にPythonのある位置をPythonに教えます。 初期化後、そのままではbuiltinモジュールが使えないため、mainモジュールをインポートし、それをglobalsとします。
// Pythonの初期化 Py_SetPythonHome("C:/Python26"); Py_Initialize(); printf("Py_Initialize\n"); // globals空間の生成 PyObject* main = PyImport_AddModule("__main__"); gGlobals = PyModule_GetDict(main);
ExtendScriptにクラスを登録する
ExtendScript側からC++を呼び出せるようにクラスを登録します。
// ExtendScript側のPyAEクラス SoObjectInterface objectInterface = { PySample::constructor, // initialize NULL, // put NULL, // get PySample::call, // call NULL, // valueOf NULL, // toString PySample::destructor // finalize };
これは登録するクラスのインターフェイスです。 コンストラクタ、アトリビュートの取得設定、メソッドの呼び出し、デストラクタなどの際に呼ばれる関数を定義します。
// ExtendScriptにPyAEクラスを登録する ret = gpServer->addClass(hServer, "PyAE", &objectInterface) ; if (ret!=kESErrOK) printf("Failed register pyAE Class.\n");
これをExtendScript側に渡すことで、ExtendScript側から
var pyae = New PyAE();
とすることでコンストラクタが呼ばれるようになります。
次にコンストラクタですが、
// PyAEクラスのコンストラクタ ESerror_t PySample::constructor(SoHObject hObject,int argc, TaggedData* argv) { gpServer->addMethod(hObject, "eval", 0, NULL); return kESErrOK; }
と先ほどインターフェイスで定義した関数が呼び出され、その中でメソッドを登録します。
こうすることで、
pyae.eval("print 'aaa'");
のようにメソッドが呼び出しできるようになります。
Pythonでevalする
PyAEクラスのevalで呼び出されると、PyAE::callが呼び出されます。 eval以外のメソッドが呼び出された場合も一度すべてここが呼び出されます。 そのため、必要であれば引数nameに入っている値で処理を分けます。
// PyAEクラスのメソッドをcallすると呼ばれる ESerror_t PyAE::call(SoHObject hObject, SoCClientName* name, int argc, TaggedData* argv, TaggedData* pResult) { // name->name_sigにcallされたメソッド名が入っている if (strcmp(name->name_sig, "eval")==0) return PyAE::pyEval(hObject, name, argc, argv, pResult); return kESErrNotImplemented; }
ExtendScriptで呼び出された際の引数とその数はargv, argcに入っているので、そこから実行するPythonスクリプトの文字列を取得します。 retがNULLの際は何かしらエラーが発生しているので、トレースバック文字列を表示して終了します。
// Pythonのevalを行う ESerror_t PyAE::pyEval(SoHObject hObject, SoCClientName* name, int argc, TaggedData* argv, TaggedData* pResult) { if (argc!=1) return kESErrBadArgumentList; int start_symbol = Py_eval_input; // 引数に渡された文字列をPythonでevalする PyObject* ret = PyRun_String(argv[0].data.string, start_symbol, gGlobals, gGlobals); if (ret==NULL) { PyErr_Print(); PyErr_Clear(); return kESErrException; } py2as(ret, pResult); return kESErrOK; }
Pythonで評価した値とAfterEffectのScriptの値を変換する
Pythonで評価した結果の値をAfterEffectのScriptへ返すため、変換を行います。 今回は文字列、整数、小数の3種類のみに対応しています。
// PyObject -> TaggedDataへ変換する void py2as(PyObject* pyObj, TaggedData* aeObj) { if (PyInt_Check(pyObj)||PyLong_Check(pyObj)) { aeObj->data.intval = PyInt_AsLong(pyObj); aeObj->type = kTypeInteger; } else if (PyFloat_Check(pyObj)) { aeObj->data.fltval = PyFloat_AsDouble(pyObj); aeObj->type = kTypeDouble; } else if (PyString_Check(pyObj)) { aeObj->data.string = strdup(PyString_AsString(pyObj)); aeObj->type = kTypeString; } }
おわりに
さくっとAfterEffectsにPythonを組み込んでみました。 実際に使うには色々と問題が残っているのですが、基本的な組み込み方は分かっていただけたのではないかと思います。
次回があるとすれば、このままでは標準出力がどこにも出ないので、そのあたりの話を書こうかと思います。