Un nouveau monde parfumé

香り立つ備忘録

AWS で爆速エンコード -準備編-

こんにちは。みなさんは録画をする人でしょうか。

このブログを読みに来るようなオタクの皆さんの場合、日々増え続ける 15Mbps の MPEG-TS との戦いを往々にして繰り広げているものと思います。
我が家の録画サーバーも録画済データが 1TB に迫り、いよいよ真面目にエンコードをしていかないと先行きが不安です。

しかしながらエンコードにはCPUパワーとなおも多大な時間を要します。
試しにウチの録画管理に使っている LIVA (Bay-Trail-M Celeron N2807) で x264 によるエンコードを試してみたところ、30分の動画を処理するのに8時間強を要しました。未エンコード件数は400件近くあり、不眠不休で頑張らせても100日ほどかかることになりますね。

とてもじゃないけどやってらんないので、Amazon パワーでぶん殴っていくことを考えました。

アメイゾンパワー

AWS の EC2 には GPU を積んだインスタンスとして g2 シリーズというものがあります。

g2 は NVIDIA GRID K520 が利用可能であり、これを利用して NVENC を通じたハードウェアエンコードが可能です。

というわけで AWSエンコードをやってもらう仕組みを作ってみました。
流れとしては、S3 にデータをぶっ込む → イベントを受けて Lambda が EC2 インスタンスを起動 → EC2 がエンコードしてデータを S3 に吐いて自動でシャットダウン ということになります。

f:id:pikatenor:20170517052739p:plain
こういうどうでもいい絵を一回作ってみたかった。この記事はこれで満足です。以下余談となります

今回は準備編として、AWS EC2 上でハードウェアエンコードできるようになるまでを書きます。

EC2 の下準備

やっていくため、大ざっぱに以下のものを用意していきます。

  • nvidia ディスプレイドライバー (367.57)
  • CUDA Toolkit (エンコードのみなら不要ですが、デコードやスケーリングも GPU に任せたいなら必要です)
  • nvenc に対応した ffmpeg 3.3

AWS Marketplace では NVIDIA によりドライバーインストール済みの AMI イメージが公開されていますが、ドライバーのバージョンが古く(352.99) ffmpeg 3.3 が正常に動かなかった && アップデートも上手くいかなかったため、結局素の RHEL 7 AMI でやりました。

EC2 インスタンスを生やす

キリがないんでサクッと言うと S3 を読み書きできる IAM ロール を持った RHEL 7 g2.2xlarge インスタンスをシュッと立てた。
なお GPU インスタンスは us-east-1 がちょっと安い。

お約束

$ sudo yum -y update
$ sudo yum -y groupinstall "Development Tools"

ディスプレイドライバーのインストー

このへんから結構手こずったのでマジメに書いておきます。

ちなみに本来であれば NVENCODE/DECODE API を使う場合 nvidia ドライバー >= 378.13 が必要らしいんですが、GRID K520 向けには 367.57 までしか提供されていません。
375.10 という beta 版もあるっぽいですがインストールしても使えませんでした。

ダウンロードします。

$ wget http://us.download.nvidia.com/XFree86/Linux-x86_64/367.57/NVIDIA-Linux-x86_64-367.57.run

RHEL の場合、もともと入っている nouveau ドライバが邪魔なので無効化します。
/etc/default/grub を編集し、GRUB_CMDLINE_LINUXrd.driver.blacklist=nouveau nouveau.modeset=0 を加えます。

この段階で一度インストーラーを起動し、nouveau を無効化するファイルをシステムに加えます。

$ chmod +x NVIDIA-Linux-x86_64-367.57.run 
$ sudo ./NVIDIA-Linux-x86_64-367.57.run

nouveau 無効化云々に Yes みたいな感じで答えると一旦インストーラーが終了するので、システムを再起動します。

再起動後、DKMS のビルドに必要なパッケージを入れたのち再びインストーラーを起動します。

