IntelQSVを使ったHWEncode環境 (Ubuntu版)

以前にnvidiaのnvencを使ったffmpegHWEncode環境についての投稿をしました。

https://qiita.com/yamakenjp/items/7474f210efd82bb28490


今度はIntel内蔵GPUの機能であるQSVを使った例です。
下記をベースに入れていきます。

https://github.com/Intel-Media-SDK/MediaSDK/wiki/Build-FFmpeg-QSV

このテーマに向いている人

・多量のMPEG2やH.264動画を所有していて、H.265に変換したい人
　（画質にそこまでこだわりはなく、ストレージの容量逼迫してるからさっさと片付けたい！など）

・何かしらのMPEG2-TSを録画する環境があり、録画した後にH.265へのトランスコードをしたいなと思ってる人
　（多分このような用途の人には一番おすすめな気がする）

・動画配信などでリアルタイムトランスコードが必要な人
　（画質より速度が重要視される分野）

# 必須環境

・intelCPUを使っていて内蔵GPUが使えるUbuntu 24.04 マシン
・Broadwell世代以降のGPU内蔵CPU

今回は Intel Media SDK + iHDで作成してるためCPU世代でgen8以降が対象です。

GPU世代によってH.265のエンコードができない等があります。詳しくは
https://trac.ffmpeg.org/wiki/Hardware/QuickSync


# 準備
## Intel media stack componentsとiHDドライバのインストール

Ubuntu19.04以降は公式aptに用意されてるので非常に楽です。

```
# Intel media stack componentsのインストール
sudo apt-get install libva-dev libmfx-dev intel-media-va-driver-non-free vainfo
export LIBVA_DRIVER_NAME=iHD
```

インストール完了後、vainfoを実行し、ドライバが見えるか確認しましょう。

```
test@test:~$ sudo vainfo
error: XDG_RUNTIME_DIR is invalid or not set in the environment.
error: can't connect to X server!
libva info: VA-API version 1.20.0
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so
libva info: Found init function __vaDriverInit_1_20
libva info: va_openDriver() returns 0
vainfo: VA-API version: 1.20 (libva 2.12.0)
vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics - 24.1.0 ()
vainfo: Supported profile and entrypoints
      VAProfileNone                   :	VAEntrypointVideoProc
      VAProfileNone                   :	VAEntrypointStats
      VAProfileMPEG2Simple            :	VAEntrypointVLD
      VAProfileMPEG2Simple            :	VAEntrypointEncSlice
      VAProfileMPEG2Main              :	VAEntrypointVLD
      VAProfileMPEG2Main              :	VAEntrypointEncSlice
      VAProfileH264Main               :	VAEntrypointVLD
      VAProfileH264Main               :	VAEntrypointEncSlice
      VAProfileH264Main               :	VAEntrypointFEI
      VAProfileH264Main               :	VAEntrypointEncSliceLP
      VAProfileH264High               :	VAEntrypointVLD
      VAProfileH264High               :	VAEntrypointEncSlice
      VAProfileH264High               :	VAEntrypointFEI
      VAProfileH264High               :	VAEntrypointEncSliceLP
      VAProfileVC1Simple              :	VAEntrypointVLD
      VAProfileVC1Main                :	VAEntrypointVLD
      VAProfileVC1Advanced            :	VAEntrypointVLD
      VAProfileJPEGBaseline           :	VAEntrypointVLD
      VAProfileJPEGBaseline           :	VAEntrypointEncPicture
      VAProfileH264ConstrainedBaseline:	VAEntrypointVLD
      VAProfileH264ConstrainedBaseline:	VAEntrypointEncSlice
      VAProfileH264ConstrainedBaseline:	VAEntrypointFEI
      VAProfileH264ConstrainedBaseline:	VAEntrypointEncSliceLP
      VAProfileVP8Version0_3          :	VAEntrypointVLD
      VAProfileVP8Version0_3          :	VAEntrypointEncSlice
      VAProfileHEVCMain               :	VAEntrypointVLD
      VAProfileHEVCMain               :	VAEntrypointEncSlice
      VAProfileHEVCMain               :	VAEntrypointFEI
      VAProfileHEVCMain10             :	VAEntrypointVLD
      VAProfileHEVCMain10             :	VAEntrypointEncSlice
      VAProfileVP9Profile0            :	VAEntrypointVLD
      VAProfileVP9Profile2            :	VAEntrypointVLD
```

`Driver version: Intel iHD driver for Intel(R) Gen Graphics - 24.1.0 `
とのことで、問題なくiHDが使えます。

しかし、このままでは、エンコードに使う /dev/dri/renderD128 のパーミッションの問題で、一般ユーザーでffmpegを使う場合、HWエンコードができなくなります。

