MinGW(gcc)でAviUtlプラグインを作成する方法を簡単に説明します。
必要ない気がしますが、一応説明しておくと、MinGW
とは、完全にフリーのWindows用の(最低限の)開発環境です。GNUのGCC,binutils,その他と、
Windows用のヘッダやライブラリを組み合わせ、Windowsアプリケーションを単独で作成する
ことができます。(もちろん、コンソールだけでなくWindowプログラムも)
ただし、ATL/MFCは付属していないので、基本的にはWin32APIを使うことになります。
で、何でMinGWを使うの?ということになりますが、現在では、MicrosoftやBorlandが
フリーの(多分フルスペックの)C/C++コンパイラを提供しており、「タダだから」というのは
理由にはならないでしょう。
ではどんな理由かというと、一つは、将来的にもMicrosoftがフリーのフルスペックの
コンパイラを提供するか怪しいという点、もう一つは、gcc に慣れている人には
使いやすい(かもしれない)という点でしょうか。
特に、Linux/*BSDでプログラミングを学んで、gcc についてある程度知っている、
という人にとっては、MinGW(gcc)の方がとっつきやすいのではないかと思います。
cygwin はどうなの?という疑問には、とりあえず「gccが使えればいいだけだから」
と答えておくことにします。
ということで以降の説明では、ある程度、gcc や ld(リンカ)について知識があることを 前提にしています。分かりにくい点があれば、メール・その他でご質問ください。
まずはMinGWを入手してインストールします。
MinGWのダウンロードページ
から、MinGW-X.X.X.exe(Xは数字、名前は少しちがうかも)をダウンロード
してインストールします。
パスについてはインストーラが設定してくれた気がしますが、だめな場合は、
環境変数の"Path"に、MinGWインストールディレクトリの bin ディレクトリを
追加してください。
日本語を扱う場合(SJISファイルで日本語コメントがある場合も含む)は MinGW 日本版 を利用した方が良いかもしれません。
Unixライクな環境も利用したい場合は、MSYSも導入してください。(必須ではありません)
この時点で、適当なプログラムが作成できるようになっているものとします。
たとえばコンソールプログラムだと、こんな感じでしょうか。
===ソース===
/* hello.c */
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("hello, world\n");
return 0;
}
===コンパイルと実行===
>gcc hello.c
>a.exe
hello, world
>
gcc が使えるようになったところで、本題のプラグインのコードを書きます。 プラグインSDKのサンプル等を参考にしましょう。
AviUtl/AvisynthプラグインフィルタのPMDの作者さんが、 「 AviUtlプラグインの作り方 」 というページを公開されたので、そちらが参考になると思います。 このようにまとまった形でのAviUtlプラグイン作成解説ページは 今までなかったと思いますので、ぜひご覧になることをおすすめします。
ソースが準備できたら、次はコンパイルとリンクです。
.c, .cpp をコンパイルします。入力ファイル名を hoge.c(.cpp)、
出力を hoge.obj とします。
デフォルトでは、出力は hoge.o になるので、-o オプションを使って
出力ファイル名を指定していますが、別にデフォルトのままでもかまいません。
>gcc -c -O3 -Wall hoge.c -o hoge.obj
>g++ -c -O3 -Wall hoge.cpp -o hoge.obj
最適化などのコンパイルオプションは適当に設定してください。 オプションについてはGCCのマニュアルに詳しく載っています (GCC online documentation)
独自のリソースを持つフィルタの場合、まずどうにかしてリソースファイルを作りましょう。
About Resource Files(MSDN)に一応解説がありますが、よほど単純なものでない限り、
手書きは面倒です。
次に .rc をコンパイルします。入力ファイル名を hoge.rc 、出力を hoge.res とします。
>windres --input-format=rc --output-format=coff -o hoge.res hoge.rc
日本語を扱う場合は、MinGW日本版にて提供されている windres.exe を使わないとマズイようです
(ダイアログを開いた瞬間に aviutl.exe 自体が落ちる、等の問題があります)。
"winres.h"を要求される場合は、とりあえず"winresrc.h"に書き換え、
ついでに"afxres.h"もインクルードすれば何とかなるかもしれません(たぶん)。
リンクを行う前に .def ファイルを準備します。
.def ファイルなしでもプラグインを作成することは可能なのですが、
MinGW以外の環境でコンパイルすることも想定して、一応 .def ファイルを
用意しておいたほうが良いでしょう。
プラグインのファイル名を hogehoge.auf とすると、次のような内容のファイルを 作ります。(例として、名前を hogehoge.def とします)
LIBRARY hogehoge
EXPORTS
GetFilterTable @1
出力プラグインの場合、GetFilterTable を GetOutputPluginTable、
入力プラグインの場合は GetInputPluginTable にします。
オブジェクトファイル・その他が準備できたので、最後にリンクして完成です。
これまでに準備した入力ファイル名を hoge1.obj, hoge2.obj, hoge.res, hogehoge.def 、
出力を hogehoge.auf とします。次のコマンドを実行してください。
>gcc -shared -Wl,--dll,--enable-stdcall-fixup -o hogehoge.auf
hoge1.obj hoge2.obj hoge.res hogehoge.def
(実際は1行です)
.def ファイルを用意していない場合は、gcc に与えるオプションの"--enable-stdcall-fixup"を "--add-stdcall-alias"に変更すればよいはずです。 (正確には、-Wlオプションの引数であり、リンカに渡されます)
gcc の代わりに dllwrap を利用することもできます。(が、何か変?)
>dllwrap --def hogehoge.def -o hogehoge.auf hoge1.obj hoge2.obj hoge.res
ここまでのステップで作成された hogehoge.auf を、aviutl.exe と同じディレクトリに置けばOKです。 ただ、これだけだと、デバッグ情報等を含んでいて大きいので strip を利用して除去します。
>strip hogehoge.auf
プラグイン作成についての説明は以上ですが、知っておいたほうがお得かもしれない トピックをいくつか挙げておきます。
AviUtlの内部では一画素を、Y,Cb,Crのそれぞれ16bit(計48bit/pixel PIXEL_YC構造体) の符号あり整数として扱っています。各値については、輝度(0.0, 1.0)を(0, 4096)、 色差(-0.5, 0.5)を(-2048, 2048)にマッピングしています。
また、AviUtlでは入出力フォーマットとして、RGB24および、YUY2形式をサポートしています。 ここでは、YUY2とPIXEL_YC形式との相互変換がどのように行われるのかを、私が調べた範囲で 書いておきます。なお、検証は基本的に ver0.99 で行いました。 間違いがあるかもしれないので、参考程度にしておいてください。
注意:ver0.99h にて変換関数(変換式)を指定、または、外部プラグインにより与える機能が追加されました。そのため、ver0.99h 以降の本体では、下に書いてある内容は当てはまらない可能性が高いです。(一応記録として残しておきます)
PIXEL_YCからYUY2へのC言語風の変換式を示します。 なお、シフトは算術シフトとし、実際の出力は0-255にクリップする必要があります。 ver0.99 では設定により、出力をY(16-235),C(16-240)に制限する(ver0.99以前はこちらのみ) かどうかを指定することができます。
Yout = ((Yin*219 + 383)>>12) + 16;
Cout = (((Cin + 2048)*7 + 66)>>7) + 16;
PIXEL_YCからYUY2に変換する場合、輝度はそのままで良いのですが、色差については
水平サンプル数を半分にする必要があります。AviUtlでは単純に色差を半分に間引いています。
具体的には、画像左端のピクセルを0番目として、偶数のピクセルのみを出力し、
奇数番目は捨てています(つまり left origin ということです)。
たいていはこれで問題ないのですが、色差に高い周波数成分が含まれる場合
(BMPで、自然画でないような画像を読み込んだ場合など)、
エイリアシングにより、正しい色が再現できなくなってしまいます。
これを防ぐには、色差に低域通過フィルタをかけてやる必要があります。
YUY2からPIXEL_YCへの変換式を示します。シフトは算術シフトです。
輝度は(16, 235)が(0, 4096)、色差は(16, 240)が(-2048, 2048)の範囲に
変換されることが分かります。
Yout = ((Yin * 1197)>>6) - 299;
Cout = ((Cin - 128)*4681 + 164)>>8;
YUY2からPIXEL_YCへの変換では、前の場合とは逆に、色差を補完する必要があります。
が、ver0.99 ではこの補間処理にバグがあり、これを回避するためには、
外部プラグインを導入し、色差アップサンプリングを行う必要があります。(注:ver0.99a以降では修正済み)
なお、ver0.98d では(左ピクセルの色差+右ピクセルの色差)/2 で、
色差の補間が行われているようです。
毎回、長ったらしいコマンドを打つのが面倒な場合は、Makefile を作成しましょう。
とりあえずはどこかから雛形を拾ってきて適当に書き換えるのが簡単です。
ただ、make.exe は mingw32-make を別途入手する必要があるはずです。
(なお、MSVCな開発環境では、nmake.exe を使います)
# Makefile の例
# '#'以降はコメント
CC = gcc
CFLAG = -O3 -Wall
LINK = dllwrap
LFLAG = --enable-stdcall-fixup
AS = nasmw
TARGET = hoge.auf
OBJ1 = hoge.obj
OBJ2 = moge.obj
DEF = hoge.def
all: $(TARGET)
# 字下げはTAB(\t)であることに注意
$(TARGET): $(OBJ1) $(OBJ2) $(DEF)
$(LINK) $(LFLAG) --def $(DEF) -o $(TARGET) $(OBJ1) $(OBJ2)
strip $(TARGET)
$(OBJ1): hoge.c filter.h
$(CC) $(CFLAG) hoge.c -c -o$(OBJ1)
$(OBJ2): moge.asm
$(AS) -DPREFIX -fwin32 -o$(OBJ2) moge.asm
clean:
del *.obj
del *~
gcc のインラインアセンブラは intel 形式でなく、MSVC等とも書式が異なるので、 nasm 等を利用して別にアセンブラ の関数を作る、という例が多いようです。
あるいは(少々速度は落ちますが)、intrinsic を利用するのも一つの手です。
GCC3.4.4では、mmintrin.h(MMX), xmmintrin.h(SSE), emmintrin.h(SSE2),
pmmintrin.h(SSE3)が付属しているので、これらのヘッダで定義されている
intrinsicを利用すれば、MSVC,ICLでも(たいていは)
そのままコンパイルすることができます。ただ、ivec.h は提供されていないので、
どこかから拾ってくる必要があると思います。
MMX, SSE, SIMD Intrinsics については以下のリンク先を参考にしてください。
AviUtlはMMXの使えるCPUでしか使えないので、MMXが使えるかどうかのチェックは 必要ないのですが、SSEの方はチェックする必要があるような気がします (注:SYS_INFO構造体中のフラグが利用できるかどうかは試してません)。
デバッガについては、gdb が利用できると思いますが、Windowsで使ったことは無いのでよく分かりません。
MSやIntelのコンパイラをコマンドラインで使う場合は、まるもさんが公開しているプラグインの ソースに含まれる Makefile を参考するのが良いと思います。