CCTV Camera Setup in ArchLinux
Network setup for cameras
┌──────────────────────────┐ ┌─────────────┐
│ ArchLinux Desktop │─────│ enp5s0 │──────── (Internet/WAN)
│ (vector@ArchLinuxR72700)│ │ (Built-in │
└──────────────────────────┘ │ Ethernet) │
│ └─────────────┘
│
│ ┌─────────────┐
└─────────────────│ br1 │ 10.0.0.1/24
│ (Bridge │ DHCP Server
│ Switch) │ (No Internet Access)
└─────────────┘
│ │
│ │ enp10s0f3u2
│ │ (USB-to-Ethernet adapter)
│ │ (Just a port, no IP address)
│ │
│ ┌─────────┐
│ │Physical │
│ │Network │
│ │Switch │
│ └─────────┘
│ │
│ │
┌───────┘ ├──────────────┐
│ │ │
│ │ │
┌─────────┐ ┌─────────┐ ┌─────────┐
│Windows │ │6 Network│ │ArchLinux│
│VM │ │Cameras │ │Laptop │
│(Virtual)│ │ │ │ │
└─────────┘ └─────────┘ └─────────┘
DHCP Clients on 10.0.0.0/24 Network
Setting up the Linux bridge br1
We run the DHCP server on a linux bridge br1 instead of running it directly on
ethernet interface connected to the physical network switch. You can think of the
linux bridge as a virtual device with its own IP address, here enp10s0f3u2
becomes
a port of this device.
This setup can be achieved with these files:
/etc/systemd/network/br1.network |
---|
| [Match]
Name=br1
[Network]
Address=10.0.0.1/24
IPForward=yes
|
/etc/systemd/network/br1.netdev |
---|
| [NetDev]
Name=br1
Kind=bridge
|
/etc/systemd/network/enp10s0f3u2.network |
---|
| [Match]
Name=enp10s0f3u2
[Network]
Bridge=br1
|
Setting up DHCPD
sudo systemctl edit dhcpd4.service
Change the ExecStart line to:
[Service]
ExecStart=
ExecStart=/usr/bin/dhcpd -4 -q -cf /etc/dhcpd.conf -pf /run/dhcpd4/dhcpd.pid br1
sudo systemctl enable systemd-networkd
sudo systemctl start systemd-networkd
sudo systemctl enable dhcpd4.service
sudo systemctl start dhcpd4.service
/etc/dhcpd.conf |
---|
| # dhcpd.conf
# # Global settings
default-lease-time 600;
max-lease-time 7200;
authoritative;
# Subnet configuration
subnet 10.0.0.0 netmask 255.255.255.0 {
range 10.0.0.10 10.0.0.100;
option routers 10.0.0.1;
option subnet-mask 255.255.255.0;
option domain-name-servers 8.8.8.8, 8.8.4.4;
option broadcast-address 10.0.0.255;
}
# Fixed IP assignments for specific MAC addresses
host device-22b04b70e729-1 {
hardware ethernet 12:23:34:37:36:37;
fixed-address 10.0.0.15;
}
host device-525400051ff05 {
hardware ethernet 11:22:34:35:35:36;
fixed-address 10.0.0.78;
}
|
Systemd NVR
~/.config/systemd/user/nvr-dirs.service |
---|
| [Unit]
Description=Create NVR recording directories
After=network.target
[Service]
Type=oneshot
ExecStart=/home/vector/bin/create-nvr-dirs.sh
|
~/.config/systemd/user/nvr-dirs.timer |
---|
| [Unit]
Description=Daily NVR directory creation
Requires=nvr-dirs.service
[Timer]
OnCalendar=daily
Persistent=true
AccuracySec=1min
[Install]
WantedBy=timers.target
|
~/bin/create-nvr-dirs.sh |
---|
| #!/bin/bash
# Create NVR recording directories for today and tomorrow
BASE_DIR="/home/vector/recordings"
# Create today's directory
TODAY=$(date +%Y%m%d)
mkdir -p "$BASE_DIR/$TODAY"
# Create tomorrow's directory (in case recording crosses midnight)
TOMORROW=$(date -d tomorrow +%Y%m%d)
mkdir -p "$BASE_DIR/$TOMORROW"
echo "Created directories: $TODAY, $TOMORROW"
|
~/.config/systemd/user/camera-1.service |
---|
| [Unit]
Description=NVR Camera 1 Recording
After=network.target nvr-dirs.service
Wants=nvr-dirs.service
[Service]
Type=simple
ExecStart=ffmpeg -hide_banner -loglevel error \
-rtsp_transport tcp \
-timeout 10000000 \
-i "rtsp://admin:thecamerapassowrd4@10.0.0.101:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif" \
-c copy \
-avoid_negative_ts make_zero \
-f segment \
-segment_time 600 \
-segment_format mp4 \
-segment_list_flags +live \
-segment_list_size 10 \
-segment_wrap 288 \
-reset_timestamps 1 \
-strftime 1 \
"/home/vector/recordings/%%Y%%m%%d/camera-1_%%H%%M%%S.mp4"
Restart=always
RestartSec=5
WorkingDirectory=/home/vector/recordings
[Install]
WantedBy=default.target
|
Accessing camera in the LAN with internet (enp5s0 network)
Don't run this script without understanding what it does.
configure-iptables.sh |
---|
| #!/bin/bash
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -X
sudo iptables -t nat -X
# Reset policies to ACCEPT
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo sysctl net.ipv4.ip_forward=1
sudo iptables -t nat -A PREROUTING -p tcp --dport 1101 -j DNAT --to-destination 10.0.0.101:554
sudo iptables -t nat -A PREROUTING -p udp --dport 1101 -j DNAT --to-destination 10.0.0.101:554
sudo iptables -t nat -A PREROUTING -p tcp --dport 1102 -j DNAT --to-destination 10.0.0.102:554
sudo iptables -t nat -A PREROUTING -p udp --dport 1102 -j DNAT --to-destination 10.0.0.102:554
sudo iptables -t nat -A PREROUTING -p tcp --dport 1103 -j DNAT --to-destination 10.0.0.103:554
sudo iptables -t nat -A PREROUTING -p udp --dport 1103 -j DNAT --to-destination 10.0.0.103:554
sudo iptables -t nat -A PREROUTING -p tcp --dport 1105 -j DNAT --to-destination 10.0.0.105:554
sudo iptables -t nat -A PREROUTING -p udp --dport 1105 -j DNAT --to-destination 10.0.0.105:554
sudo iptables -t nat -A PREROUTING -p tcp --dport 1106 -j DNAT --to-destination 10.0.0.106:554
sudo iptables -t nat -A PREROUTING -p udp --dport 1106 -j DNAT --to-destination 10.0.0.106:554
# Add a more restrictive rule that only allows traffic initiated from the main network
sudo iptables -A FORWARD -i enp8s0 -o br1 -j ACCEPT
sudo iptables -A FORWARD -i br1 -o enp8s0 -m state --state RELATED,ESTABLISHED -m conntrack --ctorigdst 10.0.0.0/24 -j ACCEPT
# ESSENTIAL: MASQUERADE rule for NAT to work
# sudo iptables -t nat -A POSTROUTING -o enp8s0 -j MASQUERADE
# Only MASQUERADE traffic that was DNAT'd to the isolated network (The above command gave 10.0.0.0/24 internet access)
sudo iptables -t nat -A POSTROUTING -o enp8s0 -m conntrack --ctorigdst 10.0.0.0/24 -j MASQUERADE
# Make IP forwarding permanent
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
# Save current rules
sudo iptables-save > /etc/iptables/iptables.rules
# Enable iptables service
sudo systemctl enable --now iptables
|
I no longer use frigate
It seems like you either need a powerful GPU or a Coral device or running Frigate becomes
too CPU intensive. I'm just keep this here for future reference.
Frigate
source: https://ipv6.rs/tutorial/Arch_Linux/Frigate/
sudo pacman -S git python ffmpeg sudo pacman -S docker docker-buildx sudo systemctl start docker sudo systemctl enable docker
git clone --depth 1 https://github.com/blakeblackshear/frigate.git
cd frigate
cp config/config.yml.example config/config.yml
DOCKER_BUILDKIT=1 docker build -f docker/main/Dockerfile -t frigate .
Check out: https://github.com/blakeblackshear/frigate/discussions/4161
Find out the rtsp link for your camera. For mine it turned out to be:
ffmpeg -i "rtsp://admin:L288B3D8@10.0.0.12:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif" result.mp4
proto=Onvif
Without "proto=Onvif" I got no stream.
Where L288B3D8 was the safety code written on camera. In the link proto=Onvif was necessary. This page really helped: https://www.ispyconnect.com/camera/imou
~/frigate/config.yaml |
---|
| mqtt:
enabled: false
# ffmpeg:
# hwaccel_args: preset-nvidia-h264
# detectors:
# coral:
# type: edgetpu
# device: usb
record:
enabled: True
retain:
days: 7
mode: motion
alerts:
retain:
days: 30
detections:
retain:
days: 30
snapshots:
enabled: True
retain:
default: 30
cameras:
nomi_bf:
enabled: true
ffmpeg:
inputs:
# If your camera supports dual streams, use substream for detection
- path: rtsp://admin:L288B3D8@10.0.0.34:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif
roles:
- record
- path: rtsp://admin:L288B3D8@10.0.0.34:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
roles:
- detect
detect:
enabled: true
width: 640
height: 360
fps: 5
detect:
enabled: true
version: 0.16-0
|
Finally:
docker run --name frigate -p 5000:5000 -v $PWD:/config --restart always frigate