```
test@test:~$ ls -l /dev/dri/render*
crw-rw---- 1 root render 226, 128 Feb 23 17:45 /dev/dri/renderD128
test@test:~$ 
```

そこで、一般ユーザーでもアクセス可能にするための設定ファイルを作成します。

```
sudo vi /etc/udev/rules.d/99-render.rules
```

```99-render.rules
KERNEL=="render*" GROUP="render", MODE="0666"
```

OS再起動後、デバイスのパーミッションを確認します。

```
test@test:~$ ls -l /dev/dri/render*
crw-rw-rw- 1 root render 226, 128 Aug 15 13:53 /dev/dri/renderD128
test@test:~$ 
```

vainfoも一般ユーザーで実行できるようになるはずです。

```
test@test:~$ vainfo
error: can't connect to X server!
libva info: VA-API version 1.20.0
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so
libva info: Found init function __vaDriverInit_1_20
libva info: va_openDriver() returns 0
vainfo: VA-API version: 1.20 (libva 2.12.0)
vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics - 24.1.0 ()
vainfo: Supported profile and entrypoints
      VAProfileNone                   :	VAEntrypointVideoProc
      VAProfileNone                   :	VAEntrypointStats
      VAProfileMPEG2Simple            :	VAEntrypointVLD
      VAProfileMPEG2Simple            :	VAEntrypointEncSlice
      VAProfileMPEG2Main              :	VAEntrypointVLD
      VAProfileMPEG2Main              :	VAEntrypointEncSlice
      VAProfileH264Main               :	VAEntrypointVLD
      VAProfileH264Main               :	VAEntrypointEncSlice
      VAProfileH264Main               :	VAEntrypointFEI
      VAProfileH264Main               :	VAEntrypointEncSliceLP
      VAProfileH264High               :	VAEntrypointVLD
      VAProfileH264High               :	VAEntrypointEncSlice
      VAProfileH264High               :	VAEntrypointFEI
      VAProfileH264High               :	VAEntrypointEncSliceLP
      VAProfileVC1Simple              :	VAEntrypointVLD
      VAProfileVC1Main                :	VAEntrypointVLD
      VAProfileVC1Advanced            :	VAEntrypointVLD
      VAProfileJPEGBaseline           :	VAEntrypointVLD
      VAProfileJPEGBaseline           :	VAEntrypointEncPicture
      VAProfileH264ConstrainedBaseline:	VAEntrypointVLD
      VAProfileH264ConstrainedBaseline:	VAEntrypointEncSlice
      VAProfileH264ConstrainedBaseline:	VAEntrypointFEI
      VAProfileH264ConstrainedBaseline:	VAEntrypointEncSliceLP
      VAProfileVP8Version0_3          :	VAEntrypointVLD
      VAProfileVP8Version0_3          :	VAEntrypointEncSlice
      VAProfileHEVCMain               :	VAEntrypointVLD
      VAProfileHEVCMain               :	VAEntrypointEncSlice
      VAProfileHEVCMain               :	VAEntrypointFEI
      VAProfileHEVCMain10             :	VAEntrypointVLD
      VAProfileHEVCMain10             :	VAEntrypointEncSlice
      VAProfileVP9Profile0            :	VAEntrypointVLD
      VAProfileVP9Profile2            :	VAEntrypointVLD
```

## FFmpegのインストール
Compile FFmpeg for Ubuntu, Debian, or Mint
https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu
こちらをベースに全てソースコードで入れていきます。

個人的にhapエンコードを使いたいので libsnappy-dev をインストールの上
`--enable-libsnappy` を追記しています。

またTLS対応のために libssl-dev をインストールの上
`--enable-openssl` を追記しています。

