1

I'm trying to get started on a raspberry pi security camera project. It would function akin to a dash camera, with the following features:

  • Always recording and saving 3-5 minute files
  • Once the folder where these files are stored reaches a certain size, the oldest files are deleted and replaced with the newer files (goal being the camera will always have the past ~3 days)
  • Uploading to OneDrive or Google Drive (on the same cycle above)

Are there any options out there currently? Everything I'm finding has motion as the most important factor, but that's really not important for my situation. The end goal is to have a few cameras setup inside and always recording entryways, etc.

1 Answer 1

5

This is fairly easy to accomplish with the picamera API, specifically the record_sequence method. I won't tackle the cloud upload bit as that'll bloat this example horribly, but it's pretty easy to add Google Drive integration (they've got plenty of docs on using Python to interface to Google Drive), and I'll point out where you'd want to add this.

Firstly, some assumptions given this is for a security camera:

  • 720p is "good enough" resolution. I'd recommend not using 1080p as it has a limited field of view (esp. with V2 modules)
  • 15fps is a "good enough" framerate. Most security cameras use a slower framerate to reduce storage requirements, but in the case of the Pi's camera module with its rolling shutter this also allows it to use a longer exposure when it needs to (e.g. in low light situations).
  • We want to output "proper" MP4 files so they'll play back at our slower framerate (see FAQ 13)
  • We want to limit the bitrate and quality fairly strictly. If cloud upload is going to be added at some point we'll need to be producing video at a rate slower than our available upstream bandwidth. This is why I've selected 1Mbps as the bitrate limit in the example (drop the quality parameter to 25 and you could probably decrease this further).

The following script defines a small custom output called VideoFile which uses VLC in a sub-process to mux the H264 into a "proper" MP4, and provides methods and properties for querying the size of the resulting file and removing it when necessary. It uses an infinite generator (outputs) to construct instances of VideoFile, and finally some simple logic in the main loop to track when the total size of video files exceeds a specified limit.

import io
import os
import logging
import datetime as dt
from subprocess import Popen, PIPE, DEVNULL
from picamera import PiCamera


DESTINATION = '.'
RESOLUTION = '1280x720'
FRAMERATE = 15 # fps
BITRATE = 1000000 # bps
QUALITY = 22
CHUNK_LENGTH = 300 # seconds
SIZE_LIMIT = 1024 * 1048576 # bytes


class VideoFile:
    def __init__(self, dest=DESTINATION):
        self._filename = os.path.join(
            dest, dt.datetime.utcnow().strftime('CAM-%Y%m%d-%H%M%S.mp4'))
        # Use a VLC sub-process to handle muxing to MP4
        self._process = Popen([
            'cvlc',
            'stream:///dev/stdin',
            '--demux', 'h264',
            '--h264-fps', str(FRAMERATE),
            '--play-and-exit',
            '--sout',
            '#standard{access=file,mux=mp4,dst=%s}' % self._filename,
            ], stdin=PIPE, stdout=DEVNULL, stderr=DEVNULL)
        logging.info('Recording to %s', self._filename)

    def write(self, buf):
        return self._process.stdin.write(buf)

    def close(self):
        self._process.stdin.close()
        self._process.wait()
        # If you want to add a cloud upload, I'd suggest starting it
        # in a background thread here; make sure it keeps an open handle
        # on the output file (self._filename) in case it gets deleted

    @property
    def name(self):
        return self._filename

    @property
    def size(self):
        return os.stat(self._filename).st_size

    def remove(self):
        logging.info('Removing %s', self._filename)
        os.unlink(self._filename)
        self._filename = None


def outputs():
    while True:
        yield VideoFile()


logging.getLogger().setLevel(logging.INFO)
with PiCamera(resolution=RESOLUTION, framerate=FRAMERATE) as camera:
    files = []
    last_output = None
    for output in camera.record_sequence(
            outputs(), format='h264',
            bitrate=BITRATE, quality=QUALITY, intra_period=5 * FRAMERATE):
        if last_output is not None:
            last_output.close()
            files.append(last_output)
            total_size = sum(f.size for f in files)
            while total_size > SIZE_LIMIT:
                f = files.pop(0)
                total_size -= f.size
                f.remove()
        last_output = output
        camera.wait_recording(CHUNK_LENGTH)

Some things to note:

  • I've assumed Python 3
  • The length of each clip won't be precisely the specified chunk length. Files will only split on an intra-frame and to improve compression ratios, I've allowed a long intra-period (5 times the framerate so there'll only be an intra-frame scheduled every 5 seconds, although record_sequence does request intra-frames when it iterates).
  • The specified file-size limit will be exceeded by the currently recording chunk, but hopefully that's minimal compared to the overall limit. Keeping the video recordings strictly within the defined limit is actually quite hard (for the reasons mentioned above you can't split a recording precisely on demand; you have to request and wait for an intra-frame).
  • UTC timestamps are used to generate the filenames so that if the Pi's clock rolls back (at the end of daylight savings) we don't accidentally clobber earlier recordings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.