これは さくらインターネットAdvent Calendar 2018 15日目の記事です。(公開遅くなりました)
こんにちは、さくらインターネットのやまけんです
さくらのアドベントカレンダーなのにあまりにも関係性が低いような内容ですが、さくらの高火力GPUサーバ環境でも試せる内容なので、今回は記事にさせていただきました。
GPUサーバというと、機械学習などのイメージが大きいですが、本来のグラフィック演算ユニットとしての使い方としてハードウェアエンコードのプラットフォームとして、使ってみようというのが今回のテーマです。
ちょうどH.265も再生環境が整ってきたかと思いますので、MPEG2やH.264からのエンコードを高速に並列処理できる環境として、GPUサーバを利用します。
このテーマに向いている人
・多量のMPEG2やH.264動画を所有していて、H.265に変換したい人
(画質にそこまでこだわりはなく、ストレージの容量逼迫してるからさっさと片付けたい!など)
・何かしらのMPEG2-TSを録画する環境があり、録画した後にH.265へのトランスコードをしたいなと思ってる人
(多分このような用途の人には一番おすすめな気がする)
・動画配信などでリアルタイムトランスコードが必要な人
(画質より速度が重要視される分野)
2022年12月26日注記
CUDA12以降nvccの -gencode=arch がcompute_50以降じゃないと動かないため、ffmpegコンパイル時に --nvccflags="-gencode arch=compute_52,code=sm_52 -O2" というオプションで指定しています。
そもそも本来自分の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-2024 NVIDIA Corporation
Built on Thu_Sep_12_02:18:05_PDT_2024
Cuda compilation tools, release 12.6, V12.6.77
Build cuda_12.6.r12.6/compiler.34841621_0
test@test:~$ nvcc --list-gpu-arch
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 / 22.04 /24.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のエンコードができないやav1のエンコードができない等があります。詳しくは
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
こちらを進めれば良いです。
例(Ubuntu24.04でCUDA12.6の場合)
# Kernel Headerを入れる
sudo apt-get install linux-headers-$(uname -r)
# 古いapt-keyを削除する
sudo apt-key del 7fa2af80
# cuda-keyring パッケージをDL
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu$(lsb_release -rs | tr -d '.')/$(arch)/cuda-keyring_1.1-1_all.deb
# cuda-keyring パッケージをインストール
sudo dpkg -i cuda-keyring_1.1-1_all.deb
# リポジトリ更新
sudo apt-get update
# GPUドライバインストール
sudo apt-get -y install nvidia-open
※Pascal以前のGPUの場合は sudo apt-get install -y cuda-drivers
# CUDAインストール
sudo apt-get -y install cuda-toolkit
# パスを通す
echo 'export PATH=/usr/local/cuda/bin${PATH:+:${PATH}}' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda/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
Sun Oct 6 04:34:42 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 560.35.03 Driver Version: 560.35.03 CUDA Version: 12.6 |
|-----------------------------------------+------------------------+----------------------+
| 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 Off | 00000000:86:00.0 Off | N/A |
| 41% 53C 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
に変更しています。
なお、インストールされるドライババージョンによって、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 \
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 && \
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/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
# 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/include" \
--extra-ldflags="-L/usr/local/cuda/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-12.2/include
--extra-ldflags=-L/usr/local/cuda-12.2/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:
... bilateral_cuda V->V GPU accelerated bilateral filter
T.. bwdif_cuda V->V Deinterlace CUDA frames
... chromakey_cuda V->V GPU accelerated chromakey filter
... colorspace_cuda V->V CUDA accelerated video color converter
... 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
... scale2ref_npp VV->VV NVIDIA Performance Primitives video scaling and format conversion to the given reference.
... sharpen_npp V->V NVIDIA Performance Primitives video sharpening filter.
... 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 av1_nvenc NVIDIA NVENC av1 encoder (codec av1)
V....D h264_nvenc NVIDIA NVENC H.264 encoder (codec h264)
V....D hevc_nvenc NVIDIA NVENC hevc encoder (codec hevc)
test@test:~$
きちんとビルド時のオプションが有効なこと、デコーダ、フィルター、エンコーダにnvenc関連が入っているのがわかります。
表示が出ない場合、今回コンパイルしたffmpegではないパッケージでインストールしたffmpegなどの結果になっている可能性があります。 which ffmpeg
にてffmpegのパスを確認し、今回コンパイルしたffmpegで間違い無いかを確認してください。
実際に使ってみる
では実際に使ってみましょう。
MPEG2からH.265へ720pでエンコード
ffmpeg -y -hwaccel cuda -hwaccel_output_format cuda -c:v mpeg2_cuvid -i "input.mp2ts" -vf "yadif_cuda,scale_cuda=-2:720:interp_algo=lanczos" -c:v hevc_nvenc -preset slow -tag:v hvc1 -f mp4 -aspect 16:9 out.mp4
解説です。
-hwaccel cuda
HWエンコードでcudaを明示
-hwaccel_output_format cuda
GPUメモリ上にデコードしたデータを展開することによってメインメモリとの間のやりとりを減らして処理を高速化させます。
-c:v mpeg2_cuvid
MPEG2のハードウェアデコードを明示
-i input.mp2ts
変換元ファイルを指定します。
vf "yadif_cuda,scale_cuda=-2:720:interp_algo=lanczos"
まず yadif_cuda でHWにてインターレス解除。その後720Pになるよう縮小。
通常はscale=と使うかと思いますが、scale_cudaでnvidiaのHWスケーリングを使うので高速になります。
-c:v hevc_nvenc
H.265でNVEncを使います
-tag:v hvc1
Apple製品で扱えるH.265は hev1ではなくhvc1の方なので、タグで明示します
-f mp4
出力ファイルフォーマットがをmp4であると明示します
-aspect 16:9
16:9を指定
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が必要です。