From 410cea71f1f1821466858aa518e8dd12b6a92178 Mon Sep 17 00:00:00 2001 From: herostrat Date: Fri, 18 Nov 2022 13:33:10 +0100 Subject: [PATCH 1/6] Easy Python Updates (#4424) * Update black requirement from ==22.3.* to ==22.10.* Updates the requirements on [black](https://github.com/psf/black) to permit the latest version. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.3.0...22.10.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:development ... Signed-off-by: dependabot[bot] * Update pylint requirement from ==2.13.* to ==2.15.* Updates the requirements on [pylint](https://github.com/PyCQA/pylint) to permit the latest version. - [Release notes](https://github.com/PyCQA/pylint/releases) - [Commits](https://github.com/PyCQA/pylint/compare/v2.13.0...v2.15.5) --- updated-dependencies: - dependency-name: pylint dependency-type: direct:development ... Signed-off-by: dependabot[bot] * Update types-requests requirement from ==2.27.* to ==2.28.* Updates the requirements on [types-requests](https://github.com/python/typeshed) to permit the latest version. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update pyyaml requirement from ==6.0.* to ==6.0 Updates the requirements on [pyyaml](https://github.com/yaml/pyyaml) to permit the latest version. - [Release notes](https://github.com/yaml/pyyaml/releases) - [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES) - [Commits](https://github.com/yaml/pyyaml/compare/6.0b1...6.0) --- updated-dependencies: - dependency-name: pyyaml dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-dev.txt | 4 ++-- requirements-wheels.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5cba885d9..dbc8f87f4 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,2 @@ -pylint == 2.13.* -black == 22.3.* +pylint == 2.15.* +black == 22.10.* diff --git a/requirements-wheels.txt b/requirements-wheels.txt index 2dd2356cc..7df0be770 100644 --- a/requirements-wheels.txt +++ b/requirements-wheels.txt @@ -10,10 +10,10 @@ peewee == 3.14.* peewee_migrate == 1.4.* psutil == 5.9.* pydantic == 1.9.* -PyYAML == 6.0.* +PyYAML == 6.0 types-PyYAML == 6.0.* requests == 2.27.* -types-requests == 2.27.* +types-requests == 2.28.* scipy == 1.8.* setproctitle == 1.2.* ws4py == 0.5.* From 53045de1c268a17d08afc37fbc4fc30aea2f1d40 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sat, 19 Nov 2022 06:14:22 -0700 Subject: [PATCH 2/6] Update hardware_acceleration.md (#4436) --- docs/docs/configuration/hardware_acceleration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/configuration/hardware_acceleration.md b/docs/docs/configuration/hardware_acceleration.md index 54be32c1c..67df7ca60 100644 --- a/docs/docs/configuration/hardware_acceleration.md +++ b/docs/docs/configuration/hardware_acceleration.md @@ -58,6 +58,7 @@ services: devices: - driver: nvidia device_ids: ['0'] # this is only needed when using multiple GPUs + count: 1 # number of GPUs capabilities: [gpu] ``` From ad3f6fc5514761e4078002c32dfe2aa2c84b9ac4 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sat, 19 Nov 2022 06:14:54 -0700 Subject: [PATCH 3/6] Fix mixed params (#4432) --- web/src/routes/Debug.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/routes/Debug.jsx b/web/src/routes/Debug.jsx index 3c8c8a8ff..f96b40b03 100644 --- a/web/src/routes/Debug.jsx +++ b/web/src/routes/Debug.jsx @@ -159,14 +159,14 @@ export default function Debug() { {cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped) {cpu_usages[cameras[camera]['pid']]['cpu']}% - {cpu_usages[cameras[camera]['pid']]['cpu']}% + {cpu_usages[cameras[camera]['pid']]['mem']}% ffmpeg {cameras[camera]['ffmpeg_pid']} {cameras[camera]['camera_fps']} {cpu_usages[cameras[camera]['ffmpeg_pid']]['cpu']}% - {cpu_usages[cameras[camera]['ffmpeg_pid']]['cpu']}% + {cpu_usages[cameras[camera]['ffmpeg_pid']]['mem']}% From a04fa105efec5eaeaac06d67142743727fbce3a2 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sat, 19 Nov 2022 06:15:20 -0700 Subject: [PATCH 4/6] Fix internal restream error and add docs for reducing camera connections (#4431) * Start restream before detection * Add docs explaining how to reduce connections to the camera * Fix typos for consistency * Add link to other part of doc for readability --- docs/docs/configuration/restream.md | 44 ++++++++++++++++++++++++++++- frigate/app.py | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/docs/docs/configuration/restream.md b/docs/docs/configuration/restream.md index b11f0ca92..1f21e8fdc 100644 --- a/docs/docs/configuration/restream.md +++ b/docs/docs/configuration/restream.md @@ -5,8 +5,50 @@ title: Restream ### RTSP -Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp://:8554/`. Port 8554 must be open. This allows you to use a video feed for detection in frigate and Home Assistant live view at the same time without having to make two separate connections to the camera. The video feed is copied from the original video feed directly to avoid re-encoding. This feed does not include any annotation by Frigate. +Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp://:8554/`. Port 8554 must be open. [This allows you to use a video feed for detection in frigate and Home Assistant live view at the same time without having to make two separate connections to the camera](#reduce-connections-to-camera). The video feed is copied from the original video feed directly to avoid re-encoding. This feed does not include any annotation by Frigate. ### RTMP (Deprecated) In previous Frigate versions RTMP was used for re-streaming. RTMP has disadvantages however including being incompatible with H.265, high bitrates, and certain audio codecs. RTMP is deprecated and it is recommended to move to the new restream role. + +## Reduce Connections To Camera + +Some cameras only support one active connection or you may just want to have a single connection open to the camera. The RTSP restream allows this to be possible. + +### With Single Stream + +One connection is made to the camera. One for the restream, `detect` and `record` connect to the restream. + +```yaml +cameras: + test_cam: + ffmpeg: + inputs: + - path: rtsp://localhost:8554/test_cam # <--- the name here must match the name of the camera + roles: + - record + - detect + - path: rtsp://192.168.1.5:554/live0 # <--- 1 connection to camera stream + roles: + - restream +``` + +### With Sub Stream + +Two connections are made to the camera. One for the sub stream, one for the restream, `record` connects to the restream. + +```yaml +cameras: + test_cam: + ffmpeg: + inputs: + - path: rtsp://localhost:8554/test_cam # <--- the name here must match the name of the camera + roles: + - record + - path: rtsp://192.168.1.5:554/stream # <--- camera high res stream + roles: + - restream + - path: rtsp://192.168.1.5:554/substream # <--- camera sub stream + roles: + - detect +``` diff --git a/frigate/app.py b/frigate/app.py index 0f8acce66..16cbf8735 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -355,6 +355,7 @@ class FrigateApp: print(e) self.log_process.terminate() sys.exit(1) + self.init_restream() self.start_detectors() self.start_video_output_processor() self.start_detected_frames_processor() @@ -362,7 +363,6 @@ class FrigateApp: self.start_camera_capture_processes() self.init_stats() self.init_web_server() - self.init_restream() self.start_mqtt_relay() self.start_event_processor() self.start_event_cleanup() From 2e81c94d8e9cfe06a7363d57b6e166c51b1b9754 Mon Sep 17 00:00:00 2001 From: herostrat Date: Sat, 19 Nov 2022 14:16:33 +0100 Subject: [PATCH 5/6] Typing Part 3: events.py (#3352) * Typing: events.py * Remove unused variable * Fix return Any from return statement Not all elements from the event dict are sure to be something that can be evaluated See e.g.: https://github.com/python/mypy/issues/5697 * Sort out Event disambiguity There was a name collision of multiprocessing Event type and frigate events Co-authored-by: Sebastian Englbrecht --- frigate/app.py | 6 +++--- frigate/events.py | 47 +++++++++++++++++++++++++++++---------------- frigate/mypy.ini | 3 +++ frigate/stats.py | 4 ++-- frigate/watchdog.py | 4 ++-- 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/frigate/app.py b/frigate/app.py index 16cbf8735..f72093546 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -1,7 +1,7 @@ import logging import multiprocessing as mp from multiprocessing.queues import Queue -from multiprocessing.synchronize import Event +from multiprocessing.synchronize import Event as MpEvent import os import signal import sys @@ -38,10 +38,10 @@ logger = logging.getLogger(__name__) class FrigateApp: def __init__(self) -> None: - self.stop_event: Event = mp.Event() + self.stop_event: MpEvent = mp.Event() self.detection_queue: Queue = mp.Queue() self.detectors: dict[str, ObjectDetectProcess] = {} - self.detection_out_events: dict[str, Event] = {} + self.detection_out_events: dict[str, MpEvent] = {} self.detection_shms: list[mp.shared_memory.SharedMemory] = [] self.log_queue: Queue = mp.Queue() self.plus_api = PlusApi() diff --git a/frigate/events.py b/frigate/events.py index 2dfe40f3e..5f30f8633 100644 --- a/frigate/events.py +++ b/frigate/events.py @@ -11,43 +11,55 @@ from peewee import fn from frigate.config import EventsConfig, FrigateConfig, RecordConfig from frigate.const import CLIPS_DIR from frigate.models import Event +from frigate.types import CameraMetricsTypes + +from multiprocessing.queues import Queue +from multiprocessing.synchronize import Event as MpEvent +from typing import Dict logger = logging.getLogger(__name__) -def should_insert_db(prev_event, current_event): +def should_insert_db(prev_event: Event, current_event: Event) -> bool: """If current event has new clip or snapshot.""" return (not prev_event["has_clip"] and not prev_event["has_snapshot"]) and ( current_event["has_clip"] or current_event["has_snapshot"] ) -def should_update_db(prev_event, current_event): +def should_update_db(prev_event: Event, current_event: Event) -> bool: """If current_event has updated fields and (clip or snapshot).""" - return (current_event["has_clip"] or current_event["has_snapshot"]) and ( - prev_event["top_score"] != current_event["top_score"] - or prev_event["entered_zones"] != current_event["entered_zones"] - or prev_event["thumbnail"] != current_event["thumbnail"] - or prev_event["has_clip"] != current_event["has_clip"] - or prev_event["has_snapshot"] != current_event["has_snapshot"] - ) + if current_event["has_clip"] or current_event["has_snapshot"]: + if ( + prev_event["top_score"] != current_event["top_score"] + or prev_event["entered_zones"] != current_event["entered_zones"] + or prev_event["thumbnail"] != current_event["thumbnail"] + or prev_event["has_clip"] != current_event["has_clip"] + or prev_event["has_snapshot"] != current_event["has_snapshot"] + ): + return True + return False class EventProcessor(threading.Thread): def __init__( - self, config, camera_processes, event_queue, event_processed_queue, stop_event + self, + config: FrigateConfig, + camera_processes: dict[str, CameraMetricsTypes], + event_queue: Queue, + event_processed_queue: Queue, + stop_event: MpEvent, ): threading.Thread.__init__(self) self.name = "event_processor" self.config = config 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.events_in_process: Dict[str, Event] = {} self.stop_event = stop_event - def run(self): + def run(self) -> None: # set an end_time on events without an end_time on startup Event.update(end_time=Event.start_time + 30).where( Event.end_time == None @@ -147,14 +159,15 @@ class EventProcessor(threading.Thread): class EventCleanup(threading.Thread): - def __init__(self, config: FrigateConfig, stop_event): + def __init__(self, config: FrigateConfig, stop_event: MpEvent): threading.Thread.__init__(self) self.name = "event_cleanup" self.config = config self.stop_event = stop_event self.camera_keys = list(self.config.cameras.keys()) - def expire(self, media_type): + def expire(self, media_type: str) -> None: + # TODO: Refactor media_type to enum ## Expire events from unlisted cameras based on the global config if media_type == "clips": retain_config = self.config.record.events.retain @@ -253,7 +266,7 @@ class EventCleanup(threading.Thread): ) update_query.execute() - def purge_duplicates(self): + def purge_duplicates(self) -> None: duplicate_query = """with grouped_events as ( select id, label, @@ -287,7 +300,7 @@ class EventCleanup(threading.Thread): .execute() ) - def run(self): + def run(self) -> None: # only expire events every 5 minutes while not self.stop_event.wait(300): self.expire("clips") diff --git a/frigate/mypy.ini b/frigate/mypy.ini index c789241d0..4769ab96a 100644 --- a/frigate/mypy.ini +++ b/frigate/mypy.ini @@ -34,6 +34,9 @@ disallow_untyped_calls = false [mypy-frigate.const] ignore_errors = false +[mypy-frigate.events] +ignore_errors = false + [mypy-frigate.log] ignore_errors = false diff --git a/frigate/stats.py b/frigate/stats.py index d4899bb6d..62c8eeec6 100644 --- a/frigate/stats.py +++ b/frigate/stats.py @@ -8,7 +8,7 @@ import os import requests from typing import Optional, Any from paho.mqtt.client import Client -from multiprocessing.synchronize import Event +from multiprocessing.synchronize import Event as MpEvent from frigate.config import FrigateConfig from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR @@ -148,7 +148,7 @@ class StatsEmitter(threading.Thread): stats_tracking: StatsTrackingTypes, mqtt_client: Client, topic_prefix: str, - stop_event: Event, + stop_event: MpEvent, ): threading.Thread.__init__(self) self.name = "frigate_stats_emitter" diff --git a/frigate/watchdog.py b/frigate/watchdog.py index 4316dc316..96ba2e371 100644 --- a/frigate/watchdog.py +++ b/frigate/watchdog.py @@ -7,13 +7,13 @@ import signal from frigate.object_detection import ObjectDetectProcess from frigate.util import restart_frigate -from multiprocessing.synchronize import Event +from multiprocessing.synchronize import Event as MpEvent logger = logging.getLogger(__name__) class FrigateWatchdog(threading.Thread): - def __init__(self, detectors: dict[str, ObjectDetectProcess], stop_event: Event): + def __init__(self, detectors: dict[str, ObjectDetectProcess], stop_event: MpEvent): threading.Thread.__init__(self) self.name = "frigate_watchdog" self.detectors = detectors From a46c37018f3cd1ed15dc15ed47e91999fe1920c2 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sat, 19 Nov 2022 06:21:43 -0700 Subject: [PATCH 6/6] Using testing repo for hwaccel dependencies (#4368) --- docker/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 37b639520..fb7f09da0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -92,7 +92,9 @@ RUN apt-get -qq update \ fi \ # arch specific packages && if [ "${TARGETARCH}" = "amd64" ]; then \ - apt-get -qq install --no-install-recommends --no-install-suggests -y \ + echo 'deb http://deb.debian.org/debian testing main non-free' >> /etc/apt/sources.list.d/deb.list \ + && apt-get -qq update \ + && apt-get -qq install --no-install-recommends --no-install-suggests -y \ mesa-va-drivers libva-drm2 intel-media-va-driver-non-free i965-va-driver libmfx1; \ fi \ && if [ "${TARGETARCH}" = "arm64" ]; then \