2013年7月14日日曜日

LutとLut2を改良した

VapourSynthのissueリストにこんなのがありました。
http://code.google.com/p/vapoursynth/issues/detail?id=52
「LutとLut2なんだけど、いちいちlook up tableを作ってから渡すのってメンドイから、生成用関数渡したらフィルタ側で作るようにしたいんだけど、誰かやらんかね?」

たしかにあれはめんどくさい。
特にLut2の場合は二重ループ必須だから、余計に書くのがめんどくさい。
というわけで、パッチ書いて送ったのが昨日mergeされました。

core.std.Lut(clip:clip;planes:int[];lut:int[]:opt;function:func:opt;)
core.std.Lut2(clips:clip[];planes:int[];lut:int[]:opt;function:func:opt;bits:int:opt;)

例:クリップのすべてのYの値を50下げたい場合。
clip = something

#これまでの書き方
lut = [max(x - 50, 0) for x in range(2 ** clip.format.bits_per_sample)]
clip = core.std.Lut(clip, lut=lut, planes=0)

#改良後
def func(x):
    return max(x - 50, 0)
clip = core.std.Lut(clip, function=func, planes=0)

#または
clip = core.std.Lut(clip, function=lambda x: max(x - 50, 0), planes=0)

例:二つのクリップを平均化して新しいクリップを作りたい。
clipx = something
clipy = something

