Raspberry Pi 4Bに接続したUVCカメラのMJPEG画像をAmazon Kinesis Video Streamsにアップロード

# 1. 概要
Raspberry Pi 4Bに接続したUVCカメラのMJPEG画像をAmazon Kinesis Video Streams(AWS KVS)にアップロードすることを試みた。

## 結論
この記事の時点で、MJPEG画像をAWS KVSにアップロードできていない。

## 背景
　知人からRaspberry Pi 3BでUVCカメラのMJPEG画像をAWS KVSにアプロードする方法について尋ねられた時、自分がやったことある手順やそのとき困ったこと情報を提供した。その後、知人はRaspberry Pi 4Bで試したところ、うまくいかないと連絡してきた。尚、UVCカメラはちょっと変わってる製品であるとのこと。
そこで、4Bが原因なのか、UVCカメラのせいなのか、連絡した手順が悪いのか確認することにした。

# 2. 準備
## Raspberry Pi 4Bの準備
* ラズベリーパイ財団のWebサイトのRaspberry Pi Imagerとか使う
* Raspberry Pi OS(32bit)

## Amazon Kinesis Video Stream の WebRTC のサンプルをビルドする
```
sudo apt-get install libcap2 libcap-dev
sudo apt-get install cmake pkg-config libssl-dev
sudo apt-get install gstreamer1.0-tools libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-omx gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly
```

作業ディレクトリを作成し、移動
```
mkdir videocam
cd videocam
```
SDKのダウンロード
```
git clone --recursive https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git
```

ビルド用ディレクトリを作成してビルド
```
mkdir -p amazon-kinesis-video-streams-webrtc-sdk-c/build
cd amazon-kinesis-video-streams-webrtc-sdk-c/build
cmake ..
```

そこそこ時間かかる

```
make
```

## UVCカメラの性能確認
video0 の部分は接続されている機器や数によって違うことがある。

Motion JPEGに対応していることがわかる。
```
pi@raspberrypi:build $ v4l2-ctl -d /dev/video0 --list-formats
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'MJPG' (Motion-JPEG, compressed)
	[1]: 'YUYV' (YUYV 4:2:2)
```

解像度やフレームレートの確認
```
pi@raspberrypi:build $ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'MJPG' (Motion-JPEG, compressed)
		Size: Discrete 1920x1080
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 1280x800
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.040s (25.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 1920x1080
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
	[1]: 'YUYV' (YUYV 4:2:2)
		Size: Discrete 1920x1080
			Interval: Discrete 0.200s (5.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 1280x800
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.040s (25.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.040s (25.000 fps)
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 1920x1080
			Interval: Discrete 0.200s (5.000 fps)
			Interval: Discrete 0.200s (5.000 fps)
```

カメラの映像をモニターに映してみる。
```
gst-launch-1.0 v4l2src ! videoconvert ! ximagesink
```
出力されたので、カメラは動いており、Gstreamerも動作することが分かった。

# 3. Amazon Kinesis Video Streams(AWS KVS)に映像を送る

## AWS用の環境変数
AWSにアカウントを作成したときのキーを設定する。

```
export AWS_ACCESS_KEY_ID=「アクセスキーID」
export AWS_SECRET_ACCESS_KEY=「シークレットアクセスキー」
export AWS_DEFAULT_REGION=「AWSリージョン」
```

## とりあえずサンプルコードをそのまま動かしてみる。
現在の作業パスはビルドしたところから移動していない前提。

```
cd samples
./kvsWebrtcClientMasterGstSample test-ch
```

AWSへアクセスし、Kinesis Video Streams >シグナリングチャネルを開く。
メディア再生ビューワーで再生テストを行う。うまくいっていれば、UVCカメラの映像が見れる。
だが、みれない。これは前もそうだった気がする。サンプルコードそのままだとなぜかうまくいかない。

## サンプルコードを変更して試す
amazon-kinesis-video-streams-webrtc-sdk-c/samples/kvsWebRTCClientMasterGstreamerSample.c
の　155行目付近を変更する。

