mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-30 13:48:07 +02:00
Fix previews failing when disabled (#16962)
* Fix previews failing when offline * Simplify frame cache handling
This commit is contained in:
parent
ad0e89e147
commit
73c2c34127
@ -41,22 +41,30 @@ def check_disabled_camera_update(
|
|||||||
has_enabled_camera = False
|
has_enabled_camera = False
|
||||||
|
|
||||||
for camera, last_update in write_times.items():
|
for camera, last_update in write_times.items():
|
||||||
|
offline_time = now - last_update
|
||||||
|
|
||||||
if config.cameras[camera].enabled:
|
if config.cameras[camera].enabled:
|
||||||
has_enabled_camera = True
|
has_enabled_camera = True
|
||||||
|
else:
|
||||||
|
# flag camera as offline when it is disabled
|
||||||
|
previews[camera].flag_offline(now)
|
||||||
|
|
||||||
if now - last_update > 1:
|
if offline_time > 1:
|
||||||
# last camera update was more than one second ago
|
# last camera update was more than 1 second ago
|
||||||
# need to send empty data to updaters because current
|
# need to send empty data to birdseye because current
|
||||||
# frame is now out of date
|
# frame is now out of date
|
||||||
frame = get_blank_yuv_frame(
|
if birdseye and offline_time < 10:
|
||||||
config.cameras[camera].detect.width,
|
# we only need to send blank frames to birdseye at the beginning of a camera being offline
|
||||||
config.cameras[camera].detect.height,
|
birdseye.write_data(
|
||||||
)
|
camera,
|
||||||
|
[],
|
||||||
if birdseye:
|
[],
|
||||||
birdseye.write_data(camera, [], [], now, frame)
|
now,
|
||||||
|
get_blank_yuv_frame(
|
||||||
previews[camera].write_data([], [], now, frame)
|
config.cameras[camera].detect.width,
|
||||||
|
config.cameras[camera].detect.height,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if not has_enabled_camera and birdseye:
|
if not has_enabled_camera and birdseye:
|
||||||
birdseye.all_cameras_disabled()
|
birdseye.all_cameras_disabled()
|
||||||
@ -170,6 +178,12 @@ def output_frames(
|
|||||||
else:
|
else:
|
||||||
failed_frame_requests[camera] = 0
|
failed_frame_requests[camera] = 0
|
||||||
|
|
||||||
|
# send frames for low fps recording
|
||||||
|
preview_recorders[camera].write_data(
|
||||||
|
current_tracked_objects, motion_boxes, frame_time, frame
|
||||||
|
)
|
||||||
|
preview_write_times[camera] = frame_time
|
||||||
|
|
||||||
# send camera frame to ffmpeg process if websockets are connected
|
# send camera frame to ffmpeg process if websockets are connected
|
||||||
if any(
|
if any(
|
||||||
ws.environ["PATH_INFO"].endswith(camera) for ws in websocket_server.manager
|
ws.environ["PATH_INFO"].endswith(camera) for ws in websocket_server.manager
|
||||||
@ -193,11 +207,6 @@ def output_frames(
|
|||||||
frame,
|
frame,
|
||||||
)
|
)
|
||||||
|
|
||||||
# send frames for low fps recording
|
|
||||||
preview_recorders[camera].write_data(
|
|
||||||
current_tracked_objects, motion_boxes, frame_time, frame
|
|
||||||
)
|
|
||||||
preview_write_times[camera] = frame_time
|
|
||||||
frame_manager.close(frame_name)
|
frame_manager.close(frame_name)
|
||||||
|
|
||||||
move_preview_frames("clips")
|
move_preview_frames("clips")
|
||||||
|
@ -23,7 +23,7 @@ from frigate.ffmpeg_presets import (
|
|||||||
)
|
)
|
||||||
from frigate.models import Previews
|
from frigate.models import Previews
|
||||||
from frigate.object_processing import TrackedObject
|
from frigate.object_processing import TrackedObject
|
||||||
from frigate.util.image import copy_yuv_to_position, get_yuv_crop
|
from frigate.util.image import copy_yuv_to_position, get_blank_yuv_frame, get_yuv_crop
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -153,6 +153,7 @@ class PreviewRecorder:
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.start_time = 0
|
self.start_time = 0
|
||||||
self.last_output_time = 0
|
self.last_output_time = 0
|
||||||
|
self.offline = False
|
||||||
self.output_frames = []
|
self.output_frames = []
|
||||||
|
|
||||||
if config.detect.width > config.detect.height:
|
if config.detect.width > config.detect.height:
|
||||||
@ -241,6 +242,17 @@ class PreviewRecorder:
|
|||||||
self.last_output_time = ts
|
self.last_output_time = ts
|
||||||
self.output_frames.append(ts)
|
self.output_frames.append(ts)
|
||||||
|
|
||||||
|
def reset_frame_cache(self, frame_time: float) -> None:
|
||||||
|
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: list[float] = []
|
||||||
|
|
||||||
def should_write_frame(
|
def should_write_frame(
|
||||||
self,
|
self,
|
||||||
current_tracked_objects: list[dict[str, any]],
|
current_tracked_objects: list[dict[str, any]],
|
||||||
@ -307,7 +319,9 @@ class PreviewRecorder:
|
|||||||
motion_boxes: list[list[int]],
|
motion_boxes: list[list[int]],
|
||||||
frame_time: float,
|
frame_time: float,
|
||||||
frame: np.ndarray,
|
frame: np.ndarray,
|
||||||
) -> bool:
|
) -> None:
|
||||||
|
self.offline = False
|
||||||
|
|
||||||
# check for updated record config
|
# check for updated record config
|
||||||
_, updated_record_config = self.config_subscriber.check_for_update()
|
_, updated_record_config = self.config_subscriber.check_for_update()
|
||||||
|
|
||||||
@ -319,7 +333,7 @@ class PreviewRecorder:
|
|||||||
self.start_time = frame_time
|
self.start_time = frame_time
|
||||||
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)
|
||||||
return False
|
return
|
||||||
|
|
||||||
# check if PREVIEW clip should be generated and cached frames reset
|
# check if PREVIEW clip should be generated and cached frames reset
|
||||||
if frame_time >= self.segment_end:
|
if frame_time >= self.segment_end:
|
||||||
@ -340,32 +354,35 @@ class PreviewRecorder:
|
|||||||
f"Not saving preview for {self.config.name} because there are no saved frames."
|
f"Not saving preview for {self.config.name} because there are no saved frames."
|
||||||
)
|
)
|
||||||
|
|
||||||
# reset frame cache
|
self.reset_frame_cache(frame_time)
|
||||||
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: list[float] = []
|
|
||||||
|
|
||||||
# include first frame to ensure consistent duration
|
# include first frame to ensure consistent duration
|
||||||
if self.config.record.enabled:
|
if self.config.record.enabled:
|
||||||
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)
|
||||||
|
|
||||||
return True
|
return
|
||||||
elif self.should_write_frame(current_tracked_objects, motion_boxes, frame_time):
|
elif self.should_write_frame(current_tracked_objects, motion_boxes, frame_time):
|
||||||
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)
|
||||||
return False
|
return
|
||||||
|
|
||||||
def flag_offline(self, frame_time: float) -> None:
|
def flag_offline(self, frame_time: float) -> None:
|
||||||
|
if not self.offline:
|
||||||
|
self.write_frame_to_cache(
|
||||||
|
frame_time,
|
||||||
|
get_blank_yuv_frame(
|
||||||
|
self.config.detect.width, self.config.detect.height
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.offline = True
|
||||||
|
|
||||||
# check if PREVIEW clip should be generated and cached frames reset
|
# check if PREVIEW clip should be generated and cached frames reset
|
||||||
if frame_time >= self.segment_end:
|
if frame_time >= self.segment_end:
|
||||||
if len(self.output_frames) == 0:
|
if len(self.output_frames) == 0:
|
||||||
|
# camera has been offline for entire hour
|
||||||
|
# we have no preview to create
|
||||||
|
self.reset_frame_cache(frame_time)
|
||||||
return
|
return
|
||||||
|
|
||||||
old_frame_path = get_cache_image_name(
|
old_frame_path = get_cache_image_name(
|
||||||
@ -382,16 +399,7 @@ class PreviewRecorder:
|
|||||||
self.requestor,
|
self.requestor,
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
# reset frame cache
|
self.reset_frame_cache(frame_time)
|
||||||
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()
|
||||||
|
Loading…
Reference in New Issue
Block a user