GStreamerで映像処理のボトルネックを見つける方法

# 前提

GStreamer のインストール方法、基本的な使い方などについては記載しません。

筆者の実行環境は次の通りです。
- OS：Windows10（※Ubuntu などでも同様なはずです。）
- GStreamer のバージョン：1.18.6（※1.0 以上なら同様なはずです。）


# 概要

以下の方法でボトルネックを見つけます。
1. "**fpsdisplaysink**" というエレメントに "**video-sink=fakesink**" というオプションを付け、パイプラインの末尾[^1]のエレメントと置換する。
1. "**-v**" オプションを付け[^2]、gst-launch-1.0 で変更後のパイプラインを実行する。
1. コンソール出力としてパイプライン全体の処理のフレームレートが表示されるので、そのフレームレートが入力映像のフレームレートと同等であるか確認する。
1. 同等でない場合、"**fpsdisplaysink**" の１つ前段のエレメントを削除する。
1. "3." の結果が同等となるまで "2~4." の手順を繰り返す。
1. 同等となったとき、最後に削除したエレメントがボトルネックであったと言える。

[^1]: どこからでも構いませんが、わかりやすさを重視して末尾から順に前段を確かめるように記載しています。
[^2]: fpsdisplaysink が出力するフレームレートをコンソール画面から確認するためです。



# 詳細


次のパイプラインを例とし、解説を行います。
筆者環境では目視で約1fpsしか出ません。（VP9は高圧縮率な分やはり重たいようですね）

```
# FHD/30fps のテスト映像に経過時間を重畳し、VP9でエンコード、直後にデコードして映像を表示するパイプライン。（入力フレーム数：300）
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1920,height=1080,framerate=30/1 ! timeoverlay ! vp9enc ! vp9dec ! videoconvert ! autovideosink sync=false -v
```

<br>

概要に記載した手順の通りに進めていきます。
当然といえば当然ですが、VP9エンコードがボトルネックになっていますね。

```
# 画面表示以外の部分（videoconvert した時点）でどれだけのフレームレートが出ているか。
# 後ろに色空間の指定などが続かないため、videoconvert は何もしていないはず。
# -> ~0.9fps
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1920,height=1080,framerate=30/1 ! timeoverlay ! vp9enc ! vp9dec ! videoconvert ! fpsdisplaysink video-sink=fakesink -v
-> /GstPipeline:pipeline0/GstFPSDisplaySink:fpsdisplaysink0: last-message = rendered: 277, dropped: 0, current: 0.04, average: 0.87

# デコードした時点でどれだけのフレームレートが出ているか。
# -> ~0.9fps
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1920,height=1080,framerate=30/1 ! timeoverlay ! vp9enc ! vp9dec ! fpsdisplaysink video-sink=fakesink -v
-> /GstPipeline:pipeline0/GstFPSDisplaySink:fpsdisplaysink0: last-message = rendered: 277, dropped: 0, current: 0.04, average: 0.88

# エンコードした時点でどれだけのフレームレートが出ているか。
# -> ~0.9fps
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1920,height=1080,framerate=30/1 ! timeoverlay ! vp9enc ! fpsdisplaysink video-sink=fakesink -v
-> /GstPipeline:pipeline0/GstFPSDisplaySink:fpsdisplaysink0: last-message = rendered: 277, dropped: 0, current: 0.04, average: 0.89

# 経過時間を重畳した時点でどれだけのフレームレートが出ているか。
# -> ~29.6fps（≒入力フレームレート）
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1920,height=1080,framerate=30/1 ! timeoverlay ! vp9enc ! fpsdisplaysink video-sink=fakesink -v
-> /GstPipeline:pipeline0/GstFPSDisplaySink:fpsdisplaysink0: last-message = rendered: 294, dropped: 0, current: 30.12, average: 29.64
```


## ちなみに

解像度を小さくしたりコーデックを変更してみましたが、やはりリアルタイムでエンコードするにはハードウェアエンコーダが必要ですね。
"vp9enc"のオプションを色々と指定してやることである程度は改善できると思うので、興味のある方はぜひ調査してみてください。結果の共有お待ちしてます！！！
```
# 解像度をHDに変更し、エンコードした時点でどれだけのフレームレートが出ているか。
# -> ~2.0fps
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1280,height=720,framerate=30/1 ! timeoverlay ! vp9enc ! fpsdisplaysink video-sink=fakesink -v
-> /GstPipeline:pipeline0/GstFPSDisplaySink:fpsdisplaysink0: last-message = rendered: 277, dropped: 0, current: 0.17, average: 1.97

# コーデックをVP8に変更し、エンコードした時点でどれだけのフレームレートが出ているか。（解像度：FHD）
# -> ~2.4fps
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1920,height=1080,framerate=30/1 ! timeoverlay ! vp8enc ! fpsdisplaysink video-sink=fakesink -v
-> /GstPipeline:pipeline0/GstFPSDisplaySink:fpsdisplaysink0: last-message = rendered: 299, dropped: 0, current: 2.28, average: 2.44

# コーデックをVP8に変更し、エンコードした時点でどれだけのフレームレートが出ているか。（解像度：HD）
# -> ~5.6fps
gst-launch-1.0 videotestsrc horizontal-speed=15 num-buffers=300 ! video/x-raw,width=1280,height=720,framerate=30/1 ! timeoverlay ! vp8enc ! fpsdisplaysink video-sink=fakesink -v
-> /GstPipeline:pipeline0/GstFPSDisplaySink:fpsdisplaysink0: last-message = rendered: 298, dropped: 0, current: 5.30, average: 5.57
```