search
LoginSignup
53
Help us understand the problem. What are the problem?

さくらインターネット Advent Calendar 2018 Day 15

posted at

updated at

Organization

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

これは さくらインターネットAdvent Calendar 2018 15日目の記事です。(公開遅くなりました)

こんにちは、さくらインターネットのやまけんです

さくらのアドベントカレンダーなのにあまりにも関係性が低いような内容ですが、さくらの高火力GPUサーバ環境でも試せる内容なので、今回は記事にさせていただきました。

GPUサーバというと、機械学習などのイメージが大きいですが、本来のグラフィック演算ユニットとしての使い方としてハードウェアエンコードのプラットフォームとして、使ってみようというのが今回のテーマです。

ちょうどH.265も再生環境が整ってきたかと思いますので、MPEG2やH.264からのエンコードを高速に並列処理できる環境として、GPUサーバを利用します。

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

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

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

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

2020年09月24日注記
CUDA11以降nvccの -gencode=arch がcompute_35以降じゃないと動かないため、ffmpegコンパイル時に --nvccflags="-gencode arch=compute_52,code=sm_52 -O2" というオプションで指定しています。
(ffmpegのtracには上がってる https://trac.ffmpeg.org/ticket/8790)
そもそも本来自分のGPUの世代と同様のものを選ぶのが正しいですが、nvenc自体のコンパイルの場合GPU最適化がそこまで必要ないので汎用的な設定になっていたと思われる。

ちなみにNVIDIA T400の場合は
--nvccflags="-gencode arch=compute_75,code=sm_75 -O2"
になります。

正しいGPUを選ぶ際の参考(Your GPU Compute Capabilityを参照)
https://developer.nvidia.com/cuda-gpus

nvcc warning : The 'compute_35', 'compute_37', 'compute_50', 'sm_35', 'sm_37' and 'sm_50' architectures are deprecated, and may be removed in a future release
test@test:~$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2022 NVIDIA Corporation
Built on Wed_Sep_21_10:33:58_PDT_2022
Cuda compilation tools, release 11.8, V11.8.89
Build cuda_11.8.r11.8/compiler.31833905_0

test@test:~$ nvcc --list-gpu-arch 
compute_35
compute_37
compute_50
compute_52
compute_53
compute_60
compute_61
compute_62
compute_70
compute_72
compute_75
compute_80
compute_86
compute_87
compute_89
compute_90

必須環境

・NVIDIAのGPUが載っている Ubuntu 18.04 / 20.04 x86_64 マシン
→自宅サーバに載せるのであれば、2021/08/08現在だと NVIDIA T400でNVENCが良さげ
1スロットLowProfile対応のグラボで消費電力31Wと1Uサーバに余裕で搭載できるサイズ。
ぷらっとホームで15000円ちょっとで買えました。
https://online.plathome.co.jp/item/detail/41866468/NVIDIA/NVIDIA-T400/NVT400

T1000やT600でも良いのですが、エンコードだけだったら、メモリも少なくても問題ないですし、CUDAコアもそんな必要ないです。

しかし、残念ながら、6GenのNVENCチップのため、H.265のBフレームはサポートされてませんでした。(GTX1650と同じTU117ですから、そうですよね。)

[hevc_nvenc @ 0x562b8294bbc0] Each B frame as reference is not supported
[hevc_nvenc @ 0x562b8294bbc0] Provided device doesn't support required NVENC features

あと、T400やGeforce系コンシューマーGPUは同時にエンコードできる数が3つまでに制限されます。
他のQuadro系やデータセンター向けTeslaだと並行して大量にエンコードできます。(とはいえ1チップに複数ストリーム入れると処理時間は増えていくのである程度調整しましょう)

私の好みでUbuntuにしていますが、CentでもCUDA入れてffmpegコンパイルできれば同じようになるかと。

ちなみに古いUbuntuのバージョンだと最新のドライバが入らないので、NVIDIA Video Codec SDKのバージョンを落とせば対応可能だと思いますが、検証はしていません。

GPU世代によってH.265のエンコードができない等があります。詳しくは
Video Encode and Decode GPU Support Matrixにて
https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new

準備

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
こちらを進めれば良いです。

例(Ubuntu20.04でCUDA11.6の場合)

# Kernel Headerを入れる
sudo apt-get install software-properties-common linux-headers-$(uname -r)

# aptプライオリティpinファイルをDL
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin

# aptプライオリティpinファイルをaptのフォルダに移動
sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600

# GPGキーをインポート
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/3bf863cc.pub

リポジトリを登録
sudo add-apt-repository "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/ /"

# リポジトリ更新
sudo apt-get update

# CUDAインストール
sudo apt-get -y install cuda

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

source ~/.bashrc

サーバなどで、GUIを使っておらず、CLI利用の場合、ドライバ類のインストールと共にX関連も入れるので、必要に応じてCLI起動するように設定しましょう。(必須ではありません。Tesla使ってたらそもそも不要)

# 現在の設定を確認
test@test:~$ systemctl get-default
graphical.target

graphical.targetと帰ってくると、GUI起動になってるので、CLI(sysvinitでいうrunlevel3)に設定変更

# multi-user.target へ変更
test@test:~$ sudo systemctl set-default multi-user.target
Created symlink /etc/systemd/system/default.target → /lib/systemd/system/multi-user.target.

これで再起動後よりCLIになります。

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

test@test:~$ nvidia-smi
Sat Oct  8 07:27:25 2022       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 520.61.05    Driver Version: 520.61.05    CUDA Version: 11.8     |
|-------------------------------+----------------------+----------------------+
| 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 T400         On   | 00000000:02:00.0 Off |                  N/A |
| 38%   41C    P8    N/A /  31W |      1MiB /  2048MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
test@test:~$ 

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 を追記しています。
Netflixが開発しているVMAFを使って動画評価したいため、libvmafをインストールの上
--enable-libvmaf を追記しています。

また、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.5/include"
--extra-ldflags="-L/usr/local/cuda-11.5/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 -C nv-codec-headers pull 2> /dev/null || git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers -b sdk/9.0 && \
のように -b sdk/9.0 でブランチを指定してインストールしてみてください。

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

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

# NASM インストール
cd ~/ffmpeg_sources && \
wget 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 && \
PATH="$HOME/bin:$PATH" ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" && \
make && \
make install

# 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 && \
wget -O x265.tar.bz2 https://bitbucket.org/multicoreware/x265_git/get/master.tar.bz2 && \
tar xjvf x265.tar.bz2 && \
cd multicoreware*/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

# libdav1d インストール
cd ~/ffmpeg_sources && \
git -C dav1d pull 2> /dev/null || git clone --depth 1 https://code.videolan.org/videolan/dav1d.git && \
mkdir -p dav1d/build && \
cd dav1d/build && \
meson setup -Denable_tools=false -Denable_tests=false --default-library=static .. --prefix "$HOME/ffmpeg_build" --libdir="$HOME/ffmpeg_build/lib" && \
ninja && \
ninja install

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

# NVIDIA codec API インストール
cd ~/ffmpeg_sources && \
git -C nv-codec-headers pull 2> /dev/null || 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 && \
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" \
  --enable-cuda-nvcc \
  --nvccflags="-gencode arch=compute_75,code=sm_75 -O2" \
  --enable-cuvid \
  --enable-nvenc \
  --enable-libnpp \
  --extra-cflags="-I/usr/local/cuda-11.8/include" \
  --extra-ldflags="-L/usr/local/cuda-11.8/lib64" \
  --extra-libs="-lpthread -lm" \
  --ld="g++" \
  --bindir="$HOME/bin" \
  --enable-openssl \
  --enable-gpl \
  --enable-libaom \
  --enable-libass \
  --enable-libfdk-aac \
  --enable-libfreetype \
  --enable-libmp3lame \
  --enable-libopus \
  --enable-libsvtav1 \
  --enable-libdav1d \
  --enable-libvorbis \
  --enable-libsnappy \
  --enable-libvpx \
  --enable-libx264 \
  --enable-libx265 \
  --enable-libvmaf \
  --enable-nonfree && \
PATH="$HOME/bin:$PATH" make && \
make install && \
hash -r

パスを通したり
source ~/.profile
echo "MANPATH_MAP $HOME/bin $HOME/ffmpeg_build/share/man" >> ~/.manpath

動作確認

実際にインストールが成功したか、下記のコマンドで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 cuda -hwaccel_output_format cuda -c:v mpeg2_cuvid -deint adaptive -drop_second_field 1 -i input.mp2ts -vf scale_npp=-1:720 -c:v hevc_nvenc -tag:v hvc1 -f mp4 out.mp4

解説です。

-hwaccel cuda

HWエンコードでcudaを明示

-hwaccel_output_format cuda

GPUメモリ上にデコードしたデータを展開することによってメインメモリとの間のやりとりを減らして処理を高速化させます。

-c:v mpeg2_cuvid

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

-deint adaptive -drop_second_field 1

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

-i input.mp2ts

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

-vf scale_npp=-1:720

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

-c:v hevc_nvenc

H.265でNVEncを使います

-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では、同時にエンコードできるストリームは3本に制限されてますので、同時に複数のHWエンコードが必要(ストリーミング環境など)はQuadroやTeslaが必要です。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
53
Help us understand the problem. What are the problem?