mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-03 01:35:22 +03:00
Merge branch 'dev' of https://github.com/blakeblackshear/frigate into improve-devcontainer
This commit is contained in:
commit
86283a95f9
@ -94,7 +94,9 @@ RUN --mount=type=bind,from=wheels,source=/wheels,target=/wheels \
|
|||||||
fi \
|
fi \
|
||||||
# arch specific packages
|
# arch specific packages
|
||||||
&& if [ "${TARGETARCH}" = "amd64" ]; then \
|
&& 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; \
|
mesa-va-drivers libva-drm2 intel-media-va-driver-non-free i965-va-driver libmfx1; \
|
||||||
fi \
|
fi \
|
||||||
&& if [ "${TARGETARCH}" = "arm64" ]; then \
|
&& if [ "${TARGETARCH}" = "arm64" ]; then \
|
||||||
|
|||||||
@ -58,6 +58,7 @@ services:
|
|||||||
devices:
|
devices:
|
||||||
- driver: nvidia
|
- driver: nvidia
|
||||||
device_ids: ['0'] # this is only needed when using multiple GPUs
|
device_ids: ['0'] # this is only needed when using multiple GPUs
|
||||||
|
count: 1 # number of GPUs
|
||||||
capabilities: [gpu]
|
capabilities: [gpu]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,50 @@ title: Restream
|
|||||||
|
|
||||||
### RTSP
|
### 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)
|
### 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.
|
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
|
||||||
|
```
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
from multiprocessing.queues import Queue
|
from multiprocessing.queues import Queue
|
||||||
from multiprocessing.synchronize import Event
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
@ -38,10 +38,10 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class FrigateApp:
|
class FrigateApp:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.stop_event: Event = mp.Event()
|
self.stop_event: MpEvent = mp.Event()
|
||||||
self.detection_queue: Queue = mp.Queue()
|
self.detection_queue: Queue = mp.Queue()
|
||||||
self.detectors: dict[str, ObjectDetectProcess] = {}
|
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.detection_shms: list[mp.shared_memory.SharedMemory] = []
|
||||||
self.log_queue: Queue = mp.Queue()
|
self.log_queue: Queue = mp.Queue()
|
||||||
self.plus_api = PlusApi()
|
self.plus_api = PlusApi()
|
||||||
@ -355,6 +355,7 @@ class FrigateApp:
|
|||||||
print(e)
|
print(e)
|
||||||
self.log_process.terminate()
|
self.log_process.terminate()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
self.init_restream()
|
||||||
self.start_detectors()
|
self.start_detectors()
|
||||||
self.start_video_output_processor()
|
self.start_video_output_processor()
|
||||||
self.start_detected_frames_processor()
|
self.start_detected_frames_processor()
|
||||||
@ -362,7 +363,6 @@ class FrigateApp:
|
|||||||
self.start_camera_capture_processes()
|
self.start_camera_capture_processes()
|
||||||
self.init_stats()
|
self.init_stats()
|
||||||
self.init_web_server()
|
self.init_web_server()
|
||||||
self.init_restream()
|
|
||||||
self.start_mqtt_relay()
|
self.start_mqtt_relay()
|
||||||
self.start_event_processor()
|
self.start_event_processor()
|
||||||
self.start_event_cleanup()
|
self.start_event_cleanup()
|
||||||
|
|||||||
@ -11,43 +11,55 @@ from peewee import fn
|
|||||||
from frigate.config import EventsConfig, FrigateConfig, RecordConfig
|
from frigate.config import EventsConfig, FrigateConfig, RecordConfig
|
||||||
from frigate.const import CLIPS_DIR
|
from frigate.const import CLIPS_DIR
|
||||||
from frigate.models import Event
|
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__)
|
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."""
|
"""If current event has new clip or snapshot."""
|
||||||
return (not prev_event["has_clip"] and not prev_event["has_snapshot"]) and (
|
return (not prev_event["has_clip"] and not prev_event["has_snapshot"]) and (
|
||||||
current_event["has_clip"] or current_event["has_snapshot"]
|
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)."""
|
"""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"]:
|
||||||
prev_event["top_score"] != current_event["top_score"]
|
if (
|
||||||
or prev_event["entered_zones"] != current_event["entered_zones"]
|
prev_event["top_score"] != current_event["top_score"]
|
||||||
or prev_event["thumbnail"] != current_event["thumbnail"]
|
or prev_event["entered_zones"] != current_event["entered_zones"]
|
||||||
or prev_event["has_clip"] != current_event["has_clip"]
|
or prev_event["thumbnail"] != current_event["thumbnail"]
|
||||||
or prev_event["has_snapshot"] != current_event["has_snapshot"]
|
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):
|
class EventProcessor(threading.Thread):
|
||||||
def __init__(
|
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)
|
threading.Thread.__init__(self)
|
||||||
self.name = "event_processor"
|
self.name = "event_processor"
|
||||||
self.config = config
|
self.config = config
|
||||||
self.camera_processes = camera_processes
|
self.camera_processes = camera_processes
|
||||||
self.cached_clips = {}
|
|
||||||
self.event_queue = event_queue
|
self.event_queue = event_queue
|
||||||
self.event_processed_queue = event_processed_queue
|
self.event_processed_queue = event_processed_queue
|
||||||
self.events_in_process = {}
|
self.events_in_process: Dict[str, Event] = {}
|
||||||
self.stop_event = stop_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
|
# set an end_time on events without an end_time on startup
|
||||||
Event.update(end_time=Event.start_time + 30).where(
|
Event.update(end_time=Event.start_time + 30).where(
|
||||||
Event.end_time == None
|
Event.end_time == None
|
||||||
@ -147,14 +159,15 @@ class EventProcessor(threading.Thread):
|
|||||||
|
|
||||||
|
|
||||||
class EventCleanup(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)
|
threading.Thread.__init__(self)
|
||||||
self.name = "event_cleanup"
|
self.name = "event_cleanup"
|
||||||
self.config = config
|
self.config = config
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
self.camera_keys = list(self.config.cameras.keys())
|
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
|
## Expire events from unlisted cameras based on the global config
|
||||||
if media_type == "clips":
|
if media_type == "clips":
|
||||||
retain_config = self.config.record.events.retain
|
retain_config = self.config.record.events.retain
|
||||||
@ -253,7 +266,7 @@ class EventCleanup(threading.Thread):
|
|||||||
)
|
)
|
||||||
update_query.execute()
|
update_query.execute()
|
||||||
|
|
||||||
def purge_duplicates(self):
|
def purge_duplicates(self) -> None:
|
||||||
duplicate_query = """with grouped_events as (
|
duplicate_query = """with grouped_events as (
|
||||||
select id,
|
select id,
|
||||||
label,
|
label,
|
||||||
@ -287,7 +300,7 @@ class EventCleanup(threading.Thread):
|
|||||||
.execute()
|
.execute()
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
# only expire events every 5 minutes
|
# only expire events every 5 minutes
|
||||||
while not self.stop_event.wait(300):
|
while not self.stop_event.wait(300):
|
||||||
self.expire("clips")
|
self.expire("clips")
|
||||||
|
|||||||
@ -34,6 +34,9 @@ disallow_untyped_calls = false
|
|||||||
[mypy-frigate.const]
|
[mypy-frigate.const]
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
|
|
||||||
|
[mypy-frigate.events]
|
||||||
|
ignore_errors = false
|
||||||
|
|
||||||
[mypy-frigate.log]
|
[mypy-frigate.log]
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import os
|
|||||||
import requests
|
import requests
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
from paho.mqtt.client import Client
|
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.config import FrigateConfig
|
||||||
from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR
|
from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR
|
||||||
@ -148,7 +148,7 @@ class StatsEmitter(threading.Thread):
|
|||||||
stats_tracking: StatsTrackingTypes,
|
stats_tracking: StatsTrackingTypes,
|
||||||
mqtt_client: Client,
|
mqtt_client: Client,
|
||||||
topic_prefix: str,
|
topic_prefix: str,
|
||||||
stop_event: Event,
|
stop_event: MpEvent,
|
||||||
):
|
):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.name = "frigate_stats_emitter"
|
self.name = "frigate_stats_emitter"
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import signal
|
|||||||
|
|
||||||
from frigate.object_detection import ObjectDetectProcess
|
from frigate.object_detection import ObjectDetectProcess
|
||||||
from frigate.util import restart_frigate
|
from frigate.util import restart_frigate
|
||||||
from multiprocessing.synchronize import Event
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FrigateWatchdog(threading.Thread):
|
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)
|
threading.Thread.__init__(self)
|
||||||
self.name = "frigate_watchdog"
|
self.name = "frigate_watchdog"
|
||||||
self.detectors = detectors
|
self.detectors = detectors
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
pylint == 2.13.*
|
pylint == 2.15.*
|
||||||
black == 22.3.*
|
black == 22.10.*
|
||||||
|
|||||||
@ -10,10 +10,10 @@ peewee == 3.14.*
|
|||||||
peewee_migrate == 1.4.*
|
peewee_migrate == 1.4.*
|
||||||
psutil == 5.9.*
|
psutil == 5.9.*
|
||||||
pydantic == 1.9.*
|
pydantic == 1.9.*
|
||||||
PyYAML == 6.0.*
|
PyYAML == 6.0
|
||||||
types-PyYAML == 6.0.*
|
types-PyYAML == 6.0.*
|
||||||
requests == 2.27.*
|
requests == 2.27.*
|
||||||
types-requests == 2.27.*
|
types-requests == 2.28.*
|
||||||
scipy == 1.8.*
|
scipy == 1.8.*
|
||||||
setproctitle == 1.2.*
|
setproctitle == 1.2.*
|
||||||
ws4py == 0.5.*
|
ws4py == 0.5.*
|
||||||
|
|||||||
@ -159,14 +159,14 @@ export default function Debug() {
|
|||||||
{cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped)
|
{cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped)
|
||||||
</Td>
|
</Td>
|
||||||
<Td>{cpu_usages[cameras[camera]['pid']]['cpu']}%</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>
|
||||||
<Tr key="ffmpeg" index="2">
|
<Tr key="ffmpeg" index="2">
|
||||||
<Td>ffmpeg</Td>
|
<Td>ffmpeg</Td>
|
||||||
<Td>{cameras[camera]['ffmpeg_pid']}</Td>
|
<Td>{cameras[camera]['ffmpeg_pid']}</Td>
|
||||||
<Td>{cameras[camera]['camera_fps']}</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']]['cpu']}%</Td>
|
<Td>{cpu_usages[cameras[camera]['ffmpeg_pid']]['mem']}%</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Tbody>
|
</Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user