A Complete, Modern CCTV System with Raspberry Pi (Part II)

Sun Rui
5 min readMar 2, 2019

--

Part II: A Step-by-step Tutorial (with references)

My Raspberry Pi

Component: Your Pi and RPi Camera

Note: I only tested these with the official RPi camera. Not sure if it works with USB cameras.

Do this first to create a device file for your camera: sudo modprobe bcm2835-v4l2 , thanks for sharing from dkyancey .

Then create a script anywhere (preferably under your home directory, like /home/pi/stream_livingroom.sh) to stream video to a server-to-be-provisioned:

#!/bin/sh
# Above is important when we proceed to creating a service! Otherwise you will have EXEC format error.
# Use V4L2 (preferred) instead of raspivid
# exposure_dynamic_framerate=1 (raspivid --fps 0) - reduce framerate/increase exposure in low light
# scene_mode=8 (raspivid --exposure night) - allow framerate reduction to increase exposure
v4l2-ctl -v width=1280,height=720,pixelformat=H264 \
--set-ctrl=exposure_dynamic_framerate=1 \
--set-ctrl=video_bitrate=5000000 \
--set-ctrl=scene_mode=8 \
--set-ctrl=rotate=180
exec ffmpeg -f video4linux2 -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vcodec copy -an -f flv rtmp://INGEST_SERVER_IP_OF_VPN_INTERFACE/live/STREAM_NAME# Use flv output wrapper format (-f flv) here. I tried h264 but suffered Broken Pipe error shortly after starting the script. Not sure why. Flv is reliable.
# We will provision the ingest server shortly.

Don’t forget to make it executable! Do this: chmod 700 stream_livingroom.sh

Then create a service so streaming starts automatically with our Pi:

[Unit]
Description=CCTV for Living Room
After=openvpn.service # So the service starts only after we have
# established VPN connection to ingestion
# server.
StartLimitBurst=3
StartLimitIntervalSec=10
[Service]
Type=simple
Restart=on-failure
RestartSec=2
User=pi
ExecStart=/home/pi/stream_livingroom.sh
[Install]
WantedBy=multi-user.target

Put this as /etc/systemd/system/cctv.service , then do

sudo systemctl enable cctv.service
sudo systemctl start cctv.service
sudo systemctl status cctv.service

to make sure it’s working.

Additional reference for this part:

[1] How can I stream H.264 video from the Raspberry Pi camera module via a web server? https://raspberrypi.stackexchange.com/a/63649/93076

[2] Startup script not executed by Systemd https://bbs.archlinux.org/viewtopic.php?pid=1165210#p1165210

Component: OpenVPN Tunnel

First of all, you will need to provision a VM on any public cloud. It does not need to be an expensive one — 2vCores, 7.5GB memory will suffice. Search for help articles if you do not know how to do this. I chose Google Cloud Platform because I had free credit from Google, though I think Microsoft Azure or AWS is far more better cloud service. Sorry, Google.

DigitalOcean has a very nice (although very long) tutorial on how to set up an OpenVPN server on Ubuntu. I followed the guide with a few customizations:

Each time you see a DNS setting, skip it. We do not need special DNS because we do not use our VPN for Internet access (aka we do not route all traffic). Neither do you need to do anything with UFW except opening the appropriate VPN port.

On your Pi, in addition to what has been mentioned above, automatically connect VPN on system reboot by making OpenVPN a service. Follow this answer on StackOverflow:

Component: Stream Ingest Server

For simplicity and cost consideration, my ingestion server resides on the same machine as my VPN server. If you want further simplicity and at the same time you don’t care about bills, I know Microsoft Azure provides Stream Ingest Server as a service (aka Azure Media Service). There is a guide on how to do this. It’s an old article and the UI for Azure has probably changed, but I believe the functionality will not break. If you prefer to do it yourself, read on!

We use nginx along with its rtmp module. Nginx is often used as a reserve proxy and/or lightweight web server, whose job is to listen to user requests and then serve content or forward requests to downstream servers. With its RTMP module, nginx can listen for RTMP protocol, then serve it on an endpoint.

There are two widely used rtmp modules on GitHub, from sergey and arut, respectively. Sergey forked Arut’s repository and added some functionality, which was why some articles claimed the former was better than the original one. But that was only true at the time of their writing. In 2019, use Arut’s latest version. Sergey’s release failed to build. Some StackOverflow answers mentioned using a pre-built version of RTMP module, while I had no luck with it.

Build the latest nginx with RTMP support as you would. Please note that you will need to install sudo apt-get install zlib1g-dev as one of the dependencies, while no post on the Internet mentioned this. You probably also need to make nginx a service, for a similar purpose as above.

worker_processes  1;events {
worker_connections 1024;
}
rtmp {
server {
listen INGEST_SERVER_IP_ON_VPN_INTERFACE:1935;
chunk_size 4096;
application live {
live on;
record off; # We use Shinobi for recording
}
}
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / { # This is the default nginx page. You can remove
# it if you don't want it.
root html;
index index.html index.htm;
}
location /stat { # Show some stat of our RTMP ingest server
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
# you can move stat.xsl to a different location
root /usr/local/nginx/html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

Reference in this part:

[1] How to live stream to multiple services with an RTMP server https://linustechtips.com/main/topic/174603-how-to-live-stream-to-multiple-services-with-a-rtmp-server/

Component: Web Server

Shinobi is a very nice open source CCTV front-end solution. Just follow their official documentation to install:

After installation, you will want to enable HTTPS so your video stream will stay private. Suppose you followed the guide in setting up OpenVPN and you had EasyRSA:

./easyrsa build-server-full YOUR_SHINOBI_SERVER_EXTERNAL_IP nopassThen add the following in /home/Shinobi/conf.json:
"ssl": {
"key": "/path/to/key.key",
"cert": "/path/to/cert.crt",
"port": 443
},
And restart Shinobi:
pm2 restart all

Boom! Login to your Shinobi and add your first camera — just make sure the video codec is H.264 (the default), and source address is the rtmp url you set in the Pi component. Go to Part III for a demo screenshot!

--

--