IP camera recording on the Raspberry Pi

In 2020, when most of us were stuck inside homes, I stumbled upon an old WiFi camera that I had bought back in 2017 from a site with a questionable name. The problem with this camera was that it’s not a closed-circuit system and had to connect to a third-party web service to function which was the reason I ended up not using it. I did a bit of research on the internet and found a custom firmware which prevents any communication with the internet but the downside is that it will stop writing to the SD card. Having the footage on the SD card inside the camera is not a good idea anyway. So now with the custom firmware, the camera doesn’t communicate with anything other than the LAN and doesn’t write to the SD card, which doesn’t sound very useful.

The saving grace was that the firmware provides a password-protected RTSP stream with or without audio depending on the URL requested. This is only accessible within the LAN by using the IP address so it can behave similar to a closed-circuit system. Of course, I wouldn’t have been able to write this article if the camera its firmware didn’t support RTSP.

The URL formats supported by this firmware are given below.

Video only (example IP address)
rtsp://username:password@ (1280x720)
Audio and video (example IP address)
rtsp://username:password@ (1280x720)
rtsp://username:password@ (640x360)

This RTSP stream can be live-viewed with VLC Media Player on a desktop computer or a smartphone. On my Android device, I use an app called RTSP CCTV to view the stream. However, none of this can record the stream to a file which was my main requirement.

Enter ffmpeg. I read somewhere that this can write RTSP streams to a files and even segment them. Segmentation is very important because several smaller files means you can have timestamps on their file names to find the one you need easily and if one becomes corrupted, the others won’t be affected. This is how dashcams work too.

I tried a simple script on my Windows laptop and it seemed to do the trick well. I don’t have that script with me anymore, but you can find examples on the internet. I left this script running overnight and the results came out as I expected. Five-minute video clips with the timestamp as the file name. About 8 hours of recording took around 1GB which isn’t too bad. The problem now is that I can’t keep running the laptop all the time so there has to be a better option.

Enter Raspberry Pi. I had an RPi 4 Model B with 4GB RAM (which is overkill for this job probably) because I wanted a relatively high-performing one for other work as well. On this tiny computer running Raspberry Pi OS, I could install ffmpeg on it just like how you’d do it on any Debian-based OS.

The command to start recording with ffmpeg is shown below. I’ll explain what each part means. You can also refer to the documentation.

Note: There may be a few other things that you need to do (like setting permissions and paths) before these commands work. I’m assuming you have some knowledge in Linux to solve those issues should they arise.

ffmpeg -i rtsp://${username}:${password}@${ip}/0/av0 -c:a aac -c:v copy -map 0 -f segment -strftime 1 -segment_time 300 -segment_format mp4 -reset_timestamps 1 "${savepath}/%Y%m%d_%H%M%S.mp4"

-i to specify the input (in this case, the RTSP URL).
${username}, ${password} are replaced with username and password for the camera stream.
${ip} is replaced with the IP address of the camera.
-c:a aac to use AAC as audio recording format.
-c:v copy to indicate that the video stream is not to be re-encoded.
-map 0 to select the first stream (or all) which is the default.
-f segment to allow segmenting the stream to many files.
-strftime 1 to allow naming the created files with local time.
-segment_time 300 so that each file will be 5 minutes in duration.
-segment_format mp4 so that the file type will be MP4.
-reset_timestamps 1 to make sure that each file starts at 0:00 and not the combined length from files before.
${savepath} is replaced with the path to save.
Note how the file name is defined with the timestamp format. An example file will have the name “20210613_230200.mp4” in this case.

FFMPEG running on the terminal

The camera I had isn’t a high-end one, so it outputs the video files with a 1280x720 (HD) resolution and a variable frame rate around 10 FPS. For best quality and smoothness in video, you need at least 1920x1080 (FHD) resoltion and a 30 FPS frame rate. There are some warnings shown in ffmpeg for me but those can be due to network latency or the camera performance, so I ignored them.

Dynamic IP address woes

I couldn’t find out a way to set a static IP address for the camera. This means that every time there’s a power outage or any other network issue, the camera gets assigned with the next available IP address, which differs based on the number of devices already on the network.

To figure out which IP address is that of the camera, I loop through the IP addresses on the subnet, and check which of them provides an RTSP. The catch here is that, my logic will break if there are multiple IPs that output RTSP streams. I’m using the “curl” command to determine this.

Below is the part of the script that I use to achieve this. Assuming the address of my router is xxx.xxx.xxx.1 and the RPi also has an IP in the same subnet, I could start checking that subnet skipping those two IPs. Additionally, I limit the search to the first few addresses only because I know that there’s usually no more than 20 devices in my LAN.

Once it finds the correct IP address, it saves this to a text file so that the next time, the script can start looking there first. This makes things faster in case the camera somehow retained the same IP address.

This can be saved as a shell script (*.sh) in a preferred location and given the execute permissions (using chmod +x).

Scheduling to start and stop recording at given times

If we want to schedule to start recording at a given time daily rather than running it 24/7, then we can make use of another useful feature in Linux, which is cron. This is a powerful scheduler widely used for many different purposes. To open the cron editor, the command “crontab -e” is used. The default editor can be chosen and I have selected the default “nano” text editor.

To stop the recording, there’s no other way other than to kill the ffmpeg process. Here, we can just terminate all ffmpeg processes currently running because we’re sure that there should only be one.

Assuming we want to start recording at 9 PM daily and end at 8 AM, the below can be defined in cron respectively.

0 21 * * * <enter full path to the script here>
0 8 * * * killall -9 ffmpeg

We use “-9” here for killall to send a SIGKILL signal to terminate immediately.

This behaviour can further be improved to start recording if the RPi was restarted (most likely due to a power outage) during the “active” time slot (9 PM-8 AM) by adding a new “@reboot” line in cron to run the script and add a condition inside the script to check if the current time is within the active slot to start recording or exit.

Deleting old recordings automatically

Because of space limitations, one would have to delete the old recording after a certain number of days. Looking at the file sizes from this particular camera, a 32GB SD card can approximately store about 250 hours of footage. But since this is the same SD card as the one running the OS, we’d want to keep the writes to a minimum and can’t fill it up entirely. A good solution here is to use an external hard disk drive to store the recordings, especially as the RPi 4 has USB 3.0 ports. If you’re going to stick to the micro SD card, use a high-endurance one like those made for use in dashcams.

The script to delete files is nothing special and uses standard Linux delete commands. Replace the “x” with the number of days to consider the files as expired and replace the path. Be very careful not to place other files in this directory or those will be deleted as well!

# Deletes files older than x days
find <full path to the recordings directory> -type f -mtime +x -exec rm {} \;

And enter this into the crontab to execute every day at midnight.

@midnight <enter full path to the script here>

With all this, I was able to set up a system that works similar to a dashcam but on an RPi with a camera that’s on the network as the input. This could potentially be improved to use multiple cameras for a truly wireless surveillance system. It’s a bit more work than a purchased product but this is worth trying out for the sake of some DIY enjoyment 😁.

Hope you learned something from this article and thanks for reading!

Coffee-to-code converter, passionate about art and music