Posted at

ffmpeg でファイルから4K HEVC疑似ライブストリーミング (その2 リベンジ編)

More than 1 year has passed since last update.

こんにちは、先日人生初屋形船に乗って興奮を隠しきれない streampack の木村です。

前回の記事 ffmpeg でファイルから4K HEVC疑似ライブストリーミング で映像品質が安定しなかったので、その原因を探るべく調査しました。

ちなみに画面が乱れたときはこんな状態です。

症状からみてパケット落ちだろうーなー


UDP パケットロスの痕跡

netstat -su および  Wowza エラーログ から UDP のパケロスが発生していることが確認できます。

ffmpeg 側

$ netstat -su

IcmpMsg:
InType3: 12
Udp:
553 packets received
0 packets to unknown port received.
0 packet receive errors
3505549 packets sent
UdpLite:
IpExt:
InOctets: 938459
OutOctets: 4705709500
InNoECTPkts: 12881

Wowza 側

$ netstat -su

IcmpMsg:
OutType3: 12
Udp:
3511272 packets received
140 packets to unknown port received.
3461 packet receive errors
9961 packets sent
RcvbufErrors: 3461
UdpLite:
IpExt:
InOctets: 4754674233
OutOctets: 3982940345
InNoECTPkts: 4360851

Wowza のエラーログ

こんな感じでビデオフレーム欠落のメッセージが多数表示されます

WARN    server  comment 2018-05-17      13:32:54        -       -       -       -       -       244.771 -       -       -       -       -       -       -       -       RTPDePacketizerMPEGTS[live/_definst_/4Kdash.stream]: RTPDePacketizerMPEGTS.flushVideoBuffer: Video frame incomplete, dropping[250213:0:false]: 5346000

WARN server comment 2018-05-17 13:32:54 - - - - - 245.301 - - - - - - - - RTPDePacketizerMPEGTS[live/_definst_/4Kdash.stream]: RTPDePacketizerMPEGTS.flushVideoBuffer: Video frame incomplete, dropping[200173:0:false]: 5394000
WARN server comment 2018-05-17 13:33:44 - - - - - 294.763 - - - - - - - - RTPDePacketizerMPEGTS[live/_definst_/4Kdash.stream]: RTPDePacketizerMPEGTS.flushVideoBuffer: Video frame incomplete, dropping[264749:0:false]: 9846000
WARN server comment 2018-05-17 13:33:48 - - - - - 299.132 - - - - - - - - RTPDePacketizerMPEGTS[live/_definst_/4Kdash.stream]: RTPDePacketizerMPEGTS.flushVideoBuffer: Video frame incomplete, dropping[185453:0:false]: 10239000
WARN server comment 2018-05-17 13:33:53 - - - - - 303.972 - - - - - - - - RTPDePacketizerMPEGTS[live/_definst_/4Kdash.stream]: RTPDePacketizerMPEGTS.flushVideoBuffer: Video frame incomplete, dropping[186557:0:false]: 10674000

うにゃー、パケロスが起こっていることはわかったけど、送信側、受信側、ネットワーク、アプリ、どこ起因なのか判断つかず。

まず送信側が怪しいと踏んで、ffmpeg から調査します。


ffmpeg 側

ffmpeg の方で StreamingGuide が用意されており、パケットロスについて記載があるので、これらを順に試してみました。

https://trac.ffmpeg.org/wiki/StreamingGuide


対策1 pthreads

udp パケットロスが出るようであれば、pthreads が有効かどうか確認しろ! と書いているので確認したところ、そんなもんは有効になっていなかったので、ffmpeg を再コンパイルしました。

PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure --prefix="$HOME/ffmpeg_build" --extra-cflags="-I$HOME/ffmpeg_build/include" --extra-ldflags="-L$HOME/ffmpeg_build/lib" --bindir="$HOME/bin" --pkg-config-flags="--static" --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-openssl --enable-pthreads --extra-libs=-lpthread

ここでまた ブッパマったのですが、それはまた別のお話で・・・

とりあえず再インスールしました。 これで大丈夫かな?

$ ffmpeg

ffmpeg version N-91085-ge351882 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 4.8.5 (GCC) 20150623 (Red Hat 4.8.5-11)
configuration: --prefix=/home/kimura/ffmpeg_build --extra-cflags=-I/home/kimura/ffmpeg_build/include --extra-ldflags=-L/home/kimura/ffmpeg_build/lib --bindir=/home/kimura/bin --pkg-config-flags=--static --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-openssl --enable-pthreads --extra-libs=-lpthread


結果

流してみるとダメでした。


対策2 fifo_size=10000

ffmpeg で流す時に fifo_size=10000 を付けろ!とあるので付けてみます

