Nginx rtmp配信 ffmpegを利用したマルチビットレート化成功

Nginxをrtmpサーバとして利用し、エンコーダから受信した1つのストリーミングをNginx側でffmpegを利用して再エンコし、3つに分ける。

Screenshot of nginx-rtmp.blogspot.jp

利用するサーバ Cloudn上
Ubuntu Server v16.04.01 64bitにおいて

sudo apt-get install ffmpeg libtool
sudo vi /etc/nginx/nginx.conf
rtmp {
   server {
   listen 1935;
   access_log logs/rtmp_access.log;
   publish_notify on;
   drop_idle_publisher 5s;
   buflen 30s;

   application live {
      live on;
      exec /usr/bin/ffmpeg -i rtmp://localhost/live/$name
      -c:a aac -strict -2 -b:a 32k -c:v libx264 -x264opts bitrate=128:vbv-maxrate=128:vbv-bufsize=128 -rtbufsize 100M -bufsize 256k -preset veryfast -f flv rtmp://localhost/multi/$name_lw
      -c:a aac -strict -2 -b:a 64k -c:v libx264 -x264opts bitrate=256:vbv-maxrate=256:vbv-bufsize=256 -rtbufsize 100M -bufsize 512k -preset veryfast -f flv rtmp://localhost/multi/$name_md
      -c:a aac -strict -2 -b:a 128k -c:v libx264 -x264opts bitrate=512:vbv-maxrate=512:vbv-bufsize=512 -rtbufsize 100M -bufsize 1024k -preset veryfast -f flv rtmp://localhost/multi/$name_hi 2>>/tmp/log; //問題が解決したら /tmp/log は消す(ログが膨大な量になるから)
   }

   application multi {
      live on;
   }
   }
}

nginxをリスタート

sudo /usr/sbin/nginx -s stop
sudo /usr/sbin/nginx

再生用VideoJS HTMLの配置

sourceのrtmp urlは

rtmp://IPアドレス/live/test
rtmp://IPアドレス/multi/test_hi
rtmp://IPアドレス/multi/test_md
rtmp://IPアドレス/multi/test_lw

の4種類をそれぞれのファイルで準備する

<html>
<head>
<link href="http://vjs.zencdn.net/5.11.6/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/5.11.6/video.js"></script>
</head>
<body>
//配置したい場所へ記載
<video id="rtmp_test" class="video-js vjs-default-skin" autoplay="autoplay" controls="controls" width="800" height="450" data-setup="{}">
//それぞれの配信速度で変える
<source src="rtmp://IPアドレス/multi/test_md" type="rtmp/mp4" />
</video>
</html>

追記(経緯) 最初はエラーが出てうまくいかなかった。

webページにある通りの設定

application live {
live on;

exec ffmpeg -i rtmp://localhost/live/$name
-c:a libfdk_aac -b:a 32k -c:v libx264 -b:v 128K -f flv rtmp://localhost/multi/$name_low
-c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 256k -f flv rtmp://localhost/multi/$name_mid
-c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv rtmp://localhost/multi/$name_hi 2>>/tmp/log;
//問題が解決したら /tmp/log は消す(ログが膨大な量になるから)
}

/tmp/log

Duration: 00:00:00.00, start: 172.617000, bitrate: N/A
Stream #0:0: Video: h264 (High), yuv420p, 1092x614, 2560 kb/s, 30.30 fps, 30 tbr, 1k tbn, 60 tbc
Stream #0:1: Audio: aac (LC), 44100 Hz, stereo, fltp, 163 kb/s
Unknown encoder 'libfdk_aac'

ffmpeg -formats |lessを見ると

aac raw ADTS AAC (Advanced Audio Coding)

とあるので、ffmpeg のオプションをlibfdk_aacからaacへ変更

まだ映像がでないので、エラーログを見ると

/tmp/log

[aac @ 0x1e17620] The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it.

となっていて、

aac -strict -2とした。

つまりこんな感じ

-c:a aac -strict -2 -b:a 32k -c:v libx264 -b:v 128K -f flv rtmp://localhost/multi/$name_low