```
#FFmpegコンパイルに必要なものを導入
sudo apt-get update -qq && sudo apt-get -y install \
  autoconf \
  automake \
  build-essential \
  cmake \
  git-core \
  libass-dev \
  libfreetype6-dev \
  libmp3lame-dev \
  libssl-dev \
  libtool \
  libvorbis-dev \
  libsnappy-dev \
  meson \
  ninja-build \
  pkg-config \
  texinfo \
  wget \
  yasm \
  nasm \
  zlib1g-dev

#作業ディレクトリ作成
mkdir -p ~/ffmpeg_sources ~/bin

# libx264 インストール
cd ~/ffmpeg_sources && \
git -C x264 pull 2> /dev/null || git clone --depth 1 https://code.videolan.org/videolan/x264.git && \
cd x264 && \
PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --enable-static --enable-pic && \
PATH="$HOME/bin:$PATH" make && \
make install


# libx265 インストール
sudo apt-get install libnuma-dev && \
cd ~/ffmpeg_sources && \
git -C x265_git pull 2> /dev/null || git clone https://bitbucket.org/multicoreware/x265_git && \
cd x265_git/build/linux && \
PATH="$HOME/bin:$PATH" cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$HOME/ffmpeg_build" -DENABLE_SHARED=off ../../source && \
PATH="$HOME/bin:$PATH" make && \
make install


# libvpx インストール
cd ~/ffmpeg_sources && \
git -C libvpx pull 2> /dev/null || git clone --depth 1 https://chromium.googlesource.com/webm/libvpx.git && \
cd libvpx && \
PATH="$HOME/bin:$PATH" ./configure --prefix="$HOME/ffmpeg_build" --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --as=yasm && \
PATH="$HOME/bin:$PATH" make && \
make install


# libfdk-aac インストール
cd ~/ffmpeg_sources && \
git -C fdk-aac pull 2> /dev/null || git clone --depth 1 https://github.com/mstorsjo/fdk-aac && \
cd fdk-aac && \
autoreconf -fiv && \
./configure --prefix="$HOME/ffmpeg_build" --disable-shared && \
make && \
make install


# libopus インストール
cd ~/ffmpeg_sources && \
git -C opus pull 2> /dev/null || git clone --depth 1 https://github.com/xiph/opus.git && \
cd opus && \
./autogen.sh && \
./configure --prefix="$HOME/ffmpeg_build" --disable-shared && \
make && \
make install


# libaom インストール
cd ~/ffmpeg_sources && \
git -C aom pull 2> /dev/null || git clone --depth 1 https://aomedia.googlesource.com/aom && \
mkdir -p aom_build && \
cd aom_build && \
PATH="$HOME/bin:$PATH" cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$HOME/ffmpeg_build" -DENABLE_TESTS=OFF -DENABLE_NASM=on ../aom && \
PATH="$HOME/bin:$PATH" make && \
make install


# libsvtav1 インストール
cd ~/ffmpeg_sources && \
git -C SVT-AV1 pull 2> /dev/null || git clone https://gitlab.com/AOMediaCodec/SVT-AV1.git && \
mkdir -p SVT-AV1/build && \
cd SVT-AV1/build && \
PATH="$HOME/bin:$PATH" cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$HOME/ffmpeg_build" -DCMAKE_BUILD_TYPE=Release -DBUILD_DEC=OFF -DBUILD_SHARED_LIBS=OFF .. && \
PATH="$HOME/bin:$PATH" make && \
make install


# libvmaf インストール
cd ~/ffmpeg_sources && \
wget https://github.com/Netflix/vmaf/archive/v3.0.0.tar.gz && \
tar xvf v3.0.0.tar.gz && \
mkdir -p vmaf-3.0.0/libvmaf/build &&\
cd vmaf-3.0.0/libvmaf/build && \
meson setup -Denable_tests=false -Denable_docs=false --buildtype=release --default-library=static .. --prefix "$HOME/ffmpeg_build" --libdir="$HOME/ffmpeg_build/lib" && \
ninja && \
ninja install


# FFmpeg インストール
cd ~/ffmpeg_sources && \
git -C ffmpeg pull 2> /dev/null || git clone --depth 1 https://git.ffmpeg.org/ffmpeg.git && \
cd ffmpeg && \
PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/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" \
  --enable-vaapi \
  --enable-libmfx \
  --enable-openssl \
  --enable-gpl \
  --enable-libaom \
  --enable-libass \
  --enable-libfdk-aac \
  --enable-libfreetype \
  --enable-libmp3lame \
  --enable-libopus \
  --enable-libsvtav1 \
  --enable-libvorbis \
  --enable-libsnappy \
  --enable-libvpx \
  --enable-libx264 \
  --enable-libx265 \
  --enable-static \
  --enable-nonfree && \
PATH="$HOME/bin:$PATH" make && \
make install && \
hash -r

パスを通したり
source ~/.profile
echo "MANPATH_MAP $HOME/bin $HOME/ffmpeg_build/share/man" >> ~/.manpath
```
## 動作確認
実際にインストールが成功したか、下記のコマンドでQSVエンコード関連オプションが使えるか確認します。

```
for i in buildconf hwaccels decoders filters encoders; do echo $i:; ffmpeg -hide_banner -${i} | egrep -i "qsv|vaapi|libmfx"; done
```

結果はこのようになると思います。

