Recording#
Motion#
script to record streams captured by video devices with Motion.
We will assume that Motion is already configured and running.
You can use hardware acceleration instead of using software for the encoding process to reduce the load on the processors, although the quality is lower than software encoding. Since we are dealing with video surveillance footage we don’t care about quality so much.
Setup#
install the dependencies
apt-get install bash ffmpeg python3-yaml python3-requests
install fpyutils. See reference
create a new user
useradd --system -s /bin/bash -U surveillance passwd surveillance usermod -aG jobs surveillance
create the jobs directories. See reference
mkdir -p /home/jobs/{scripts,services}/by-user/surveillance
create the
script
1#!/usr/bin/env python3 2# 3# record_motion.py 4# 5# Copyright (C) 2019-2022 Franco Masotti (franco \D\o\T masotti {-A-T-} tutanota \D\o\T com) 6# 7# This program is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <https://www.gnu.org/licenses/>. 19r"""Record motion camera videos.""" 20 21import datetime 22import pathlib 23import shlex 24import sys 25 26import fpyutils 27import yaml 28 29if __name__ == '__main__': 30 configuration_file = shlex.quote(sys.argv[1]) 31 config = yaml.load(open(configuration_file), Loader=yaml.SafeLoader) 32 33 try: 34 message = 'started recording motion camera: ' + config[ 35 'camera_name'] + '\n' 36 if config['notify']['gotify']['enabled']: 37 m = config['notify']['gotify']['message'] + '\n' + message 38 fpyutils.notify.send_gotify_message( 39 config['notify']['gotify']['url'], 40 config['notify']['gotify']['token'], m, 41 config['notify']['gotify']['title'], 42 config['notify']['gotify']['priority']) 43 if config['notify']['email']['enabled']: 44 fpyutils.notify.send_email( 45 message, config['notify']['email']['smtp_server'], 46 config['notify']['email']['port'], 47 config['notify']['email']['sender'], 48 config['notify']['email']['user'], 49 config['notify']['email']['password'], 50 config['notify']['email']['receiver'], 51 config['notify']['email']['subject']) 52 except Exception as e: 53 # Ignore errors. 54 print(e) 55 56 if config['ffmpeg']['quality']['global_quality'] == '': 57 quality_string = '-q:v ' + config['ffmpeg']['quality']['quality'] 58 else: 59 quality_string = '-global_quality ' + config['ffmpeg']['quality'][ 60 'global_quality'] 61 62 pathlib.Path(config['dst_directory']).mkdir(parents=True, exist_ok=True) 63 while True: 64 # Delete videos older than 'days to keep' days. 65 for d in pathlib.Path(config['dst_directory']).iterdir(): 66 # Work with naive datetime objects because we assume 67 # that everyting is handled on the same computer. 68 if (datetime.datetime.now() - 69 datetime.datetime.fromtimestamp(d.stat().st_mtime) 70 ).total_seconds() > config['retention_seconds']: 71 d.unlink() 72 73 # Record the video as a motion JPEG incapsulated in a Martoska file. 74 # Usually this script is run on the same computer handling the video 75 # stream. 76 video_path = str( 77 pathlib.Path( 78 config['dst_directory'], 'video_' + 79 str(datetime.datetime.now().strftime('%F_%H-%M-%S')) + '.mkv')) 80 command = (config['ffmpeg']['executable'] + ' ' + 81 config['ffmpeg']['extra_options']['pre'] + ' -an ' + 82 ' -i ' + config['stream_url'] + ' ' + quality_string + 83 ' -video_size ' + config['ffmpeg']['video']['size'] + 84 ' -c:v ' + config['ffmpeg']['video']['codec'] + 85 ' -vframes ' + 86 config['ffmpeg']['video']['frames_per_file'] + ' ' + 87 config['ffmpeg']['extra_options']['post'] + ' ' + 88 video_path) 89 fpyutils.shell.execute_command_live_output(command, dry_run=False)
create a
configuration file
1# 2# record_motion.camera1.yaml 3# 4# Copyright (C) 2020,2022 Franco Masotti (franco \D\o\T masotti {-A-T-} tutanota \D\o\T com) 5# 6# This program is free software: you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation, either version 3 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <https://www.gnu.org/licenses/>. 18 19ffmpeg: 20 executable: '/usr/bin/ffmpeg' 21 video: 22 # The total number of video frames per file before creating a new one. 23 frames_per_file: '300' 24 # Resolution in pixels. 25 size: '1280x720' 26 codec: 'mjpeg' 27 extra_options: 28 pre: '' 29 post: "-b:v 15000 -r 10 -filter:v 'setpts=0.25*PTS'" 30 quality: 31 # If empty use quality otherwise use global_quality. 32 global_quality: '' 33 34 # The nearer to 0 the higher the quality and the file size. 35 quality: '0.1' 36 37stream_url: 'http://host:port' 38 39# dst_directory: '/home/surveillance/video/camera1' 40dst_directory: '/tmp/camera' 41 42# 2 days. 43retention_seconds: 172800 44 45camera_name: 'camera 1' 46 47notify: 48 email: 49 enabled: false 50 smtp_server: 'smtp.gmail.com' 51 port: 465 52 sender: 'myusername@gmail.com' 53 user: 'myusername' 54 password: 'my awesome password' 55 receiver: 'myusername@gmail.com' 56 subject: 'record motion' 57 gotify: 58 enabled: false 59 url: '<gotify url>' 60 token: '<app token>' 61 title: 'record motion' 62 message: 'record motion started' 63 priority: 5
Note
If you have an intel processor that supports VAAPI (Video Acceleration API) you can use the following settings
video: # [ ... ] codec: 'mjpeg_vaapi' extra_options: pre: '-hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi' # [ ... ] quality: # If empty use quality otherwise use global_quality. global_quality: '60' # [ ... ] # [ ... ]
fix the permissions
chown -R surveillance:surveillance /home/jobs/{scripts,services}/by-user/surveillance chmod 700 -R /home/jobs/{scripts,services}/by-user/surveillance
run the deploy script
Footnotes