RaspberryPi zero + Pythonでアクションカメラを作る

スキルは無いのだけれども色々と組み合わせて RaspberryPi Zero を使ってアクションカメラを作れないかやってみました。"アクションカメラ"を構成する主な機能について以下に概要を挙げる事にします。

# 構成

![プレゼンテーション1.png](https://qiita-image-store.s3.amazonaws.com/0/123579/5aaa9b6b-f6eb-5007-8eb3-5bcab3bd6fca.png)

RaspberryPi zero。pimoroniで"Scroll pHAT"付きキットを購入しました。
外ヘ持ち出すのでバッテリーが必要。(Anker PowerCore 10000)
USBカメラは探した中で最も安かった Logicool HD WEBCAM C270。
USBハブを使用して、カメラとBluetoothのUSBアダプタを接続しています。
Pi2や3ならばハブは必要ないですね。
AB Shutter 3 というのは自撮り棒についてきた安価なカメラリモコンで、これを使用して録画の開始停止を操作します。

# USBカメラによる録画

以下を参考させて頂きました。
http://d.hatena.ne.jp/embedded/20151012/p2

gstreamerをインストールした後、以下で音声付きで録画される事を確認しました。

```bash
$ gst-launch-1.0 -e v4l2src  ! "video/x-raw,width=640,height=480,framerate=30/1" ! omxh264enc target-bitrate=1000000 control-rate=variable ! video/x-h264,profile=high ! h264parse ! queue ! mp4mux name=mux alsasrc device=hw:1 ! audioresample ! audio/x-raw,rate=48000 ! queue ! voaacenc bitrate=32000 ! queue ! mux. mux. ! filesink location=test.mp4
```

pythonコード上では上記コマンドをpopenで実行させる事にしました。gstreamerを制御するpythonのライブラリを探したのですが、すぐに理解できる簡単なものがみつからなかったので。

また、解像度は640x480で低いのですが、これ以上解像度を上げるとC270の仕様上フレームレートが30fpsに満たなくなるため我慢です。

```py3
import subprocess
import shlex
import signal

exec_command = "gst-launch-1.0 -e v4l2src  ! \"video/x-raw,width=640,height=480,framerate=30/1\" ! omxh264enc target-bitrate=1000000 control-rate=variable ! video/x-h264,profile=high ! h264parse ! queue ! mp4mux name=mux alsasrc device=hw:1 ! audioresample ! audio/x-raw,rate=48000 ! queue ! voaacenc bitrate=32000 ! queue ! mux. mux. ! filesink location={}".format(filename)

args = shlex.split(exec_command)
proc = subprocess.Popen(args)
```

録画を終了する場合、いきなりPopen.terminate()とかPopen.kill()をしてしまうとファイルフォーマットが正しい状態にならず再生出来ないファイルになってしまうようなので、SIGINTのシグナルを送って、プロセスが終了するのを待つようにします。

```py3
proc.send_signal(signal.SIGINT)
proc.wait()
```

# Bluetoothデバイスによる制御

録画の開始と停止を制御したく、何か良い方法は無いかな考えていたところ、昔購入した自撮り棒に付いていた、カメラリモコンが使えないかなと思いました。ちなみに付いていたのは以下。

http://www.fk-solutions.co.jp/ab_shutter_hp/ab_shutter3.html

AB Shutter 3 というもの。

もしRaspberryPiでBluethooth接続できて、Pythonでボタン押下を検知できるならば録画の開始停止の制御に使えると思った次第。

```text:デバイス検出を有効に
$ bluetoothctl
[bluetooth]# scan on
Discovery started
[CHG] Controller YY:YY:YY:YY:YY:YY Discovering: yes
```

Bluethooth機器を探してみます。カメラリモコンの電源をOnにしてみると、

```text:カメラリモコン発見
[NEW] Device XX:XX:XX:XX:XX:XX AB Shutter 3
```

見つかりました。ペアリングしてみます。

```text:ペアリング
[bluetooth]# pair XX:XX:XX:XX:XX:XX
Attempting to pair with XX:XX:XX:XX:XX:XX
[CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
[CHG] Device XX:XX:XX:XX:XX:XX Modalias: bluetooth:v0A5Cp4502d011B
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs:
        00001000-0000-1000-8000-00805f9b34fb
        00001124-0000-1000-8000-00805f9b34fb
        00001200-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
Pairing successful
```

つながりました。以降、自動接続されるようにしておく。

```
[bluetooth]# trust XX:XX:XX:XX:XX:XX
[CHG] Device XX:XX:XX:XX:XX:XX Trusted: yes
Changing XX:XX:XX:XX:XX:XX trust succeeded
```


デバイスファイルを見てみます。

```bash
pi@pizero:/dev/input $ cd /dev/input/
pi@pizero:/dev/input $ ls
by-id  by-path  event0  mice
```

カメラリモコンの電源をONにすると

```bash
pi@pizero:/dev/input $ ls
by-id  by-path  event0  event1  mice
```

event1というデバイスファイルが増えました。ここから入力を検出できそう。
python-evdev(https://python-evdev.readthedocs.io) で/dev/inputにあるデバイスファイルから入力を取ります。evdevのサンプルコードを試してみます。

```py3:evdev_test.py
import evdev

devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
for device in devices:
    print (device.fn, device.name, device.phys)

#
shutterdev = evdev.InputDevice('/dev/input/event1')
print(shutterdev)

for event in shutterdev.read_loop():
    if event.type == evdev.ecodes.EV_KEY:
        print(evdev.util.categorize(event))
```

```shell-session
pi@pizero:~/app/python3 $ python3 evdev_test.py
/dev/input/event1 AB Shutter 3 00:1b:dc:06:a6:39
/dev/input/event0 UVC Camera (046d:0825) usb-20980000.usb-1.2/button
device /dev/input/event1, name "AB Shutter 3", phys "00:1b:dc:06:a6:39"
key event at 1478875240.192857, 28 (KEY_ENTER), down
key event at 1478875240.305511, 28 (KEY_ENTER), up
key event at 1478875242.780943, 115 (KEY_VOLUMEUP), down
key event at 1478875242.826080, 115 (KEY_VOLUMEUP), up
```

カメラリモコンの2つのボタンを押すと、それぞれENTERとVOLUME_UPキーが検出されているのがわかりました。これでPythonでカメラリモコンのキー押下に応じて録画制御が出来そうです。

# LED表示

録画中あるいは停止中などの状態表示が必要なので、Scroll pHATを使ってみます。

https://shop.pimoroni.com/products/pi-zero-project-kits

ライブラリが用意されていて、英字であれば簡単に表示できます。以下は例で"REC"をスクロール表示させているところ。

![led_display.jpg](https://qiita-image-store.s3.amazonaws.com/0/123579/cfe8ae54-93eb-f1ef-ca9e-87f7334b0418.jpeg)

```py3:文字の表示とスクロール
import scrollphat
import time

scrollphat.clear_buffer()
scrollphat.write_string("REC",11)
length = scrollphat.buffer_len()
for i in range(length):
    scrollphat.scroll()
    time.sleep(0.1)
```


# できあがり

上記を組合せて、BluetoothデバイスからVloumeUpキーが得られれば、録画を開始してLEDに録画開始のメッセージを表示させる。Enterキーを得たのであれば、停止とその旨をLEDに表示させるようにします。

また、バッテリーが切れると録画ファイルが破損してしまうので、30分毎に自動的に録画を区切るようにしました。

バッテリーがどれほど持つのか確認するために、夜中の12時頃から録画を開始して放置しておきました。

![rec_video.png](https://qiita-image-store.s3.amazonaws.com/0/123579/1d9ab3ce-abb6-b2a0-e3b3-5e3e5f2b8b75.png)

最後に作成されたファイルはバッテリー切れでファイルが破損しているので、30分の動画が20個録画されています。10時間分は録画出来ているという事になりますが、深夜の時間帯は暗い部屋で放置していて、全く動きのない動画が作成されているので、バッテリーの消費が抑えられているのかもしれません。