diff --git a/frigate/app.py b/frigate/app.py index c15fd16df..15e4db119 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -55,8 +55,9 @@ class FrigateApp(): } def init_queues(self): - # Queue for clip processing + # Queues for clip processing self.event_queue = mp.Queue() + self.event_processed_queue = mp.Queue() # Queue for cameras to push tracked objects to self.detected_frames_queue = mp.Queue(maxsize=len(self.config.cameras.keys())*2) @@ -89,7 +90,7 @@ class FrigateApp(): def start_detected_frames_processor(self): self.detected_frames_processor = TrackedObjectProcessor(self.config, self.mqtt_client, self.config.mqtt.topic_prefix, - self.detected_frames_queue, self.event_queue, self.stop_event) + self.detected_frames_queue, self.event_queue, self.event_processed_queue, self.stop_event) self.detected_frames_processor.start() def start_camera_processors(self): @@ -112,7 +113,7 @@ class FrigateApp(): logger.info(f"Capture process started for {name}: {capture_process.pid}") def start_event_processor(self): - self.event_processor = EventProcessor(self.config, self.camera_metrics, self.event_queue, self.stop_event) + self.event_processor = EventProcessor(self.config, self.camera_metrics, self.event_queue, self.event_processed_queue, self.stop_event) self.event_processor.start() def start_event_cleanup(self): diff --git a/frigate/events.py b/frigate/events.py index cdb769073..251e4c9c9 100644 --- a/frigate/events.py +++ b/frigate/events.py @@ -19,7 +19,7 @@ from peewee import fn logger = logging.getLogger(__name__) class EventProcessor(threading.Thread): - def __init__(self, config, camera_processes, event_queue, stop_event): + def __init__(self, config, camera_processes, event_queue, event_processed_queue, stop_event): threading.Thread.__init__(self) self.name = 'event_processor' self.config = config @@ -28,6 +28,7 @@ class EventProcessor(threading.Thread): self.camera_processes = camera_processes self.cached_clips = {} self.event_queue = event_queue + self.event_processed_queue = event_processed_queue self.events_in_process = {} self.stop_event = stop_event @@ -190,6 +191,7 @@ class EventProcessor(threading.Thread): thumbnail=event_data['thumbnail'] ) del self.events_in_process[event_data['id']] + self.event_processed_queue.put((event_data['id'], camera)) class EventCleanup(threading.Thread): def __init__(self, config: FrigateConfig, stop_event): diff --git a/frigate/http.py b/frigate/http.py index d49f2f44d..59fe561de 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -14,6 +14,8 @@ from playhouse.shortcuts import model_to_dict from frigate.models import Event +logger = logging.getLogger(__name__) + bp = Blueprint('frigate', __name__) def create_app(frigate_config, database: SqliteDatabase, camera_metrics, detectors, detected_frames_processor): @@ -79,6 +81,17 @@ def event_snapshot(id): response.headers['Content-Type'] = 'image/jpg' return response except DoesNotExist: + # see if the object is currently being tracked + try: + for camera_state in current_app.detected_frames_processor.camera_states.values(): + if id in camera_state.tracked_objects: + tracked_obj = camera_state.tracked_objects.get(id) + if not tracked_obj is None: + response = make_response(tracked_obj.get_jpg_bytes()) + response.headers['Content-Type'] = 'image/jpg' + return response + except: + return "Event not found", 404 return "Event not found", 404 @bp.route('/events') diff --git a/frigate/object_processing.py b/frigate/object_processing.py index 266c3c1df..a03521ea1 100644 --- a/frigate/object_processing.py +++ b/frigate/object_processing.py @@ -279,6 +279,9 @@ class CameraState(): return frame_copy + def finished(self, obj_id): + del self.tracked_objects[obj_id] + def on(self, event_type: str, callback: Callable[[Dict], None]): self.callbacks[event_type].append(callback) @@ -317,10 +320,10 @@ class CameraState(): for id in removed_ids: # publish events to mqtt removed_obj = self.tracked_objects[id] - removed_obj.obj_data['end_time'] = frame_time - for c in self.callbacks['end']: - c(self.name, removed_obj) - del self.tracked_objects[id] + if not 'end_time' in removed_obj.obj_data: + removed_obj.obj_data['end_time'] = frame_time + for c in self.callbacks['end']: + c(self.name, removed_obj) # TODO: can i switch to looking this up and only changing when an event ends? # maybe make an api endpoint that pulls the thumbnail from the file system? @@ -382,7 +385,7 @@ class CameraState(): self.previous_frame_id = frame_id class TrackedObjectProcessor(threading.Thread): - def __init__(self, config: FrigateConfig, client, topic_prefix, tracked_objects_queue, event_queue, stop_event): + def __init__(self, config: FrigateConfig, client, topic_prefix, tracked_objects_queue, event_queue, event_processed_queue, stop_event): threading.Thread.__init__(self) self.name = "detected_frames_processor" self.config = config @@ -390,6 +393,7 @@ class TrackedObjectProcessor(threading.Thread): self.topic_prefix = topic_prefix self.tracked_objects_queue = tracked_objects_queue self.event_queue = event_queue + self.event_processed_queue = event_processed_queue self.stop_event = stop_event self.camera_states: Dict[str, CameraState] = {} self.frame_manager = SharedMemoryFrameManager() @@ -480,3 +484,8 @@ class TrackedObjectProcessor(threading.Thread): self.client.publish(f"{self.topic_prefix}/{zone}/{label}", 'ON', retain=False) elif previous_state == True and new_state == False: self.client.publish(f"{self.topic_prefix}/{zone}/{label}", 'OFF', retain=False) + + # cleanup event finished queue + while not self.event_processed_queue.empty(): + event_id, camera = self.event_processed_queue.get() + self.camera_states[camera].finished(event_id)