QSV対応FFmpegをコンパイルする2022(Gemini Lake、Ubuntu20.04編)


皆さん、FFmpegコンパイルしてますか?
誰しも人生のうち一度はFFmpegコンパイルしたいと思う時があると思います。
今回、ついに私もパッケージマネージャのバイナリでは不満な事例がでてきてしまい、さらに先日FFmpeg 5.0がリリースされましたので、いい機会と思いコンパイルしてみようと思った次第です。

想定環境

CPUはCeleron J4105(Gemini Lake)、OSはUbuntu Server 20.04 LTS。

Gemini LakeはIntel Atom系列のブランドの一つです。超低価格ノートにありがちなCeleron N4000がまさにこれとか、iGPUだけでいうとKaby Lakeなどと一緒の世代とか、でなんとなく分るでしょうか。
そしてUbuntuは20.04 LTSです。22.04 LTSはあと2か月あります。
……なんでこんなことわざわざ書いてるのかは後述。

目標

動画ファイルのエンコードをしたい、けど画質よりも変換スピードを重視したい。使用するCPUが高速とは言えない。
そのために、想定環境において(主要な映像コーデックの)ハードウェアアクセラレーションでのデコードおよびエンコード対応のffmpegがほしい。
ffmpeg本体のコンパイル以外はなるべく楽をしたいのでインストールできるパッケージがあればそちらで済ませる。
ヘッドレスサーバで動作させるのでX11関連のパッケージをなるべくインストールしたくない。

ドキュメントを読む

ffmpegコンパイル方法などインターネット上に数え切れないほどありますが、基本的には公式方面からドキュメントを集めていきます。

https://www.ffmpeg.org/documentation.html
他を読んでどうしても分からないときは結局ここ

HWAccelIntro – FFmpeg
ffmpegで使えるハードウェアアクセラレーションの確認。一応目を通す

CompilationGuide/Ubuntu – FFmpeg
Ubuntu等における一般的なffmpegコンパイルのガイド。3回読む

Hardware/QuickSync – FFmpeg
Intel CPUの世代とそれぞれで対応する映像コーデックのリスト、QSV対応バイナリのコンパイルオプションおよび実際のffmpegのコマンド例。3回読む

GitHub - intel/media-driver
Intel Media Driver(iHD driver)のビルド方法はREADMEに全部書いてある

Build FFmpeg QSV · Intel-Media-SDK/MediaSDK Wiki · GitHub
Intel Media SDK(MSDK)(libmfx)のドキュメント内にもffmpegコンパイル方法が書いてある。ふたつ上のとすこしかぶるけど

以上より

あたりが肝、というのが分かります。

それでは、主にCompilationGuide/Ubuntuを見つつ始めていきます。

FFmpeg依存パッケージのインストールとか下準備とか

コンパイルに必須のパッケージ。ただし、ガイドにあるようにffplayが必要ない場合それらの依存パッケージはインストールしなくてよいのと、Ubuntu 20.04に限り追加でlibunistring-devがインストールが必要のようですね。

sudo apt install \
  autoconf \
  automake \
  build-essential \
  cmake \
  git-core \
  libass-dev \
  libfreetype6-dev \
  libgnutls28-dev \
  libmp3lame-dev \
  libtool \
  libvorbis-dev \
  pkg-config \
  texinfo \
  wget \
  yasm \
  zlib1g-dev \
  libunistring-dev
sudo apt install \
  nasm \
  libx264-dev \
  libx265-dev \
  libnuma-dev \
  libvpx-dev \
  libfdk-aac-dev \
  libopus-dev

上記のガイドではH.265やVP9、AV1など新しめの映像フォーマットにも対応するようにパッケージのインストールをしています。今回の主目的とずれるこれらの(ソフトウェア)コーデックは必要性は無いのですが、まあまあ汎用的に使えるバイナリを作ったほうがいいだろうという思いでほぼガイド通りにインストールします。
ただしAV1およびVMAF(これはコーデックではなく映像の定量的な評価システム)に関しては今回は除外しました。現代のハイパワーなCPUならともかくGemini Lakeには荷が重すぎると判断したためです。とはいえH.265とかOpusとかいつ使うかな......

mkdir -p ~/ffmpeg_sources ~/bin

ffmpegコンパイル済ファイルを置くディレクトリを用意します。

echo -e "/usr/local/lib\n/opt/intel/mediasdk/lib" | sudo tee -a /etc/ld.so.conf.d/locallib.conf
sudo ldconfig -v

この後自分でビルドした諸々がインストールされる場所をライブラリ検索パスに追加します。後ででもいいかも。

Intel Media Driver(iHD driver)

自分でビルドします。
ドキュメントにはUbuntu19.04以降ならaptでインストールできると書いてあるのですが罠です。
Ubuntu20.04でaptでインストールできるMedia Driverのパッケージを使ってビルドしたFFmpegはGemini Lakeでだけ使用できないという問題があるのです。
[Issue] KO hardware decode/encode with Gemini lake family · Issue #930 · intel/media-driver · GitHub
このissueが建てられたのは2020年5月で後にちゃんと修正されていますが、Ubuntu 20.04のaptでインストールできるバージョンは今も相変わらず2020Q1。
Ubuntu 22.04リリース後とか、Gemini Lake以外のCPUを使ってるとか、そういう場合はわざわざビルドする必要は無いと思われます......

