NVENCを使ったHWEncode環境  (RHEL8版)

どうも、やまけんです。
以前書いた下記の記事のRHEL8版です

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

普段あまりRHELを使うことがないので、この記事はほぼ検証のためだけに実行した結果を残すだけで、元記事のように永続的に更新する予定はありません。

おそらくCUDAのバージョン差異でその時々でインストール方法が変わるぐらいだとは思いますので、なんとか読み替えてください。（元記事のUbuntu向けは自分がその環境で使ってるので、変更があれば適宜直してるんですけど。ちょっとRHELまでは無理）

#環境
```
# cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.4 (Ootpa)

# lspci | grep -i nvidia
01:00.0 VGA compatible controller: NVIDIA Corporation TU117 [GeForce GTX 1650] (rev a1)
01:00.1 Audio device: NVIDIA Corporation Device 10fa (rev a1)
```

#準備
##CUDAのインストール
NVIDIA CUDA Installation Guide for Linux に従って入れます
https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html

今回はパッケージで入れたいと思いますので、
2. Pre-installation Actions
3. Package Manager Installation
7. Post-installation Actions
こちらを進めれば良いです。

```
# Development ToolsとKernel Headerを入れる
sudo dnf group install "Development Tools"
sudo dnf install kernel-devel-$(uname -r) kernel-headers-$(uname -r)

# dkmsを入れるためにEPELリポジトリを有効化
yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm

# nvidiaリポジトリを追加
sudo dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo

# dnf キャッシュをクリーン
sudo dnf clean all

# ドライバーをインストール
sudo dnf -y module install nvidia-driver:latest-dkms

# cudaをインストール
sudo dnf -y install cuda

# パスを通す
echo 'export PATH=/usr/local/cuda-11.4/bin${PATH:+:${PATH}}' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-11.4/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}' >> ~/.bashrc

source ~/.bashrc
```

インストールが完了したら再起動し、 nvidia-smi にてドライバインストールも含め完了しているか確認しましょう。



```
# nvidia-smi
Fri Aug 13 10:43:38 2021       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 470.57.02    Driver Version: 470.57.02    CUDA Version: 11.4     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  Off  | 00000000:01:00.0 Off |                  N/A |
| 54%   33C    P0     1W /  75W |      0MiB /  3908MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
# 
```

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

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

また、FFmpeg4以降でnvencを使用する場合は nv-codec-headers が必要です。
これらはNVIDIA Video Codec SDKの一部を使用しています。
https://developer.nvidia.com/ffmpeg
https://github.com/FFmpeg/nv-codec-headers

ちなみに
https://developer.nvidia.com/blog/nvidia-ffmpeg-transcoding-guide/
において、コンパイル時は
`./configure --enable-cuda --enable-cuvid --enable-nvenc --enable-nonfree --enable-libnpp --extra-cflags=-I/usr/local/cuda/include  --extra-ldflags=-L/usr/local/cuda/lib64`
という記載がありますが、 `./configure --help | grep cuda` で調べればわかるとおり、現在は

```
  --enable-cuda-nvcc       enable Nvidia CUDA compiler [no]
```

が正しいため、`--enable-cuda` ではなく `--enable-cuda-nvcc` に変更しています。

またインストールされるCUDAのバージョン次第で
`  --extra-cflags="-I/usr/local/cuda-11.4/include" `
`  --extra-ldflags="-L/usr/local/cuda-11.4/lib64" `
を正しいCUDAライブラリのパスに直す必要があります。

なお、インストールされるドライババージョンによって、NVIDIA Video Codec SDKのバージョン依存が発生します。
https://github.com/FFmpeg/nv-codec-headers のREADMEにて対応バージョンを確認ください。

本ドキュメントは基本的に最新のドライバをインストールしてある前提にあります。

必ず `nvidia-smi` にてインストールされているドライババーションを確認してください。

古いグラボなどで最新ドライバが入らない場合、
https://github.com/FFmpeg/nv-codec-headers の ブランチを切り替えると、過去のSDKでの必要ドライババーションの確認が確認ができます。

