QSV 対応 Intel CPU で H.264 のハードウェアエンコード on Linux

  • 22
    いいね
  • 0
    コメント

はじめに

H.264 エンコードもすっかり普及して久しい感のある今日この頃。
画質ではソフトウェアエンコードに及ばないものの、速度や負荷の面で有利なハードウェアエンコードは用途によってはとても有用です。
今回は、バージョン 3.1 で VAAPI によるエンコードに対応した FFmpeg を使って QSV 対応 CPU による H.264 のハードウェアエンコードを試します。

Intel QSV

Intel QSV (Quick Sync Video) は、最近 (Sandy Bridge 以降) の Intel の CPU に搭載されているハードウェアエンコーダ/デコーダです。
CPU の世代によっては MPEG2 や H.264 だけでなく H.265 や VP8 のエンコード/デコードにも対応しています。
QSV を利用するには対応したアプリケーションが必要です。

Intel Media Server Studio

Linux 上で QSV を利用するには Intel Media Server Studio (以下 MSS) をインストールし、QSV 対応アプリ (FFmpeg 等) から利用するという方法があるようです。1 2 3 4
MSS に含まれている Intel Media SDK を使用することで QSV を利用できるようになるのですが、MSS は対応する CPU の世代が限定されていたり5、特定のディストリビューション・カーネルバージョンが推奨されていたり6、カーネルのビルドが必要だったり7、と気軽に利用するには少しハードルが高い感じがします。

VAAPI

VAAPI (Video Acceleration API) とは、Linux 上で動画のエンコード/デコードにハードウェアアクセラレーションを利用するためのオープンソースな API で、Intel の Graphics for Linux* というプロジェクトの一環として開発されています。
クローズドソースな Intel Media SDK を使用する場合に比べてパフォーマンスは劣るようですが8、MSS のように CPU の世代やディストリビューションに縛られることなく QSV が利用できるので、手軽に試してみるには良さそうです。

インストール

さっそく自宅サーバとして稼働中の PRIMERGY TX1310 M1 で VAAPI を試してみました。
スペックは以下の通りです

CPU : Xeon E3-1246v3 (Haswell Refresh / Intel HD Graphics P4600)
RAM : 32GB (DDR3-1600 non-ECC)
Chipset : Intel C226
OS : CentOS 7.2

VAAPI / Intel ドライバ

CentOS で VAAPI を利用するためには libvalibva-intel-driver が必要です。
(Ubuntu では libva1i965-va-driver がそれらしい?)
今回は該当パッケージが含まれている Simone Caronni さんのリポジトリ を利用します。
野良リポジトリの利用が気になる場合は VAAPI の公式 からソースをダウンロードしてビルドするのが良いでしょう。

まず epel リポジトリを追加します。
ついでに yum-config-manager を使うために yum-utils も入れておきます。

$ sudo yum install epel-release yum-utils

Caronni さんの epel-handbrake リポジトリを追加します。

$ sudo yum-config-manager --add-repo=http://negativo17.org/repos/epel-handbrake.repo

追加したリポジトリをデフォルトでは不使用にしておきます。

$ sudo yum-config-manager --disable epel epel-HandBrake

必要なパッケージをインストールします。
libva-devel は後ほど ffmpeg のビルド時に必要となります。
libva-utils に含まれる vainfo は VAAPI の状況を確認するのに便利です。

$ sudo yum --enablerepo=epel,epel-HandBrake libva libva-devel libva-intel-driver libva-utils

VAAPI を使用できるように、自ユーザを video グループに追加します。
( /etc/udev/rules.d/ に適当なルールを作成して /dev/dri/renderD128 のパーミッションを 666 等にしても良いと思います)

$ sudo usermod -aG video ユーザ名

ログインし直して id コマンド等で video グループに属していることを確認したら vainfo コマンドで VAAPI が利用できることを確認します。

$ vainfo
error: can't connect to X server!
libva info: VA-API version 0.38.1
libva info: va_getDriverName() returns 0
libva info: Trying to open /usr/lib64/dri/i965_drv_video.so
libva info: Found init function __vaDriverInit_0_38
libva info: va_openDriver() returns 0
vainfo: VA-API version: 0.38 (libva 1.6.2)
vainfo: Driver version: Intel i965 driver for Intel(R) Haswell Server - 1.6.2
vainfo: Supported profile and entrypoints
      VAProfileMPEG2Simple            : VAEntrypointVLD
      VAProfileMPEG2Simple            : VAEntrypointEncSlice
      VAProfileMPEG2Main              : VAEntrypointVLD
      VAProfileMPEG2Main              : VAEntrypointEncSlice
      VAProfileH264ConstrainedBaseline: VAEntrypointVLD
      VAProfileH264ConstrainedBaseline: VAEntrypointEncSlice
      VAProfileH264Main               : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointEncSlice
      VAProfileH264High               : VAEntrypointVLD
      VAProfileH264High               : VAEntrypointEncSlice
      VAProfileH264MultiviewHigh      : VAEntrypointVLD
      VAProfileH264MultiviewHigh      : VAEntrypointEncSlice
      VAProfileH264StereoHigh         : VAEntrypointVLD
      VAProfileH264StereoHigh         : VAEntrypointEncSlice
      VAProfileVC1Simple              : VAEntrypointVLD
      VAProfileVC1Main                : VAEntrypointVLD
      VAProfileVC1Advanced            : VAEntrypointVLD
      VAProfileNone                   : VAEntrypointVideoProc
      VAProfileJPEGBaseline           : VAEntrypointVLD
      VAProfileH264MultiviewHigh      : VAEntrypointVLD
      VAProfileH264MultiviewHigh      : VAEntrypointEncSlice
      VAProfileH264StereoHigh         : VAEntrypointVLD
      VAProfileH264StereoHigh         : VAEntrypointEncSlice