ただ、エンコードされrtmpで映像が確認できるようになったが、どのビットレートもちょいちょい止まる。そこで、サーバ性能を疑い、Cloudnで、一番高い8コア 16Gメモリにパワーアップして配信を開始してみたけどやっぱりだめ。

試しに、これもやってみた。

ffmpeg入れ変えた。

無数にこのエラーが出ている事を確認した。

Larger timestamp than 24-bit: 0xffffef26
Past duration 0.949989 too large

とりあえずググって

http://comments.gmane.org/gmane.comp.video.ffmpeg.user/55889

So if it must be FLV _format_:
-c:v libx264 -f flv
or in your case:
-c:v copy -f flv

へぇ、コーデックはcopyってかける事を知り、

-c:a copy -b:a 32k -c:v copy -b:v 128K -s 320x180 -f flv rtmp://localhost/multi/$name_low
-c:a copy -b:a 64k -c:v copy -b:v 256k -s 480x270 -f flv rtmp://localhost/multi/$name_mid
-c:a copy -b:a 128k -c:v copy -b:v 512K -s 640x360 -f flv rtmp://localhost/multi/$name_hi 2>>/tmp/log;

としたら止まることなく配信できると思ったら、ビデオサイズは全く変わっていない事に気づいた。つまり再エンコしてない状態だと思う。

色々と調べると、ffmpegエンコの際にもバッファを持てる事が分かり以下を追加。

-x264opts bitrate=512:vbv-maxrate=512:vbv-bufsize=512 -rtbufsize 100M

rtbufsize が映像受取時のバッファ(溢れないように)
vbf-bufsizeが、エンコ時に動きが激しい時などに利用されるバッファ

を設定した。これで改善するかと思ったら、いい時と悪い時があり、最終的に再エンコを1本にしたら安定して配信できるようになった。

さらに設定を重ね、ビットレートにもbufferを持てる事が分かった

Screenshot of www.wikihouse.com

レートコントロールのバッファサイズ (in bits)。デフォルト=0 (指定なし)。
※配信ビットレートの2倍までにする事

これをつける事で2つまでの再エンコはできるようになった。ただ、3つめを加えるとどうしても、映像が時々止まってしまう。

最後の決め手になったのがこれ、

https://trac.ffmpeg.org/wiki/EncodingForStreamingSites
-preset

This provides the compression to encoding speed ratio.
ultrafast,superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo

エンコードの圧縮スピード比率を変える事ができる。恐らくだけど、これを早い側 「今回の設定ではこれveryfast」にすると、CPU使用率が上がるのと圧縮スピードを優先させ、その代わり品質は落ちるのだと思う。

ちなみに Cloudn 最安プランでこの設定を動かすと

Plan v1(1CPU/2GB RAM) 月額上限3,400円

top - 06:29:13 up 4 days, 8:59, 1 user, load average: 0.86, 0.42, 0.17
Tasks: 122 total, 2 running, 120 sleeping, 0 stopped, 0 zombie
%Cpu(s): 95.7 us, 1.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 1.0 si, 1.6 st
KiB Mem : 500224 total, 15236 free, 279052 used, 205936 buff/cache
KiB Swap: 1998844 total, 1993512 free, 5332 used. 190052 avail Mem

と、かなり高負荷で安定稼働は見込めない。

Plan v2(2CPU/4GB RAM) 月額上限6,200円に変更して

top - 06:35:14 up 2 min, 1 user, load average: 2.01, 0.47, 0.16
Tasks: 136 total, 2 running, 134 sleeping, 0 stopped, 0 zombie
%Cpu(s): 27.7 us, 0.8 sy, 62.2 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.3 si, 9.1 st
KiB Mem : 4046684 total, 3424032 free, 456964 used, 165688 buff/cache
KiB Swap: 1998844 total, 1998844 free, 0 used. 3376604 avail Mem

30%程度で推移するのでこれなら使えそう。

Cloudn料金

Screenshot of www.ntt.com

参考
http://www.wikihouse.com/netvista/index.php?%BA%C7%BF%B7ffmpeg%2F%B9%E2%C5%D9%A4%CA%A5%AA%A5%D7%A5%B7%A5%E7%A5%F3
https://trac.ffmpeg.org/wiki/StreamingGuide
https://sites.google.com/site/streamsetting/home/ffmpeg