ドライバが古い場合は、NVIDIA codec API のインストール時の
`git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers -b sdk/9.0`
のように -b sdk/9.0 でブランチを指定してインストールしてみてください。

```
# FFmpegコンパイルに必要なものを導入
sudo dnf install autoconf automake bzip2 bzip2-devel cmake freetype-devel gcc gcc-c++ git libtool make pkgconfig zlib-devel openssl-devel

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

# NASM インストール
cd ~/ffmpeg_sources
curl -O -L https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.bz2
tar xjvf nasm-2.15.05.tar.bz2
cd nasm-2.15.05
./autogen.sh
./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin"
make
make install

# Yasm インストール
cd ~/ffmpeg_sources
curl -O -L https://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
tar xzvf yasm-1.3.0.tar.gz
cd yasm-1.3.0
./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin"
make
make install

# libx264 インストール
cd ~/ffmpeg_sources
git clone --branch stable --depth 1 https://code.videolan.org/videolan/x264.git
cd x264
PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --enable-static
make
make install

# libx265 インストール
cd ~/ffmpeg_sources
git clone --branch stable --depth 2 https://bitbucket.org/multicoreware/x265_git
cd ~/ffmpeg_sources/x265_git/build/linux
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$HOME/ffmpeg_build" -DENABLE_SHARED:bool=off ../../source
make
make install

# libfdk-aac インストール
cd ~/ffmpeg_sources
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

# libmp3lame インストール
cd ~/ffmpeg_sources
curl -O -L https://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz
tar xzvf lame-3.100.tar.gz
cd lame-3.100
./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --disable-shared --enable-nasm
make
make install

# libopus インストール
cd ~/ffmpeg_sources
curl -O -L https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz
tar xzvf opus-1.3.1.tar.gz
cd opus-1.3.1
./configure --prefix="$HOME/ffmpeg_build" --disable-shared
make
make install

# libvpx インストール
cd ~/ffmpeg_sources
git clone --depth 1 https://chromium.googlesource.com/webm/libvpx.git
cd libvpx
./configure --prefix="$HOME/ffmpeg_build" --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --as=yasm
make
make install

# NVIDIA codec API インストール
cd ~/ffmpeg_sources
git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers
cd nv-codec-headers
make
make install PREFIX="$HOME/ffmpeg_build"

# FFmpeg インストール
cd ~/ffmpeg_sources
curl -O -L https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2
tar xjvf ffmpeg-snapshot.tar.bz2
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" \
  --enable-cuda-nvcc \
  --nvccflags="-gencode arch=compute_52,code=sm_52 -O2" \
  --enable-cuvid \
  --enable-nvenc \
  --enable-libnpp \
  --extra-cflags="-I/usr/local/cuda-11.4/include" \
  --extra-ldflags="-L/usr/local/cuda-11.4/lib64" \
  --extra-libs="-lpthread -lm" \
  --ld="g++" \
  --bindir="$HOME/bin" \
  --enable-openssl \
  --enable-gpl \
  --enable-libfdk-aac \
  --enable-libfreetype \
  --enable-libmp3lame \
  --enable-libopus \
  --enable-libvpx \
  --enable-libx264 \
  --enable-libx265 \
  --enable-static \
  --enable-nonfree
make
make install

```
##動作確認
実際にインストールが成功したか、下記のコマンドでnvencエンコード関連オプションが使えるか確認します。

```
for i in buildconf hwaccels decoders filters encoders; do echo $i:; ffmpeg -hide_banner -${i} | egrep -i "npp|cuvid|nvenc|cuda|nvdec"; done
```

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

