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