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

結論として、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に近い使い方が出来るように工夫されているのだ。自動化されているだけで、処理自体がないわけではない。