ffmpeg -re -stream_loop -1 -i hevcuhd.mp4 -vcodec copy -acodec copy -flags +loop-global_header -f mpegts "udp://{Wowza IP Address}:6000?pkt_size=1316&fifo_size=10000"


結果

流してみるとダメでした。


対策3 buffer_size=10000000

バッファ上げろ! とあるので &buffer_size=10000000 を付けてみます

ffmpeg -re -stream_loop -1 -i hevcuhd.mp4 -vcodec copy -acodec copy -flags +loop-global_header -flush_packets 0 -f mpegts "udp://{Wowza IP Address}:6000?pkt_size=1316&buffer_size=10000000&fifo_size=10000"


結果

流してみるとダメでした。

パラメータの順番変えたりしてもダメでした。


Wowza 側

https://www.wowza.com/docs/how-to-fix-udp-packet-loss-mpeg-ts-rtp

なになに? datagramMaximumPacketSize_in8192 にしろと?

仰せのままに・・・


/usr/local/WowzaStreamingEngine/conf/VHost.xml

<DatagramConfiguration>

<Incoming>
<ReuseAddress>true</ReuseAddress>
<ReceiveBufferSize>2048000</ReceiveBufferSize>
<SendBufferSize>65000</SendBufferSize>
<!-- <MulticastBindToAddress>true</MulticastBindToAddress> -->
<!-- <MulticastInterfaceAddress>[ip-address]</MulticastInterfaceAddress> -->
<!-- <TrafficClass>0</TrafficClass> -->
<MulticastTimeout>50</MulticastTimeout>
<DatagramMaximumPacketSize>8192</DatagramMaximumPacketSize>
</Incoming>
<Outgoing>
...
</Outgoing>
</DatagramConfiguration>


結果

流してみるとダメでした。


EC2 インスタンスタイプ変更

書いてませんでしたが、ffmpeg も Wowza も普段は t2.small で動かしています。

配信時のリソース状況を見ても負荷はほとんどかかっていない状況ですが、念のためにスケールアップしてみます。

エイヤ!!! 両方共 c4.xlarge でどうだ!


結果

流してみるとダメでした。

ああーもうどうしたらええんじゃいっ!!


そしてふと思い出す・・・

もしかしたら 10Mbps のビットレート送出に UDP 送受信バッファが低すぎるんじゃねーの??

そういえばだいぶ昔に Linux カーネルパラメーター弄って調整した案件があったなあ・・・

ってことで UDP 関連の送受信バッファの項目を弄ります。


Linux カーネル パラメーター変更

まず EC2 Amazon Linux の現在の設定値を確認してみます。

変更前

$ sudo sysctl -a |grep "net.core.*mem_*"

net.core.optmem_max = 20480
net.core.rmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_default = 212992
net.core.wmem_max = 212992

これを以下のコマンドで送受信バッファのデフォルトと MAX の設定値を 16MB に変更し、ffmpeg 側、Wowza 側の両方に適用します。

$ sudo sysctl -w net.core.rmem_default=16777216

$ sudo sysctl -w net.core.wmem_default=16777216
$ sudo sysctl -w net.core.rmem_max=16777216
$ sudo sysctl -w net.core.wmem_max=16777216

変更後

$ sudo sysctl -a |grep "net.core.*mem_*"

net.core.optmem_max = 20480
net.core.rmem_default = 16777216
net.core.rmem_max = 16777216
net.core.wmem_default = 16777216
net.core.wmem_max = 16777216

注:上記は変更は TCP 通信にも適用され、TCP/UDP ソケットを利用する他のサービス/アプリケーションにも影響を与えますので、十分にご注意下さい。

また、sysctl コマンドでの設定変更は一時的な変更ですので、OS 再起動すると元に戻ります。 恒久的に適用するには /etc/sysctl.conf に書き込みます。


結果

流してみるとダメじゃありませんでした!!

お金かかるので ffmpeg 側、Wowza 側共に t2.small に戻して 40分位回しましたが、映像の乱れもなく、パケロスも起きておりません。

Wowza 側

# netstat -su

Udp:
1480023 packets received
0 packets to unknown port received.
0 packet receive errors
389 packets sent
UdpLite:
IpExt:
InOctets: 2000575930
OutOctets: 1814875998

高画質、高品質ですわー

調子に乗ってコンテンツを 30Mbps で再エンコして試してみましたが、パケットロスは確認できず!

(ただ、30Mbps だと今度は携帯側での受信が辛く、頻繁に止まっちゃいます)


まとめ

色々と躓きましたが、最終的には目的を達成することができました。

個人的に 4Kハードウェアエンコーダーを用意せずに事前検証が行えるのはとても助かります!