はじめに
監視カメラシステムのソフトウェアは Shinobi, ZoneMinder, motionEye などがある。
Shinobi, ZoneMinder はいずれも NASync で動作させるための手順が複雑で、インストールできても CPU 使用率が 15% くらいと高負荷になった。
調査を進めたところ Frigate というソフトウェアが Docker で簡単にインストールでき、カメラの映像を中継するだけであれば CPU 使用率が 9% くらいと低負荷で動作した。
motionEye は試していないが Frigate が優秀だったのでこれを使うことにする。
接続するカメラは RTSP 配信機能があるものがよい。USB 接続の Web カメラも使えるようだが Frigate では試してはいない。
Shinobi の環境を作って v4l2loopback を使って Web カメラを使ったときは CPU 負荷が常時 20% 超えるようになってしまった。
RTSP 対応カメラは TP-Link 社の Tapo シリーズ や ATOM 社の ATOM シリーズ がおすすめ。今回は Tapo C120 を使った。
Frigate のインストール
まず Docker でプロジェクトを作成する。
プロジェクト名: frigate-ipcam-station
保存パス: 共有フォルダ/docker/frigate-ipcam-station
Compose設定: 以下
# version: "3.9"
services:
frigate:
container_name: frigate-ipcam-station
# privileged: true # this may not be necessary for all setups
restart: unless-stopped
stop_grace_period: 30s # allow enough time to shut down the various services
image: ghcr.io/blakeblackshear/frigate:stable
shm_size: "512mb" # update for your cameras based on calculation above
devices:
# - /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions
# - /dev/apex_0:/dev/apex_0 # Passes a PCIe Coral, follow driver instructions here https://coral.ai/docs/m2/get-started/#2a-on-linux
# - /dev/video11:/dev/video11 # For Raspberry Pi 4B
- /dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel, needs to be updated for your hardware
volumes:
- /etc/localtime:/etc/localtime:ro
- ./config:/config
- ./media:/media/frigate
- type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
target: /tmp/cache
tmpfs:
size: 1000000000
ports:
- "8971:8971"
- "5000:5000" # Internal unauthenticated access. Expose carefully.
# - "8554:8554" # RTSP feeds
- "8555:8555/tcp" # WebRTC over tcp
- "8555:8555/udp" # WebRTC over udp
environment:
FRIGATE_RTSP_PASSWORD: "password"
Compose 設定は公式の記述を元にした。
以下、Compose 設定についての説明。
# privileged: true # this may not be necessary for all setups
privileged フラグは有効にせず動いたので無効にしておく。
shm_size: "512mb" # update for your cameras based on calculation above
以下の式で計算されるサイズを入れる。メモリに余裕があるので多めにした。# Replace <width> and <height>
$ python -c 'print("{:.2f}MB".format((<width> * <height> * 1.5 * 20 + 270480) / 1048576))'
devices:
# - /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions
# - /dev/apex_0:/dev/apex_0 # Passes a PCIe Coral, follow driver instructions here https://coral.ai/docs/m2/get-started/#2a-on-linux
# - /dev/video11:/dev/video11 # For Raspberry Pi 4B
- /dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel, needs to be updated for your hardware
ハードウェアアクセラレータの設定。
USB Coral, PCIe Coral は Google Edge TPU(Tensor Processing Unit) を載せたデバイスで、TensorFlow という機械学習用の演算デバイスのこと。
Frigate は機械学習で物体検出を行っていて、Google Edge TPU での演算に対応している。NASync であれば USB タイプか M.2 タイプのデバイスを繋げば有効にできるが、持っていないので無効にする。
NASync は、Google Edge TPU のドライバーがインストールされておらずデバイスが読み込まれないので無効にする。
renderD128 はおそらく Intel 内蔵 GPU のこと。コメントアウトしたら CPU 使用率が数パーセント上がってしまったので有効にしておく。
- type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
target: /tmp/cache
tmpfs:
size: 1000000000
映像処理用のキャッシュ領域。メモリに余裕があれば有効にしておく。
ports:
- "8971:8971"
- "5000:5000" # Internal unauthenticated access. Expose carefully.
# - "8554:8554" # RTSP feeds
- "8555:8555/tcp" # WebRTC over tcp
- "8555:8555/udp" # WebRTC over udp
8971 番ポートはユーザー名・パスワードを入力してログインする https 接続を行うポート。
5000 番ポートは認証なしで見られる http 接続を行うポート。
8555 番ポートはブラウザに映像を転送するポート。
8554 番ポートは RTSP 送信ポート。RTSP 送信機能は使わない。
カメラへ接続
frigate-ipcam-station プロジェクトを実行すると初期設定ファイルが生成される。
“共有フォルダ/docker/frigate-ipcam-station/config/config.yaml” を編集する。
最低限、カメラの名前と RTSP パスを設定しておく。
mqtt:
enabled: False
cameras:
home_cam: # <------ Name the camera
enabled: True
ffmpeg:
inputs:
- path: rtsp://frigate:passwrd/stream1 # <----- The stream you want to use for detection
roles:
- detect
detect:
enabled: False # <---- disable detection until you have a working camera feed
width: 1280
height: 720
config.yaml を書き換えたら http://192.168.56.56:5000 を開く。
カメラの映像が映らないときは左下の歯車アイコンをクリックして “Restart Frigate” でサービスを再起動する。
config.yaml が間違えていた場合は書き直して ”Restart Frigate” でやり直す。
録画機能を有効化する
カメラの動作が確認できたら録画機能を有効化する。
http://192.168.56.56:5000 が開けるようになれば Frigate のページから config.yaml を編集できるようになる。
Settings >> Configuration editor を選択して config.yaml を編集する。
mqtt:
enabled: False
cameras:
home_cam: # <------ Name the camera
enabled: true
ffmpeg:
inputs:
- path: rtsp://frigate:passwrd@192.168.56.120/stream1 # <----- The stream you want to use for detection
roles:
- record # <-- record に変更
# record セクションを追加
# 設定内容: https://docs.frigate.video/configuration/record/
record:
enabled: True
retain:
days: 14 # <-- 録画データ保持日数を指定
#detect:
# enabled: false # <---- disable detection until you have a working camera feed
# width: 1280
# height: 720
録画データは “共有フォルダ/docker/frigate-ipcam-station/media/recordings/$yyyy-$mm-$dd/$HH/home_cam/$MM.$SS.mp4” の形式で約 10 秒置きの連番動画ファイルが生成される。
このような短い尺の動画ファイルが生成されるのには理由があり、カメラの通信が途切れたときや Frigate のプロセスが中断したときも録画途中のデータが破棄されないようにするための仕組みとなっており、ここに手を加えるのは非推奨となっている。
Tapo C120 を使って 2560 x 1440 の解像度で録画した場合は 1 ファイルにつき 0.8 ~ 2MB くらいのサイズで、1 日で 13 GB くらいのサイズになった。
“共有フォルダ/docker/frigate-ipcam-station” を SSD に指定すると SSD の寿命を短くする原因になるので、出力先を USB メモリか SD カードに変更したい。
録画用のメディアはいくつか選択肢があるが、今回は以下を考慮して SD カードに決めた。
- M.2 SSD ― M.2 接続の Google Edge TPU があるのと、ミラーリング用の M.2 SSD を追加する可能性もあり開けておきたい。
- USB メモリ ― USB 接続のキーボード、Google Edge TPU、その他を USB 接続するためポートを開けておきたい。デバイスが飛び出るのも好みではない。
- SD カード ― 特に用途なし。今後も埋まる予定なし。
録画データの出力先を SD カードに変更する
録画データは一定期間で自動削除させても 13 GB * 365 日 で毎年約 4.7TB の書き込みがストレージに発生する。
WD Blue 512GB (WDS500G4B0E) の場合、書き込み耐性は 300TBW なので余裕はあるが、メインの SSD のデータが破損すると復旧が苦労するので念のため SD カード出力に変更する。
SD カードは SanDisk の 512GB MicroSD カード SDSQQNR-512G-GN6IA を購入した。
これは MicroSD だが SD カードとして接続するための変換アダプタが付属している。
SD カードを NASync に挿し込んで、SD カードのフォーマットを行う。
UGOS の管理画面の ストレージマネージャー >> 外部ストレージ管理 でストレージの編集メニューを開き「フォーマット」を選択する。
ファイルシステムは exFat か ext4 のどちらかにする。
Windows に差し替えてデータを転送したいときは exFat。それ以外の場合は ext4。
パーティションラベルは自動で設定されてしまうので、SSH ログインして lsblk で対象の SD カードを探して一度アンマウントしてから exfatlabel または e2label でラベル名を変更する。
nasuser@MYNASYNC:/$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 7.3T 0 disk
├─sda1 8:1 0 15.3G 0 part
└─sda2 8:2 0 7.3T 0 part
└─md2 9:2 0 7.3T 0 raid1
└─ug_55EBAA_1749742835_pool2-volume1 253:1 0 7.3T 0 lvm /volume2
sdb 8:16 0 3.6T 0 disk
├─sdb1 8:17 0 15.3G 0 part
└─sdb2 8:18 0 3.6T 0 part
sdc 8:32 0 3.6T 0 disk
├─sdc1 8:33 0 15.3G 0 part
└─sdc2 8:34 0 3.6T 0 part
sdd 8:48 1 0B 0 disk
sde 8:64 1 476.7G 0 disk
└─sde1 8:65 1 476.7G 0 part /mnt/@usb/sde1
zram0 252:0 0 7.8G 0 disk [SWAP]
zram1 252:1 0 7.8G 0 disk [SWAP]
zram2 252:2 0 7.8G 0 disk [SWAP]
zram3 252:3 0 7.8G 0 disk [SWAP]
nvme0n1 259:0 0 1.8T 0 disk
├─nvme0n1p1 259:11 0 15.3G 0 part
└─nvme0n1p2 259:12 0 1.8T 0 part
└─md1 9:1 0 1.8T 0 raid1
└─ug_55EBAA_1749741907_pool1-volume1 253:0 0 1.8T 0 lvm /volume1
nvme1n1 259:3 0 119.2G 0 disk
├─nvme1n1p1 259:4 0 256M 0 part /boot
├─nvme1n1p2 259:5 0 2G 0 part /rom
├─nvme1n1p3 259:6 0 10M 0 part /mnt/factory
├─nvme1n1p4 259:7 0 2G 0 part
├─nvme1n1p5 259:8 0 2G 0 part [SWAP]
├─nvme1n1p6 259:9 0 4G 0 part /ugreen
└─nvme1n1p7 259:10 0 108.9G 0 part /overlay
nasuser@MYNASYNC:/$ sudo umount /dev/sde1
# /$ sudo exfatlabel /dev/sde1 RecCard
nasuser@MYNASYNC:/$ sudo e2label /dev/sde1 RecCard
SD カードは抜き差しすることで自動でマウントされるが、内部ストレージのマウント状態によってマウント先が /mnt/@usb/sdd1 になったり /mnt/@usb/sde1 に変わったりする。
録画データの保存先として docker-compose に SD カードのマウント先を指定したいので、fstab に SD カードのマウント先を固定させる設定を追加する。
ポートを調べるため、一度 SD カードを再マウントさせる。
再マウントするには UGOS の管理画面から ストレージマネージャー >> 外部ストレージ管理 でストレージの編集メニューから「取り出し」を選択。
物理的に SD カードを抜き差しすると自動で SD カードが再マウントされる。
再マウントできたら、by-path を見て SD カードのポートを見つける。
nasuser@MYNASYNC:/$ ls -al /dev/disk/by-path
total 0
drwxr-xr-x 2 root root 780 Jul 13 10:51 .
drwxr-xr-x 9 root root 180 Jul 13 10:51 ..
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:00:0d.0-usb-0:3:1.0-scsi-0:0:0:0 -> ../../sde
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:00:0d.0-usb-0:3:1.0-scsi-0:0:0:0-part1 -> ../../sde1
lrwxrwxrwx 1 root root 13 Jul 13 10:51 pci-0000:02:00.0-nvme-1 -> ../../nvme0n1
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:02:00.0-nvme-1-part1 -> ../../nvme0n1p1
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:02:00.0-nvme-1-part2 -> ../../nvme0n1p2
lrwxrwxrwx 1 root root 13 Jul 13 10:51 pci-0000:04:00.0-nvme-1 -> ../../nvme1n1
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:04:00.0-nvme-1-part1 -> ../../nvme1n1p1
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:04:00.0-nvme-1-part2 -> ../../nvme1n1p2
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:04:00.0-nvme-1-part3 -> ../../nvme1n1p3
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:04:00.0-nvme-1-part4 -> ../../nvme1n1p4
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:04:00.0-nvme-1-part5 -> ../../nvme1n1p5
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:04:00.0-nvme-1-part6 -> ../../nvme1n1p6
lrwxrwxrwx 1 root root 15 Jul 13 10:51 pci-0000:04:00.0-nvme-1-part7 -> ../../nvme1n1p7
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-1 -> ../../sda
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-1.0 -> ../../sda
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-1.0-part1 -> ../../sda1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-1.0-part2 -> ../../sda2
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-1-part1 -> ../../sda1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-1-part2 -> ../../sda2
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-2 -> ../../sdc
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-2.0 -> ../../sdc
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-2.0-part1 -> ../../sdc1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-2.0-part2 -> ../../sdc2
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-2-part1 -> ../../sdc1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-2-part2 -> ../../sdc2
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-3 -> ../../sdd
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-3.0 -> ../../sdd
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-3.0-part1 -> ../../sdd1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-3.0-part2 -> ../../sdd2
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-3-part1 -> ../../sdd1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-3-part2 -> ../../sdd2
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-4 -> ../../sdb
lrwxrwxrwx 1 root root 9 Jul 13 10:51 pci-0000:05:00.0-ata-4.0 -> ../../sdb
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-4.0-part1 -> ../../sdb1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-4.0-part2 -> ../../sdb2
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-4-part1 -> ../../sdb1
lrwxrwxrwx 1 root root 10 Jul 13 10:51 pci-0000:05:00.0-ata-4-part2 -> ../../sdb2
この場合 ../../sde1 にマウントしている pci-0000:00:0d.0-usb-0:3:1.0-scsi-0:0:0:0-part1 が対象のポートだった。
ポート ID が分かったら、/etc/fstab に以下の行を追加する。
(ポート番号、マウント先ディレクトリ、ファイルシステムは環境に合わせて書き換える)
/dev/disk/by-path/pci-0000:00:0d.0-usb-0:3:1.0-scsi-0:0:0:0-part1 /media/sdcard ext4 defaults,user 0 0
マウント先のディレクトリを作成して再起動すると fstab で指定したディレクトリに SD カードがマウントされる。
nasuser@MYNASYNC:/$ sudo mkdir /media/sdcard
nasuser@MYNASYNC:/$ sudo shutdown -r now
Frigate の docker-compose の volumes セクションを書き換えて録画データを SD カードに出力させる。
volumes:
- /etc/localtime:/etc/localtime:ro
- /media/sdcard/frigate/config:/config # ./config:/config から書き換え
- /media/sdcard/frigate/media:/media/frigate # ./media:/media/frigate から書き換え
- type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
target: /tmp/cache
tmpfs:
size: 1000000000
SD カードのマウント先を書き換えない場合
SD カードのマウント先を fstab で書き換えずに docker-compose の volumes セクションで /mnt/@usb/sde1 を指定したが構文エラーになった。
対策として “共有フォルダ/docker/frigate-ipcam-station/sdcard” から “/mnt/@usb/sde1/frigate” へのシンボリックリンクを張る。
# SD カードへのシンボリックリンクを張る
/$ cd /volume1/docker/frigate-ipcam-station
/volume1/docker/frigate-ipcam-station$ sudo ln -s /mnt/@usb/sde1/frigate sdcard
/volume1/docker/frigate-ipcam-station$ sudo mkdir /mnt/@usb/sde1/frigate
# 既存の設定データ・録画データを SD カードに移動させる
/volume1/docker/frigate-ipcam-station$ sudo mv config sdcard/config
/volume1/docker/frigate-ipcam-station$ sudo mv media sdcard/media
これでマウント先を書き換えない場合も Frigate の録画データ SD カードに保存されるようになる。
ただし、ストレージの接続状態によってマウント先が変わることがあるので、そのときはシンボリックリンクを張り直す必要がある。