Edited at

NVENCを使ったHWEncode環境

2019/10/06 現在 Video Codec SDK 9.1がリリースされた影響でドライバーバージョンが

Linux: 435.21 or newer

となってしまいました。

https://github.com/FFmpeg/nv-codec-headers

https://developer.nvidia.com/nvidia-video-codec-sdk/download

動作確認取れている435.21は

https://www.nvidia.com/download/driverResults.aspx/150803/en-us

よりDLはできますが、残念ながらCUDA側がこのドライババージョンに対応していないため、現状Video Codec SDK 9.0にてコンパイルするようになっています。


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

こんにちは、やまけんです

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

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

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

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

・多量のMPEG2やH.264動画を所有していて、H.265に変換したい人

 (画質にそこまでこだわりはなく、ストレージの容量逼迫してるからさっさと片付けたい!など)

・何かしらのMPEG2-TSを録画する環境があり、録画した後にH.265へのトランスコードをしたいなと思ってる人

 (多分このような用途の人には一番おすすめな気がする)

・動画配信などでリアルタイムトランスコードが必要な人

 (画質より速度が重要視される分野)


必須環境

・NVIDIAのGPUが載っているUbuntu 18.04 マシン

→自宅サーバに載せるのであれば拡張電源の不要なGeForce GTX 1050あたりのカードが良さげ。

しかし、同時にエンコードできる数が2つまでに制限されるためQuadroやTeslaだと並行して大量にエンコードできます。

GPU世代によってH.265のエンコードができない等があります。詳しくは

Video Encode and Decode GPU Support Matrixにて

https://developer.nvidia.com/video-encode-decode-gpu-support-matrix


準備


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

こちらを進めれば良いです。

# Kernel Headerを入れる

sudo apt-get install linux-headers-$(uname -r)

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

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

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

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

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

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

# パスを通す
export PATH=/usr/local/cuda-10.1/bin:/usr/local/cuda-10.1/NsightCompute-2019.1${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-10.1/lib64\
${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

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

test@test:~$ nvidia-smi

Thu Oct 3 22:49:29 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.87.00 Driver Version: 418.87.00 CUDA Version: 10.1 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 GeForce GTX 1650 Off | 00000000:01:00.0 Off | N/A |
| 55% 37C P0 4W / 75W | 0MiB / 3881MiB | 0% Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+


FFmpegのインストール

Compile FFmpeg for Ubuntu, Debian, or Mint

https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu

こちらをベースに全てソースコードで入れていきます。

また、FFmpeg4以降でnvencを使用する場合はnv-codec-headers が必要です。

https://github.com/FFmpeg/nv-codec-headers

#FFmpegコンパイルに必要なものを導入

sudo apt-get update -qq && sudo apt-get -y install \
autoconf \
automake \
build-essential \
cmake \
git-core \
libass-dev \
libfreetype6-dev \
libsdl2-dev \
libssl-dev \
libtool \
libva-dev \
libvdpau-dev \
libvorbis-dev \
libxcb1-dev \
libxcb-shm0-dev \
libxcb-xfixes0-dev \
pkg-config \
texinfo \
wget \
zlib1g-dev

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

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

#Yasm インストール
cd ~/ffmpeg_sources && \
wget -O yasm-1.3.0.tar.gz 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 -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 mercurial libnuma-dev && \
cd ~/ffmpeg_sources && \
if cd x265 2> /dev/null; then hg pull && hg update && cd ..; else hg clone https://bitbucket.org/multicoreware/x265; fi && \
cd x265/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

#libmp3lame インストール
cd ~/ffmpeg_sources && \
wget -O lame-3.100.tar.gz 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 && \
PATH="$HOME/bin:$PATH" ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --disable-shared --enable-nasm && \
PATH="$HOME/bin:$PATH" 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_SHARED=off -DENABLE_NASM=on ../aom && \
PATH="$HOME/bin:$PATH" make && \
make install

#Nvidias codec API インストール (暫定Video Codec SDK 9.0)
cd ~/ffmpeg_sources && \
git -C nv-codec-headers pull 2> /dev/null || git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers -b sdk/9.0 && \
cd nv-codec-headers && \
PATH="$HOME/bin:$PATH" make && \
make install PREFIX="$HOME/ffmpeg_build"

#FFmpeg インストール
cd ~/ffmpeg_sources && \
wget -O ffmpeg-snapshot.tar.bz2 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" \
--extra-libs="-lpthread -lm" \
--bindir="$HOME/bin" \
--enable-openssl \
--enable-gpl \
--enable-libaom \
--enable-libass \
--enable-libfdk-aac \
--enable-libfreetype \
--enable-libmp3lame \
--enable-libopus \
--enable-libvorbis \
--enable-libvpx \
--enable-libx264 \
--enable-libx265 \
--enable-static \
--enable-cuda \
--enable-cuvid \
--enable-nvenc \
--enable-libnpp \
--extra-cflags=-I/usr/local/cuda/include \
--extra-ldflags=-L/usr/local/cuda/lib64 \
--enable-nonfree && \
PATH="$HOME/bin:$PATH" make && \
make install && \
hash -r

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


実際に使ってみる

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

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 out.mp4

解説です。


-hwaccel cuvid


HWエンコードでcuvidを明示


-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になるよう縮小


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が必要です。