こんにちは、先日人生初屋形船に乗って興奮を隠しきれない 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ハードウェアエンコーダーを用意せずに事前検証が行えるのはとても助かります!

元記事はこちら

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