#これまでの書き方
lut = []
for y in range(2 ** clipy.format.bits_per_sample):
    for x in range(2 ** clipx.format.bits_per_sample):
        lut.append((x + y) // 2)
clip = core.std.Lut2([clipx, clipy], lut=lut, planes=[0, 1, 2])

#改良後
clip = core.std.Lut2([clipx, clipy], planes=[0, 1, 2], function=lambda x, y: (x + y) // 2)

functionの引数名はmasktools2のmt_lut/mt_lutxyに合わせて、xとyで固定です。
今までのようにlutをPython側で作って渡すこともできますが、lutとfunctionの両方を渡したらfunctionは無視されます。

次のリリース(多分r19test5)から使えるようになります。

2013年7月13日土曜日

MultiByteToWideCharToMultiByte その4

5.VapourSynthと文字エンコード

VapourSynthはr19でいろいろと大きな変更が入ることが見えたきたが、そのなかでも特に大きなものはVSScriptの導入である。

r18までのVSはvapoursynth.dll(Linuxならlibvapoursynth.so)と、vapoursynth.pydの二つに分かれていた。
vapoursynth.dllはメイン処理を担当するC++で書かれたライブラリであり、vapoursynth.pydはvapoursynth.dllをPythonで操作するためのcython製モジュールである。
この構成は全てをPython上で実行するだけなら特に問題はなかったが、いざPython以外の外部プログラムと連携させようとするとどうやったらいいものなのかが非常にわかりにくかった。
それこそコードを全部読みこんでMyrsloik氏自身と同程度の理解を要求されるレベルなのだ。
Windowsならばvsvfwとvsfsがあるのでまだ良いが、LinuxやMacではどうしたらいいものかさっぱりだった(筆者自身、半年ほど前にavs2yuvのようなツールを作ってみようとして、半日で挫折している)。

VSScriptはこのような状況を改善し外部プログラムとの連携を容易にするために作られたAPIである。
現段階ではまだ未完成ではあるが、既にAvsPmod開発者のvdcrim氏などは非常に強い関心を寄せている。
LinuxやMacでもAvsPmodを使ってVS用スクリプトを編集できるようになる日もそう遠くはないかもしれない。

さて、VSScriptのコードを追っかけてみると、vapoursynth.pydを構成するコード(vapoursynth.pyx)に次のような記述が追加されていた。
cdef public api int vpy_evaluateScript(VPYScriptExport *se, const char *script, const char *errorFilename) nogil:
...
        comp = compile(script.decode('utf-8'), errorFilename.decode('utf-8'), 'exec')
        exec(comp) in evaldict
...
このコードから察するに、VSScriptを使ってメモリ上に展開されたスクリプトのバイト列をPythonに渡すと、Pythonはバイト列をUTF-8でデコードしてユニコード文字列に変換し、しかる後にバイトコードにコンパイルすることになる。
これは事実上、r19以降はスクリプトの記述はUTF-8(BOMなし)で統一されるというこどだ。

そもそもPythonでは、スクリプトはASCII文字のみで書くか、それがダメならばUTF-8にすることとPEP8で決められている。
これは絶対の掟ではないが、今時Shift_jisやEUC-JPでPython書きたがるのはただの馬鹿だ(過去に書かれた負債のため嫌々書くならば理解できる)。
また、Avisynthでも前回で述べたようなファイル名の問題等を解決するため、スクリプトはUTF-8で書くように出来ないかとか、ユニコード対応ソフトウェアに改修しようといった議論が行われたことがあった(結局互換性重視の方針により実現はしなかったが)。
この変更は、大いに歓迎すべきものだろう。

VapourSynthはまだまだ若いプロジェクトであり、プラグイン作者もそれほど多くはない。
しかもほぼ全員が#darkhold(IRCチャンネル)に常駐しているため、舵取りも容易な状態である。
あきらかに自分のコードの書き方が悪かっただけなのに、プラグインの互換性を損ねたと駄々をこねる困った子も今のところはいない。
変更を追いかけるのはキツイかもしれないが、あと1年くらいは色々と変わるのではないかと思う。

2013年7月10日水曜日

MutiByteToWideCharToMultiByte その3

3. fopen()と_wfopen()

fopen()はC言語でファイルを開く際に使われる標準関数だが、Windowsには別に_wfopen()という独自関数がある。
FILE *fopen(const char *filename, const char *mode)
FILE *_wfopen(const wchar_t *filename, const wchar_t *mode)
そもそもNT系Windowsのカーネルはファイル名はユニコードで扱い、NTFSはファイル名をユニコードで保存している。
たとえば"あいうえお.txt"というファイルを開く場合、
fopen("あいうえお.txt", "rw")
は、まず"あいうえお.txt"というACPでエンコードされた文字列をユニコードに変換した後、そのユニコード文字列と合致する名前のファイルを検索し、見つかればそれを開く。
一方、
_wfopen(L"あいうえお.txt", L"rw")
は、"あいうえお.txt"というUTF-16LEでエンコードされた文字列(文字列リテラールの前にLを付ければ、その文字列はコンパイル時にUTF-16LEに変換される)をユニコードに変換してから処理を行う。

では、もし両関数に"魔法少女♥マジカルJEEBたん 第3話.mkv"という文字列を渡すとどうなるか?

_wfopen(L"魔法少女♥マジカルJEEBたん 第3話.mkv", L"rw")
は、ファイルが存在するならば成功する。
しかし
fopen("魔法少女♥マジカルJEEBたん 第3話.mkv", "rw")
は、ファイルの有無に関わらずWindowsでは失敗する。
なぜなら文字列中の''という文字はACPにはない文字なので、ユニコードへの変換ができないからである。
ACPに存在しない文字が使われていた場合、その文字は'?'(クエスチョンマーク)に置換えられ、その後の処理を行う。

4. Avisynthと文字列のエンコード

Avisynthの全関数のうちで最も重要なものは一体なんだろうか?

答えはもちろんEval()である。
Eval()はメモリ上に展開された文字列データをパースしてどのような処理を行えばいいのかを判別し、それを実行する機能である。
Eval()とは、まさにAvisynthインタプリタそのものなのだ。
AVISource("abcde.avi")
というスクリプトを実行するとき、Avisynth内部では
Eval("""AVISource("abcde.avi")""")
を行なっている。
どんなスクリプトもEval()なしでは決して動かない。

さて、この大事な大事なEval()は、ASCII非互換なエンコーディングの文字列をパースすることが出来ない。
よって、あなたはスクリプトをBOM付きUTF-8やUTF-16、UTF-32で書いてはいけない

では、Eval()の次に重要な関数はなにか?

答えはもちろんImport()だ。
Import()はスクリプトが書かれたファイルを開いて中のデータをメモリ上に展開し、Eval()を呼び出す機能である。
VirtualDubであれx264.exeであれ、avsを入力する際はImport()が働いている。
Import()なしで動くプログラムなんて、AvsPmodのプレビューとvsavsreaderのavsr.Eval()くらいしかない(これらは自らスクリプトをメモリ上に展開し、直接Eval()を呼び出している)。

さて、このとっても大事なImport()だが、ファイルを開く際にはfopen()を使っている。
よって、あなたは決してavsのファイル名にユニコード限定文字を使ってはならない

さらにもう一つ重要なことを述べておく。
Avisynthは開発開始よりこれまでずっとマルチバイト文字ソフトウェアであったため、内部関数もプラグインも文字列のエンコーディングはACPであることを前提として書かれてきた。
よってあなたは自分の使っているWindowsのシステムロケールのACP以外でスクリプトを保存してはならない。
そしてこれは、素材となる動画/音声ファイル名にもACPにない文字を使ってはならないということを意味する。

唯一の例外としてffms2のutf8オプションがある。
ffms2は主にwine上でAvisynthを使う非Windows環境のユーザーのため、このオプションを設けている。
しかしffms2が考慮しているのはffms2が読み込む対象のファイル名だけなので、それ以外に非ASCIIな文字がスクリプト中に存在すれば、結果は保証されないものとなるだろう。

2013年7月8日月曜日

MultiByteToWideCharToMultiByte その2

承前

2.FFmpegとAvisynth

前回に書いたような経緯でffmpeg.exeやavconv.exeでも日本語ファイル名のavsを読めるようになったのだが、今年の3月に今度はFFmpegのほうだけでこれが再発した。
原因はこのコミット
Avxsynthの開発者がAvxsynth入力を導入するべくFFmpegのほうだけavformat/avisynth.cを全部書きなおしてしまったことにより発生したのだった。
そして、自分がバグの再発を知ったのは先月(6月)の半ば頃のことである(2ちゃんねるのffmpegスレで見かけた)。

さて、問題のコードは次のとおりだった。
arg = avs_new_value_string(s->filename);
val = avs_library->avs_invoke(avs->env, "Import", arg, 0);
このコード、x264のavs.cを参考にしているわけだが、大事なことを忘れている。
前回も書いたように、s->filenameの中身はUTF-8でエンコードされた文字列である。
一方、Avisynthはマルチバイト文字アプリケーションで、文字列はACPエンコードが前提になっている。
だから非ASCIIな文字を含んだ文字列をそのままでAvisynthに渡したら、Avisynthはファイルを見つけられないのである。

書きなおすのは構わないんだけど、できればもうちょっと既存のコードを注意して読んでおいて欲しかった……。
ちょっと腹がたったから言っちゃうけど、Avxsynthユーザーなんて、非英語圏のAvisynthユーザーの千分の一もいないよ。

さて、原因は前回と同じなので対策も前回とまったく同じである。
再びパッチを書いて今度はFFmpegのMLに送った(Avxsynthの中の人はFFmpegだけに送ったのでLibavは昔のまま)。
かくしてコードは次のように変わった。
#ifdef _WIN32
    char filename_ansi[MAX_PATH * 4];
    wchar_t filename_wc[MAX_PATH * 4];
    MultiByteToWideChar(CP_UTF8, 0, s->filename, -1, filename_wc, MAX_PATH * 4);
    WideCharToMultiByte(CP_THREAD_ACP, 0, filename_wc, -1, filename_ansi, MAX_PATH * 4, NULL, NULL);
    arg = avs_new_value_string(filename_ansi);
#else
    arg = avs_new_value_string(s->filename);
#endif
    val = avs_library->avs_invoke(avs->env, "Import", arg, 0);
変数名が変わったのと MAX_PATH * 4 は、そのほうがいいんじゃね?とレビューしてもらったDaemon404(Derek Builtenhuis)氏に言われたから。

さすがにもう再発することはないと思うけど、もし再発しても次は知らない。気づいた人が直して下さい。
(そもそも筆者はエンコード素材のファイル名は半角英数以外使わないので、自分で気づくことはまずありません)

それともうひとつ。
このFFmpegの新Avisynth入力は、Avisynth2.5.8では不具合が出るそうです(参考:avisynth scripts fail to load in ffmpeg)。
まだ2.5.8を使ってる人は、とっとと2.6alphaに更新しましょう。

2013年7月7日日曜日

MultiByteToWideCharToMultiByte その1

最近、文字のエンコード/デコード関連のコードを書くことが多いのでメモ程度に。

1.FFmpeg/LibavとAvisynth

FFmpeg/Libavのライブラリは文字エンコードはUTF-8で統一されている。
しかし世の中にはUTF-8をそのまま扱うには色々問題があるOS(つまりWindows)が存在するので、2011年4月にいくつかのハックが取り入れられた。

ハックその1:
cmdutils(ffmpeg/avconv/avprobeといったコマンドラインツール用共通コード)は入力されたコマンドラインをWindowsの場合のみACP(ANSI code page、日本語版Windowsなら標準はCP932)からUTF-8に変換する。
ハックその2:
FFmpeg/Libavのツール/ライブラリにおいて、ファイルの開閉はavformatの仕事である。ほとんどのファイルはavformat_open_input()を使って開かれる。
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);
avformat_open_inputは普通はio.hのopen()関数を使ってファイルを開くが、Windowsの場合はfilename(UTF-8)をワイド文字(UTF-16LE)に変換してから_wsopen()関数(マイクロソフトによる独自拡張)で開く。
このようになるべく問題が出ないよう配慮されているわけだが、avformatの対応形式の中にはこれだけだとまずいものが存在する。それがAvisynthである。

FFmpeg/LibavにおけるAvisynth入力は、昔DivX社の人が書いてMLに送ったもので、ファイルの操作はVideo for Windowsで行なっていた。
res = AVIFileOpen(&avs->file, s->filename, OF_READ|OF_SHARE_DENY_WRITE, NULL);
このコード、上記のハックが取り入れられるまでは特に問題がなかったのだけど、2011年4月以降はs->filenameの中身がUTF-8でエンコードされた文字列になってしまったので、ASCII以外の文字を使ったファイル名は文字化けしてAVIFileOpen()が失敗するようになったのである。

さて、このバグの存在を自分が知ったのは昨年の5月だった(2ちゃんねるのcygwin/mingwスレで見かけた)。
で、その日のうちにパッチを書いてLibavのMLに送り(Libavに入った変更は大抵3日以内にFFmpegにも取り込まれるので、送るならLibavのほうが手間が省ける)、ML上でのちょっとしたやり取りの後、以下のように変わった。
wchar_t filename_wchar[1024] = { 0 };
char filename_char[1024] = { 0 };
MultiByteToWideChar(CP_UTF8, 0, s->filename, -1, filename_wchar, 1024);
WideCharToMultiByte(CP_THREAD_ACP, 0, filename_wchar, -1, filename_char, 1024, NULL, NULL);
res = AVIFileOpen(&avs->file, filename_char, OF_READ|OF_SHARE_DENY_WRITE, NULL);
ACP -> UTF-16LE -> UTF-8とcmdutilsで変換された文字列を今度は逆にUTF-8 -> UTF-16LE -> ACPと変換するだけ。
変換用の一時的なバッファのサイズが1024で決め打ちなのは「とりあえずこの程度あればいいんじゃね?」と適当に書いただけで特に意味は無い。そしてなぜか誰もこのマジックナンバーにツッコミを入れなかったのでそのままになった。
かくしてffmpeg.exeやavconv.exeで再び日本語ファイル名が(ひょっとするとUTF-8で255文字を超えるファイル名の場合、コケる可能性があるけど)使えるようになった。

あと、ひょっとするとCP_THREAD_ACPよりもCP_OEMCPのほうが良かったのかもしれないけど、そこらへんはよくわからないので分かる人がいたらよろしくお願いします。

次回へ続く。

2013年7月3日水曜日

AviSynth2.6用プラグインの書き方

AviSynthプラグインを書く場合、各プラグインは必ず#include "avisynth.h"しなければならないわけですが、このavisynth.hが2.6でこれまでのものに比べて大きく変更されました。

2.5xまでのavisynth.hは
...
  bool HasVideo() const { return (width!=0); }
  bool HasAudio() const { return (audio_samples_per_second!=0); }
  bool IsRGB() const { return !!(pixel_type&CS_BGR); }
  bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional
...
の様な感じで、helper系の関数はヘッダーに実体が書かれているというなんとも自由奔放で微妙なものでした。
さすがにこれはかっこ悪すぎるだろってことで2.6からはこれらの関数はavisynth.dllの内部に用意されるようになったわけですが、これにともなって新しいavisynth.hを使用したプラグインは2.6以降でしか使えなくなりました。

まあ、2.6でも2.5用のプラグインは特に問題なく使えますし、新しいものを古いavisynthで使うのはナンセンスです。
そもそもsource forgeで配布されている公式バイナリは、かの悪名高きVC++6でビルドされているため、XPどころかPentium2なWindows2000でも動きます。
みんながIanB氏に「いい加減コンパイラ新しくしてよ」と言っても頑なに古いものにこだわり続けているのです(そういえばAviUtlもVC++6だっけ?)。
まだ2.5xを使っている人は早急に2.6に変えましょう。

さて、2.6用avisynth.hを使う場合のプラグインの書き方ですが、以下のようになります。

・AVS_Linkage構造体
これまでavisynth.hに実体が書かれていた(つまり各プラグイン毎にコンパイルされていた)関数群がavisynth.dll内にAVS_Linkage構造体としてまとめられ、各プラグインはLoadPlugin()する際にこれのポインタを受け取るように変更されました。これに伴いinitializerもこれまでのAvisynthPluginInit2からAvisynthPluginInit3に変更されています。
で、avisynth.hには以下のように記述されていますので
extern const AVS_Linkage* AVS_linkage;
よってPlugin.cppはこう書きます。
...
const AVS_Linkage* AVS_linkage = 0;
...
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors)
{
    AVS_linkage = vectors;
    env->AddFunction("FunctionName", "arguments string", create_filter_function, user_data);
    return "some string";
}
avisynth.dllが各プラグインのinitializerを呼び出す際にAVS_Linkageのポインタを渡してくるので、それを保持するための変数を"AVS_linkage"という名前で静的領域に用意するだけです。
あとは今までと何も変わりません。

