mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-07 11:21:11 +03:00
Miscellaneous fixes (#22913)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
* add log when probing detect stream on startup when users don't explicitly set detect.width and detect.height, we probe for them. sometimes the probe hangs (camera doesn't support UDP, like some Reolinks), so this log message will make that clearer * add faq about probing detect stream * fix stuck activity ring when tracked object transitions to stationary * drop cache segments past retain cutoff regardless of retention mode * add maintainer test
This commit is contained in:
@@ -730,6 +730,9 @@ class FrigateConfig(FrigateBaseModel):
|
||||
)
|
||||
|
||||
if need_detect_dimensions:
|
||||
logger.info(
|
||||
f"detect.width and detect.height not set for {camera_config.name}, probing detect stream to determine resolution."
|
||||
)
|
||||
stream_info = {"width": 0, "height": 0, "fourcc": None}
|
||||
try:
|
||||
stream_info = stream_info_retriever.get_stream_info(
|
||||
|
||||
@@ -464,10 +464,12 @@ class RecordingMaintainer(threading.Thread):
|
||||
self.drop_segment(cache_path)
|
||||
return None
|
||||
|
||||
# if it doesn't overlap with an review item, go ahead and drop the segment
|
||||
# if it ends more than the configured pre_capture for the camera
|
||||
# BUT only if continuous/motion is NOT enabled (otherwise wait for processing)
|
||||
elif highest is None:
|
||||
# if it doesn't overlap with a review item, drop the segment once it
|
||||
# ends more than event_pre_capture before the most recently processed
|
||||
# frame. at this point we've already decided not to keep it for
|
||||
# continuous/motion retention (either disabled or segment_stats said
|
||||
# discard), so waiting longer just fills the cache.
|
||||
else:
|
||||
camera_info = self.object_recordings_info[camera]
|
||||
most_recently_processed_frame_time = (
|
||||
camera_info[-1][0] if len(camera_info) > 0 else 0
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import datetime
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
@@ -74,6 +75,46 @@ class TestMaintainer(unittest.IsolatedAsyncioTestCase):
|
||||
f"Expected a single warning for unexpected files, got {len(matching)}",
|
||||
)
|
||||
|
||||
async def test_drops_quiet_segment_when_only_motion_retention(self):
|
||||
# Regression: when motion retention is enabled but a segment has no
|
||||
# motion and no review overlaps it, the segment must still be dropped.
|
||||
# Otherwise it sits in cache forever, accumulates, and triggers the
|
||||
# "Unable to keep up with recording segments in cache" warning every
|
||||
# ~10s as the overflow trim in move_files discards the oldest one.
|
||||
config = MagicMock(spec=FrigateConfig)
|
||||
|
||||
camera_config = MagicMock()
|
||||
camera_config.record.enabled = True
|
||||
camera_config.record.continuous.days = 0
|
||||
camera_config.record.motion.days = 1
|
||||
camera_config.record.event_pre_capture = 5
|
||||
config.cameras = {"test_cam": camera_config}
|
||||
|
||||
stop_event = MagicMock()
|
||||
maintainer = RecordingMaintainer(config, stop_event)
|
||||
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
start_time = now - datetime.timedelta(seconds=20)
|
||||
end_time = now - datetime.timedelta(seconds=10)
|
||||
cache_path = "/tmp/cache/test_cam@20260417150000+0000.mp4"
|
||||
|
||||
maintainer.end_time_cache = {cache_path: (end_time, 10.0)}
|
||||
# Single processed frame well past end_time with no motion/objects.
|
||||
maintainer.object_recordings_info["test_cam"] = [(now.timestamp(), [], [], [])]
|
||||
maintainer.audio_recordings_info["test_cam"] = []
|
||||
|
||||
maintainer.drop_segment = MagicMock()
|
||||
maintainer.recordings_publisher = MagicMock()
|
||||
|
||||
result = await maintainer.validate_and_move_segment(
|
||||
"test_cam",
|
||||
reviews=[],
|
||||
recording={"start_time": start_time, "cache_path": cache_path},
|
||||
)
|
||||
|
||||
self.assertIsNone(result)
|
||||
maintainer.drop_segment.assert_called_once_with(cache_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user