From: Calin Crisan Date: Sun, 19 Jan 2014 15:53:43 +0000 (+0200) Subject: added support for new motion config directives X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=f7aa23bbbf10cec52a130c17b0f1bdb64504b234;p=motioneye-debian added support for new motion config directives --- diff --git a/src/config.py b/src/config.py index 3f7f968..236bdcd 100644 --- a/src/config.py +++ b/src/config.py @@ -22,7 +22,9 @@ import re from collections import OrderedDict +import motionctl import settings +import update import utils import v4l2ctl @@ -31,9 +33,12 @@ _CAMERA_CONFIG_FILE_NAME = 'thread-%(id)s.conf' _MAIN_CONFIG_FILE_NAME = 'motion.conf' _main_config_cache = None -_camera_config_cache = None +_camera_config_cache = {} _camera_ids_cache = None +# starting with r490 motion config directives have changed a bit +_LAST_OLD_CONFIG_VERSIONS = (490, '3.2.12') + def get_main(as_lines=False): global _main_config_cache @@ -184,7 +189,7 @@ def has_enabled_cameras(): def get_camera(camera_id, as_lines=False): global _camera_config_cache - if not as_lines and _camera_config_cache is not None and camera_id in _camera_config_cache: + if not as_lines and camera_id in _camera_config_cache: return _camera_config_cache[camera_id] camera_config_path = os.path.join(settings.CONF_PATH, _CAMERA_CONFIG_FILE_NAME) % {'id': camera_id} @@ -224,22 +229,72 @@ def get_camera(camera_id, as_lines=False): threads = main_config.get('thread', []) camera_config['@enabled'] = _CAMERA_CONFIG_FILE_NAME % {'id': camera_id} in threads camera_config['@id'] = camera_id - - _set_default_motion_camera(camera_id, camera_config) - - if _camera_config_cache is None: - _camera_config_cache = {} - - _camera_config_cache[camera_id] = camera_config + + old_motion = _is_old_motion() + + # adapt directives from old configuration, if needed + if old_motion: + logging.debug('using old motion config directives') + + if 'output_normal' in camera_config: + camera_config['output_pictures'] = camera_config.pop('output_normal') + if 'ffmpeg_cap_new' in camera_config: + camera_config['ffmpeg_output_movies'] = camera_config.pop('ffmpeg_cap_new') + if 'locate' in camera_config: + camera_config['locate_motion_mode'] = camera_config.pop('locate') + if 'jpeg_filename' in camera_config: + camera_config['picture_filename'] = camera_config.pop('jpeg_filename') + if 'webcam_port' in camera_config: + camera_config['stream_port'] = camera_config.pop('webcam_port') + if 'webcam_quality' in camera_config: + camera_config['stream_quality'] = camera_config.pop('webcam_quality') + if 'webcam_motion' in camera_config: + camera_config['stream_motion'] = camera_config.pop('webcam_motion') + if 'webcam_maxrate' in camera_config: + camera_config['stream_maxrate'] = camera_config.pop('webcam_maxrate') + if 'webcam_localhost' in camera_config: + camera_config['stream_localhost'] = camera_config.pop('webcam_localhost') + + _set_default_motion_camera(camera_id, camera_config, False) + + _camera_config_cache[camera_id] = dict(camera_config) return camera_config def set_camera(camera_id, camera_config): global _camera_config_cache + + camera_config['@id'] = camera_id + _camera_config_cache[camera_id] = dict(camera_config) if camera_config['@proto'] == 'v4l2': - _set_default_motion_camera(camera_id, camera_config) + old_motion = _is_old_motion() + + # adapt directives to old configuration, if needed + if old_motion: + logging.debug('using old motion config directives') + + if 'output_pictures' in camera_config: + camera_config['output_normal'] = camera_config.pop('output_pictures') + if 'ffmpeg_output_movies' in camera_config: + camera_config['ffmpeg_cap_new'] = camera_config.pop('ffmpeg_output_movies') + if 'locate_motion_mode' in camera_config: + camera_config['locate'] = camera_config.pop('locate_motion_mode') + if 'picture_filename' in camera_config: + camera_config['jpeg_filename'] = camera_config.pop('picture_filename') + if 'stream_port' in camera_config: + camera_config['webcam_port'] = camera_config.pop('stream_port') + if 'stream_quality' in camera_config: + camera_config['webcam_quality'] = camera_config.pop('stream_quality') + if 'stream_motion' in camera_config: + camera_config['webcam_motion'] = camera_config.pop('stream_motion') + if 'stream_maxrate' in camera_config: + camera_config['webcam_maxrate'] = camera_config.pop('stream_maxrate') + if 'stream_localhost' in camera_config: + camera_config['webcam_localhost'] = camera_config.pop('stream_localhost') + + _set_default_motion_camera(camera_id, camera_config, old_motion) # set the enabled status in main config main_config = get_main() @@ -253,8 +308,6 @@ def set_camera(camera_id, camera_config): main_config['thread'] = threads - camera_config['@id'] = camera_id - set_main(main_config) # try to create the target_dir @@ -300,11 +353,6 @@ def set_camera(camera_id, camera_config): finally: file.close() - if _camera_config_cache is None: - _camera_config_cache = {} - - _camera_config_cache[camera_id] = camera_config - return camera_config @@ -348,7 +396,9 @@ def add_camera(device_details): set_camera(camera_id, data) _camera_ids_cache = None - _camera_config_cache = None + _camera_config_cache = {} + + data = get_camera(camera_id) return camera_id, data @@ -372,7 +422,7 @@ def rem_camera(camera_id): logging.info('removing camera config file %(path)s...' % {'path': camera_config_path}) _camera_ids_cache = None - _camera_config_cache = None + _camera_config_cache = {} try: os.remove(camera_config_path) @@ -416,7 +466,7 @@ def camera_ui_to_dict(ui): '@enabled': ui['enabled'], '@proto': ui['proto'], 'videodevice': ui['device_uri'], - 'lightswitch': int(ui['light_switch_detect']) * 5, + 'lightswitch': int(ui['light_switch_detect']) * 50, 'auto_brightness': ui['auto_brightness'], 'width': int(ui['resolution'].split('x')[0]), 'height': int(ui['resolution'].split('x')[1]), @@ -437,32 +487,32 @@ def camera_ui_to_dict(ui): 'text_double': False, # streaming - 'webcam_localhost': not ui['video_streaming'], - 'webcam_port': int(ui['streaming_port']), - 'webcam_maxrate': int(ui['streaming_framerate']), - 'webcam_quality': max(1, int(ui['streaming_quality'])), + 'stream_localhost': not ui['video_streaming'], + 'stream_port': int(ui['streaming_port']), + 'stream_maxrate': int(ui['streaming_framerate']), + 'stream_quality': max(1, int(ui['streaming_quality'])), '@webcam_resolution': max(1, int(ui['streaming_resolution'])), '@webcam_server_resize': ui['streaming_server_resize'], - 'webcam_motion': ui['streaming_motion'], + 'stream_motion': ui['streaming_motion'], # still images - 'output_normal': False, + 'output_pictures': False, 'output_all': False, 'output_motion': False, 'snapshot_interval': 0, - 'jpeg_filename': '', + 'picture_filename': '', 'snapshot_filename': '', '@preserve_pictures': int(ui['preserve_pictures']), # movies - 'ffmpeg_cap_new': ui['motion_movies'], + 'ffmpeg_output_movies': ui['motion_movies'], 'movie_filename': ui['movie_file_name'], 'ffmpeg_bps': 400000, '@preserve_movies': int(ui['preserve_movies']), # motion detection 'text_changes': ui['show_frame_changes'], - 'locate': ui['show_frame_changes'], + 'locate_motion_mode': ui['show_frame_changes'], 'threshold': ui['frame_change_threshold'], 'noise_tune': ui['auto_noise_detect'], 'noise_level': max(1, int(round(int(ui['noise_level']) * 2.55))), @@ -533,8 +583,8 @@ def camera_ui_to_dict(ui): if ui['still_images']: capture_mode = ui['capture_mode'] if capture_mode == 'motion-triggered': - data['output_normal'] = True - data['jpeg_filename'] = ui['image_file_name'] + data['output_pictures'] = True + data['picture_filename'] = ui['image_file_name'] elif capture_mode == 'interval-snapshots': data['snapshot_interval'] = int(ui['snapshot_interval']) @@ -542,7 +592,7 @@ def camera_ui_to_dict(ui): elif capture_mode == 'all-frames': data['output_all'] = True - data['jpeg_filename'] = ui['image_file_name'] + data['picture_filename'] = ui['image_file_name'] data['quality'] = max(1, int(ui['image_quality'])) @@ -610,13 +660,13 @@ def camera_dict_to_ui(data): 'custom_right_text': '', # streaming - 'video_streaming': not data['webcam_localhost'], - 'streaming_framerate': int(data['webcam_maxrate']), - 'streaming_quality': int(data['webcam_quality']), + 'video_streaming': not data['stream_localhost'], + 'streaming_framerate': int(data['stream_maxrate']), + 'streaming_quality': int(data['stream_quality']), 'streaming_resolution': int(data['@webcam_resolution']), 'streaming_server_resize': int(data['@webcam_server_resize']), - 'streaming_port': int(data['webcam_port']), - 'streaming_motion': int(data['webcam_motion']), + 'streaming_port': int(data['stream_port']), + 'streaming_motion': int(data['stream_motion']), # still images 'still_images': False, @@ -627,12 +677,12 @@ def camera_dict_to_ui(data): 'preserve_pictures': data['@preserve_pictures'], # motion movies - 'motion_movies': data['ffmpeg_cap_new'], + 'motion_movies': data['ffmpeg_output_movies'], 'movie_file_name': data['movie_filename'], 'preserve_movies': data['@preserve_movies'], # motion detection - 'show_frame_changes': data['text_changes'] or data['locate'], + 'show_frame_changes': data['text_changes'] or data['locate_motion_mode'], 'frame_change_threshold': data['threshold'], 'auto_noise_detect': data['noise_tune'], 'noise_level': int(int(data['noise_level']) / 2.55), @@ -716,28 +766,28 @@ def camera_dict_to_ui(data): ui['custom_right_text'] = text_right output_all = data['output_all'] - output_normal = data['output_normal'] - jpeg_filename = data['jpeg_filename'] + output_pictures = data['output_pictures'] + picture_filename = data['picture_filename'] snapshot_interval = data['snapshot_interval'] snapshot_filename = data['snapshot_filename'] - if (((output_all or output_normal) and jpeg_filename) or + if (((output_all or output_pictures) and picture_filename) or (snapshot_interval and snapshot_filename)): ui['still_images'] = True if output_all: ui['capture_mode'] = 'all-frames' - ui['image_file_name'] = jpeg_filename + ui['image_file_name'] = picture_filename elif snapshot_interval: ui['capture_mode'] = 'interval-snapshots' ui['image_file_name'] = snapshot_filename ui['snapshot_interval'] = snapshot_interval - elif output_normal: + elif output_pictures: ui['capture_mode'] = 'motion-triggered' - ui['image_file_name'] = jpeg_filename + ui['image_file_name'] = picture_filename ui['image_quality'] = data['quality'] @@ -918,6 +968,21 @@ def _dict_to_conf(lines, data, list_names=[]): return lines +def _is_old_motion(): + try: + binary, version = motionctl.find_motion() # @UnusedVariable + + if version.startswith('trunkREV'): # e.g. trunkREV599 + version = int(version[8:]) + return version < _LAST_OLD_CONFIG_VERSIONS[0] + + else: # stable release, should be in the format x.y.z + return update.compare_versions(version, _LAST_OLD_CONFIG_VERSIONS[1]) <= 0 + + except: + return False + + def _set_default_motion(data): data.setdefault('@enabled', True) data.setdefault('@show_advanced', False) @@ -927,12 +992,12 @@ def _set_default_motion(data): data.setdefault('@normal_password', '') -def _set_default_motion_camera(camera_id, data): +def _set_default_motion_camera(camera_id, data, old_motion): data.setdefault('@name', 'Camera' + str(camera_id)) data.setdefault('@enabled', False) data.setdefault('@proto', 'v4l2') data.setdefault('videodevice', '/dev/video0') - data.setdefault('lightswitch', 5) + data.setdefault('lightswitch', 50) data.setdefault('auto_brightness', False) data.setdefault('brightness', 0) data.setdefault('contrast', 0) @@ -950,32 +1015,50 @@ def _set_default_motion_camera(camera_id, data): data.setdefault('@network_password', '') data.setdefault('target_dir', settings.RUN_PATH) - data.setdefault('webcam_localhost', False) - data.setdefault('webcam_port', int('808' + str(camera_id))) - data.setdefault('webcam_maxrate', 5) - data.setdefault('webcam_quality', 85) + if old_motion: + data.setdefault('webcam_localhost', False) + data.setdefault('webcam_port', int('808' + str(camera_id))) + data.setdefault('webcam_maxrate', 5) + data.setdefault('webcam_quality', 85) + data.setdefault('webcam_motion', False) + + else: + data.setdefault('stream_localhost', False) + data.setdefault('stream_port', int('808' + str(camera_id))) + data.setdefault('stream_maxrate', 5) + data.setdefault('stream_quality', 85) + data.setdefault('stream_motion', False) data.setdefault('@webcam_resolution', 100) data.setdefault('@webcam_server_resize', True) - data.setdefault('webcam_motion', False) data.setdefault('text_left', data['@name']) data.setdefault('text_right', '%Y-%m-%d\\n%T') data.setdefault('text_double', False) data.setdefault('text_changes', False) - data.setdefault('locate', False) + if old_motion: + data.setdefault('locate', False) + + else: + data.setdefault('locate_motion_mode', False) + data.setdefault('locate_motion_style', 'redbox') data.setdefault('threshold', 1500) data.setdefault('noise_tune', True) data.setdefault('noise_level', 32) - data.setdefault('minimum_motion_frames', 5) + data.setdefault('minimum_motion_frames', 1) data.setdefault('gap', 30) data.setdefault('pre_capture', 2) data.setdefault('post_capture', 4) data.setdefault('output_all', False) - data.setdefault('output_normal', False) - data.setdefault('jpeg_filename', '') + if old_motion: + data.setdefault('output_normal', False) + data.setdefault('jpeg_filename', '') + + else: + data.setdefault('output_pictures', False) + data.setdefault('picture_filename', '') data.setdefault('snapshot_interval', 0) data.setdefault('snapshot_filename', '') data.setdefault('quality', 85) @@ -984,7 +1067,11 @@ def _set_default_motion_camera(camera_id, data): data.setdefault('ffmpeg_variable_bitrate', 0) data.setdefault('ffmpeg_bps', 400000) data.setdefault('movie_filename', '%Y-%m-%d/%H-%M-%S') - data.setdefault('ffmpeg_cap_new', False) + if old_motion: + data.setdefault('ffmpeg_cap_new', False) + + else: + data.setdefault('ffmpeg_output_movies', False) data.setdefault('ffmpeg_video_codec', 'msmpeg4') data.setdefault('@preserve_movies', 0) diff --git a/src/mjpgclient.py b/src/mjpgclient.py index 33609c2..7793318 100644 --- a/src/mjpgclient.py +++ b/src/mjpgclient.py @@ -47,7 +47,7 @@ class MjpgClient(iostream.IOStream): logging.debug('mjpg client for camera %(camera_id)s connecting on port %(port)s...' % { 'port': self._port, 'camera_id': self._camera_id}) - def close(self): + def close(self, exc_info=False): if MjpgClient.clients.pop(self._camera_id, None): MjpgClient.last_access.pop(self._camera_id, None) MjpgClient.last_jpg_moment.pop(self._camera_id, None) @@ -56,7 +56,7 @@ class MjpgClient(iostream.IOStream): 'port': self._port, 'camera_id': self._camera_id}) try: - iostream.IOStream.close(self) + iostream.IOStream.close(self, exc_info=exc_info) except: pass # already closed, nevermind @@ -191,7 +191,7 @@ def get_jpg(camera_id): return None - port = camera_config['webcam_port'] + port = camera_config['stream_port'] client = MjpgClient(camera_id, port) client.connect() diff --git a/src/motionctl.py b/src/motionctl.py index c73497f..6b2910c 100644 --- a/src/motionctl.py +++ b/src/motionctl.py @@ -17,6 +17,7 @@ import errno import os.path +import re import signal import subprocess import time @@ -27,11 +28,22 @@ import settings def find_motion(): try: - return subprocess.check_output('which motion', shell=True).strip() + binary = subprocess.check_output('which motion', shell=True).strip() except subprocess.CalledProcessError: # not found return None + try: + help = subprocess.check_output(binary + ' -h || true', shell=True) + + except subprocess.CalledProcessError: # not found + return None + + result = re.findall('^motion Version ([^,]+)', help) + version = result and result[0] or '' + + return (binary, version) + def start(): if running(): @@ -41,6 +53,8 @@ def start(): if not program: raise Exception('motion executable could not be found') + program, version = program # @UnusedVariable + motion_config_path = os.path.join(settings.CONF_PATH, 'motion.conf') motion_log_path = os.path.join(settings.RUN_PATH, 'motion.log') motion_pid_path = os.path.join(settings.RUN_PATH, 'motion.pid') @@ -107,14 +121,16 @@ def running(): if pid is None: return False + try: + os.waitpid(pid, os.WNOHANG) os.kill(pid, 0) # the process is running return True except OSError as e: - if e.errno != errno.ESRCH: + if e.errno not in (errno.ESRCH, errno.ECHILD): raise return False