2013年6月30日日曜日

VapourSynth r19(テスト版)

VapourSynthがr19で色々変わるようです。
とりあえずテスト版でのr18までとの違いを簡単にまとめておきます。

*この記事は現時点でのテスト版を元に書かれており、今後の展開によっては特に断りなく修正されます*

変更1:Coreクラスがsingletonになり、Core()がget_core()になった。

singletonは、デザインパターン(オブジェクト指向プログラミングのノウハウ)の一種ですが説明はメンドイので省きます。
具体的には、いままでこう書いていたのが
import vapoursynth as vs
core = vs.Core(threads=2, add_cache=False, accept_lowercase=True)
c0 = core.std.BlankClip(format=vs.YUV420P8)
c1 = core.std.BlankClip(format=vs.YUV422P9)
c2 = core.std.BlankClip(format=vs.YUV444P16)
こうなります。
import vapoursynth as vs
core = vs.get_core(threads=2, add_cache=False, accept_lowercase=True)
c0 = core.std.BlankClip(format=vs.YUV420P8)
c1 = core.std.BlankClip(format=vs.YUV422P9)
c2 = core.std.BlankClip(format=vs.YUV444P16)
もっと言えばこう書いてもいいです(あまりオススメはしませんが)。
import vapoursynth as vs
c0 = vs.get_core(threads=2, add_cache=False, accept_lowercase=True).std.BlankClip(format=vs.YUV420P8)
c1 = vs.get_core().std.BlankClip(format=vs.YUV422P9)
c2 = vs.get_core().std.BlankClip(format=vs.YUV444P16)
これまでは
Note that the loaded plugins are per core instance and not global so if you do create
several processing cores at once (not recommended) you will have to load the plugins
you want for each core.
と、Coreオブジェクトは何個でも作れましたが、r19以降は1プロセスに付き1個しか作れなくなりました(最初にget_core()した時に作られます)。
これにより、いままではユーザー定義のクラスや関数に引数でCoreオブジェクトを渡す必要がありましたが、今後はなくなりました(必要なところでget_core()すれば、どこで呼んでもプロセス内で共通のものが使われます)。

