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再インストールしたら余計にハマった。