```
test@test:~$ for i in buildconf hwaccels decoders filters encoders; do echo $i:; ffmpeg -hide_banner -${i} | egrep -i "npp|cuvid|nvenc|cuda|nvdec"; done
buildconf:
    --enable-cuda-nvcc
    --enable-cuvid
    --enable-nvenc
    --enable-libnpp
    --extra-cflags=-I/usr/local/cuda-11.4/include
    --extra-ldflags=-L/usr/local/cuda-11.4/lib64
hwaccels:
cuda
decoders:
 V..... av1_cuvid            Nvidia CUVID AV1 decoder (codec av1)
 V..... h264_cuvid           Nvidia CUVID H264 decoder (codec h264)
 V..... hevc_cuvid           Nvidia CUVID HEVC decoder (codec hevc)
 V..... mjpeg_cuvid          Nvidia CUVID MJPEG decoder (codec mjpeg)
 V..... mpeg1_cuvid          Nvidia CUVID MPEG1VIDEO decoder (codec mpeg1video)
 V..... mpeg2_cuvid          Nvidia CUVID MPEG2VIDEO decoder (codec mpeg2video)
 V..... mpeg4_cuvid          Nvidia CUVID MPEG4 decoder (codec mpeg4)
 V..... vc1_cuvid            Nvidia CUVID VC1 decoder (codec vc1)
 V..... vp8_cuvid            Nvidia CUVID VP8 decoder (codec vp8)
 V..... vp9_cuvid            Nvidia CUVID VP9 decoder (codec vp9)
filters:
 ... hwupload_cuda     V->V       Upload a system memory frame to a CUDA device.
 ... overlay_cuda      VV->V      Overlay one video on top of another using CUDA
 ... scale_cuda        V->V       GPU accelerated video resizer
 ... scale_npp         V->V       NVIDIA Performance Primitives video scaling and format conversion
 ... thumbnail_cuda    V->V       Select the most representative frame in a given sequence of consecutive frames.
 ... transpose_npp     V->V       NVIDIA Performance Primitives video transpose
 T.. yadif_cuda        V->V       Deinterlace CUDA frames
encoders:
 V....D h264_nvenc           NVIDIA NVENC H.264 encoder (codec h264)
 V..... nvenc                NVIDIA NVENC H.264 encoder (codec h264)
 V..... nvenc_h264           NVIDIA NVENC H.264 encoder (codec h264)
 V..... nvenc_hevc           NVIDIA NVENC hevc encoder (codec hevc)
 V....D hevc_nvenc           NVIDIA NVENC hevc encoder (codec hevc)
test@test:~$

```

きちんとビルド時のオプションが有効なこと、デコーダ、フィルター、エンコーダにnvenc関連が入っているのがわかります。

表示が出ない場合、今回コンパイルしたffmpegではないパッケージでインストールしたffmpegなどの結果になっている可能性があります。 `which ffmpeg` にてffmpegのパスを確認し、今回コンパイルしたffmpegで間違い無いかを確認してください。

#実際に使ってみる

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

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

```
ffmpeg -hwaccel cuvid -c:v mpeg2_cuvid -deint adaptive -drop_second_field 1 -i input.mp2ts -c:v hevc_nvenc -vf scale_npp=-1:720 -tag:v hvc1 -f mp4 out.mp4
```
解説です。

> -hwaccel cuvid

HWエンコードでcuvidを明示(cuvidについては互換性維持のため残っていますが、そのうちcudaに変わります。しかしscale_npp周りで実装が変わるので、明示的に古いままにしています。)

> -c:v mpeg2_cuvid

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

> -deint adaptive -drop_second_field 1

インターレス解除します。-deintだけだとフレーム数が2倍に増えるだけなので-drop_second_field 1 で増えたフレームを間引いています。

> -i input.mp2ts

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

> -c:v hevc_nvenc

H.265でNVEncを使います

> -vf scale_npp=-1:720

720Pになるよう縮小　通常はscale=と使うかと思いますが、scale_nppでnvidiaのHWスケーリングを使うので高速になります。

> -tag:v hvc1

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

> -f mp4

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

> out.mp4

出力先ファイル名を指定

詳しいオプションは
Using FFmpeg with NVIDIA GPU Hardware Acceleration
https://developer.nvidia.com/designworks/dl/Using_FFmpeg_with_NVIDIA_GPU_Hardware_Acceleration-pdf
にて。

##注意
Geforce などのコンシュマー向けGPUでは、同時にエンコードできるストリームは2本に制限されてますので、同時に複数のHWエンコードが必要（ストリーミング環境など）はQuadroやTeslaが必要です。