変更2:output()がなくなり、set_output()とvspipe.exeが出来た。またlastというクリップ名に特別な意味がなくなった。

これまでは
#sample_pre.vpy
import vapoursynth as vs
core = vs.Core()
last = core.std.BlankClip(format=vs.YUV420P8)

if __name__ == '__main__':
    last.output(sys.stdout, y4m=True)
このスクリプトをコマンドラインで使う場合は
$ python sample_pre.vpy | x264 - --demuxer y4m -o out.264
という感じで実行し、VirtualDub等で読みこめばlastクリップがプレビュー出来ましたが今後はこうなります。
#sample_r19.vpy
import vapoursynth as vs
core = vs.get_core()
clip = core.std.BlankClip(format=vs.YUV420P8)
clip.set_output()
このスクリプトをVirtualDub等VfWを利用するソフトウェアで読みこめば、set_output()したクリップ(変数名はなんでもよい)がプレビューできます。
そしてx264等に入力するにはvspipe(インストールすればおまけでついてきます)を使います。
$ vspipe sample_r19.vpy - -y4m | x264 - --demuxer y4m -o out.264 
output()がなくなってしまったので、これまでのようにスクリプト内でsubprocessつかってコマンド実行が困難(出来ないことはないけど)になりましたが、かわりに外部プログラムから使うためのAPIがかなり整備されたので、そのうちx264等にAVS入力同様、VS入力が実装されるでしょう。

