NanoPi NEOでウェブカメラ (実用化編)

ウェブカメラ

この記事はNanoPi NEO用として書いているけどおそらくBanana Pi M2+, Beelink X2, NanoPi M1+, NanoPi NEO Air, NanoPi M1, Orange Pi+ 2, Orange Pi PC+, Orange Pi+ 2e, Orange Pi Lite, Orange Pi 2, Orange Pi One, Orange Pi PC, Orange Pi+ あたりのH3搭載ボードなら同様にできる筈。

NanoPi NEOでウェブカメラを書いてから、あれもこれもやりたいということで忙しくてだいぶ間が空いてしまったが、再びウェブカメラ。
前回は動画の速度と画質を無理くり我慢してなんとか実用ぎりぎり(アウト?)な感じだったので今回はより実用的なものにしたい。

NanoPi NEOで動画がめっちゃ遅いのは動画の支援機能が働いてないから。この手のシングルボードコンピュータはPCと比べるとCPUが圧倒的に遅いので支援無しでHDサイズの動画というのは苦しい。でも、動画支援機能は技術力のあるプログラマでないとなかなか使えないというのが辛いところ。

と、思っていたが、ググるとAllwinnerのSoCなボード用に動画支援機能を追加した改変版FFMpegが出ていた。しかも2年ほど前に・・

今回はこの2つを合わせて使用する。上のリンクがffmpegのソースそのものだけど2年ほど前のものということで、出来上がるFFMpegのバージョンもgit-2015-01-22-f86a076というちょっと古そうなものになる。2つめのリンクはそのH3用パッチ。

この記事ではarmbianのDebian Jessie レガシーカーネル版を使用している。一応、新しいMainline系でも試したが残念ながら支援機能は使えなかった。

$ uname -a
Linux nanopineo 3.4.113-sun8i #2 SMP PREEMPT Sat Apr 1 23:02:38 JST 2017 armv7l GNU/Linux

事前準備

必要になるであろうパッケージをインストールする。

# apt-get install libtool pkg-config autogen gettext
# apt-get install libx264-dev libv4l-dev libpulse-dev libmp3lame-dev libjson-c-dev libjpeg-dev libflac-dev libogg-dev libvdpau-dev
# apt-get install v4l-utils

1行目はアプリケーションをビルドするようなことがあるときに入れておきたいもの。
2行目はffmpegのビルドに必要なパッケージ。今回の記事のconfigureオプションなら上のもので足りる筈だが、他のオプションも追加するなら別途追加が必要になるパッケージがあるかと。
3行目はffmpegのビルドには関係ないが、USBウェブカメラの画質を制御したい場合は入れると良い。(この記事最後の方)

作り方

https://github.com/stulluk/FFmpeg-Cedrus からソースを取得

$ cd ~
$ git clone https://github.com/stulluk/FFmpeg-Cedrus.git

https://github.com/uboborov/ffmpeg_h264_H3 からソースを取得

$ git clone https://github.com/uboborov/ffmpeg_h264_H3.git

以下、ビルド手順

$ cd ~/FFmpeg-Cedrus/libavcodec
$ mv cedrus264.c cedrus264.c.ORG
$ cp ~/ffmpeg_h264_H3/cedrus264.c ./
$ cd ./arm
$ mv sunxi sunxi.ORG
$ cp -R ~/ffmpeg_h264_H3/sunxi ./
$ cd ../../

$ ./configure --prefix=/usr --enable-nonfree --enable-gpl --enable-version3 --enable-vdpau --enable-libx264 --enable-libmp3lame --enable-libpulse --enable-libv4l2
$ make -j4
# make install

configureからmake installまでNanoPi NEOでやるとかなりの時間がかかる。
で、今回作ったffmpegが普通のと違うのは通常のH.264用のエンコーダー libx264の他にcedrus264というのが追加されていること。これを使うとかなり速い。

使用してみる

$ ffmpeg -t 60 -f v4l2  -s 1280x720 -i /dev/video0 -pix_fmt nv12  -vcodec libx264 -r 30 ./test.mp4

Input #0, video4linux2,v4l2, from '/dev/video0':
  Duration: N/A, start: 17743.887218, bitrate: 73728 kb/s
    Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 1280x720, 73728 kb/s, 5 fps, 5 tbr, 1000k tbn, 1000k tbc
[libx264 @ 0x218c510] using cpu capabilities: ARMv6 NEON
[libx264 @ 0x218c510] profile High, level 3.1
[libx264 @ 0x218c510] 264 - core 142 r2431 a5831aa - H.264/MPEG-4 AVC codec - Copyleft 2003-2014 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to './test.mp4':
  Metadata:
    encoder         : Lavf56.2.100
    Stream #0:0: Video: h264 (libx264) ([33][0][0][0] / 0x0021), nv12, 1280x720, q=-1--1, 30 fps, 15360 tbn, 30 tbc
    Metadata:
      encoder         : Lavc56.0.101 libx264
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (libx264))
Press [q] to stop, [?] for help
frame= 1785 fps= 17 q=-1.0 Lsize=     405kB time=00:00:59.43 bitrate=  55.9kbits/s dup=1672 drop=0    
video:384kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 5.659878%

