Merge branch 'dev' of https://github.com/blakeblackshear/frigate into improve-devcontainer

This commit is contained in:
Felipe Santos 2022-11-19 14:31:09 -03:00
commit 86283a95f9
11 changed files with 94 additions and 33 deletions

View File

@ -94,7 +94,9 @@ RUN --mount=type=bind,from=wheels,source=/wheels,target=/wheels \
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 \

View File

@ -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]
```

View File

@ -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://<frigate_host>:8554/<camera_name>`. 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://<frigate_host>:8554/<camera_name>`. 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
```

View File

@ -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()
@ -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()

View File

@ -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 (
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")

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -1,2 +1,2 @@
pylint == 2.13.*
black == 22.3.*
pylint == 2.15.*
black == 22.10.*

View File

@ -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.*

View File

@ -159,14 +159,14 @@ export default function Debug() {
{cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped)
</Td>
<Td>{cpu_usages[cameras[camera]['pid']]['cpu']}%</Td>
<Td>{cpu_usages[cameras[camera]['pid']]['cpu']}%</Td>
<Td>{cpu_usages[cameras[camera]['pid']]['mem']}%</Td>
</Tr>
<Tr key="ffmpeg" index="2">
<Td>ffmpeg</Td>
<Td>{cameras[camera]['ffmpeg_pid']}</Td>
<Td>{cameras[camera]['camera_fps']}</Td>
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]['cpu']}%</Td>
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]['cpu']}%</Td>
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]['mem']}%</Td>
</Tr>
</Tbody>
</Table>