From a748b70da174c617552ea71ed370f9ee3ebd72de Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Fri, 19 Nov 2021 07:19:45 -0600 Subject: [PATCH] avoid running ffprobe for each segment multiple times --- frigate/record.py | 52 +++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/frigate/record.py b/frigate/record.py index 749ab6dff..bfa1f24d5 100644 --- a/frigate/record.py +++ b/frigate/record.py @@ -46,6 +46,7 @@ class RecordingMaintainer(threading.Thread): self.config = config self.stop_event = stop_event self.first_pass = True + self.end_time_cache = {} def move_files(self): cache_files = [ @@ -89,15 +90,17 @@ class RecordingMaintainer(threading.Thread): ) # delete all cached files past the most recent 5 + keep_count = 5 for camera in grouped_recordings.keys(): - if len(grouped_recordings[camera]) > 5: + if len(grouped_recordings[camera]) > keep_count: sorted_recordings = sorted( grouped_recordings[camera], key=lambda i: i["start_time"] ) - to_remove = sorted_recordings[:-2] + to_remove = sorted_recordings[:-keep_count] for f in to_remove: Path(f["cache_path"]).unlink(missing_ok=True) - grouped_recordings[camera] = sorted_recordings[-2:] + self.end_time_cache.pop(f["cache_path"], None) + grouped_recordings[camera] = sorted_recordings[-keep_count:] for camera, recordings in grouped_recordings.items(): # get all events with the end time after the start of the oldest cache file @@ -122,26 +125,32 @@ class RecordingMaintainer(threading.Thread): or not self.config.cameras[camera].record.enabled ): Path(cache_path).unlink(missing_ok=True) + self.end_time_cache.pop(cache_path, None) continue - ffprobe_cmd = [ - "ffprobe", - "-v", - "error", - "-show_entries", - "format=duration", - "-of", - "default=noprint_wrappers=1:nokey=1", - f"{cache_path}", - ] - p = sp.run(ffprobe_cmd, capture_output=True) - if p.returncode == 0: - duration = float(p.stdout.decode().strip()) - end_time = start_time + datetime.timedelta(seconds=duration) + if cache_path in self.end_time_cache: + end_time = self.end_time_cache[cache_path] else: - logger.warning(f"Discarding a corrupt recording segment: {f}") - Path(cache_path).unlink(missing_ok=True) - continue + ffprobe_cmd = [ + "ffprobe", + "-v", + "error", + "-show_entries", + "format=duration", + "-of", + "default=noprint_wrappers=1:nokey=1", + f"{cache_path}", + ] + p = sp.run(ffprobe_cmd, capture_output=True) + if p.returncode == 0: + duration = float(p.stdout.decode().strip()) + self.end_time_cache[ + cache_path + ] = end_time = start_time + datetime.timedelta(seconds=duration) + else: + logger.warning(f"Discarding a corrupt recording segment: {f}") + Path(cache_path).unlink(missing_ok=True) + continue # if cached file's start_time is earlier than the retain_days for the camera if start_time <= ( @@ -216,6 +225,9 @@ class RecordingMaintainer(threading.Thread): Path(cache_path).unlink(missing_ok=True) logger.error(e) + # clear end_time cache + self.end_time_cache.pop(cache_path, None) + def run(self): # Check for new files every 5 seconds wait_time = 5