利用可能なプロファイル一覧が出てくれば成功です。
X を起動していないので X と接続できない旨のエラーが出ていますが特に問題ありません。
ここで VAEntrypointVLD の項目はハードウェアデコード可能、VAEntrypointEncSlice の項目はハードウェアエンコード可能です。

これで VAAPI を利用する準備が整いました。

FFmpeg

前項で利用した epel-HandBrake リポジトリからも ffmpeg は入手できるのですが、現時点でバージョンが 2.8.8 と少し古いため、以下の公式手順に従って最新版の FFmpeg をビルドします。
https://trac.ffmpeg.org/wiki/CompilationGuide/Centos
この手順ではホームディレクトリ以下に必要なライブラリを展開してビルドするので OS 環境に影響を与えません。

libva-devel が正しくインストールされていれば自動的に VAAPI に対応した ffmpeg がビルドされますが、念のため ffmpegconfigure を実行した際の出力の中で

  • External libraries providing hardware acceleration: の項目に vaapi が含まれていること
  • Enabled hwaccels: の項目に h264_vaapi 等の利用したいコーデックが含まれていること

を確認しておきましょう。

ビルドに成功したら改めて VAAPI への対応を確認します。

$ ~/bin/ffmpeg -hwaccels 2>/dev/null
Hardware acceleration methods:
vaapi

$ ~/bin/ffmpeg -encoders 2>/dev/null |fgrep vaapi
 V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)
 V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)
 V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)

エンコード

それでは実際にエンコードしてみましょう。

今回はアニメ 1 話 30 分の TS ファイルをエンコードしてみます。
ffmpeg 実行時はオプションの順番にも気をつけましょう。

$ ~/bin/ffmpeg -vaapi_device /dev/dri/renderD128 \
> -hwaccel vaapi -hwaccel_output_format vaapi \
> -i anime.ts \
> -vf 'format=nv12|vaapi,hwupload,scale_vaapi=w=1280:h=720' \
> -c:v h264_vaapi -profile 100 -level 40 -qp 23 -aspect 16:9 \
> -c:a copy \
> anime.mp4
(略)
frame=53848 fps=343 q=-0.0 Lsize=  711827kB time=00:29:56.66 bitrate=3245.6kbits/s dup=24 drop=0 speed=11.4x
$ 

2分38秒でエンコードが完了しました!速いですね!
top で確認すると ffmpeg プロセスのCPU 使用率は終始 12% 程度でした。

オリジナル エンコード後
長さ 29:56 29:56 (ノーカット)
ファイルサイズ 2,030 MB 695 MB
解像度 1440x1080 1280x720
フレームレート 29.97 (60i) 29.97 (30p)
映像コーデック MPEG2 H.264
音声コーデック AAC AAC (無変換)

オプション

ffmpeg で VAAPI を使用するときのオプションは以下のような感じです。

-vaapi_device
使用する VAAPI デバイスを指定します。QSV 対応 CPU を利用する場合は /dev/dri/renderD128 の模様。
-hwaccel
デコードに使用するハードウェア機能を指定します。今回はデコードも QSV を利用したため vaapi を指定しました。デコードをソフトウェアで行う場合はこのオプションは不要です。
-hwaccel_output_format
-hwaccel と一緒に使用し、ハードウェアデコード後の形式を指定します。デコードもエンコードも VAAPI で行う場合、ここで vaapi を指定するとデコード結果が直接エンコーダに渡され、処理がより高速になります。ただしエンコードの前に ffmpeg のデインターレース等のソフトウェアフィルタを利用したい場合、デコード結果を一旦デコーダから ffmepg 側に渡す必要があるので yuv420p 等を指定します。
-i
入力ファイルを指定します。ハードウェアデコードを使用する場合はこれより先に上記のオプションを記述しておきます。
-vf
フィルタを指定します。複数のフィルタは , で区切って記述します。VAAPI を利用する場合は基本的に format=nv12|vaapi,hwupload を指定しておけば大丈夫だと思います。今回は scale_vaapi フィルタを加えてリサイズにもハードウェアを使用してみました。
-c:v
映像コーデックを指定します。h264_vaapi を指定すると VAAPI を使用して H.264 のエンコードが行われます。
-profile
H.264 のプロファイルです。デフォルト値は 100 (High) です。Baseline は 66、Main は 77 を指定するようです。
-level
H.264 のレベルです。実際のレベルを 10 倍した値を指定します (Level 4.1 なら 41)。解像度・フレームレートや再生デバイスによって選択しましょう。デフォルト値は 51 (Level 5.1) です。今回は Level 4.0 としました。
-qp
P フレームの品質を指定します。1 (最良) ~ 51 (最悪) で設定します。デフォルトは 20 です。数値が小さいほど高品質・高ビットレートになり、大きいほど低品質・低ビットレートになります。ffmpeg で VAAPI を使用する場合、デフォルトではこの値を元にした CQP エンコードとなるようです。
-b
ビットレートを指定します。このオプションでビットレートが指定された場合 -qp オプションは無視され、CBR エンコードとなるようです。今回は CQP でのエンコードを試したためこのオプションは指定しませんでした。
-aspect
今回はエンコード前後で PAR が異なるため DAR を明示的に指定しています。

