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

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

前回の記事 [ffmpeg でファイルから4K HEVC疑似ライブストリーミング](https://qiita.com/tomopyonsama/items/d231f2f241ed25a68bb5) で映像品質が安定しなかったので、その原因を探るべく調査しました。

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

![Screenshot_20180516-144907_ExoPlayer.jpg](https://qiita-image-store.s3.amazonaws.com/0/172426/eaf28b00-668c-295d-0d1a-9670f0c13a53.jpeg)

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

# 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
```

ここでまた <font color="Red">ブッパマった</font>のですが、それはまた別のお話で・・・

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

```
$ 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 でどうだ！

![Image uploaded from iOS (3).jpg](https://qiita-image-store.s3.amazonaws.com/0/172426/81b211e5-b744-24c3-bfa1-ff4f3720cac3.jpeg)

### 結果

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

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

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


もしかしたら 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 の設定値を １６MB に変更し、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
```

高画質、高品質ですわー

![Screenshot_20180516-145832_ExoPlayer.jpg](https://qiita-image-store.s3.amazonaws.com/0/172426/cdd0db4f-6e29-7aaa-0995-36f1a57cb64d.jpeg)

調子に乗ってコンテンツを 30Mbps で再エンコして試してみましたが、パケットロスは確認できず！
（ただ、30Mbps だと今度は携帯側での受信が辛く、頻繁に止まっちゃいます）



<img width="1066" alt="SS.png" src="https://qiita-image-store.s3.amazonaws.com/0/172426/4b54fc59-0ab2-98aa-e835-92acbaeb3a2d.png">



# まとめ

色々と躓きましたが、最終的には目的を達成することができました。
個人的に 4Kハードウェアエンコーダーを用意せずに事前検証が行えるのはとても助かります！