エンコードやらフレームレートやらを変更してみる。変更箇所は以下。
```
if (pSampleConfiguration->useTestSrc) {
} else {
	ここのコード
}
```

### ソフトウェアH.264エンコード, UVC Cam(YUV) -> H.264
上記に書いた箇所（elseの中）を以下に入れ替える。
```
pipeline = gst_parse_launch(
    "v4l2src do-timestamp=TRUE device=/dev/video0 is-live=TRUE ! queue ! videoconvert ! video/x-raw,width=1280,height=720,framerate=10/1 ! " 
    "x264enc bframes=0 speed-preset=veryfast bitrate=512 byte-stream=TRUE tune=zerolatency ! " 
    "h264parse config-interval=-1 ! video/x-h264,stream-format=byte-stream,alignment=au,profile=baseline ! appsink sync=TRUE emit-signals=TRUE name=appsink-video",
&error);
```

上の階層samplesに移動してもう一度ビルドし、サンプルアプリを実行する。
再びシグナリングチャネルをみると映像が再生された。カメラやGstreamer、AWS関係に問題はないようだ。

```
cd ..
cmake ..
make 
cd samples
./kvsWebrtcClientMasterGstSample test-ch
```

### ハードウェアH.264エンコード, UVC Cam(YUV) -> H.264
同様にハードウェアH.264エンコードを試す。

```
pipeline = gst_parse_launch(
    "v4l2src device=/dev/video0 ! queue ! videoconvert ! video/x-raw,width=1280,height=720,framerate=10/1 ! " 
    "omxh264enc control-rate=1 target-bitrate=5120000 periodicty-idr=10 inline-header=FALSE ! " 
    "h264parse config-interval=-1 ! video/x-h264,stream-format=byte-stream,alignment=au,profile=baseline ! appsink sync=TRUE emit-signals=TRUE name=appsink-video",
&error);
```

### ハードウェアH.264エンコード, UVC Cam(MJPEG) -> H.264
で、肝心のMJPEGをためす。

```
pipeline = gst_parse_launch(
    "v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720,framerate=10/1 ! omxmjpegdec ! " 
    "omxh264enc control-rate=1 target-bitrate=5120000 periodicty-idr=10 inline-header=FALSE ! " 
    "h264parse config-interval=-1 ! video/x-h264,stream-format=byte-stream,alignment=au,profile=baseline ! appsink sync=TRUE emit-signals=TRUE name=appsink-video",
&error);
```

結果は・・・ダメだった。以前、Raspberry Pi 3Bを使ったときはうまく言った記憶があるのだが・・・

# 4.原因を調べてみる
## OMXは？
omxか？と思ったが、以下のコマンドによる確認で、omxmjpegdecとomxh264enc機能があることはわかった。
```
pi@raspberrypi:~ $ gst-inspect-1.0 | grep omx
omx:  omxmpeg2videodec: OpenMAX MPEG2 Video Decoder
omx:  omxmpeg4videodec: OpenMAX MPEG4 Video Decoder
omx:  omxh263dec: OpenMAX H.263 Video Decoder
omx:  omxh264dec: OpenMAX H.264 Video Decoder
omx:  omxtheoradec: OpenMAX Theora Video Decoder
omx:  omxvp8dec: OpenMAX VP8 Video Decoder
omx:  omxmjpegdec: OpenMAX MJPEG Video Decoder
omx:  omxvc1dec: OpenMAX WMV Video Decoder
omx:  omxh264enc: OpenMAX H.264 Video Encoder
omx:  omxanalogaudiosink: OpenMAX Analog Audio Sink
omx:  omxhdmiaudiosink: OpenMAX HDMI Audio Sink
libav:  avenc_h264_omx: libav OpenMAX IL H.264 video encoder encoder
```

## 3Bと4Bで何が違うの？
この違いはいったい・・・？
3Bではエラーが出ていない。だから3Bではうまくいき、4Bでは失敗しているということか？

