ffmpegをwindows向けにビルドした際にそこそこ苦戦したので知見をまとめます。
- windows: Windows 10 Pro, x64
- ffmpeg:
n3.2.2
- msys2:
msys2-runtime 2.6.0-1
- visual studio:
Microsoft Visual Studio Community 2015 Version 14.0.25431.01 Update 3
アプローチ
公式サイトや公式wikiにも書いてあるとおり、大きくいくつかのアプローチがあります。今回は、MSYS2/MinGW環境でのビルドと、Visual Studio 2015 Community版でのビルドの両方に成功したので、それぞれ説明します。なおどちらの方法でもポータブルなバイナリ(DLL)が作成できます。
共通のMSYS2環境の構築
Visual Studioでのビルドとは言っても、cmake製のビルドシステムのようにVisual Studioで開くプロジェクトファイルを生成するわけではありません。Visual Studioに同梱されているコンパイラやリンカなどのツールキットを使ってconfigure/makeベースのビルドをします。そのため、 このconfigure/make自体を動かすためにMSYS2シェルが必要になります。なので、いずれにせよまずはMSYS2をインストールします。
ちなみに、cygwin環境でのビルドもできるそうなのですが、msysはwindowsネイティブなバイナリを作ることを主としているのに対して、cygwinはcygwin用のバイナリを作るのが標準の世界です。なので、cygwinからwindowsへのクロスコンパイルということになるのですが、これは無駄に問題がややこしくなっていると思ったのでやめました。
MSYS2は公式サイトからインストーラーを取ってきます。作業環境が64bit windowsなので64bit版を選びました。推奨どおり C:\msys64
にインストールしました。なお、msys2自体はその中でさらに3つの世界に分かれていて、mingw32, mingw64, msys2の3つの環境があります。これはこのmsysシェルの中でのツールキットや、リンクするライブラリバイナリなどがまるっと切り替わります。今回はターゲットが32bitなのと、ffmpegのドキュメントどおりに、mingw32環境を使用します。
msys/mingw32シェルは C:\msys64\mingw32.exe
から起動できます。インストール後初回は、まず全パッケージのアップデートを行います。
pacman -Syuu
そして下記のツールをインストールします。
pacman -S make
pacman -S diffutils
pacman -S yasm
pacman -S mingw-w64-i686-gcc
gccについてはターゲットが32bitなのでi686を指定します。このホストのところがw64である理由はよくわかっていないのですが、mingw32環境であっても64bitで動いているということなのでしょうか?
また先程msys2/mingw32シェルを起動する方法を説明しましたが、もう一つ、windowsシェルの cmd
からmsys2を開く方法があります。これが後々の手順で必要になるのですが、このときに元々のwindowsシェルのパス変数を引き継いでmsys2シェルを起動するように設定します。 C:\msys64\msys2_shell.cmd
をテキストエディタで開いて冒頭部の下記コメントをコメントインします。
rem To export full current PATH from environment into MSYS2 use '-use-full-path' parameter
rem or uncomment next line
set MSYS2_PATH_TYPE=inherit
以降は方式別の手順になります。
Visual Studio
まずyasmをインストールします。公式サイトからバイナリを落としてきてwindows側のパスを通します。仕組みから言ってmsys2の方で入れたyasmだけあれば大丈夫な気がしますが自分は念のため入れました。
次がポイントです。Visual Studioはクロスコンパイルするためにホスト環境とターゲット環境の組み合わせに対してツールキットを切り替える仕組みになっています。これは、それぞれの組み合わせに応じたコンパイラなどの実行ファイルが分けて用意されていて、どれを使うのかをパス変数にどのフォルダを登録するかによって切り替える仕組みになっています。そして、そのような適切なパスを通すために、 C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC
に vcvarsall.bat
というスクリプトが用意されています。これを windows側のシェルの cmd
から叩くことでパスが通ります。また、わざわざ vcvarsall.bat
を叩かなくても、自分の環境では専用のショートカットがインストールされていました。 スタート > Visual Studio 2015 > VS2015 x86 Native Tools Command Prompt
といったものです。自分の環境ではホストが32bitと64bitの2種類、ターゲットが32bit、64bit、ARM(!!)の3種類の組み合わせで6つのショートカットがありました。今回のターゲットは32bit(x86)です。作業環境は64bitのWindowsですが、これに引っ張られてターゲット設定が影響を受けると嫌なので、ツールキットも32bitを使うことにしました。先述の VS2015 x86 Native Tools Command Prompt
をクリックして起動します。起動したら下記コマンドでちゃんとパスが通っていることを確かめられます。
set PATH
さて、次にこのvisual studioのツールキットが通った環境から、MSYS2シェルを起動します。それには C:\msys64\msys2_shell.cmd -mingw32
を実行します。オプションの -mingw32
によって msys2/mingw32の環境になります。起動したらパスがちゃんと引き継げているか確認します。
echo $PATH
もしここでパスが引き継げていない場合、先述したpathのinherit設定を見直してください。
次にパスの設定を修正します。Visual Studioが用意する link
というコマンドがあるのですが、msysの方も /usr/bin/link
というコマンドがあります。引き継いだパスよりmsysのパスのほうが優先設定されるので、このままだとmsysのlinkが使われるのですが、これを前者のVisual Studioのlinkを使う必要があるので、こちらが選択されるようにVCのパスを前方に持ってきます。
まず今パスが通っているVCのパスを取得します。
$ which -a cl
/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/BIN/cl
この cl
の入っているフォルダがbinパスなので、下記のようにしてパスの前方に持ってきます。
export PATH="/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/BIN:$PATH"
こうして、下記出力でVCパスが先に出てくればOKです。
$ which -a link
/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/BIN/link
/usr/bin/link
/bin/link
/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/BIN/link
もともとのパスのも後ろに残っているので2回出てますがまあ問題ないです。
いよいよビルドです。作業ディレクトリにffmpegがチェックアウトされているとして、下記手順でconfigureします。
mkdir out
cd out
../ffmpeg/configure --toolchain=msvc --prefix=./ --enable-shared
重要なのは --toolchain=msvc
を指定することです。今回はDLLでほしいので --enable-shared
も指定します。それ以外は普通のffmpegのconfigureなので目的に応じて適宜指定してください。configureが成功したら、 config.mak
が生成されます。これを Makefile
が読み込んでビルドする仕組みです。
しかしこのままだと問題が生じます。最後にリンクするときに、 GetDesktopWindow
のシンボルが見つからないというエラーが出てしまいました。 コマンドラインを確認すると、この関数が入っている user32.lib
がリンク対象に入っていないようなので、それを追加します。
config.mak
の中から下記のような行を探します。
EXTRALIBS= secur32.lib psapi.lib advapi32.lib shell32.lib
これに下記のように末尾に user32.lib
を追記します。
EXTRALIBS= secur32.lib psapi.lib advapi32.lib shell32.lib user32.lib
修正したらあとはmakeをします。
make
make install
うまくいけば bin/avcodec.lib
, bin/avcodec-57.dll
等が生成されます。
MSYS2/MinGW32
こちらの方式では、コンパイラなどのツールキットとしてgccとその仲間を使います。
msys2/mingw32シェルを開きます。作業ディレクトリにffmpegがチェックアウトされているとして、下記手順でconfigureします。
mkdir out
cd out
../ffmpeg/configure --enable-shared
特に特別なことはありません。自動的にmsys2/mingw32環境のツールキットが選択されます。ここではDLLがほしいので --enable-shared
を指定しています。その他のオプションは普通のffmpegのconfigureなので目的に応じて適宜指定してください。configureが成功したら config.mak
が生成されます。これを Makefile
が読み込んでビルドする仕組みです。
しかしこのままだと問題が生じます。ビルドすること自体はできるのですが、最終的に、 libgcc_s_seh-1.dll
, libwinpthread-1.dll
という2つの謎のDLLへの動的依存が残ったDLLが生成されてしまいます。これだとmingw32環境専用のバイナリになってしまっていて、通常のwindows環境で起動できません。これを解決するために、 config.mak
にこの2つのライブラリを静的リンクさせるように修正を行います。
下記のような行を探します。
EXTRALIBS= -lsecur32 -lm -lbz2 -lz -lpsapi -ladvapi32 -lshell32
ここに下記のように -static-libgcc -Wl,-Bstatic -lpthread -Wl,-Bdynamic
を挿入します。
EXTRALIBS=-static-libgcc -Wl,-Bstatic -lpthread -Wl,-Bdynamic -lsecur32 -lm -lbz2 -lz -lpsapi -ladvapi32 -lshell32
これはgccのCRTとpthreadのライブラリを静的リンクして、それ以降は動的リンクするように指定しています。
修正したらあとはmakeをします。
make
make install
うまくいけば bin/avcodec.lib
, bin/avcodec-57.dll
等が生成されます。
生成物の確認
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin
に dumpbin
というツールが入っていて、これを使うとDLLの依存関係などが確認できます。
下記で依存先DLLが確認できます。
dumpbin -dependents bin/avcodec-57.dll
下記で依存先DLLから取り込むシンボルが確認できます。
dumpbin -imports bin/avcodec-57.dll
下記でエクスポートしているシンボルが確認できます。
dumpbin -exports bin/avcodec-57.dll
上記コマンドで調べると、vs製とgcc製で少し違いが出ます。
gccの方は msvcrt.dll
への依存があり、vs製のものはそれがありません。gccの方はどうやらlibcが静的リンクされるが、libc自体が msvcrt.dll
に動的依存しています。ここらへんのVisual Studio標準DLLは 公式サイト に説明があります。また、msys2のサイトにも説明があります。 それによると結構昔からあるやつで、Windowsに同梱されているそうです。なので、ポータビリティとしては問題なさそうです。vs製のほうはCRT部分は静的リンクされているようです。コマンドラインでは特に指定されていませんが、指定されていない場合 /MT
扱いになるようです。VCのCRTのオプションはここに情報があります。