$ sudo kernel-devel kernel-headers dkms
$ sudo ./NVIDIA-Linux-x86_64-367.57.run

nvidia-smi あたりで確認して入ってるようなら大丈夫です。

CUDA のインストー

ダウンロードします。

$ wget https://developer.nvidia.com/compute/cuda/8.0/Prod2/local_installers/cuda_8.0.61_375.26_linux-run

あとは起動するだけですが、 Display Driver はインストールせず(古いので)、CUDAのみをインストールします。
したらば /etc/ld.so.conf/usr/local/cuda/lib64 を書いて ldconfig しておく。

ffmpeg のビルド

この辺はサクッと。 3.3 ともなれば ./configure 時になにも書かずともデフォルトで cuvid/nvenc が有効になります。よかったですね。 以下では一応やっています。libnppはデフォルト無効のようです。 ところで素の RHEL 7 には bzip2 が入っていなくて驚いた。

$ sudo yum install autoconf automake bzip2 cmake freetype-devel gcc gcc-c++ git libtool make mercurial nasm pkgconfig zlib-devel
$ wget http://ffmpeg.org/releases/ffmpeg-3.3.tar.bz2
$ tar xvf ffmpeg-3.3.tar.bz2 
$ cd ffmpeg-3.3
$ ./configure --enable-cuda --enable-cuvid --enable-nvenc --enable-libnpp --enable-gpl --enable-nonfree --extra-cflags=-I/usr/local/cuda/include/ --extra-ldflags=-L/usr/local/cuda/lib64/
$ make
$ sudo make install

こうして ffmpeg ができた、

$ ffmpeg -decoders | grep cuvid
ffmpeg version 3.3 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 4.8.5 (GCC) 20150623 (Red Hat 4.8.5-11)
  configuration: --enable-cuda --enable-cuvid --enable-nvenc --enable-libnpp --enable-gpl --enable-nonfree --enable-libfdk-aac --extra-cflags=-I/usr/local/cuda/include/ --extra-ldflags=-L/usr/local/cuda/lib64/
  libavutil      55. 58.100 / 55. 58.100
  libavcodec     57. 89.100 / 57. 89.100
  libavformat    57. 71.100 / 57. 71.100
  libavdevice    57.  6.100 / 57.  6.100
  libavfilter     6. 82.100 /  6. 82.100
  libswscale      4.  6.100 /  4.  6.100
  libswresample   2.  7.100 /  2.  7.100
  libpostproc    54.  5.100 / 54.  5.100
 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)
$ ffmpeg -encoders | grep nvenc
ffmpeg version 3.3 Copyright (c) 2000-2017 the FFmpeg developers
...
 V..... 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..... hevc_nvenc           NVIDIA NVENC hevc encoder (codec hevc)

これは後で fdk_aac も加えたやつです。

使い方

このように

$ ffmpeg -c:v mpeg2_cuvid -i input.m2ts -c:v h264_nvenc output.mp4

とすると、cuvid でデコードして、nvenc でエンコードすることができます。CPU は殆ど使いません。

今の僕の設定はこうです、

$ ffmpeg -c:v mpeg2_cuvid -deint adaptive -i "input.m2ts" -c:v h264_nvenc -vf hwupload_cuda,scale_npp=-1:720 -preset slow -rc vbr -cq 10 -b:v 2M -minrate 500k -maxrate 5M "output.mp4"

cuvid でデコードすると同時にそのまま GPU でデインターレース、720p にスケーリングして 2Mbps でエンコード、です。

ぶっちゃけ画質は微妙で、1Mbps では相当アラが目立ちます。この辺は職人が緻密にパラメーターを調整してエンコードした動画には勝てませんね……

その分速度は爆速で、30分の 1440x1080/60i MPEG-2 の 地デジ動画を 100fps ちょっと、9分でエンコードしてしまいます。ぶっちゃけ S3 への転送より速い。

長くなったので続きは別記事にします。次は S3 と Lambda とアレするやつ書きます。

pikachuism.hatenablog.com