トップ 検索 一覧 差分 ソース ヘルプ RSS ログイン

MinGW+ffmpeg(A)pthreadは複雑

MinGW+ffmpeg(A)pthreadは複雑

メインの記事MinGW+ffmpeg(5)pthreadでは、御託を省くために仕組みを略していきなりmakeターゲットをGCに決定したが、そこにたどり着く過程は面倒な話だ。

 スレッドの環境依存性

スレッドは一般に、OSやコンパイラによってすべからく仕様が異なり、専門的な話で言えばスタックの扱いや排他制御、例外の扱いが様々である。

pthread-w32のREADMEの一部を訳してみる。

注1:非互換製は異なるコンパイラのEH(訳注:ExceptionHandling=例外処理)の実装によるものだ。複数の異なるコンパイラでビルドされるC++アプリケーションにおいては、どちらのコンパイラからも、標準Cバージョンを使用することは可能だろう。EHバージョンのライブラリを使用するなら、アプリケーション側も同じコンパイラを使用せねばならない。これはあくまで(標準的ではない)付加的な複雑性と依存性であって、標準Cバージョンのライブラリのみを使用することにより回避できる。

注2:スタンダードCのpthread*.dllをC++アプリケーションで使用する場合、pthread_cleanup_push()で呼び出されるように意図した関数は__cdeclでなければならない。

注3:pthread.libやlibpthread.aも含み、pthread.dllのVCやGCといったバージョンの名前にも適切な注意を払わねばならない。これはもはや発生しないだろう。

つまり、コンパイラや例外処理モデルにより、pthread-w32には様々な形態がある。pthread*.dllの"*"部分がVC/VSE/GC/GCEなどと変化するのは、それぞれがその1形態ということだ。適切な形態を選択しなければ、x264やffmpegがビルドできないか、まともに動作しない。

pthreadの選択できるライブラリ形態は、MS-VCとGCCの違いも含めれば十数種類あり、この中から適切なライブラリを選択しなければならない。ここではビルド環境はMinGW(GCC)であり、x264とffmpegはCで書かれているので、GCC向けでCクリーンアップコード付きのターゲットを選択すればよい。つまりGCまたはGC-staticだ。

 pthread-w32のstaticライブラリの注意事項

そしてやっかいなのが、staticライブラリの注意事項だ。これをそれほど気にかけなくてもビルドは簡単に通ってしまうため、見過ごしていた筆者はドハマリしてしまった。

pthread-w32をstaticライブラリで使用する場合、まずアプリケーションのビルド時にPTW32_STATIC_LIBを定義しなければならない。一般的にはgccのオプションに-DPTW32_STATIC_LIBを付加すればOKだ。

そして、プロセス(アプリケーション)の開始時・終了時と、スレッドの開始時・終了時にそれぞれ下記の対応する関数をアプリケーションで呼び出さなければならない。

BOOL pthread_win32_process_attach_np (void);
BOOL pthread_win32_process_detach_np (void);
BOOL pthread_win32_thread_attach_np (void); // Currently a no-op
BOOL pthread_win32_thread_detach_np (void);

x264のソースコードはこれらの仕様に対応しているようだが、ffmpegは素のままでは単純にpthreadを有効にしたビルド(./configure --enable-pthreads)さえ通らない。つまりffmpegが想定しているpthreadはまさにPOSIXのもので、似非であるpthread-w32には対応していないのだ。当然、上記の関数も呼び出してはいない。

pthread-w32をstaticライブラリとしてffmpegで利用するには、ffmpegを改造しなければならない。

 ffmpegとpthread-w32

上記問題に対し、ffmpegのみ--enable-w32-threads(Windowsのスレッドを直接使用する)でビルドすればいいと思うかも知れない。が、pthreadを使うlibx264.aをリンクする以上、結局ffmpegでもlibpthreadGC2.aのリンクが必要だ。そしてlibx264.aはプロセスの開始・終了を検出できない(main関数は当然ffmpegにある)ため、結局プロセスの開始・終了時のコードをffmpegに埋め込む必要がある。そもそも上記の訳にもあるように、1つのアプリケーションで複数のスレッドモデルを混在させるのは危険だ。ffmpegとx264のスレッドは別管理であるため、実現可能性は十分あるが、わざわざ際どいところを突きたくない。

一方、ffmpegにpthread-w32への対応コードを記述し、全体をpthreadに統一するのも若干の不安がある。修正箇所はそれほど多くないが、スレッドプログラミングは一般にバグが発生しやすく、一年生プログラマ・SEが最もハマるものの一つだ。筆者は何十ものスレッドが動作するプログラムを設計・開発した経験があるが、ffmpeg自体にそれほど詳しくもない段階で気楽に取りかかりたくはない。

ffmpegは巨大なアプリケーションで、常にα版的な状態にある。本特集に沿ってビルドしたとしても、不正終了などの問題が発生することは十分に考えられる。そのようなソフトウェアに、最初からこれ以上の不安要因を持ち込むのは愚かであり、問題発生時の解析を困難にするばかりだ。

結論として、ffmpegに修正が要らず、余計な心配も要らないDLL版を選択することになった。なお、このMinGW+ffmpegシリーズが好評であればそのうち「完全staticリンクバージョン」にチャレンジしてみたい。その場合でもDLL版と比較すれば、問題発生時に原因の切り分けができるのだ。

 おまけ

DLL版ではなぜ下記の関数を呼び出す必要がないのだろうか。

BOOL pthread_win32_process_attach_np (void);
BOOL pthread_win32_process_detach_np (void);
BOOL pthread_win32_thread_attach_np (void); // Currently a no-op
BOOL pthread_win32_thread_detach_np (void);

これはMinGWというよりWindowsプログラミングの知識になるが、WindowsのDLLにはDllMain()という特殊な関数を定義できる。このDllMain()はDLLがプロセスやスレッドに組み込まれるとき・切り離されるときに呼び出され、そのタイミングはまさに上記関数を呼び出すべきタイミングと一致している。

つまりpthread-w32はDLL版としてビルドされると、DllMain()で上記関数を自動で呼び出し、よりPOSIXのpthreadに近い使い方が出来るように工夫されているのだ。自動化されているだけで、処理自体がないわけではない。

最終更新時間:2008年04月10日 01時42分41秒