Skip to content

Commit

Permalink
Round robin SHM management (blakeblackshear#15027)
Browse files Browse the repository at this point in the history
* Output frame name to frames processor

* Finish implementing round robin

* Formatting
  • Loading branch information
NickM-27 authored Nov 16, 2024
1 parent f9c1600 commit 45e9030
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 97 deletions.
23 changes: 13 additions & 10 deletions frigate/object_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,17 +233,18 @@ def finished(self, obj_id):
def on(self, event_type: str, callback: Callable[[dict], None]):
self.callbacks[event_type].append(callback)

def update(self, frame_time, current_detections, motion_boxes, regions):
# get the new frame
frame_id = f"{self.name}{frame_time}"

def update(
self,
frame_name: str,
frame_time: float,
current_detections: dict[str, dict[str, any]],
motion_boxes: list[tuple[int, int, int, int]],
regions: list[tuple[int, int, int, int]],
):
current_frame = self.frame_manager.get(
frame_id, self.camera_config.frame_shape_yuv
frame_name, self.camera_config.frame_shape_yuv
)

if current_frame is None:
logger.debug(f"Failed to get frame {frame_id} from SHM")

tracked_objects = self.tracked_objects.copy()
current_ids = set(current_detections.keys())
previous_ids = set(tracked_objects.keys())
Expand Down Expand Up @@ -477,7 +478,7 @@ def update(self, frame_time, current_detections, motion_boxes, regions):
if self.previous_frame_id is not None:
self.frame_manager.close(self.previous_frame_id)

self.previous_frame_id = frame_id
self.previous_frame_id = frame_name


class TrackedObjectProcessor(threading.Thread):
Expand Down Expand Up @@ -798,6 +799,7 @@ def run(self):
try:
(
camera,
frame_name,
frame_time,
current_tracked_objects,
motion_boxes,
Expand All @@ -809,7 +811,7 @@ def run(self):
camera_state = self.camera_states[camera]

camera_state.update(
frame_time, current_tracked_objects, motion_boxes, regions
frame_name, frame_time, current_tracked_objects, motion_boxes, regions
)

self.update_mqtt_motion(camera, frame_time, motion_boxes)
Expand All @@ -822,6 +824,7 @@ def run(self):
self.detection_publisher.publish(
(
camera,
frame_name,
frame_time,
tracked_objects,
motion_boxes,
Expand Down
35 changes: 17 additions & 18 deletions frigate/output/birdseye.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,10 @@ class BirdsEyeFrameManager:
def __init__(
self,
config: FrigateConfig,
frame_manager: SharedMemoryFrameManager,
stop_event: mp.Event,
):
self.config = config
self.mode = config.birdseye.mode
self.frame_manager = frame_manager
width, height = get_canvas_shape(config.birdseye.width, config.birdseye.height)
self.frame_shape = (height, width)
self.yuv_shape = (height * 3 // 2, width)
Expand Down Expand Up @@ -351,18 +349,13 @@ def clear_frame(self):
logger.debug("Clearing the birdseye frame")
self.frame[:] = self.blank_frame

def copy_to_position(self, position, camera=None, frame_time=None):
def copy_to_position(self, position, camera=None, frame: np.ndarray = None):
if camera is None:
frame = None
channel_dims = None
else:
frame_id = f"{camera}{frame_time}"
frame = self.frame_manager.get(
frame_id, self.config.cameras[camera].frame_shape_yuv
)

if frame is None:
logger.debug(f"Unable to copy frame {camera}{frame_time} to birdseye.")
logger.debug(f"Unable to copy frame {camera} to birdseye.")
return

channel_dims = self.cameras[camera]["channel_dims"]
Expand All @@ -375,8 +368,6 @@ def copy_to_position(self, position, camera=None, frame_time=None):
channel_dims,
)

self.frame_manager.close(frame_id)

def camera_active(self, mode, object_box_count, motion_box_count):
if mode == BirdseyeModeEnum.continuous:
return True
Expand All @@ -387,7 +378,7 @@ def camera_active(self, mode, object_box_count, motion_box_count):
if mode == BirdseyeModeEnum.objects and object_box_count > 0:
return True

def update_frame(self):
def update_frame(self, frame: np.ndarray):
"""Update to a new frame for birdseye."""

# determine how many cameras are tracking objects within the last inactivity_threshold seconds
Expand Down Expand Up @@ -524,7 +515,9 @@ def update_frame(self):
for row in self.camera_layout:
for position in row:
self.copy_to_position(
position[1], position[0], self.cameras[position[0]]["current_frame"]
position[1],
position[0],
frame,
)

return True
Expand Down Expand Up @@ -672,7 +665,14 @@ def map_layout(camera_layout: list[list[any]], row_height: int):
else:
return standard_candidate_layout

def update(self, camera, object_count, motion_count, frame_time, frame) -> bool:
def update(
self,
camera: str,
object_count: int,
motion_count: int,
frame_time: float,
frame: np.ndarray,
) -> bool:
# don't process if birdseye is disabled for this camera
camera_config = self.config.cameras[camera].birdseye

Expand Down Expand Up @@ -700,7 +700,7 @@ def update(self, camera, object_count, motion_count, frame_time, frame) -> bool:
return False

try:
updated_frame = self.update_frame()
updated_frame = self.update_frame(frame)
except Exception:
updated_frame = False
self.active_cameras = []
Expand Down Expand Up @@ -737,12 +737,11 @@ def __init__(
self.broadcaster = BroadcastThread(
"birdseye", self.converter, websocket_server, stop_event
)
frame_manager = SharedMemoryFrameManager()
self.birdseye_manager = BirdsEyeFrameManager(config, frame_manager, stop_event)
self.birdseye_manager = BirdsEyeFrameManager(config, stop_event)
self.config_subscriber = ConfigSubscriber("config/birdseye/")

if config.birdseye.restream:
self.birdseye_buffer = frame_manager.create(
self.birdseye_buffer = SharedMemoryFrameManager().create(
"birdseye",
self.birdseye_manager.yuv_shape[0] * self.birdseye_manager.yuv_shape[1],
)
Expand Down
17 changes: 8 additions & 9 deletions frigate/output/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,17 @@ def receiveSignal(signalNumber, frame):

(
camera,
frame_name,
frame_time,
current_tracked_objects,
motion_boxes,
regions,
_,
) = data

frame_id = f"{camera}{frame_time}"

frame = frame_manager.get(frame_id, config.cameras[camera].frame_shape_yuv)
frame = frame_manager.get(frame_name, config.cameras[camera].frame_shape_yuv)

if frame is None:
logger.debug(f"Failed to get frame {frame_id} from SHM")
logger.debug(f"Failed to get frame {frame_name} from SHM")
failed_frame_requests[camera] = failed_frame_requests.get(camera, 0) + 1

if failed_frame_requests[camera] > config.cameras[camera].detect.fps:
Expand Down Expand Up @@ -152,7 +151,7 @@ def receiveSignal(signalNumber, frame):
preview_recorders[camera].flag_offline(frame_time)
preview_write_times[camera] = frame_time

frame_manager.close(frame_id)
frame_manager.close(frame_name)

move_preview_frames("clips")

Expand All @@ -164,15 +163,15 @@ def receiveSignal(signalNumber, frame):

(
camera,
frame_name,
frame_time,
current_tracked_objects,
motion_boxes,
regions,
) = data

frame_id = f"{camera}{frame_time}"
frame = frame_manager.get(frame_id, config.cameras[camera].frame_shape_yuv)
frame_manager.close(frame_id)
frame = frame_manager.get(frame_name, config.cameras[camera].frame_shape_yuv)
frame_manager.close(frame_name)

detection_subscriber.stop()

Expand Down
13 changes: 9 additions & 4 deletions frigate/ptz/autotrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ def __init__(self, config: CameraConfig, ptz_metrics: PTZMetrics) -> None:
self.ptz_metrics.reset.set()
logger.debug(f"{config.name}: Motion estimator init")

def motion_estimator(self, detections, frame_time, camera):
def motion_estimator(
self,
detections: list[dict[str, any]],
frame_name: str,
frame_time: float,
camera: str,
):
# If we've just started up or returned to our preset, reset motion estimator for new tracking session
if self.ptz_metrics.reset.is_set():
self.ptz_metrics.reset.clear()
Expand Down Expand Up @@ -92,9 +98,8 @@ def motion_estimator(self, detections, frame_time, camera):
f"{camera}: Motion estimator running - frame time: {frame_time}"
)

frame_id = f"{camera}{frame_time}"
yuv_frame = self.frame_manager.get(
frame_id, self.camera_config.frame_shape_yuv
frame_name, self.camera_config.frame_shape_yuv
)

if yuv_frame is None:
Expand Down Expand Up @@ -136,7 +141,7 @@ def motion_estimator(self, detections, frame_time, camera):
except Exception:
pass

self.frame_manager.close(frame_id)
self.frame_manager.close(frame_name)

return self.coord_transformations

Expand Down
1 change: 1 addition & 0 deletions frigate/record/maintainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ def run(self) -> None:
if topic == DetectionTypeEnum.video:
(
camera,
_,
frame_time,
current_tracked_objects,
motion_boxes,
Expand Down
32 changes: 17 additions & 15 deletions frigate/review/maintainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def _publish_segment_end(
def update_existing_segment(
self,
segment: PendingReviewSegment,
frame_name: str,
frame_time: float,
objects: list[TrackedObject],
) -> None:
Expand Down Expand Up @@ -292,36 +293,34 @@ def update_existing_segment(

if should_update:
try:
frame_id = f"{camera_config.name}{frame_time}"
yuv_frame = self.frame_manager.get(
frame_id, camera_config.frame_shape_yuv
frame_name, camera_config.frame_shape_yuv
)

if yuv_frame is None:
logger.debug(f"Failed to get frame {frame_id} from SHM")
logger.debug(f"Failed to get frame {frame_name} from SHM")
return

self._publish_segment_update(
segment, camera_config, yuv_frame, active_objects, prev_data
)
self.frame_manager.close(frame_id)
self.frame_manager.close(frame_name)
except FileNotFoundError:
return

if not has_activity:
if not segment.has_frame:
try:
frame_id = f"{camera_config.name}{frame_time}"
yuv_frame = self.frame_manager.get(
frame_id, camera_config.frame_shape_yuv
frame_name, camera_config.frame_shape_yuv
)

if yuv_frame is None:
logger.debug(f"Failed to get frame {frame_id} from SHM")
logger.debug(f"Failed to get frame {frame_name} from SHM")
return

segment.save_full_frame(camera_config, yuv_frame)
self.frame_manager.close(frame_id)
self.frame_manager.close(frame_name)
self._publish_segment_update(
segment, camera_config, None, [], prev_data
)
Expand All @@ -338,6 +337,7 @@ def update_existing_segment(
def check_if_new_segment(
self,
camera: str,
frame_name: str,
frame_time: float,
objects: list[TrackedObject],
) -> None:
Expand Down Expand Up @@ -414,19 +414,18 @@ def check_if_new_segment(
)

try:
frame_id = f"{camera_config.name}{frame_time}"
yuv_frame = self.frame_manager.get(
frame_id, camera_config.frame_shape_yuv
frame_name, camera_config.frame_shape_yuv
)

if yuv_frame is None:
logger.debug(f"Failed to get frame {frame_id} from SHM")
logger.debug(f"Failed to get frame {frame_name} from SHM")
return

self.active_review_segments[camera].update_frame(
camera_config, yuv_frame, active_objects
)
self.frame_manager.close(frame_id)
self.frame_manager.close(frame_name)
self._publish_segment_start(self.active_review_segments[camera])
except FileNotFoundError:
return
Expand Down Expand Up @@ -454,16 +453,17 @@ def run(self) -> None:
if topic == DetectionTypeEnum.video:
(
camera,
frame_name,
frame_time,
current_tracked_objects,
motion_boxes,
regions,
_,
_,
) = data
elif topic == DetectionTypeEnum.audio:
(
camera,
frame_time,
dBFS,
_,
audio_detections,
) = data
elif topic == DetectionTypeEnum.api:
Expand All @@ -488,6 +488,7 @@ def run(self) -> None:
if topic == DetectionTypeEnum.video:
self.update_existing_segment(
current_segment,
frame_name,
frame_time,
current_tracked_objects,
)
Expand Down Expand Up @@ -538,6 +539,7 @@ def run(self) -> None:
if topic == DetectionTypeEnum.video:
self.check_if_new_segment(
camera,
frame_name,
frame_time,
current_tracked_objects,
)
Expand Down
4 changes: 3 additions & 1 deletion frigate/track/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ def __init__(self, config: DetectConfig) -> None:
pass

@abstractmethod
def match_and_update(self, frame_time: float, detections) -> None:
def match_and_update(
self, frame_name: str, frame_time: float, detections: list[dict[str, any]]
) -> None:
pass
2 changes: 1 addition & 1 deletion frigate/track/centroid_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def update(self, id, new_obj):

self.tracked_objects[id].update(new_obj)

def update_frame_times(self, frame_time):
def update_frame_times(self, frame_name, frame_time):
for id in list(self.tracked_objects.keys()):
self.tracked_objects[id]["frame_time"] = frame_time
self.tracked_objects[id]["motionless_count"] += 1
Expand Down
Loading

0 comments on commit 45e9030

Please sign in to comment.