Raspberry Pi 4B 
```
pi@raspberrypi:~ $ gst-launch-1.0  v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720,framerate=10/1 ! omxmjpegdec! fakesink
パイプラインを一時停止 (PAUSED) にしています...
ERROR: Pipeline doesn't want to pause.
ERROR: from element /GstPipeline:pipeline0/GstOMXMJPEGDec-omxmjpegdec:omxmjpegdec-omxmjpegdec0: サポートライブラリを初期化できません
追加のデバッグ情報:
gstvideodecoder.c(2535): gst_video_decoder_change_state (): /GstPipeline:pipeline0/GstOMXMJPEGDec-omxmjpegdec:omxmjpegdec-omxmjpegdec0:
Failed to open decoder
Setting pipeline to NULL ...
Freeing pipeline ...
```

Raspberry Pi 3B
```
pi@raspberrypi:~ $ gst-launch-1.0  v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720,framerate=10/1 ! omxmjpegdec! fakesink
パイプラインを一時停止 (PAUSED) にしています...
Pipeline is live and does not need PREROLL ...
パイプラインを再生中 (PLAYING) にしています...
New clock: GstSystemClock
```

## Gstreamerのエラーは何か？
GST_DEBUGを付けてログだししてみた
```
GST_DEBUG=FIXME gst-launch-1.0  v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720,framerate=10/1 ! omxmjpegdec!  fakesink
```

エラーログによると、gstomx.cでハンドル取得エラーらしい。これは自分や知人だけのエラーか、公式とかでも起こるのか、それが分からないが、多分みんな起きるのでは。
誰も使ってないのかMJPEGの機能・・・

```
0:00:00.219924812 20692   0x27cf80 ERROR  omx gstomx.c:799:gst_omx_component_new:<omxmjpegdec-omxmjpegdec0> Failed to get component handle 'OMX.broadcom.egl_render' from core '/opt/vc/lib/libopenmaxil.so': 0x80001000
0:00:00.219983719 20692   0x27cf80 WARN   videodecoder gstvideodecoder.c:2535:gst_video_decoder_change_state:<omxmjpegdec-omxmjpegdec0> error: Failed to open decoder
ERROR: Pipeline doesn't want to pause.
ERROR: from element /GstPipeline:pipeline0/GstOMXMJPEGDec-omxmjpegdec:omxmjpegdec-omxmjpegdec0: サポートライブラリを初期化できません
追加のデバッグ情報:
gstvideodecoder.c(2535): gst_video_decoder_change_state (): /GstPipeline:pipeline0/GstOMXMJPEGDec-omxmjpegdec:omxmjpegdec-omxmjpegdec0:
```
# 5. Pi4のOS再インストール
Gstreamerを更新しようとあれこれやっているうちにインストール済みパッケージの状態がおかしくなってきた。
そこで、OSの再インストールをした。深く考えずに使用中のSDカードにRaspberry Pi ImagerでRaspberry Pi OS 32bitを上書き。そして起動した。

# 5.1 違い
とりあえず本件関係の定番インストール。
```
sudo apt install autoconf automake libtool
sudo apt install gstreamer1.0-tools gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-bad
```

そして、確認。
GStreamerが1.14から1.18へ更新されてる！知らぬ間に公式で更新されていた？これで全て解決？
```
pi@raspberrypi:~ $ gst-launch-1.0 --version
gst-launch-1.0 version 1.18.4
GStreamer 1.18.4
```

と思ったら、omxmjpegdecがいない。あんなにたくさんいたomxがいない。
```
pi@raspberrypi:~ $ gst-inspect-1.0 | grep omx
libav:  avenc_h264_omx: libav OpenMAX IL H.264 video encoder encoder
libav:  avenc_mpeg4_omx: libav OpenMAX IL MPEG-4 video encoder encoder
```

# 今後
最新ソースもってきて自分でビルドか、自分でソース修正してみたいところ。
2022/03/27 : ソースのビルドの作業過程でOS再インストールしたら余計にハマった。