その他にもいくつか VAAPI 関連のオプションがありますが、詳細は以下が参考になります。
https://wiki.libav.org/Hardware/vaapi
https://gist.github.com/Brainiarc7/95c9338a737aa36d9bb2931bed379219

比較

-qp オプションの値をいくつか変えてエンコードしてみました。
またソフトウェアエンコード (x264) とも比較してみました。
x264 で使用したオプションは以下です。

$ ~/bin/ffmpeg -i anime.ts \
> -codec:v libx264 -profile:v high -level 4.0 -preset medium -qp XX -s 1280x720 \
> -c:a copy \
> anime_x264_qpXX.mp4

x264 では qp は 0~69 だったため、((VAAPI の qp) * 69 / 51) を指定してみました。
ついでに x264 では qp よりも crf 指定の方が一般的と思われるため crf 23 も比較してみました。
オプションは上記の -qp XX-crf 23 に変えて実行しました。

No 形式 ファイルサイズ エンコード時間
0 オリジナル 2,030 MB -
1 VAAPI / qp 20 1,027 MB 2:39
2 VAAPI / qp 23 695 MB 2:38
3 VAAPI / qp 26 501 MB 2:38
4 x264 / qp 27 321 MB 7:21
5 x264 / qp 31 219 MB 6:52
6 x264 / qp 35 161 MB 6:18
7 x264 / crf 23 349 MB 8:22

結果は上記の通りです。

画質
1,4,7 ではブロックノイズが目立つこともなくまずまずの感じでした。2,5 になると動きのある部分で少しノイズが目につき出し、3,6 ではさらにノイズがのり一部で輪郭もぼやけています。また VAAPI では他の QSV 検証記事などでも書かれているように、グラデーション表現でノイズが出やすい気がしました。
速度
VAAPI 使用時が速いです。さすがハードウェアエンコードというところですね。VAAPI では qp 値はエンコード速度にあまり影響しないようです。対して x264 では qp 値に比例して速度が上がっています。Xeon をぶん回したので x264 でもそれなりの速度が出ました。
負荷
VAAPI では ffmpeg プロセスのCPU 使用率は 12% 程度でした。一方 x264 では 700% 前後とほぼフル稼働、ファンの回転数も上がって非エコな感じでした。自宅サーバはエンコード専用マシンではないのでエンコード時に高負荷になり他プロセスに影響が出るのは避けたいところです。
圧縮率
こちらはさすが x264、VAAPI の 2 倍以上の圧縮率となっています。ディスクの節約が最優先の場合は x264 一択ですね。

これらを踏まえてどれを選択するかは完全に個人の感覚次第ですが、録画して見終わったら消すようなものであれば、画質・速度・負荷・圧縮率を考えると VAAPI / qp 23 でも十分ありかなと個人的には思います。

おわりに

ハードウェアエンコードは保存用途には向きませんが、見たら消す予定の ts ファイルを小さくしておき HDD を節約する用途としては有用ではないかと思います。
今回は 3.5GHz 4C/8T の Xeon を使用したため x264 と比べてそこまで大きな時間差はありませんでしたが、動作クロックの低い Pentium や Celeron の場合はさらに大きな差がつくでしょう。

現状 ffmpeg から VAAPI を利用する場合、QSV のデインターレース・デノイズ・シャープネス等の機能は使えないようですが、gstreamer の VAAPI プラグインではこれらも利用できるようなので VAAPI を極めたい方は gstreamer も試してみると良いと思います。

また FFmpeg 3.1 では Raspberry Pi に載っている H.264 エンコーダにも対応したようなので、これも試してみたいところです。

今さらですが、動画についても FFmpeg についてもそれほど詳しいわけではないので間違いがあったらすみません…。
とはいえ少しでも参考になればうれしいです。

期待作が目白押しの秋アニメにむけて、万全の環境で臨みましょう!

脚注