```
test@test:~$ for i in buildconf hwaccels decoders filters encoders; do echo $i:; ffmpeg -hide_banner -${i} | egrep -i "qsv|vaapi|libmfx"; done
buildconf:
    --enable-vaapi
    --enable-libmfx
hwaccels:
vaapi
qsv
decoders:
 V....D av1_qsv              AV1 video (Intel Quick Sync Video acceleration) (codec av1)
 V....D h264_qsv             H264 video (Intel Quick Sync Video acceleration) (codec h264)
 V....D hevc_qsv             HEVC video (Intel Quick Sync Video acceleration) (codec hevc)
 V....D mjpeg_qsv            MJPEG video (Intel Quick Sync Video acceleration) (codec mjpeg)
 V....D mpeg2_qsv            MPEG2VIDEO video (Intel Quick Sync Video acceleration) (codec mpeg2video)
 V....D vc1_qsv              VC1 video (Intel Quick Sync Video acceleration) (codec vc1)
 V....D vp8_qsv              VP8 video (Intel Quick Sync Video acceleration) (codec vp8)
 V....D vp9_qsv              VP9 video (Intel Quick Sync Video acceleration) (codec vp9)
 V....D vvc_qsv              VVC video (Intel Quick Sync Video acceleration) (codec vvc)
filters:
 .. deinterlace_qsv   V->V       Quick Sync Video "deinterlacing"
 .. deinterlace_vaapi V->V       Deinterlacing of VAAPI surfaces
 .. denoise_vaapi     V->V       VAAPI VPP for de-noise
 .. overlay_qsv       VV->V      Quick Sync Video overlay.
 .. overlay_vaapi     VV->V      Overlay one video on top of another
 .. procamp_vaapi     V->V       ProcAmp (color balance) adjustments for hue, saturation, brightness, contrast
 .. scale_qsv         V->V       Quick Sync Video "scaling and format conversion"
 .. scale_vaapi       V->V       Scale to/from VAAPI surfaces.
 .. sharpness_vaapi   V->V       VAAPI VPP for sharpness
 .. tonemap_vaapi     V->V       VAAPI VPP for tone-mapping
 .. transpose_vaapi   V->V       VAAPI VPP for transpose
 .. vpp_qsv           V->V       Quick Sync Video "VPP"
 .. hstack_vaapi      N->V       "VA-API" hstack
 .. vstack_vaapi      N->V       "VA-API" vstack
 .. xstack_vaapi      N->V       "VA-API" xstack
 .. hstack_qsv        N->V       "Quick Sync Video" hstack
 .. vstack_qsv        N->V       "Quick Sync Video" vstack
 .. xstack_qsv        N->V       "Quick Sync Video" xstack
 .. pad_vaapi         V->V       Pad the input video.
 .. drawbox_vaapi     V->V       Draw a colored box on the input video.
encoders:
 V....D av1_vaapi            AV1 (VAAPI) (codec av1)
 V..... h264_qsv             H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration) (codec h264)
 V....D h264_vaapi           H.264/AVC (VAAPI) (codec h264)
 V..... hevc_qsv             HEVC (Intel Quick Sync Video acceleration) (codec hevc)
 V....D hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)
 V..... mjpeg_qsv            MJPEG (Intel Quick Sync Video acceleration) (codec mjpeg)
 V....D mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)
 V..... mpeg2_qsv            MPEG-2 video (Intel Quick Sync Video acceleration) (codec mpeg2video)
 V....D mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)
 V....D vp8_vaapi            VP8 (VAAPI) (codec vp8)
 V....D vp9_vaapi            VP9 (VAAPI) (codec vp9)
 V..... vp9_qsv              VP9 video (Intel Quick Sync Video acceleration) (codec vp9)
test@test:~$ 
```


# 実際に使ってみる

では実際に使ってみましょう。

MPEG2からH.265へ720pでエンコード

```
ffmpeg -hwaccel qsv -hwaccel_output_format qsv -c:v mpeg2_qsv -i input.mp2ts -vf deinterlace_qsv,scale_qsv=-1:720 -c:v hevc_qsv -tag:v hvc1 -f mp4 out.mp4
```
解説です。

> -hwaccel qsv

HWエンコードでqsvを明示

>-hwaccel_output_format qsv 

デコードしたRAWデータをGPUメモリに残したまま処理するためのおまじない

> -c:v mpeg2_qsv

MPEG2のハードウェアデコードを明示

> -i input.mp2ts

変換元ファイルを指定します。

> -vf deinterlace_qsv,scale_qsv=-1:720

インターレス解除と720Pになるよう縮小

> -c:v hevc_qsv

H.265でQSVを使います

> -tag:v hvc1

Apple製品で扱えるH.265は hev1ではなくhvc1の方なので、タグで明示します

> -f mp4

出力ファイルフォーマットがをmp4であると明示します

> out.mp4

出力先ファイル名を指定

# 最後に
使ってみた結果だけ言うと、NVEncの方が速度が速く画質が良いのでソフトエンコするよりは早いけど。てところかなーとおもってます（といってもGPU世代によっても変わるので、出力結果をみてご自身で判断いただければ）