RaspberryPiで動体検知したら録画してDiscordにアップロード

盆休みの夜更し記録。
普段Linux殆ど使わない人が数年ぶりにRaspberryPiを触ると何もかも忘れてる。
motionは先達の叡智のおかげで特に問題なく進められたが、音声録音とDiscordの辺りで躓いたので備忘録。

# ゴール
1. RaspberryPiのカメラで動体検知する
2. 動体検知したら動画撮影（＋音声）
3. 動画をDiscordにアップロード

# 機能概要

「RaspberryPiで動体検知」をGoogle先生に聞いてみると、motionを使えば凄く楽に実現できそうだったので採用。

1. motionで動体検知
2. 動体検知時に動画撮影・録音開始
3. ffmpegで動画と音声を結合
4. 出来た動画をDiscordにアップロード

# 材料
- Raspberry Pi 4 ModelB 4GB ( Raspbian10 )
- Raspberry Pi Camera V2
- その辺に転がってたUSBマイク
- Discordサーバ

諸々初期設定まで済ませておく。
Discordの適当なテキストチャンネルの webhook URL を取得しておく。

# motionの設定

まずはmotionをインストール。

```:Terminal
$ sudo apt-get install -y motion
```

次にmotionの設定を書き換える。
ファイル作成とファイルCloseのコールバックは後で弄る。
静止画要らない。
撮影後に音声をくっつけたいのでMP4出力、あまり大きな画にしても仕方ないので640x360指定。
DiscordにMP4を上げるのが最終目標なので、添付ファイル上限サイズ8MBを考慮したサイズ設定や撮影時間にする。

```:Terminal
$ sudo nano /etc/motion/motion.conf
```

# 撮影開始時
motion.conf に音関連の項目が無いようなので自前で録音するしかない？
動画撮影開始と同時に録音開始する。arecordでwav録音出来るらしい。
motion.conf の on_movie_start が動画ファイル作成時のコールバック指定出来るっぽい。
on_event_start がevent_gap考慮されてそうなので、コチラでも良さそう。試していないが音ズレするようなら検討。
%f で動画ファイルのフルパスをくれるので、同名のwavファイルを録音開始するシェルスクリプトを指定する。

```motion.conf
on_movie_start "/home/pi/motion/motion_video__start.sh %f"
```

```shell:motion_video__start.sh
#! /bin/bash
wav_path="${1%.*}.wav"

# 録音開始
arecord -D plughw:2,0 -f cd $wav_path
```

plughw:2,0 は カード2 デバイス0 の意、`arecord -l`で調べる。

# 撮影終了時

motion.conf の on_movie_end が撮影した動画ファイルClose時に呼んでくれそう。
こちらも%fで動画のフルパスを渡してくれる。
以下を行うシェルスクリプトを指定する。
1. 録音を止める
2. wavをaacにエンコード
3. mp4＋aac
4. 途中ファイル削除（元mp4, wav, aac）
5. Discord投下

```motion.conf
on_movie_start "/home/pi/motion/motion_video__end.sh %f"
```

```shell:motion_video__end.sh
#! /bin/bash

wav_path="${1%.*}.wav"
aac_path="${1%.*}.m4a"
target_path="${1%/*}/motion_${1##*-}"
discord_path="file=@${target_path}"

# 録音強制停止
pkill -KILL -f arecord

# 元動画再生時間取得
duration=$(ffprobe -i $1 -show_entries format=duration -v error -of csv="p=0")

# wav → aac
#モノラル
ffmpeg -i $wav_path -vn -ac 1 -ar 44100 -ab 128k -acodec aac -f mp4 $aac_path -v error 

# mp4 + aac
ffmpeg -i $1 -i $aac_path  -t $duration -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 $target_path -v error 

# 後始末
rm $1
rm $wav_path
rm $aac_path

# Discord投下
curl -H 'Content-Type: multipart/form-data' \
	-X POST \
	-F $discord_path \
	$DISCORD_WEBHOOK
```

## 1. 録音を止める
調べても止め方がCtrl+Cしか出てこなかったので、無理やりarecord止める。
取り敢えずこの止め方で何とかなっているが、撮影上限時間を超えて連続撮影されると・・・
もうちょっとマシな方法無いのか？

```shell:
pkill -KILL -f arecord
```

## 2. wavをaacにエンコード
`-ac 1`：USBマイクに依るが今回はモノラル指定。
`-v error`：やたらログが流れる、エラーだけでいいです。

```shell:
ffmpeg -i $wav_path -vn -ac 1 -ar 44100 -ab 128k -acodec aac -f mp4 $aac_path -v error
```

## 3. mp4＋aac

あくまでファイルClose時のコールバックなので、実際の撮影終了はもう少し前。
このためmotionが作成したmp4の再生時間よりも、arecordで録音したwavの再生時間の方が長くなる。
このままMP4とAACを結合すると末尾数秒は音声だけが流れる動画となってしまうので、motionが作成したmp4の再生時間分だけ出力する。

### motionが作成したmp4の再生時間を取得
ffprobeそのままだとやたらと返ってくるので、オプションマシマシにして値だけ返ってくるようにする。
これで合ってんのか？

```shell:
duration=$(ffprobe -i $1 -show_entries format=duration -v error -of csv="p=0")
```

### 音声結合
`-t $duration`：先程取得した再生時間分だけ出力指定。
`-c:v copy -c:a copy`：動画・音声ともエンコード済みなのでコピーするだけ。
`-v error`：エラーログだけで十分。

```shell:
ffmpeg -i $1 -i $aac_path  -t $duration -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 $target_path -v error
```

## 4. Discordに投下

curlてやつで予め取得しといた webhook URL にPOSTすればいいらしい。

```shell:
curl -H 'Content-Type: multipart/form-data' \
    -X POST \
    -F $discord_path \
    $DISCORD_WEBHOOK
```

# 課題

1. 録音の止め方雑すぎ
2. 音声のノイズがすごい、マイク変えると改善
3. precatureするとこの方法じゃ音ズレ、音もprecaptureする方法？
4. 動画と一緒にメッセージをDiscordに送信する

# 参考

[Raspberry Pi でカメラ](https://qiita.com/suppy193/items/e9faedbc268d439bd02b)
[motionのオプションについて – guro_chanの手帳](https://www7390uo.sakura.ne.jp/wordpress/archives/250)

[Raspberry Pi3で音声録音](https://qiita.com/yozawa/items/0695664ad89dad56e62f)

[[ffmpeg] 音声形式の変換方法まとめ](https://qiita.com/suzutsuki0220/items/43c87488b4684d3d15f6)
[ffmpegを使って映像と音声を結合する](https://qiita.com/niusounds/items/f69a4438f52fbf81f0bd)
[ffmpeg 動画や音声の時間を取得する](https://qiita.com/PianoScoreJP/items/dcb875757a703c8f3322)
[最新ffmpegのオプションまとめ - MobileHackerz Knowledgebase Wiki](http://mobilehackerz.jp/archive/wiki/index.php?%BA%C7%BF%B7ffmpeg%A4%CE%A5%AA%A5%D7%A5%B7%A5%E7%A5%F3%A4%DE%A4%C8%A4%E1)

[DiscordにWebhookでいろいろ投稿する](https://qiita.com/Eai/items/1165d08dce9f183eac74)
[Discordにファイルをアップロードするactionsを作成してみた](https://qiita.com/sinshutu/items/5d363c70c3ee557d32bd)

[パス文字列からファイル名などを抜き出す](https://qiita.com/ktr_type23/items/94747a4b27e8a630ce48)