sudo apt install \
  libdrm-dev \
  libgl1-mesa-glx \
  libgl1-mesa-dev

media-driverの依存パッケージ、ただしX11関連のパッケージはインストールしません

libva

media-driverのREADMEに従ってビルドします。

cd ~/ 
wget https://github.com/intel/libva/archive/refs/tags/2.13.0.tar.gz 
tar xzvf 2.13.0.tar.gz 
 cd libva-2.13.0/ 
./autogen.sh 
make -j"$(nproc)" 
sudo make install
gmmlib

同じくビルドします

cd ~/ 
wget https://github.com/intel/gmmlib/archive/refs/tags/intel-gmmlib-22.0.2.tar.gz 
tar xzvf intel-gmmlib-22.0.2.tar.gz 
cd gmmlib-intel-gmmlib-22.0.2 
mkdir build 
cd build 
cmake -DCMAKE_BUILD_TYPE=Release -DARCH=64 .. 
make -j"$(nproc)" 
sudo make install
Media Driver
cd ~/
wget https://github.com/intel/media-driver/archive/refs/tags/intel-media-22.1.1.tar.gz 
tar xzvf intel-media-22.1.1.tar.gz 
mkdir build_media 
cd build_media 
cmake ../media-driver-intel-media-22.1.1 
make -j"$(nproc)" 
sudo make install

Media SDK(libmfx)

既に依存関係にあるパッケージのインストールやlibvaのビルドは終わってるので、ドキュメント前半をすっ飛ばしていきなりビルドできます。

cd ~/
wget https://github.com/Intel-Media-SDK/MediaSDK/archive/refs/tags/intel-mediasdk-22.1.0.tar.gz
tar xzvf intel-mediasdk-22.1.0.tar.gz
cd MediaSDK-intel-mediasdk-22.1.0
mkdir build && cd build
cmake ..
make -j"$(nproc)"
sudo make install

FFmpeg

さてメインディッシュです。
AV1関連オプションを外し、libmfxを有効、パッケージコンフィグパスをmsdkがインストールされている場所に指定しなおしています。

cd ~/ffmpeg_sources
wget https://www.ffmpeg.org/releases/ffmpeg-5.0.tar.gz
tar xzvf ffmpeg-5.0.tar.gz
cd ffmpeg-5.0
PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="/opt/intel/mediasdk/lib/pkgconfig" ./configure \
  --prefix="$HOME/ffmpeg_build" \
  --pkg-config-flags="--static" \
  --extra-cflags="-I$HOME/ffmpeg_build/include" \
  --extra-ldflags="-L$HOME/ffmpeg_build/lib" \
  --extra-libs="-lpthread -lm" \
  --ld="g++" \
  --bindir="$HOME/bin" \
  --disable-ffplay \
  --enable-gpl \
  --enable-gnutls \
  --enable-libass \
  --enable-libfdk-aac \
  --enable-libfreetype \
  --enable-libmp3lame \
  --enable-libopus \
  --enable-libvorbis \
  --enable-libvpx \
  --enable-libx264 \
  --enable-libx265 \
  --enable-nonfree \
  --enable-libmfx
PATH="$HOME/bin:$PATH" make -j$(nproc)
make install
hash -r

これで、~/binにffmpegが生えてるはずです。

エンコードテスト

Big Buck Bunnyを変換してみましょう。
Big Buck Bunny

なお素の状態だとパーミッションで怒られるので、sudoなしでエンコードしたい場合は

sudo gpasswd -a $USER render

ユーザーをrenderグループに追加して再起動、あたりが楽でしょうか? udevをいじる方法もありますが。

以下の2つのコマンドを試してみます。

ffmpeg -hwaccel qsv -c:v h264_qsv -i bbb_sunflower_1080p_30fps_normal.mp4 -vf 'scale_qsv=1280:720' -c:v h264_qsv -look_ahead 1 -global_quality:v 36 -c:a aac -b:a 128k -ac 2 output.mp4

画質とサイズのバランスをとって720p。

ffmpeg -hwaccel qsv -c:v h264_qsv -i bbb_sunflower_1080p_30fps_normal.mp4 -vf 'scale_qsv=720:480' -aspect 16:9 -c:v h264_qsv -look_ahead 1 -global_quality:v 44 -c:a libfdk_aac -b:a 48k -ac 2 -profile:a aac_he_v2 output.mp4

小さな画面サイズでの再生を想定してギリギリまでビットレートを落として480p。

global_qualityで画質の調整をしています。数値を小さくするほど画質が良くなる代わりにビットレートは高くなります。
私の場合は720pでglobal_qualityを36、480pで44という数値にしてみました。ついでに480pでは音声のビットレートも馬鹿にならないくらい映像のビットレートが落ちているので、HE-AAC V2の48kbps、つまりradikoと同じコーデック・ビットレートにしてみました。
見てわかる通り、画質を犠牲にかなりファイルサイズを抑えています。
以上のそれぞれのコマンドでの実行結果は以下の通り。

変換前
720p、1016kbps、4.73倍速
480p、239kbps(にしてはなかなか!)、7.64倍速

はい

当初の目標通り、マシンパワーを考慮すればかなり高速・ビットレートを考慮すればそこそこの画質でのエンコードに成功しました。
正直こういうのって試行錯誤から実際に動作を確認するまでの段階までが一番楽しいですよね......まあ趣味なのでどこに労力かけようが楽しければ何でもいいのです。