mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-09 16:22:48 +03:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1d1748fb3 |
@@ -30,6 +30,9 @@ from frigate.util.image import (
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Minimum seconds to hold the current camera before switching to a different one
|
||||
CAMERA_HOLD_SECONDS = 5
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class BirdseyeActivity:
|
||||
@@ -496,8 +499,11 @@ class BirdsEyeFrameManager:
|
||||
if max_cameras:
|
||||
now = datetime.datetime.now().timestamp()
|
||||
|
||||
if len(active_cameras) == max_cameras and now - self.last_refresh_time < 10:
|
||||
# don't refresh cameras too often
|
||||
if (
|
||||
len(self.active_cameras) == max_cameras
|
||||
and len(active_cameras) >= max_cameras
|
||||
and now - self.last_refresh_time < CAMERA_HOLD_SECONDS
|
||||
):
|
||||
active_cameras = self.active_cameras
|
||||
else:
|
||||
limited_active_cameras = sorted(
|
||||
|
||||
@@ -500,6 +500,80 @@ class TestBirdseyeLiveActivity(unittest.TestCase):
|
||||
assert self.manager.active_cameras == {"front"}
|
||||
|
||||
|
||||
class TestBirdseyeCameraHold(unittest.TestCase):
|
||||
"""Test that CAMERA_HOLD_SECONDS prevents rapid camera switching."""
|
||||
|
||||
def setUp(self):
|
||||
config = {
|
||||
"mqtt": {"enabled": False},
|
||||
"birdseye": {
|
||||
"enabled": True,
|
||||
"modes": ["motion"],
|
||||
"inactivity_threshold": 30,
|
||||
},
|
||||
"cameras": {
|
||||
camera: {
|
||||
"ffmpeg": {
|
||||
"inputs": [
|
||||
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
|
||||
]
|
||||
},
|
||||
"detect": {"height": 1080, "width": 1920, "fps": 5},
|
||||
}
|
||||
for camera in ("back", "front")
|
||||
},
|
||||
}
|
||||
self.config = FrigateConfig(**config)
|
||||
self.manager = BirdsEyeFrameManager(self.config, mp.Event())
|
||||
|
||||
for camera_data in self.manager.cameras.values():
|
||||
camera_data["current_frame"] = None
|
||||
camera_data["current_frame_time"] = 100.0
|
||||
camera_data["last_active_frame"] = 0.0
|
||||
camera_data["live_active"] = False
|
||||
|
||||
def test_max_cameras_cooldown_applies_when_more_active_than_max(self):
|
||||
"""The max_cameras cooldown should apply even when more cameras are active than max."""
|
||||
self.config.birdseye.layout.max_cameras = 1
|
||||
self.manager.cameras["front"]["last_active_frame"] = 95.0
|
||||
self.manager.cameras["front"]["current_frame_time"] = 100.0
|
||||
self.manager.update_frame()
|
||||
assert "front" in self.manager.active_cameras
|
||||
|
||||
self.manager.cameras["back"]["last_active_frame"] = 99.0
|
||||
self.manager.cameras["back"]["current_frame_time"] = 100.0
|
||||
self.manager.update_frame()
|
||||
|
||||
assert "front" in self.manager.active_cameras
|
||||
|
||||
def test_camera_count_change_ignores_hold(self):
|
||||
"""Adding a camera (count change) should not be blocked by the hold period."""
|
||||
self.manager.cameras["front"]["last_active_frame"] = 95.0
|
||||
self.manager.update_frame()
|
||||
assert self.manager.active_cameras == {"front"}
|
||||
|
||||
self.manager.cameras["back"]["last_active_frame"] = 99.0
|
||||
self.manager.cameras["back"]["current_frame_time"] = 100.0
|
||||
self.manager.cameras["front"]["last_active_frame"] = 95.0
|
||||
self.manager.update_frame()
|
||||
|
||||
assert self.manager.active_cameras == {"front", "back"}
|
||||
|
||||
def test_max_cameras_count_increase_not_blocked_by_hold(self):
|
||||
"""With max_cameras=2 showing 1, a second active camera must appear immediately."""
|
||||
self.config.birdseye.layout.max_cameras = 2
|
||||
self.manager.cameras["front"]["last_active_frame"] = 95.0
|
||||
self.manager.cameras["front"]["current_frame_time"] = 100.0
|
||||
self.manager.update_frame()
|
||||
assert self.manager.active_cameras == {"front"}
|
||||
|
||||
self.manager.cameras["back"]["last_active_frame"] = 99.0
|
||||
self.manager.cameras["back"]["current_frame_time"] = 100.0
|
||||
self.manager.update_frame()
|
||||
|
||||
assert self.manager.active_cameras == {"front", "back"}
|
||||
|
||||
|
||||
class TestBirdseyeModePayload(unittest.TestCase):
|
||||
"""Test the MQTT payload contract for Birdseye activity modes."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user