60秒間(-t 60)USBカメラで取得した動画を1280x720の解像度でlibx264を使用してファイルに出力。この時、入力側ではなく出力側のフレームレートを30 (-r 30)に指定。もちろん指定したからといって性能が低ければその値で処理できるわけではない。
そして、表示上は59秒43ぶんを処理したことになっているが、実際にはこの処理は1分46秒かかっている。つまり1785フレームを106秒なので17fps。(1分46秒は上の表示にはなく別途計測したもの)

$ ffmpeg -t 60 -f v4l2  -s 1280x720 -i /dev/video0 -pix_fmt nv12  -vcodec cedrus264 -r 30 ./test.mp4

Input #0, video4linux2,v4l2, from '/dev/video0':
  Duration: N/A, start: 17418.199590, bitrate: 73728 kb/s
    Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 1280x720, 73728 kb/s, 5 fps, 5 tbr, 1000k tbn, 1000k tbc
[VDPAU SUNXI] VE version 0x1680 opened.
Output #0, mp4, to './test.mp4':
  Metadata:
    encoder         : Lavf56.2.100
    Stream #0:0: Video: h264 (cedrus264) ([33][0][0][0] / 0x0021), nv12, 1280x720, q=2-31, 200 kb/s, 30 fps, 15360 tbn, 30 tbc
    Metadata:
      encoder         : Lavc56.0.101 cedrus264
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (cedrus264))
Press [q] to stop, [?] for help
frame= 1798 fps= 32 q=30.0 Lsize=    2175kB time=00:00:59.93 bitrate= 297.4kbits/s dup=1504 drop=0    
video:2168kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.359510%

条件は前と同じで、vcodecにcedrus264を指定した。
表示上は59秒93となっているが実処理時間は1分1秒。ほぼ指定どおり。1798フレームを61秒なので29.5fpsとこちらも指定に近いものになっている。
ただし、被写体の変化が少ないものであればこのように順調だが被写体が動くとときどき詰まってその後ワープが発生したようになる。
でもかなり良い感じ。

ffmpegの細かいオプション指定は実際の画質を見てどこまで我慢できるかなのでこの記事ではこれ以上やらない。

ストリーミング

NanoPi NEOは基本的にはストレージはmicroSD。もちろんUSBでハードディスク等を繋ぐというのもアリだろうが、それだとNanoPi NEOのせっかくの小型さが台無し。
そこでNanoPi NEOを監視カメラにしようというのなら本体に動画を保存するのではなく、ネットワークで飛ばして他の端末で録画・再生するというのが良いと思う。要するにやりたいことは前回と同じくストリーミング。
前回はカメラからの動画の取得とストリーミングにVLCを使ったが、今回はここまでFFMpegでやってきたのでストリーミングもFFMpegで。単に出力先をファイルからネットワークに変更する程度なので実は簡単。

$ ffmpeg -f v4l2 -i /dev/video0  -vcodec cedrus264 -s 1280x720 -pix_fmt nv12 -b:v 4096k -f mpegts udp://192.168.0.200:10000

mpegts形式で192.168.0.200 ポート10000に向けてUDPで送信。192.168.0.200とポート10000は受信させたい端末・サーバのものであって、送信しようとしているNanoPi NEOのIPアドレスとポートではないので注意。

ストリーミングで受信する側の端末・サーバ側。例えばPCでVLCを使って観るなら「ネットワークストリームを開く」で「ネットワークURL」に以下を指定。

udp://@:10000

このときNanoPi NEOのIPアドレスを指定するのではない点に注意。
動画再生が始まらない場合があるが、プレーヤー側を待ち受け状態(ネットワークURLを指定して実行)にしてからNanoPi側を動かす方が成功しやすいかも。

$ ffmpeg -framerate 15 -f v4l2  -s 1280x720 -i /dev/video0 -pix_fmt nv12  -vcodec cedrus264  -r 15  -f mpegts udp://192.168.0.10:10000
ffmpeg version git-2015-01-22-f86a076 Copyright (c) 2000-2014 the FFmpeg developers
  built on Apr  2 2017 13:29:23 with gcc 4.9.2 (Debian 4.9.2-10)
  configuration: --prefix=/usr --enable-nonfree --enable-gpl --enable-version3 --enable-vdpau --enable-libx264 --enable-libmp3lame --enable-libpulse --enable-libv4l2
  libavutil      54.  6.100 / 54.  6.100
  libavcodec     56.  0.101 / 56.  0.101
  libavformat    56.  2.100 / 56.  2.100
  libavdevice    56.  0.100 / 56.  0.100
  libavfilter     5.  0.102 /  5.  0.102
  libswscale      3.  0.100 /  3.  0.100
  libswresample   1.  1.100 /  1.  1.100
  libpostproc    53.  0.100 / 53.  0.100
