From 8719f71521b9996b389e84dfbbd36164db8e8c09 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 4 Oct 2015 18:20:01 +0300 Subject: [PATCH] added continuous movie recording mode; better movie quality adjustment settings --- motioneye/config.py | 99 +++++++++++--------- motioneye/static/js/main.js | 167 +++++++++++++++++++--------------- motioneye/templates/main.html | 120 ++++++++++++++---------- 3 files changed, 226 insertions(+), 160 deletions(-) diff --git a/motioneye/config.py b/motioneye/config.py index c17b5c9..e8c0690 100644 --- a/motioneye/config.py +++ b/motioneye/config.py @@ -653,14 +653,22 @@ def motion_camera_ui_to_dict(ui, old_config=None): # still images 'output_pictures': False, - 'emulate_motion': False, 'snapshot_interval': 0, 'picture_filename': '', 'snapshot_filename': '', + 'quality': max(1, int(ui['image_quality'])), '@preserve_pictures': int(ui['preserve_pictures']), + # movies + 'ffmpeg_output_movies': False, + 'movie_filename': ui['movie_file_name'], + 'ffmpeg_bps': 44000, # a quality of about 85% for 320x240x2fps + 'max_movie_time': ui['max_movie_length'], + '@preserve_movies': int(ui['preserve_movies']), + # motion detection '@motion_detection': ui['motion_detection'], + 'emulate_motion': False, 'text_changes': ui['show_frame_changes'], 'locate_motion_mode': ui['show_frame_changes'], 'noise_tune': ui['auto_noise_detect'], @@ -670,13 +678,6 @@ def motion_camera_ui_to_dict(ui, old_config=None): 'post_capture': int(ui['post_capture']), 'minimum_motion_frames': int(ui['minimum_motion_frames']), - # movies - 'ffmpeg_output_movies': ui['motion_movies'], - 'movie_filename': ui['movie_file_name'], - 'ffmpeg_bps': 44000, # a quality of about 85% for 320x240x2fps - 'max_movie_time': ui['max_movie_length'], - '@preserve_movies': int(ui['preserve_movies']), - # working schedule '@working_schedule': '', @@ -812,17 +813,24 @@ def motion_camera_ui_to_dict(ui, old_config=None): data['emulate_motion'] = True data['picture_filename'] = ui['image_file_name'] - data['quality'] = max(1, int(ui['image_quality'])) - + if ui['movies']: + data['ffmpeg_output_movies'] = True + recording_mode = ui['recording_mode'] + if recording_mode == 'motion-triggered': + data['emulate_motion'] = False + + elif recording_mode == 'continuous': + data['emulate_motion'] = True + if proto == 'v4l2': - max_val = data['width'] * data['height'] * data['framerate'] / 3 + max_val = data['width'] * data['height'] * data['framerate'] - else: # always assume a netcam image size of 640x480, since we have no means to know it at this point - max_val = 640 * 480 * data['framerate'] / 3 - + else: # assume a netcam image size of 640x480, since we have no means to know it at this point + max_val = 640 * 480 * data['framerate'] + max_val = min(max_val, 9999999) - - data['ffmpeg_bps'] = int(ui['movie_quality']) * max_val / 100 + + data['ffmpeg_bps'] = int(int(ui['movie_quality']) * max_val / 100) # working schedule if ui['working_schedule']: @@ -939,10 +947,17 @@ def motion_camera_dict_to_ui(data): 'still_images': False, 'capture_mode': 'motion-triggered', 'image_file_name': '%Y-%m-%d/%H-%M-%S', - 'image_quality': 85, + 'image_quality': data['quality'], 'snapshot_interval': 0, 'preserve_pictures': data['@preserve_pictures'], + # movies + 'movies': False, + 'recording_mode': 'motion-triggered', + 'movie_file_name': data['movie_filename'], + 'max_movie_length': data['max_movie_time'], + 'preserve_movies': data['@preserve_movies'], + # motion detection 'motion_detection': data['@motion_detection'], 'show_frame_changes': data['text_changes'] or data['locate_motion_mode'], @@ -953,12 +968,6 @@ def motion_camera_dict_to_ui(data): 'post_capture': int(data['post_capture']), 'minimum_motion_frames': int(data['minimum_motion_frames']), - # motion movies - 'motion_movies': data['ffmpeg_output_movies'], - 'movie_file_name': data['movie_filename'], - 'max_movie_length': data['max_movie_time'], - 'preserve_movies': data['@preserve_movies'], - # motion notifications 'email_notifications_enabled': False, 'web_hook_notifications_enabled': False, @@ -1102,33 +1111,41 @@ def motion_camera_dict_to_ui(data): snapshot_interval = data['snapshot_interval'] snapshot_filename = data['snapshot_filename'] - if (((emulate_motion or output_pictures) and picture_filename) or - (snapshot_interval and snapshot_filename)): - - ui['still_images'] = True + ui['still_images'] = (((emulate_motion or output_pictures) and picture_filename) or + (snapshot_interval and snapshot_filename)) - if emulate_motion: - ui['capture_mode'] = 'all-frames' + if emulate_motion: + ui['capture_mode'] = 'all-frames' + if picture_filename: ui['image_file_name'] = picture_filename - - elif snapshot_interval: - ui['capture_mode'] = 'interval-snapshots' + + elif snapshot_interval: + ui['capture_mode'] = 'interval-snapshots' + ui['snapshot_interval'] = snapshot_interval + if snapshot_filename: ui['image_file_name'] = snapshot_filename - ui['snapshot_interval'] = snapshot_interval - - elif output_pictures: - ui['capture_mode'] = 'motion-triggered' - ui['image_file_name'] = picture_filename - - ui['image_quality'] = data['quality'] + + elif output_pictures: + ui['capture_mode'] = 'motion-triggered' + if picture_filename: + ui['image_file_name'] = picture_filename + + if data['ffmpeg_output_movies']: + ui['movies'] = True + + if emulate_motion: + ui['recording_mode'] = 'continuous' + else: + ui['recording_mode'] = 'motion-triggered' + ffmpeg_bps = data['ffmpeg_bps'] if ffmpeg_bps is not None: if utils.v4l2_camera(data): - max_val = data['width'] * data['height'] * data['framerate'] / 3 + max_val = data['width'] * data['height'] * data['framerate'] else: # net camera - max_val = 640 * 480 * data['framerate'] / 3 + max_val = 640 * 480 * data['framerate'] max_val = min(max_val, 9999999) diff --git a/motioneye/static/js/main.js b/motioneye/static/js/main.js index 0fe6ca7..540f25b 100644 --- a/motioneye/static/js/main.js +++ b/motioneye/static/js/main.js @@ -561,35 +561,35 @@ function initUI() { } /* ui elements that enable/disable other ui elements */ - $('#motionEyeSwitch').change(updateConfigUi); - $('#showAdvancedSwitch').change(updateConfigUi); - $('#storageDeviceSelect').change(updateConfigUi); - $('#resolutionSelect').change(updateConfigUi); - $('#leftTextSelect').change(updateConfigUi); - $('#rightTextSelect').change(updateConfigUi); - $('#captureModeSelect').change(updateConfigUi); - $('#autoNoiseDetectSwitch').change(updateConfigUi); - $('#videoDeviceSwitch').change(checkMinimizeSection).change(updateConfigUi); - $('#textOverlaySwitch').change(checkMinimizeSection).change(updateConfigUi); - $('#videoStreamingSwitch').change(checkMinimizeSection).change(updateConfigUi); - $('#streamingServerResizeSwitch').change(updateConfigUi); - $('#stillImagesSwitch').change(checkMinimizeSection).change(updateConfigUi); - $('#preservePicturesSelect').change(updateConfigUi); - $('#motionDetectionSwitch').change(checkMinimizeSection).change(updateConfigUi); - $('#motionMoviesSwitch').change(checkMinimizeSection).change(updateConfigUi); - $('#preserveMoviesSelect').change(updateConfigUi); - $('#emailNotificationsSwitch').change(updateConfigUi); - $('#webHookNotificationsSwitch').change(updateConfigUi); - $('#commandNotificationsSwitch').change(updateConfigUi); - $('#workingScheduleSwitch').change(checkMinimizeSection).change(updateConfigUi); - - $('#mondayEnabledSwitch').change(updateConfigUi); - $('#tuesdayEnabledSwitch').change(updateConfigUi); - $('#wednesdayEnabledSwitch').change(updateConfigUi); - $('#thursdayEnabledSwitch').change(updateConfigUi); - $('#fridayEnabledSwitch').change(updateConfigUi); - $('#saturdayEnabledSwitch').change(updateConfigUi); - $('#sundayEnabledSwitch').change(updateConfigUi); + $('#motionEyeSwitch').change(updateConfigUI); + $('#showAdvancedSwitch').change(updateConfigUI); + $('#storageDeviceSelect').change(updateConfigUI); + $('#resolutionSelect').change(updateConfigUI); + $('#leftTextSelect').change(updateConfigUI); + $('#rightTextSelect').change(updateConfigUI); + $('#captureModeSelect').change(updateConfigUI); + $('#autoNoiseDetectSwitch').change(updateConfigUI); + $('#videoDeviceSwitch').change(checkMinimizeSection).change(updateConfigUI); + $('#textOverlaySwitch').change(checkMinimizeSection).change(updateConfigUI); + $('#videoStreamingSwitch').change(checkMinimizeSection).change(updateConfigUI); + $('#streamingServerResizeSwitch').change(updateConfigUI); + $('#stillImagesSwitch').change(checkMinimizeSection).change(updateConfigUI); + $('#preservePicturesSelect').change(updateConfigUI); + $('#moviesSwitch').change(checkMinimizeSection).change(updateConfigUI); + $('#motionDetectionSwitch').change(checkMinimizeSection).change(updateConfigUI); + $('#preserveMoviesSelect').change(updateConfigUI); + $('#emailNotificationsSwitch').change(updateConfigUI); + $('#webHookNotificationsSwitch').change(updateConfigUI); + $('#commandNotificationsSwitch').change(updateConfigUI); + $('#workingScheduleSwitch').change(checkMinimizeSection).change(updateConfigUI); + + $('#mondayEnabledSwitch').change(updateConfigUI); + $('#tuesdayEnabledSwitch').change(updateConfigUI); + $('#wednesdayEnabledSwitch').change(updateConfigUI); + $('#thursdayEnabledSwitch').change(updateConfigUI); + $('#fridayEnabledSwitch').change(updateConfigUI); + $('#saturdayEnabledSwitch').change(updateConfigUI); + $('#sundayEnabledSwitch').change(updateConfigUI); /* minimizable sections */ $('span.minimize').click(function () { @@ -608,7 +608,7 @@ function initUI() { } } - updateConfigUi(); + updateConfigUI(); }); $('a.settings-section-title').click(function () { @@ -631,7 +631,7 @@ function initUI() { seenDependNames[depend] = true; var control = $('#' + depend + 'Entry, #' + depend + 'Select, #' + depend + 'Slider, #' + depend + 'Switch'); - control.change(updateConfigUi); + control.change(updateConfigUI); }); }); @@ -673,7 +673,7 @@ function initUI() { }); /* streaming framerate must be >= device framerate */ - $('#framerateSlider').change(function (val) { + $('#framerateSlider').change(function () { var value = Number($('#framerateSlider').val()); var streamingValue = Number($('#streamingFramerateSlider').val()); @@ -682,6 +682,33 @@ function initUI() { } }); + /* capture mode and recording mode are not completely independent: + * all frames capture mode implies continuous recording (and vice-versa) */ + $('#captureModeSelect').change(function (val) { + if ($('#captureModeSelect').val() == 'all-frames') { + $('#recordingModeSelect').val('continuous'); + } + else { + if ($('#recordingModeSelect').val() == 'continuous') { + $('#recordingModeSelect').val('motion-triggered'); + } + } + + updateConfigUI(); + }); + $('#recordingModeSelect').change(function (val) { + if ($('#recordingModeSelect').val() == 'continuous') { + $('#captureModeSelect').val('all-frames'); + } + else { + if ($('#captureModeSelect').val() == 'all-frames') { + $('#captureModeSelect').val('motion-triggered'); + } + } + + updateConfigUI(); + }); + /* preview controls */ $('#brightnessSlider').change(function () {pushPreview('brightness');}); $('#contrastSlider').change(function () {pushPreview('contrast');}); @@ -749,7 +776,7 @@ function openSettings(cameraId) { $('div.page-container').addClass('stretched'); $('div.settings-top-bar').addClass('open').removeClass('closed'); - updateConfigUi(); + updateConfigUI(); } function closeSettings() { @@ -766,7 +793,7 @@ function isSettingsOpen() { return $('div.settings').hasClass('open'); } -function updateConfigUi() { +function updateConfigUI() { var objs = $('tr.settings-item, div.advanced-setting, table.advanced-setting, div.settings-section-title, table.settings, ' + 'div.check-box.camera-config, div.check-box.main-config'); @@ -885,14 +912,20 @@ function updateConfigUi() { $('#picturesLifetimeEntry').parents('tr:eq(0)').each(markHideLogic); } + /* movies switch */ + if (!$('#moviesSwitch').get(0).checked) { + $('#moviesSwitch').parent().next('table.settings').find('tr.settings-item').each(markHideLogic); + } + + /* preserve movies */ + if ($('#preserveMoviesSelect').val() != '-1') { + $('#moviesLifetimeEntry').parents('tr:eq(0)').each(markHideLogic); + } + /* motion detection switch */ if (!$('#motionDetectionSwitch').get(0).checked) { $('#motionDetectionSwitch').parent().next('table.settings').find('tr.settings-item').each(markHideLogic); - /* hide the entire motion movies section */ - $('#motionMoviesSwitch').parent().each(markHideLogic); - $('#motionMoviesSwitch').parent().next('table.settings').each(markHideLogic); - /* hide the entire notifications section */ $('#emailNotificationsSwitch').parents('table.settings').prev().each(markHideLogic); $('#emailNotificationsSwitch').parents('table.settings').each(markHideLogic); @@ -902,16 +935,6 @@ function updateConfigUi() { $('#workingScheduleSwitch').parent().next('table.settings').each(markHideLogic); } - /* motion movies switch */ - if (!$('#motionMoviesSwitch').get(0).checked) { - $('#motionMoviesSwitch').parent().next('table.settings').find('tr.settings-item').each(markHideLogic); - } - - /* preserve movies */ - if ($('#preserveMoviesSelect').val() != '-1') { - $('#moviesLifetimeEntry').parents('tr:eq(0)').each(markHideLogic); - } - /* event notifications */ if (!$('#emailNotificationsSwitch').get(0).checked) { $('#emailAddressesEntry').parents('tr:eq(0)').each(markHideLogic); @@ -1233,7 +1256,7 @@ function dict2MainUi(dict) { markHideIfNull('_' + name, id); }); - updateConfigUi(); + updateConfigUI(); } function cameraUi2Dict() { @@ -1308,6 +1331,14 @@ function cameraUi2Dict() { 'snapshot_interval': $('#snapshotIntervalEntry').val(), 'preserve_pictures': $('#preservePicturesSelect').val() >= 0 ? $('#preservePicturesSelect').val() : $('#picturesLifetimeEntry').val(), + /* movies */ + 'movies': $('#moviesSwitch')[0].checked, + 'movie_file_name': $('#movieFileNameEntry').val(), + 'movie_quality': $('#movieQualitySlider').val(), + 'recording_mode': $('#recordingModeSelect').val(), + 'max_movie_length': $('#maxMovieLengthEntry').val(), + 'preserve_movies': $('#preserveMoviesSelect').val() >= 0 ? $('#preserveMoviesSelect').val() : $('#moviesLifetimeEntry').val(), + /* motion detection */ 'motion_detection': $('#motionDetectionSwitch')[0].checked, 'show_frame_changes': $('#showFrameChangesSwitch')[0].checked, @@ -1319,13 +1350,6 @@ function cameraUi2Dict() { 'post_capture': $('#postCaptureEntry').val(), 'minimum_motion_frames': $('#minimumMotionFramesEntry').val(), - /* motion movies */ - 'motion_movies': $('#motionMoviesSwitch')[0].checked, - 'movie_file_name': $('#movieFileNameEntry').val(), - 'movie_quality': $('#movieQualitySlider').val(), - 'max_movie_length': $('#maxMovieLengthEntry').val(), - 'preserve_movies': $('#preserveMoviesSelect').val() >= 0 ? $('#preserveMoviesSelect').val() : $('#moviesLifetimeEntry').val(), - /* motion notifications */ 'email_notifications_enabled': $('#emailNotificationsSwitch')[0].checked, 'email_notifications_addresses': $('#emailAddressesEntry').val(), @@ -1428,7 +1452,7 @@ function dict2CameraUi(dict) { $('#videoDeviceSwitch')[0].error = true; $('#videoDeviceSwitch')[0].checked = true; /* so that the user can explicitly disable the camera */ - updateConfigUi(); + updateConfigUI(); return; } @@ -1610,6 +1634,19 @@ function dict2CameraUi(dict) { markHideIfNull('preserve_pictures', 'preservePicturesSelect'); $('#picturesLifetimeEntry').val(dict['preserve_pictures']); markHideIfNull('preserve_pictures', 'picturesLifetimeEntry'); + /* movies */ + $('#moviesSwitch')[0].checked = dict['movies']; markHideIfNull('movies', 'moviesSwitch'); + $('#movieFileNameEntry').val(dict['movie_file_name']); markHideIfNull('movie_file_name', 'movieFileNameEntry'); + $('#movieQualitySlider').val(dict['movie_quality']); markHideIfNull('movie_quality', 'movieQualitySlider'); + $('#recordingModeSelect').val(dict['recording_mode']); markHideIfNull('recording_mode', 'recordingModeSelect'); + $('#maxMovieLengthEntry').val(dict['max_movie_length']); markHideIfNull('max_movie_length', 'maxMovieLengthEntry'); + $('#preserveMoviesSelect').val(dict['preserve_movies']); + if ($('#preserveMoviesSelect').val() == null) { + $('#preserveMoviesSelect').val('-1'); + } + markHideIfNull('preserve_movies', 'preserveMoviesSelect'); + $('#moviesLifetimeEntry').val(dict['preserve_movies']); markHideIfNull('preserve_movies', 'moviesLifetimeEntry'); + /* motion detection */ $('#motionDetectionSwitch')[0].checked = dict['motion_detection']; markHideIfNull('motion_detection', 'motionDetectionSwitch'); $('#showFrameChangesSwitch')[0].checked = dict['show_frame_changes']; markHideIfNull('show_frame_changes', 'showFrameChangesSwitch'); @@ -1621,18 +1658,6 @@ function dict2CameraUi(dict) { $('#postCaptureEntry').val(dict['post_capture']); markHideIfNull('post_capture', 'postCaptureEntry'); $('#minimumMotionFramesEntry').val(dict['minimum_motion_frames']); markHideIfNull('minimum_motion_frames', 'minimumMotionFramesEntry'); - /* motion movies */ - $('#motionMoviesSwitch')[0].checked = dict['motion_movies']; markHideIfNull('motion_movies', 'motionMoviesSwitch'); - $('#movieFileNameEntry').val(dict['movie_file_name']); markHideIfNull('movie_file_name', 'movieFileNameEntry'); - $('#movieQualitySlider').val(dict['movie_quality']); markHideIfNull('movie_quality', 'movieQualitySlider'); - $('#maxMovieLengthEntry').val(dict['max_movie_length']); markHideIfNull('max_movie_length', 'maxMovieLengthEntry'); - $('#preserveMoviesSelect').val(dict['preserve_movies']); - if ($('#preserveMoviesSelect').val() == null) { - $('#preserveMoviesSelect').val('-1'); - } - markHideIfNull('preserve_movies', 'preserveMoviesSelect'); - $('#moviesLifetimeEntry').val(dict['preserve_movies']); markHideIfNull('preserve_movies', 'moviesLifetimeEntry'); - /* motion notifications */ $('#emailNotificationsSwitch')[0].checked = dict['email_notifications_enabled']; markHideIfNull('email_notifications_enabled', 'emailNotificationsSwitch'); $('#emailAddressesEntry').val(dict['email_notifications_addresses']); @@ -1721,7 +1746,7 @@ function dict2CameraUi(dict) { markHideIfNull('_' + name, id); }); - updateConfigUi(); + updateConfigUI(); } @@ -2294,7 +2319,7 @@ function fetchCurrentConfig(onFetch) { } } - updateConfigUi(); + updateConfigUI(); } else { /* normal user */ if (!cameras.length) { diff --git a/motioneye/templates/main.html b/motioneye/templates/main.html index 4a7390e..18e7137 100644 --- a/motioneye/templates/main.html +++ b/motioneye/templates/main.html @@ -98,6 +98,8 @@
+ +
? @@ -172,6 +174,7 @@ + {% for section in main_sections.values() %} {% if section.get('label') and section.get('configs') %}
@@ -192,6 +195,7 @@
+
? @@ -283,6 +287,7 @@ {% endfor %} +
? File Storage @@ -337,6 +342,7 @@ {% endfor %} +
? @@ -383,6 +389,7 @@ {% endfor %} +
? @@ -456,6 +463,7 @@ {% endfor %} +
? @@ -482,12 +490,12 @@ - ? + ? Snapshot Interval seconds - ? + ? Preserve Pictures @@ -501,7 +509,7 @@ - ? + ? Pictures Lifetime @@ -513,6 +521,64 @@ {% endfor %} + +
+ + ? + Movies + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% for config in camera_sections.get('movies', {}).get('configs', []) %} + {{config_item(config)}} + {% endfor %} +
Movie File Name?
Movie Quality?
Recording Mode + + ?
Maximum Movie Lengthseconds?
Preserve Movies + + ?
Movies Lifetimedays?
+ +
? @@ -568,51 +634,7 @@ {% endfor %} -
- - ? - Motion Movies - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - {% for config in camera_sections.get('motion-movies', {}).get('configs', []) %} - {{config_item(config)}} - {% endfor %} -
Movie File Name?
Movie Quality?
Maximum Movie Lengthseconds?
Preserve Movies - - ?
Movies Lifetimedays?
+
? Motion Notifications @@ -700,6 +722,7 @@ {% endfor %} +
? @@ -785,6 +808,7 @@ {% endfor %} + {% for section in camera_sections.values() %} {% if section.get('label') and section.get('configs') %}
-- 2.39.5