Windows
ffmpeg

ffmpegをwindows向けにビルドした方法

More than 1 year has passed since last update.

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\VCvcvarsall.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\bindumpbin というツールが入っていて、これを使うと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のオプションはここに情報があります。