はじめに
仕事でGStreamerを使うことがあったんですが、私は全然ド素人。
ストリーミングする部分は他の人が書いたものをそのままコピペして使ったわけですが、ストリーミングと同時にストリーミングしてる映像を切り出して静止画として使いたい処理があり、GStreamerに入れる前に静止画にすることも考えたんですけど、GStreamerの処理としてできるなら楽かなぁ、と思って調べてみました。
色々試してみたこと
まずは基本の形
gst-launch-1.0
を使って /dev/video0
から映像を取得して、fakesink
に入れて捨てるサンプル。
$ gst-launch-1.0 -e v4l2src device=/dev/video0 ! video/x-raw, width=1920, height=1080, framerate=60/1 ! queue ! videoconvert ! fakesink
tee でパイプラインをふたつに分けてみる
取得した映像を tee
でふたつにわけで、ふたつとも fakesink
に入れて捨てるサンプル(サンプルでもひどい気がする)
$ gst-launch-1.0 -e v4l2src device=/dev/video0 ! video/x-raw, width=1920, height=1080, framerate=60/1 ! tee name=t \
t. ! queue ! videoconvert ! fakesink \
t. ! queue ! videoconvert ! fakesink
これで、上の fakesink
は普通にストリーミングに使用する想定でこのままにしておいて、下の fakesink
を静止画保存用に使っていきます。
multifilesink で保存
色々調べてみて、「なるほどね。こうすればいいんだろ(よく分からない)」で、こうなった。
$ gst-launch-1.0 -e v4l2src device=/dev/video0 ! video/x-raw, width=1920, height=1080, framerate=60/1 ! tee name=t \
t. ! queue ! videoconvert ! fakesink \
t. ! queue ! videoconvert ! videoscale ! video/x-raw,width=720,height=480 ! jpegenc ! multifilesink location=capture_%05d.jpg max-files=10 next-file=max-duration max-file-duration=300000000000
videoscale
でリサイズして、jpegenc
でJPEGファイルにして、multifilesink
で保存する。
location=capture_%05d.jpg
でファイル名指定して、max-files=10
で保存されるファイルの最大数指定。next-file=max-duration max-file-duration=300000000000
の指定で、300秒毎にファイル保存。
ちなみに、capture_%05d.jpg
でファイルが勝手に連番で保存されていくけど、max-files
でファイル数を指定している場合、capture_00010.jpg
が保存されると capture_00000.jpg
が消える。capture_00000.jpg
が新たに保存されるみたいに番号が戻るのかと思ったけど、そうはならないらしい。
一見できてるように見えたけど
上のスクリプトでうまくいってるように見えるんですよ。
「これでできたな GStreamer チョロいぜ」とか思ったんですけど、このスクリプト、非常に重い。最初に framerate=60
を指定してるのに、上の fakesink
でフレームレートを確認すると、18フレーム/秒くらい。これは使えない。
たぶん、この指定方法だと、全フレームの画像サイズ変換して、JPEG変換して、そのうち5分に1度静止画保存してるんでしょうね。それは重いか。。
というわけで改良版。
$ gst-launch-1.0 -e v4l2src device=/dev/video0 ! video/x-raw, width=1920, height=1080, framerate=60/1 ! tee name=t \
t. ! queue ! videoconvert ! fakesink \
t. ! queue ! videoconvert ! videorate ! video/x-raw, framerate=1/300 ! videoscale ! video/x-raw, width=720, height=480 ! jpegenc ! multifilesink location=capture_%05d.jpg max-files=10
videorate
でフレームレートを 1/300 に下げてから、videoscale
以降の処理をするように修正。multifilesink
に来るのはフレームレート 1/300 で 5分に1回だから、next-file=max-duration max-file-duration=300000000000
の指定は不要になった。
もうちょっと短く書ける
videorate
と videoscale
はまとめてこんな感じで書いてもいいらしい。
$ gst-launch-1.0 -e v4l2src device=/dev/video0 ! video/x-raw, width=1920, height=1080, framerate=60/1 ! tee name=t \
t. ! queue ! videoconvert ! fakesink \
t. ! queue ! videoconvert ! videorate ! videoscale ! video/x-raw, width=720, height=480, framerate=1/300 ! jpegenc ! multifilesink location=capture_%05d.jpg max-files=10
ただし、これだとダメ
上の videorate
と videoscale
の順序を入れ替えただけだけど、これをやるとフレームレートが 20フレーム/秒くらいまで下がった。
このような順序にしてしまうと、全フレームに対してサイズ変更だけはやってしまうということかな。
$ gst-launch-1.0 -e v4l2src device=/dev/video0 ! video/x-raw, width=1920, height=1080, framerate=60/1 ! tee name=t \
t. ! queue ! videoconvert ! fakesink \
t. ! queue ! videoconvert ! videoscale ! videorate ! video/x-raw, width=720, height=480, framerate=1/300 ! jpegenc ! multifilesink location=capture_%05d.jpg max-files=10
まとめ
簡単にできるかなと思った、ストリーミングしながら静止画保存ですが、なかなか手強かったのでメモ的に試してみたスクリプトとその結果を書いてみました。
マシンパワーが強くて全フレームに対してガリガリ処理を入れても問題ない処理速度であれば色々考えなくていいのかもしれませんが、今回は非力なマシンでやる必要があったので色々試してみました。
ちょっと特殊な用途かもしれませんが、こんなこともできたよって感じで読んでいただけたら幸いです。