Handle case where camera is offline when generating previews

This commit is contained in:
Nicolas Mowen 2024-08-30 15:31:27 -06:00
parent 6a0b5c3a3f
commit 91abf99370
2 changed files with 41 additions and 1 deletions

View File

@ -64,6 +64,8 @@ def output_frames(
jsmpeg_cameras: dict[str, JsmpegCamera] = {} jsmpeg_cameras: dict[str, JsmpegCamera] = {}
birdseye: Optional[Birdseye] = None birdseye: Optional[Birdseye] = None
preview_recorders: dict[str, PreviewRecorder] = {} preview_recorders: dict[str, PreviewRecorder] = {}
preview_write_times: dict[str, float] = {}
frame_time: float = 0
move_preview_frames("cache") move_preview_frames("cache")
@ -73,6 +75,7 @@ def output_frames(
jsmpeg_cameras[camera] = JsmpegCamera(cam_config, stop_event, websocket_server) jsmpeg_cameras[camera] = JsmpegCamera(cam_config, stop_event, websocket_server)
preview_recorders[camera] = PreviewRecorder(cam_config) preview_recorders[camera] = PreviewRecorder(cam_config)
preview_write_times[camera] = 0
if config.birdseye.enabled: if config.birdseye.enabled:
birdseye = Birdseye(config, frame_manager, stop_event, websocket_server) birdseye = Birdseye(config, frame_manager, stop_event, websocket_server)
@ -83,6 +86,13 @@ def output_frames(
(topic, data) = detection_subscriber.check_for_update(timeout=1) (topic, data) = detection_subscriber.check_for_update(timeout=1)
if not topic: if not topic:
# all queued images have been written,
# check if any cameras have stale frames
for camera, time in preview_write_times.items():
if time != 0 and frame_time - time > 10:
preview_recorders[camera].flag_offline(frame_time)
continue continue
( (

View File

@ -3,6 +3,7 @@
import datetime import datetime
import logging import logging
import os import os
import shutil
import subprocess as sp import subprocess as sp
import threading import threading
import time import time
@ -326,7 +327,7 @@ class PreviewRecorder:
) )
self.start_time = frame_time self.start_time = frame_time
self.last_output_time = frame_time self.last_output_time = frame_time
self.output_frames = [] self.output_frames: list[float] = []
# include first frame to ensure consistent duration # include first frame to ensure consistent duration
self.output_frames.append(frame_time) self.output_frames.append(frame_time)
@ -335,6 +336,35 @@ class PreviewRecorder:
self.output_frames.append(frame_time) self.output_frames.append(frame_time)
self.write_frame_to_cache(frame_time, frame) self.write_frame_to_cache(frame_time, frame)
def flag_offline(self, frame_time: float) -> None:
# check if PREVIEW clip should be generated and cached frames reset
if frame_time >= self.segment_end:
if len(self.output_frames) == 0:
return
old_frame_path = get_cache_image_name(self.config.name, self.output_frames[-1])
new_frame_path = get_cache_image_name(self.config.name, frame_time)
shutil.copy(old_frame_path, new_frame_path)
# save last frame to ensure consistent duration
self.output_frames.append(frame_time)
FFMpegConverter(
self.config,
self.output_frames,
self.requestor,
).start()
# reset frame cache
self.segment_end = (
(datetime.datetime.now() + datetime.timedelta(hours=1))
.astimezone(datetime.timezone.utc)
.replace(minute=0, second=0, microsecond=0)
.timestamp()
)
self.start_time = frame_time
self.last_output_time = frame_time
self.output_frames = []
def stop(self) -> None: def stop(self) -> None:
self.requestor.stop() self.requestor.stop()