【NASync】Frigate で監視カメラシステムを構築

はじめに

監視カメラシステムのソフトウェアは 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 カードに保存されるようになる。
ただし、ストレージの接続状態によってマウント先が変わることがあるので、そのときはシンボリックリンクを張り直す必要がある。

参考ページ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

*