変更3:get_plugins()とget_functions()が追加された。

r18までは、core.list_functions()で現在使えるすべてのフィルタ及びその引数の一覧を文字列として取得出来ましたが、r19でさらにcore.get_plugins()とcore.***.get_functions()が追加されました。
get_plugins()は、list_functions()では文字列として取得できた情報がdictとして取得出来ます。
>>> import vapoursynth as vs
>>> vs.get_core().get_plugins()
{'com.vapoursynth.resize': {'namespace': 'resize', 'identifier': 'com.vapoursynt
h.resize', 'functions': {'Bicubic': 'clip:clip;width:int:opt;height:int:opt;form
at:int:opt;yuvrange:int:opt;', 'Sinc': 'clip:clip;width:int:opt;height:int:opt;f
ormat:int:opt;yuvrange:int:opt;', 'Bilinear': 'clip:clip;width:int:opt;height:in
t:opt;format:int:opt;yuvrange:int:opt;', 'Spline': 'clip:clip;width:int:opt;heig
ht:int:opt;format:int:opt;yuvrange:int:opt;', 'Gauss': 'clip:clip;width:int:opt;
height:int:opt;format:int:opt;yuvrange:int:opt;', 'Lanczos': 'clip:clip;width:in
t:opt;height:int:opt;format:int:opt;yuvrange:int:opt;', 'Point': 'clip:clip;widt
h:int:opt;height:int:opt;format:int:opt;yuvrange:int:opt;'}, 'name': 'VapourSynt
h Resize'}, 'com.vapoursynth.avisynth': {'namespace': 'avs', 'identifier': 'com.
vapoursynth.avisynth', 'functions': {'LoadPlugin': 'path:data;'}, 'name': 'Vapou
rSynth Avisynth Compatibility'}, 'com.vapoursynth.std': {'namespace': 'std', 'id
entifier': 'com.vapoursynth.std', 'functions': {'Loop': 'clip:clip;times:int:opt
;', 'PropToClip': 'clip:clip;prop:data:opt;', 'StackVertical': 'clips:clip[];',
'Transpose': 'clip:clip;', 'FlipVertical': 'clip:clip;', 'PEMVerifier': 'clip:cl
ip;upper:int[]:opt;lower:int[]:opt;', 'Splice': 'clips:clip[];mismatch:int:opt;'
, 'ClipToProp': 'clip:clip;mclip:clip;prop:data:opt;', 'PlaneDifference': 'clips
:clip[];plane:int;prop:data:opt;', 'Lut2': 'clips:clip[];lut:int[];planes:int[];
bits:int:opt;', 'AssumeFPS': 'clip:clip;src:clip:opt;fpsnum:int:opt;fpsden:int:o
pt;', 'Cache': 'clip:clip;size:int:opt;fixed:int:opt;', 'FlipHorizontal': 'clip:
clip;', 'Expr': 'clips:clip[];expr:data[];format:int:opt;', 'LoadPlugin': 'path:
data;forcens:data:opt;', 'MaskedMerge': 'clips:clip[];mask:clip;planes:int[]:opt
;first_plane:int:opt;', 'ShufflePlanes': 'clips:clip[];planes:int[];format:int;'
, 'CropRel': 'clip:clip;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;',
 'SeparateFields': 'clip:clip;tff:int;', 'Reverse': 'clip:clip;', 'StackHorizont
al': 'clips:clip[];', 'Trim': 'clip:clip;first:int:opt;last:int:opt;length:int:o
pt;', 'SelectEvery': 'clip:clip;cycle:int;offsets:int[];', 'Interleave': 'clips:
clip[];extend:int:opt;mismatch:int:opt;', 'ModifyFrame': 'clips:clip[];selector:
func;', 'Turn180': 'clip:clip;', 'Lut': 'clip:clip;lut:int[];planes:int[];', 'Pl
aneAverage': 'clip:clip;plane:int;prop:data:opt;', 'BlankClip': 'clip:clip:opt;w
idth:int:opt;height:int:opt;format:int:opt;length:int:opt;fpsnum:int:opt;fpsden:
int:opt;color:float[]:opt;', 'DoubleWeave': 'clip:clip;tff:int;', 'CropAbs': 'cl
ip:clip;width:int;height:int;x:int:opt;y:int:opt;', 'SelectClip': 'clips:clip[];
src:clip[];selector:func;', 'AddBorders': 'clip:clip;left:int:opt;right:int:opt;
top:int:opt;bottom:int:opt;color:float[]:opt;', 'Merge': 'clips:clip[];weight:fl
oat[]:opt;'}, 'name': 'VapourSynth Core Functions'}}
はい、dictのなかにさらにdictがたくさんあって、なにがなにやらさっぱりわかりません……。
get_functions()は、もう少しマシです。
>>> vs.get_core().std.get_functions()
{'Loop': 'clip:clip;times:int:opt;', 'PropToClip': 'clip:clip;prop:data:opt;', '
StackVertical': 'clips:clip[];', 'Transpose': 'clip:clip;', 'FlipVertical': 'cli
p:clip;', 'PEMVerifier': 'clip:clip;upper:int[]:opt;lower:int[]:opt;', 'Splice':
 'clips:clip[];mismatch:int:opt;', 'ClipToProp': 'clip:clip;mclip:clip;prop:data
:opt;', 'PlaneDifference': 'clips:clip[];plane:int;prop:data:opt;', 'Lut2': 'cli
ps:clip[];lut:int[];planes:int[];bits:int:opt;', 'AssumeFPS': 'clip:clip;src:cli
p:opt;fpsnum:int:opt;fpsden:int:opt;', 'Cache': 'clip:clip;size:int:opt;fixed:in
t:opt;', 'FlipHorizontal': 'clip:clip;', 'Expr': 'clips:clip[];expr:data[];forma
t:int:opt;', 'LoadPlugin': 'path:data;forcens:data:opt;', 'MaskedMerge': 'clips:
clip[];mask:clip;planes:int[]:opt;first_plane:int:opt;', 'ShufflePlanes': 'clips
:clip[];planes:int[];format:int;', 'CropRel': 'clip:clip;left:int:opt;right:int:
opt;top:int:opt;bottom:int:opt;', 'SeparateFields': 'clip:clip;tff:int;', 'Rever
se': 'clip:clip;', 'StackHorizontal': 'clips:clip[];', 'Trim': 'clip:clip;first:
int:opt;last:int:opt;length:int:opt;', 'SelectEvery': 'clip:clip;cycle:int;offse
ts:int[];', 'Interleave': 'clips:clip[];extend:int:opt;mismatch:int:opt;', 'Modi
fyFrame': 'clips:clip[];selector:func;', 'Turn180': 'clip:clip;', 'Lut': 'clip:c
lip;lut:int[];planes:int[];', 'PlaneAverage': 'clip:clip;plane:int;prop:data:opt
;', 'BlankClip': 'clip:clip:opt;width:int:opt;height:int:opt;format:int:opt;leng
th:int:opt;fpsnum:int:opt;fpsden:int:opt;color:float[]:opt;', 'DoubleWeave': 'cl
ip:clip;tff:int;', 'CropAbs': 'clip:clip;width:int;height:int;x:int:opt;y:int:op
t;', 'SelectClip': 'clips:clip[];src:clip[];selector:func;', 'AddBorders': 'clip
:clip;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;color:float[]:opt;',
 'Merge': 'clips:clip[];weight:float[]:opt;'}
たとえば「Lut2の引数ってどんな感じだったっけ?」と思ったら、
>>> core.std.get_functions()['Lut2']
'clips:clip[];lut:int[];planes:int[];bits:int:opt;'
このようにcore.name.get_functions()['フィルタ名']とタイプすれば確認できます。
状況によってlist_functions()と使い分けましょう。

変更4:スクリプトに使用する文字セットをUTF-8で統一

VSScript(外部アプリケーションとの連携用API)の導入に伴い、スクリプトはUTF-8で書くように統一されたようです。
スクリプトはPythonに渡された後、UTF-8決め打ちで一旦Pythonのバイトコードにコンパイルされます。
このためUTF-8以外で日本語用文字等を使っている場合は、内部で文字化けを起こしてデコードエラーになります。
これまでは日本語ファイル名等を扱う場合はCP932で保存したほうがよかったですが、今後はUTF-8で保存するようにしましょう(もちろんBOMなしで)。
そしてもうひとつ大事なことですが、Windowsの場合、日本語ファイル名が扱えるかどうかはプラグインの実装次第となりました。
とりあえず見た感じ、avisource.dllはちゃんと対応してたけどd2vsourceはどうも現状ではダメっぽいですな。
ちなみに拙作のソースフィルタ群も軒並みダメです。ああ、直すのメンドイorz