[video4linux2,v4l2 @ 0x230a4b0] The driver changed the time per frame from 1/15 to 2/15
Input #0, video4linux2,v4l2, from '/dev/video0':
  Duration: N/A, start: 67485.253012, bitrate: 110592 kb/s
    Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 1280x720, 110592 kb/s, 7.50 fps, 7.50 tbr, 1000k tbn, 1000k tbc
[VDPAU SUNXI] VE version 0x1680 opened.
Output #0, mpegts, to 'udp://192.168.0.10:10000':
  Metadata:
    encoder         : Lavf56.2.100
    Stream #0:0: Video: h264 (cedrus264), nv12, 1280x720, q=2-31, 200 kb/s, 15 fps, 90k tbn, 15 tbc
    Metadata:
      encoder         : Lavc56.0.101 cedrus264
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (cedrus264))
Press [q] to stop, [?] for help
frame=  932 fps= 15 q=30.0 Lsize=   60678kB time=00:01:02.13 bitrate=8000.2kbits/s dup=630 drop=0    
video:56185kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 7.997251%

無理の無い現実的な値として入力側出力側共に15FPSを指定した。これだと被写体のワープや大きな遅延は発生しない感じ。指定した15FPSよりもコマ送り感があるけど監視カメラだからこんなもんでしょ。

ここまで音声無しでやってるけど音声乗せるのもFFmpegの設定しだい。

カメラの画質変更

ffmpegの動画の画質の話ではなくカメラ側の画質の変更の話。
昼の屋外などで動画が明るすぎる(白っぽい)ので暗めにしたいなど画質を変更したい場合。
まず、コントロールできる内容を表示してみる。
# v4l2-ctl -d /dev/video0 --list-ctrls
error 22 getting ctrl Brightness
error 22 getting ctrl Contrast
error 22 getting ctrl Saturation
error 22 getting ctrl White Balance Temperature, Auto
error 22 getting ctrl Gain
error 22 getting ctrl Power Line Frequency
error 22 getting ctrl White Balance Temperature
error 22 getting ctrl Sharpness
error 22 getting ctrl Backlight Compensation
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute (int)    : min=1 max=10000 step=1 default=166 value=8
         exposure_auto_priority (bool)   : default=0 value=1
error 22 getting ctrl Brightness
error 22 getting ctrl Contrast
error 22 getting ctrl Saturation
error 22 getting ctrl White Balance Temperature, Auto
error 22 getting ctrl Gain
error 22 getting ctrl Power Line Frequency
error 22 getting ctrl White Balance Temperature
error 22 getting ctrl Sharpness
error 22 getting ctrl Backlight Compensation

今回使用したのが安物カメラだからか殆どの項目がエラーで触れない。コントロールできる項目は真ん中辺りの3つで「露光」関係だけ。現在は露光が自動になっいて、表示した時点のexposure_absolute値は8。(valueを見る)

以下のようにして指定したい値をセットする。

# v4l2-ctl -d /dev/video0 --set-ctrl=項目名=セットしたい値

デバイスが1つしかなければ -d /dev/video0 は入力しなくても大丈夫っぽい。

露光を自動からマニュアルに変える(下)。これをしないとその次の値の変更ができない。

# v4l2-ctl -d /dev/video0 --set-ctrl=exposure_auto=1

上の例では屋外の明るいところを撮っていて、露光(時間)の値が8だったのでこれより暗くするのはあまり選択範囲がない。とりあえず最低値の1を指定。 (数字を大きくしたら明るくなる=暗い所用、数字を小さくしたら暗くなる=明るい所用)

# v4l2-ctl -d /dev/video0 --set-ctrl=exposure_absolute=1

画面全体が白っぽいのは抑えられたので一応成功とはいえる。
でも、マニュアルにすると明るさが変わるところでは都度変更しなきゃならないので自動に戻しておいた方が良さそう。

# v4l2-ctl -d /dev/video0 --set-ctrl=exposure_absolute=8
# v4l2-ctl -d /dev/video0 --set-ctrl=exposure_auto=3

これまで「がとらぼ」のNanoPi NEOを何かに使う関係の記事はどれもこれも中途半端だったけど、ようやく「監視カメラ」として「使いものになる」と言えるものが出来た?

お値段的にはNanoPi NEO(無印)+ヒートシンク+送料、LOGICOOLウェブカメラC270(こちらはamazon送料無料)、合計約2,500〜3,000円。(電源とmicroSDカードは入ってない)
でネットワーク(ストリーミング)カメラとしては格安ということになる。

関連記事: