share facebook facebook twitter menu hatena pocket slack

2018.06.20 WED

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

木村 智一

WRITTEN BY 木村 智一

こんにちは、先日人生初屋形船に乗って興奮を隠しきれない 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 リベンジ編)

木村 智一

木村 智一

動画一筋! 犬と闘う streampack チームリーダー

cloudpack

cloudpackは、Amazon EC2やAmazon S3をはじめとするAWSの各種プロダクトを利用する際の、導入・設計から運用保守を含んだフルマネージドのサービスを提供し、バックアップや24時間365日の監視/障害対応、技術的な問い合わせに対するサポートなどを行っております。
AWS上のインフラ構築およびAWSを活用したシステム開発など、案件のご相談はcloudpack.jpよりご連絡ください。