2021-05-22 18:28:15 -04:00
from __future__ import annotations
2020-11-03 08:15:58 -06:00
import json
2020-12-21 08:03:27 -06:00
import logging
2020-11-03 08:15:58 -06:00
import os
2021-08-16 08:02:04 -05:00
from enum import Enum
2021-06-24 01:45:27 -04:00
from typing import Dict , List , Optional , Tuple , Union
2020-11-03 08:15:58 -06:00
import matplotlib.pyplot as plt
import numpy as np
2021-08-16 08:02:04 -05:00
import yaml
2022-12-15 07:12:52 -06:00
from pydantic import BaseModel , Extra , Field , validator , parse_obj_as
2021-06-24 01:45:27 -04:00
from pydantic.fields import PrivateAttr
2020-11-01 06:17:44 -06:00
2022-11-02 06:00:54 -06:00
from frigate.const import (
BASE_DIR ,
CACHE_DIR ,
REGEX_CAMERA_NAME ,
YAML_EXT ,
)
from frigate.util import (
create_mask ,
deep_merge ,
2022-11-28 20:48:11 -07:00
get_ffmpeg_arg_list ,
2022-11-02 06:00:54 -06:00
escape_special_characters ,
2022-11-08 18:47:45 -07:00
load_config_with_no_duplicates ,
2022-11-02 06:00:54 -06:00
load_labels ,
)
2022-11-28 20:48:11 -07:00
from frigate.ffmpeg_presets import (
2022-12-30 09:56:52 -07:00
parse_preset_hardware_acceleration_decode ,
parse_preset_hardware_acceleration_scale ,
2022-11-28 20:48:11 -07:00
parse_preset_input ,
parse_preset_output_record ,
parse_preset_output_rtmp ,
)
2022-12-15 07:12:52 -06:00
from frigate.detectors import (
PixelFormatEnum ,
InputTensorEnum ,
ModelConfig ,
DetectorConfig ,
)
2022-11-30 19:53:45 -03:00
from frigate.version import VERSION
2020-12-01 07:22:23 -06:00
2022-12-15 07:12:52 -06:00
2020-12-21 08:03:27 -06:00
logger = logging . getLogger ( __name__ )
2021-06-15 22:19:49 +02:00
# TODO: Identify what the default format to display timestamps is
DEFAULT_TIME_FORMAT = "%m/ %d /%Y %H:%M:%S"
# German Style:
# DEFAULT_TIME_FORMAT = "%d.%m.%Y %H:%M:%S"
2021-01-16 07:39:08 -06:00
2021-06-24 01:45:27 -04:00
FRIGATE_ENV_VARS = { k : v for k , v in os . environ . items () if k . startswith ( "FRIGATE_" )}
2021-06-15 22:19:49 +02:00
DEFAULT_TRACKED_OBJECTS = [ "person" ]
2021-08-16 07:39:20 -05:00
DEFAULT_DETECTORS = { "cpu" : { "type" : "cpu" }}
2020-11-01 06:17:44 -06:00
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class FrigateBaseModel ( BaseModel ):
class Config :
extra = Extra . forbid
2023-01-13 16:27:16 -07:00
class LiveModeEnum ( str , Enum ):
jsmpeg = "jsmpeg"
mse = "mse"
webrtc = "webrtc"
2022-02-27 08:04:12 -06:00
class UIConfig ( FrigateBaseModel ):
2023-01-13 16:27:16 -07:00
live_mode : LiveModeEnum = Field (
default = LiveModeEnum . mse , title = "Default Live Mode."
)
timezone : Optional [ str ] = Field ( title = "Override UI timezone." )
2022-02-27 08:04:12 -06:00
use_experimental : bool = Field ( default = False , title = "Experimental UI" )
2021-05-22 18:28:15 -04:00
2022-04-16 08:42:44 -05:00
2023-01-26 00:36:26 +00:00
class TelemetryConfig ( FrigateBaseModel ):
version_check : bool = Field ( default = True , title = "Enable latest version check." )
2021-09-04 16:56:01 -05:00
class MqttConfig ( FrigateBaseModel ):
2022-11-23 19:03:20 -07:00
enabled : bool = Field ( title = "Enable MQTT Communication." , default = True )
2023-01-03 18:24:53 -07:00
host : str = Field ( default = "" , title = "MQTT Host" )
2021-06-24 01:45:27 -04:00
port : int = Field ( default = 1883 , title = "MQTT Port" )
topic_prefix : str = Field ( default = "frigate" , title = "MQTT Topic Prefix" )
client_id : str = Field ( default = "frigate" , title = "MQTT Client ID" )
stats_interval : int = Field ( default = 60 , title = "MQTT Camera Stats Interval" )
user : Optional [ str ] = Field ( title = "MQTT Username" )
password : Optional [ str ] = Field ( title = "MQTT Password" )
tls_ca_certs : Optional [ str ] = Field ( title = "MQTT TLS CA Certificates" )
tls_client_cert : Optional [ str ] = Field ( title = "MQTT TLS Client Certificate" )
tls_client_key : Optional [ str ] = Field ( title = "MQTT TLS Client Key" )
tls_insecure : Optional [ bool ] = Field ( title = "MQTT TLS Insecure" )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
@validator ( "password" , pre = True , always = True )
def validate_password ( cls , v , values ):
if ( v is None ) != ( values [ "user" ] is None ):
raise ValueError ( "Password must be provided with username." )
2021-06-24 16:53:01 -04:00
return v
2021-05-22 18:28:15 -04:00
2021-12-11 09:22:44 -06:00
class RetainModeEnum ( str , Enum ):
all = "all"
motion = "motion"
active_objects = "active_objects"
2021-09-04 16:56:01 -05:00
class RetainConfig ( FrigateBaseModel ):
2021-09-20 18:59:16 -05:00
default : float = Field ( default = 10 , title = "Default retention period." )
2022-02-05 09:28:21 -06:00
mode : RetainModeEnum = Field ( default = RetainModeEnum . motion , title = "Retain mode." )
2021-09-20 18:59:16 -05:00
objects : Dict [ str , float ] = Field (
2021-06-24 01:45:27 -04:00
default_factory = dict , title = "Object retention period."
)
2020-11-23 08:25:46 -06:00
2021-05-22 18:28:15 -04:00
2021-09-08 08:02:26 -05:00
class EventsConfig ( FrigateBaseModel ):
pre_capture : int = Field ( default = 5 , title = "Seconds to retain before event starts." )
post_capture : int = Field ( default = 5 , title = "Seconds to retain after event ends." )
2021-07-09 16:14:16 -04:00
required_zones : List [ str ] = Field (
default_factory = list ,
2021-09-08 08:02:26 -05:00
title = "List of required zones to be entered in order to save the event." ,
2021-07-09 16:14:16 -04:00
)
objects : Optional [ List [ str ]] = Field (
2021-09-08 08:02:26 -05:00
title = "List of objects to be detected in order to save the event." ,
2021-07-09 16:14:16 -04:00
)
2021-06-24 01:45:27 -04:00
retain : RetainConfig = Field (
2021-09-08 08:02:26 -05:00
default_factory = RetainConfig , title = "Event retention settings."
2021-06-24 01:45:27 -04:00
)
2021-05-22 18:28:15 -04:00
2021-12-11 09:22:44 -06:00
class RecordRetainConfig ( FrigateBaseModel ):
days : float = Field ( default = 0 , title = "Default retention period." )
mode : RetainModeEnum = Field ( default = RetainModeEnum . all , title = "Retain mode." )
2021-09-04 16:56:01 -05:00
class RecordConfig ( FrigateBaseModel ):
2021-07-09 16:14:16 -04:00
enabled : bool = Field ( default = False , title = "Enable record on all cameras." )
2022-02-05 08:51:18 -06:00
expire_interval : int = Field (
default = 60 ,
title = "Number of minutes to wait between cleanup runs." ,
)
2021-12-11 09:22:44 -06:00
# deprecated - to be removed in a future version
retain_days : Optional [ float ] = Field ( title = "Recording retention period in days." )
retain : RecordRetainConfig = Field (
default_factory = RecordRetainConfig , title = "Record retention settings."
)
2021-09-08 08:02:26 -05:00
events : EventsConfig = Field (
default_factory = EventsConfig , title = "Event specific settings."
2021-07-09 16:14:16 -04:00
)
2021-09-04 16:56:01 -05:00
class MotionConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
threshold : int = Field (
default = 25 ,
title = "Motion detection threshold (1-255)." ,
ge = 1 ,
le = 255 ,
)
2022-03-10 07:43:12 -06:00
improve_contrast : bool = Field ( default = False , title = "Improve Contrast" )
2021-11-07 13:16:38 -06:00
contour_area : Optional [ int ] = Field ( default = 30 , title = "Contour Area" )
2021-06-24 01:45:27 -04:00
delta_alpha : float = Field ( default = 0.2 , title = "Delta Alpha" )
frame_alpha : float = Field ( default = 0.2 , title = "Frame Alpha" )
2021-11-07 13:16:38 -06:00
frame_height : Optional [ int ] = Field ( default = 50 , title = "Frame Height" )
2021-06-24 01:45:27 -04:00
mask : Union [ str , List [ str ]] = Field (
default = "" , title = "Coordinates polygon for the motion mask."
)
2022-05-15 06:03:33 -06:00
mqtt_off_delay : int = Field (
default = 30 ,
title = "Delay for updating MQTT with no motion detected." ,
)
2020-11-01 06:17:44 -06:00
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
class RuntimeMotionConfig ( MotionConfig ):
raw_mask : Union [ str , List [ str ]] = ""
mask : np . ndarray = None
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
frame_shape = config . get ( "frame_shape" , ( 1 , 1 ))
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
mask = config . get ( "mask" , "" )
config [ "raw_mask" ] = mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
if mask :
config [ "mask" ] = create_mask ( frame_shape , mask )
2021-05-22 18:28:15 -04:00
else :
2021-06-24 01:45:27 -04:00
empty_mask = np . zeros ( frame_shape , np . uint8 )
empty_mask [:] = 255
config [ "mask" ] = empty_mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
super () . __init__ ( ** config )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def dict ( self , ** kwargs ):
ret = super () . dict ( ** kwargs )
if "mask" in ret :
ret [ "mask" ] = ret [ "raw_mask" ]
ret . pop ( "raw_mask" )
return ret
class Config :
arbitrary_types_allowed = True
2021-09-04 16:56:01 -05:00
extra = Extra . ignore
2021-05-22 18:28:15 -04:00
2022-02-13 08:58:44 -06:00
class StationaryMaxFramesConfig ( FrigateBaseModel ):
default : Optional [ int ] = Field ( title = "Default max frames." , ge = 1 )
objects : Dict [ str , int ] = Field (
default_factory = dict , title = "Object specific max frames."
)
class StationaryConfig ( FrigateBaseModel ):
interval : Optional [ int ] = Field (
default = 0 ,
title = "Frame interval for checking stationary objects." ,
ge = 0 ,
)
threshold : Optional [ int ] = Field (
title = "Number of frames without a position change for an object to be considered stationary" ,
ge = 1 ,
)
max_frames : StationaryMaxFramesConfig = Field (
default_factory = StationaryMaxFramesConfig ,
title = "Max frames for stationary objects." ,
)
2021-09-04 16:56:01 -05:00
class DetectConfig ( FrigateBaseModel ):
2021-08-28 08:51:29 -05:00
height : int = Field ( default = 720 , title = "Height of the stream for the detect role." )
width : int = Field ( default = 1280 , title = "Width of the stream for the detect role." )
fps : int = Field (
default = 5 , title = "Number of frames per second to process through detection."
)
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = True , title = "Detection Enabled." )
2021-06-26 09:57:32 -05:00
max_disappeared : Optional [ int ] = Field (
2021-06-24 01:45:27 -04:00
title = "Maximum number of frames the object can dissapear before detection ends."
)
2022-02-13 08:58:44 -06:00
stationary : StationaryConfig = Field (
default_factory = StationaryConfig ,
title = "Stationary objects config." ,
2022-02-08 07:40:45 -06:00
)
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class FilterConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
min_area : int = Field (
default = 0 , title = "Minimum area of bounding box for object to be counted."
)
max_area : int = Field (
default = 24000000 , title = "Maximum area of bounding box for object to be counted."
)
2022-04-10 09:25:18 -04:00
min_ratio : float = Field (
default = 0 ,
title = "Minimum ratio of bounding box's width/height for object to be counted." ,
)
max_ratio : float = Field (
default = 24000000 ,
title = "Maximum ratio of bounding box's width/height for object to be counted." ,
)
2021-06-24 01:45:27 -04:00
threshold : float = Field (
default = 0.7 ,
title = "Average detection confidence threshold for object to be counted." ,
)
min_score : float = Field (
default = 0.5 , title = "Minimum detection confidence for object to be counted."
)
mask : Optional [ Union [ str , List [ str ]]] = Field (
title = "Detection area polygon mask for this filter configuration." ,
)
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
class RuntimeFilterConfig ( FilterConfig ):
2021-05-22 18:28:15 -04:00
mask : Optional [ np . ndarray ]
2021-06-24 01:45:27 -04:00
raw_mask : Optional [ Union [ str , List [ str ]]]
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
mask = config . get ( "mask" )
config [ "raw_mask" ] = mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
if mask is not None :
config [ "mask" ] = create_mask ( config . get ( "frame_shape" , ( 1 , 1 )), mask )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
super () . __init__ ( ** config )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def dict ( self , ** kwargs ):
ret = super () . dict ( ** kwargs )
if "mask" in ret :
ret [ "mask" ] = ret [ "raw_mask" ]
ret . pop ( "raw_mask" )
return ret
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
class Config :
arbitrary_types_allowed = True
2021-09-04 16:56:01 -05:00
extra = Extra . ignore
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
# this uses the base model because the color is an extra attribute
2021-06-24 01:45:27 -04:00
class ZoneConfig ( BaseModel ):
filters : Dict [ str , FilterConfig ] = Field (
default_factory = dict , title = "Zone filters."
)
coordinates : Union [ str , List [ str ]] = Field (
title = "Coordinates polygon for the defined zone."
)
2021-07-07 07:31:42 -05:00
objects : List [ str ] = Field (
default_factory = list ,
title = "List of objects that can trigger the zone." ,
)
2021-06-24 01:45:27 -04:00
_color : Optional [ Tuple [ int , int , int ]] = PrivateAttr ()
_contour : np . ndarray = PrivateAttr ()
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
@property
def color ( self ) -> Tuple [ int , int , int ]:
return self . _color
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
@property
def contour ( self ) -> np . ndarray :
return self . _contour
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
super () . __init__ ( ** config )
self . _color = config . get ( "color" , ( 0 , 0 , 0 ))
2021-05-22 18:28:15 -04:00
coordinates = config [ "coordinates" ]
if isinstance ( coordinates , list ):
2021-06-24 01:45:27 -04:00
self . _contour = np . array (
2021-05-22 18:28:15 -04:00
[[ int ( p . split ( "," )[ 0 ]), int ( p . split ( "," )[ 1 ])] for p in coordinates ]
)
elif isinstance ( coordinates , str ):
points = coordinates . split ( "," )
2021-06-24 01:45:27 -04:00
self . _contour = np . array (
2021-05-22 18:28:15 -04:00
[[ int ( points [ i ]), int ( points [ i + 1 ])] for i in range ( 0 , len ( points ), 2 )]
)
else :
2021-06-24 01:45:27 -04:00
self . _contour = np . array ([])
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class ObjectConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
track : List [ str ] = Field ( default = DEFAULT_TRACKED_OBJECTS , title = "Objects to track." )
filters : Optional [ Dict [ str , FilterConfig ]] = Field ( title = "Object filters." )
mask : Union [ str , List [ str ]] = Field ( default = "" , title = "Object mask." )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
class BirdseyeModeEnum ( str , Enum ):
objects = "objects"
motion = "motion"
continuous = "continuous"
2021-09-04 16:56:01 -05:00
class BirdseyeConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = True , title = "Enable birdseye view." )
2023-01-16 16:50:35 -07:00
restream : bool = Field ( default = False , title = "Restream birdseye via RTSP." )
2021-06-24 01:45:27 -04:00
width : int = Field ( default = 1280 , title = "Birdseye width." )
height : int = Field ( default = 720 , title = "Birdseye height." )
2021-07-02 07:50:09 -05:00
quality : int = Field (
default = 8 ,
title = "Encoding quality." ,
ge = 1 ,
le = 31 ,
)
2021-06-24 01:45:27 -04:00
mode : BirdseyeModeEnum = Field (
default = BirdseyeModeEnum . objects , title = "Tracking mode."
2021-05-22 18:28:15 -04:00
)
2021-06-11 07:26:35 -05:00
2022-04-16 08:44:04 -05:00
# uses BaseModel because some global attributes are not available at the camera level
class BirdseyeCameraConfig ( BaseModel ):
2022-04-15 05:59:30 -06:00
enabled : bool = Field ( default = True , title = "Enable birdseye view for camera." )
mode : BirdseyeModeEnum = Field (
default = BirdseyeModeEnum . objects , title = "Tracking mode for camera."
)
2022-12-04 12:46:03 -03:00
FFMPEG_GLOBAL_ARGS_DEFAULT = [ "-hide_banner" , "-loglevel" , "warning" ]
2022-12-16 10:41:03 -03:00
FFMPEG_INPUT_ARGS_DEFAULT = "preset-rtsp-generic"
2021-02-17 07:23:32 -06:00
DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT = [ "-f" , "rawvideo" , "-pix_fmt" , "yuv420p" ]
2022-12-16 10:41:03 -03:00
RTMP_FFMPEG_OUTPUT_ARGS_DEFAULT = "preset-rtmp-generic"
RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT = "preset-record-generic"
2020-11-01 06:17:44 -06:00
2021-09-04 16:56:01 -05:00
class FfmpegOutputArgsConfig ( FrigateBaseModel ):
2021-06-24 16:45:15 -04:00
detect : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default = DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT ,
title = "Detect role FFmpeg output arguments." ,
)
2021-06-24 16:45:15 -04:00
record : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default = RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT ,
title = "Record role FFmpeg output arguments." ,
)
2021-06-24 16:45:15 -04:00
rtmp : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default = RTMP_FFMPEG_OUTPUT_ARGS_DEFAULT ,
title = "RTMP role FFmpeg output arguments." ,
)
2020-11-01 06:17:44 -06:00
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class FfmpegConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
global_args : Union [ str , List [ str ]] = Field (
default = FFMPEG_GLOBAL_ARGS_DEFAULT , title = "Global FFmpeg arguments."
)
hwaccel_args : Union [ str , List [ str ]] = Field (
default_factory = list , title = "FFmpeg hardware acceleration arguments."
)
input_args : Union [ str , List [ str ]] = Field (
default = FFMPEG_INPUT_ARGS_DEFAULT , title = "FFmpeg input arguments."
)
output_args : FfmpegOutputArgsConfig = Field (
default_factory = FfmpegOutputArgsConfig ,
title = "FFmpeg output arguments per role." ,
2021-02-17 07:23:32 -06:00
)
2020-11-01 06:17:44 -06:00
2021-02-17 07:23:32 -06:00
2021-08-28 08:16:25 -05:00
class CameraRoleEnum ( str , Enum ):
record = "record"
rtmp = "rtmp"
detect = "detect"
2021-09-04 16:56:01 -05:00
class CameraInput ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
path : str = Field ( title = "Camera input path." )
2021-08-28 08:16:25 -05:00
roles : List [ CameraRoleEnum ] = Field ( title = "Roles assigned to this input." )
2021-06-24 16:45:15 -04:00
global_args : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default_factory = list , title = "FFmpeg global arguments."
)
2021-06-24 16:45:15 -04:00
hwaccel_args : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default_factory = list , title = "FFmpeg hardware acceleration arguments."
)
2021-06-24 16:45:15 -04:00
input_args : Union [ str , List [ str ]] = Field (
default_factory = list , title = "FFmpeg input arguments."
)
2021-05-22 18:28:15 -04:00
2021-02-17 07:23:32 -06:00
2021-06-24 01:45:27 -04:00
class CameraFfmpegConfig ( FfmpegConfig ):
inputs : List [ CameraInput ] = Field ( title = "Camera inputs." )
2021-01-09 09:26:46 -08:00
2021-06-24 01:45:27 -04:00
@validator ( "inputs" )
def validate_roles ( cls , v ):
roles = [ role for i in v for role in i . roles ]
roles_set = set ( roles )
2021-01-09 09:26:46 -08:00
2021-06-24 01:45:27 -04:00
if len ( roles ) > len ( roles_set ):
raise ValueError ( "Each input role may only be used once." )
if not "detect" in roles :
raise ValueError ( "The detect role is required." )
return v
2021-02-17 07:23:32 -06:00
2021-09-04 16:56:01 -05:00
class SnapshotsConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = False , title = "Snapshots enabled." )
clean_copy : bool = Field (
default = True , title = "Create a clean copy of the snapshot image."
)
timestamp : bool = Field (
default = False , title = "Add a timestamp overlay on the snapshot."
)
bounding_box : bool = Field (
default = True , title = "Add a bounding box overlay on the snapshot."
)
crop : bool = Field ( default = False , title = "Crop the snapshot to the detected object." )
required_zones : List [ str ] = Field (
default_factory = list ,
title = "List of required zones to be entered in order to save a snapshot." ,
)
height : Optional [ int ] = Field ( title = "Snapshot image height." )
retain : RetainConfig = Field (
default_factory = RetainConfig , title = "Snapshot retention."
)
2021-07-02 07:47:03 -05:00
quality : int = Field (
default = 70 ,
title = "Quality of the encoded jpeg (0-100)." ,
ge = 0 ,
le = 100 ,
)
2021-06-15 22:19:49 +02:00
2021-09-04 16:56:01 -05:00
class ColorConfig ( FrigateBaseModel ):
2021-09-04 16:39:56 -05:00
red : int = Field ( default = 255 , ge = 0 , le = 255 , title = "Red" )
green : int = Field ( default = 255 , ge = 0 , le = 255 , title = "Green" )
blue : int = Field ( default = 255 , ge = 0 , le = 255 , title = "Blue" )
2020-11-29 15:55:53 -06:00
2021-02-17 07:23:32 -06:00
2021-09-04 16:34:48 -05:00
class TimestampPositionEnum ( str , Enum ):
tl = "tl"
tr = "tr"
bl = "bl"
br = "br"
class TimestampEffectEnum ( str , Enum ):
solid = "solid"
shadow = "shadow"
2021-09-04 16:56:01 -05:00
class TimestampStyleConfig ( FrigateBaseModel ):
2021-09-04 16:34:48 -05:00
position : TimestampPositionEnum = Field (
default = TimestampPositionEnum . tl , title = "Timestamp position."
)
2021-06-24 01:45:27 -04:00
format : str = Field ( default = DEFAULT_TIME_FORMAT , title = "Timestamp format." )
color : ColorConfig = Field ( default_factory = ColorConfig , title = "Timestamp color." )
thickness : int = Field ( default = 2 , title = "Timestamp thickness." )
2021-09-04 16:34:48 -05:00
effect : Optional [ TimestampEffectEnum ] = Field ( title = "Timestamp effect." )
2020-11-03 08:15:58 -06:00
2021-02-17 07:23:32 -06:00
2021-09-04 16:56:01 -05:00
class CameraMqttConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = True , title = "Send image over MQTT." )
timestamp : bool = Field ( default = True , title = "Add timestamp to MQTT image." )
bounding_box : bool = Field ( default = True , title = "Add bounding box to MQTT image." )
crop : bool = Field ( default = True , title = "Crop MQTT image to detected object." )
height : int = Field ( default = 270 , title = "MQTT image height." )
required_zones : List [ str ] = Field (
default_factory = list ,
title = "List of required zones to be entered in order to send the image." ,
)
2021-07-02 07:47:03 -05:00
quality : int = Field (
default = 70 ,
title = "Quality of the encoded jpeg (0-100)." ,
ge = 0 ,
le = 100 ,
)
2021-06-23 08:09:15 -05:00
2021-09-04 16:56:01 -05:00
class RtmpConfig ( FrigateBaseModel ):
2022-11-02 05:36:09 -06:00
enabled : bool = Field ( default = False , title = "RTMP restreaming enabled." )
2021-06-24 01:45:27 -04:00
2023-01-16 16:50:35 -07:00
class CameraLiveConfig ( FrigateBaseModel ):
stream_name : str = Field ( default = "" , title = "Name of restream to use as live view." )
height : int = Field ( default = 720 , title = "Live camera view height" )
quality : int = Field ( default = 8 , ge = 1 , le = 31 , title = "Live camera view quality" )
2022-11-02 05:36:09 -06:00
2023-01-16 16:50:35 -07:00
class RestreamConfig ( BaseModel ):
class Config :
extra = Extra . allow
2021-06-24 01:45:27 -04:00
2022-04-15 06:23:02 -06:00
class CameraUiConfig ( FrigateBaseModel ):
order : int = Field ( default = 0 , title = "Order of camera in UI." )
2022-04-16 08:42:44 -05:00
dashboard : bool = Field (
default = True , title = "Show this camera in Frigate dashboard UI."
)
2022-04-15 06:23:02 -06:00
2021-09-04 16:56:01 -05:00
class CameraConfig ( FrigateBaseModel ):
2022-11-02 06:00:54 -06:00
name : Optional [ str ] = Field ( title = "Camera name." , regex = REGEX_CAMERA_NAME )
2022-11-02 12:41:44 +01:00
enabled : bool = Field ( default = True , title = "Enable camera." )
2021-06-24 01:45:27 -04:00
ffmpeg : CameraFfmpegConfig = Field ( title = "FFmpeg configuration for the camera." )
best_image_timeout : int = Field (
default = 60 ,
title = "How long to wait for the image with the highest confidence score." ,
)
zones : Dict [ str , ZoneConfig ] = Field (
default_factory = dict , title = "Zone configuration."
)
record : RecordConfig = Field (
default_factory = RecordConfig , title = "Record configuration."
)
2021-09-03 05:03:36 -07:00
rtmp : RtmpConfig = Field (
default_factory = RtmpConfig , title = "RTMP restreaming configuration."
2021-06-24 01:45:27 -04:00
)
2023-01-16 16:50:35 -07:00
live : CameraLiveConfig = Field (
default_factory = CameraLiveConfig , title = "Live playback settings."
2021-08-28 09:14:00 -05:00
)
snapshots : SnapshotsConfig = Field (
default_factory = SnapshotsConfig , title = "Snapshot configuration."
2021-06-24 01:45:27 -04:00
)
mqtt : CameraMqttConfig = Field (
default_factory = CameraMqttConfig , title = "MQTT configuration."
)
objects : ObjectConfig = Field (
default_factory = ObjectConfig , title = "Object configuration."
)
motion : Optional [ MotionConfig ] = Field ( title = "Motion detection configuration." )
2021-08-28 09:14:00 -05:00
detect : DetectConfig = Field (
default_factory = DetectConfig , title = "Object detection configuration."
)
2022-04-15 06:23:02 -06:00
ui : CameraUiConfig = Field (
default_factory = CameraUiConfig , title = "Camera UI Modifications."
)
2022-04-15 05:59:30 -06:00
birdseye : BirdseyeCameraConfig = Field (
default_factory = BirdseyeCameraConfig , title = "Birdseye camera configuration."
)
2021-06-24 17:02:46 -04:00
timestamp_style : TimestampStyleConfig = Field (
default_factory = TimestampStyleConfig , title = "Timestamp style configuration."
2021-06-24 01:45:27 -04:00
)
2021-11-08 07:32:29 -06:00
_ffmpeg_cmds : List [ Dict [ str , List [ str ]]] = PrivateAttr ()
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
# Set zone colors
if "zones" in config :
colors = plt . cm . get_cmap ( "tab10" , len ( config [ "zones" ]))
config [ "zones" ] = {
name : { ** z , "color" : tuple ( round ( 255 * c ) for c in colors ( idx )[: 3 ])}
for idx , ( name , z ) in enumerate ( config [ "zones" ] . items ())
}
2021-10-16 07:46:39 -05:00
# add roles to the input if there is only one
if len ( config [ "ffmpeg" ][ "inputs" ]) == 1 :
2022-11-02 05:36:09 -06:00
has_rtmp = "rtmp" in config [ "ffmpeg" ][ "inputs" ][ 0 ] . get ( "roles" , [])
config [ "ffmpeg" ][ "inputs" ][ 0 ][ "roles" ] = [
"record" ,
"detect" ,
]
if has_rtmp :
config [ "ffmpeg" ][ "inputs" ][ 0 ][ "roles" ] . append ( "rtmp" )
2021-10-16 07:46:39 -05:00
2021-06-24 01:45:27 -04:00
super () . __init__ ( ** config )
2020-11-03 08:15:58 -06:00
2021-05-22 18:28:15 -04:00
@property
def frame_shape ( self ) -> Tuple [ int , int ]:
2021-08-14 14:18:35 -05:00
return self . detect . height , self . detect . width
2021-05-22 18:28:15 -04:00
@property
def frame_shape_yuv ( self ) -> Tuple [ int , int ]:
2021-08-14 14:18:35 -05:00
return self . detect . height * 3 // 2 , self . detect . width
2021-05-22 18:28:15 -04:00
@property
def ffmpeg_cmds ( self ) -> List [ Dict [ str , List [ str ]]]:
2021-11-08 07:32:29 -06:00
return self . _ffmpeg_cmds
2021-11-08 19:05:39 -06:00
def create_ffmpeg_cmds ( self ):
2022-01-14 16:31:25 +03:00
if "_ffmpeg_cmds" in self :
return
2021-05-22 18:28:15 -04:00
ffmpeg_cmds = []
for ffmpeg_input in self . ffmpeg . inputs :
2020-12-07 08:07:35 -06:00
ffmpeg_cmd = self . _get_ffmpeg_cmd ( ffmpeg_input )
if ffmpeg_cmd is None :
continue
2021-05-22 18:28:15 -04:00
ffmpeg_cmds . append ({ "roles" : ffmpeg_input . roles , "cmd" : ffmpeg_cmd })
2021-11-08 18:20:47 -06:00
self . _ffmpeg_cmds = ffmpeg_cmds
2020-11-03 08:15:58 -06:00
2021-06-24 01:45:27 -04:00
def _get_ffmpeg_cmd ( self , ffmpeg_input : CameraInput ):
2020-11-29 15:55:53 -06:00
ffmpeg_output_args = []
2021-02-17 07:23:32 -06:00
if "detect" in ffmpeg_input . roles :
2022-11-28 20:48:11 -07:00
detect_args = get_ffmpeg_arg_list ( self . ffmpeg . output_args . detect )
2022-12-30 09:56:52 -07:00
scale_detect_args = parse_preset_hardware_acceleration_scale (
ffmpeg_input . hwaccel_args or self . ffmpeg . hwaccel_args ,
detect_args ,
self . detect . fps ,
self . detect . width ,
self . detect . height ,
2021-08-14 14:18:35 -05:00
)
2022-12-30 09:56:52 -07:00
ffmpeg_output_args = scale_detect_args + ffmpeg_output_args + [ "pipe:" ]
2021-02-17 07:23:32 -06:00
if "rtmp" in ffmpeg_input . roles and self . rtmp . enabled :
2022-11-28 20:48:11 -07:00
rtmp_args = get_ffmpeg_arg_list (
parse_preset_output_rtmp ( self . ffmpeg . output_args . rtmp )
or self . ffmpeg . output_args . rtmp
2021-06-24 16:45:15 -04:00
)
2022-11-28 20:48:11 -07:00
2021-06-24 16:45:15 -04:00
ffmpeg_output_args = (
rtmp_args + [ f "rtmp://127.0.0.1/live/ { self . name } " ] + ffmpeg_output_args
2021-02-17 07:23:32 -06:00
)
2021-08-15 08:30:27 -05:00
if "record" in ffmpeg_input . roles and self . record . enabled :
2022-11-28 20:48:11 -07:00
record_args = get_ffmpeg_arg_list (
parse_preset_output_record ( self . ffmpeg . output_args . record )
or self . ffmpeg . output_args . record
2021-06-24 16:45:15 -04:00
)
2021-10-16 10:36:13 -05:00
2021-06-24 16:45:15 -04:00
ffmpeg_output_args = (
record_args
2021-10-22 19:17:55 -05:00
+ [ f " { os . path . join ( CACHE_DIR , self . name ) } -%Y%m%d%H%M%S.mp4" ]
2021-02-17 07:23:32 -06:00
+ ffmpeg_output_args
)
2021-01-09 09:26:46 -08:00
2020-12-07 08:07:35 -06:00
# if there arent any outputs enabled for this input
if len ( ffmpeg_output_args ) == 0 :
return None
2022-11-28 20:48:11 -07:00
global_args = get_ffmpeg_arg_list (
ffmpeg_input . global_args or self . ffmpeg . global_args
2021-06-24 16:45:15 -04:00
)
2022-11-28 20:48:11 -07:00
hwaccel_args = get_ffmpeg_arg_list (
2022-12-30 09:56:52 -07:00
parse_preset_hardware_acceleration_decode ( ffmpeg_input . hwaccel_args )
or ffmpeg_input . hwaccel_args
or parse_preset_hardware_acceleration_decode ( self . ffmpeg . hwaccel_args )
2022-11-28 20:48:11 -07:00
or self . ffmpeg . hwaccel_args
2021-06-24 16:45:15 -04:00
)
2022-11-28 20:48:11 -07:00
input_args = get_ffmpeg_arg_list (
2022-12-30 09:56:52 -07:00
parse_preset_input ( ffmpeg_input . input_args , self . detect . fps )
or ffmpeg_input . input_args
2022-11-28 20:48:11 -07:00
or parse_preset_input ( self . ffmpeg . input_args , self . detect . fps )
or self . ffmpeg . input_args
2021-06-24 16:45:15 -04:00
)
2021-02-17 07:23:32 -06:00
cmd = (
[ "ffmpeg" ]
2021-06-24 16:45:15 -04:00
+ global_args
+ hwaccel_args
+ input_args
2022-11-02 06:00:54 -06:00
+ [ "-i" , escape_special_characters ( ffmpeg_input . path )]
2021-02-17 07:23:32 -06:00
+ ffmpeg_output_args
)
2021-01-09 09:26:46 -08:00
2021-02-17 07:23:32 -06:00
return [ part for part in cmd if part != "" ]
2021-01-09 09:26:46 -08:00
2021-06-24 01:45:27 -04:00
2021-09-04 16:56:01 -05:00
class DatabaseConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
path : str = Field (
default = os . path . join ( BASE_DIR , "frigate.db" ), title = "Database path."
)
2020-11-17 21:11:19 -06:00
2021-06-24 01:45:27 -04:00
class LogLevelEnum ( str , Enum ):
debug = "debug"
info = "info"
warning = "warning"
error = "error"
critical = "critical"
2021-09-04 16:56:01 -05:00
class LoggerConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
default : LogLevelEnum = Field (
default = LogLevelEnum . info , title = "Default logging level."
)
logs : Dict [ str , LogLevelEnum ] = Field (
default_factory = dict , title = "Log level for specified processes."
)
2022-11-08 18:47:45 -07:00
def verify_config_roles ( camera_config : CameraConfig ) -> None :
"""Verify that roles are setup in the config correctly."""
assigned_roles = list (
set ([ r for i in camera_config . ffmpeg . inputs for r in i . roles ])
)
if camera_config . record . enabled and not "record" in assigned_roles :
raise ValueError (
f "Camera { camera_config . name } has record enabled, but record is not assigned to an input."
)
if camera_config . rtmp . enabled and not "rtmp" in assigned_roles :
raise ValueError (
f "Camera { camera_config . name } has rtmp enabled, but rtmp is not assigned to an input."
)
2023-01-16 16:50:35 -07:00
def verify_valid_live_stream_name (
frigate_config : FrigateConfig , camera_config : CameraConfig
) -> None :
"""Verify that a restream exists to use for live view."""
if (
camera_config . live . stream_name
not in frigate_config . go2rtc . dict () . get ( "streams" , {}) . keys ()
):
return ValueError (
f "No restream with name { camera_config . live . stream_name } exists for camera { camera_config . name } ."
2022-11-08 18:47:45 -07:00
)
def verify_old_retain_config ( camera_config : CameraConfig ) -> None :
"""Leave log if old retain_days is used."""
if not camera_config . record . retain_days is None :
logger . warning (
"The 'retain_days' config option has been DEPRECATED and will be removed in a future version. Please use the 'days' setting under 'retain'"
)
if camera_config . record . retain . days == 0 :
camera_config . record . retain . days = camera_config . record . retain_days
def verify_recording_retention ( camera_config : CameraConfig ) -> None :
"""Verify that recording retention modes are ranked correctly."""
rank_map = {
RetainModeEnum . all : 0 ,
RetainModeEnum . motion : 1 ,
RetainModeEnum . active_objects : 2 ,
}
if (
camera_config . record . retain . days != 0
and rank_map [ camera_config . record . retain . mode ]
> rank_map [ camera_config . record . events . retain . mode ]
):
logger . warning (
f " { camera_config . name } : Recording retention is configured for { camera_config . record . retain . mode } and event retention is configured for { camera_config . record . events . retain . mode } . The more restrictive retention policy will be applied."
)
2022-12-08 20:03:54 -07:00
def verify_recording_segments_setup_with_reasonable_time (
camera_config : CameraConfig ,
) -> None :
"""Verify that recording segments are setup and segment time is not greater than 60."""
record_args : list [ str ] = get_ffmpeg_arg_list (
camera_config . ffmpeg . output_args . record
)
2022-12-09 07:35:28 -07:00
if record_args [ 0 ] . startswith ( "preset" ):
return
2022-12-08 20:03:54 -07:00
seg_arg_index = record_args . index ( "-segment_time" )
if seg_arg_index < 0 :
raise ValueError (
f "Camera { camera_config . name } has no segment_time in recording output args, segment args are required for record."
)
if int ( record_args [ seg_arg_index + 1 ]) > 60 :
raise ValueError (
f "Camera { camera_config . name } has invalid segment_time output arg, segment_time must be 60 or less."
)
2022-11-08 18:47:45 -07:00
def verify_zone_objects_are_tracked ( camera_config : CameraConfig ) -> None :
"""Verify that user has not entered zone objects that are not in the tracking config."""
for zone_name , zone in camera_config . zones . items ():
for obj in zone . objects :
if obj not in camera_config . objects . track :
raise ValueError (
f "Zone { zone_name } is configured to track { obj } but that object type is not added to objects -> track."
)
2021-09-04 16:56:01 -05:00
class FrigateConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
mqtt : MqttConfig = Field ( title = "MQTT Configuration." )
database : DatabaseConfig = Field (
default_factory = DatabaseConfig , title = "Database configuration."
)
environment_vars : Dict [ str , str ] = Field (
default_factory = dict , title = "Frigate environment variables."
)
2022-02-27 08:04:12 -06:00
ui : UIConfig = Field ( default_factory = UIConfig , title = "UI configuration." )
2023-01-26 00:36:26 +00:00
telemetry : TelemetryConfig = Field (
default_factory = TelemetryConfig , title = "Telemetry configuration."
)
2021-06-24 01:45:27 -04:00
model : ModelConfig = Field (
default_factory = ModelConfig , title = "Detection model configuration."
)
detectors : Dict [ str , DetectorConfig ] = Field (
2022-12-15 07:12:52 -06:00
default = DEFAULT_DETECTORS ,
2021-06-24 01:45:27 -04:00
title = "Detector hardware configuration." ,
)
logger : LoggerConfig = Field (
default_factory = LoggerConfig , title = "Logging configuration."
)
record : RecordConfig = Field (
default_factory = RecordConfig , title = "Global record configuration."
)
snapshots : SnapshotsConfig = Field (
default_factory = SnapshotsConfig , title = "Global snapshots configuration."
)
2021-09-03 05:03:36 -07:00
rtmp : RtmpConfig = Field (
default_factory = RtmpConfig , title = "Global RTMP restreaming configuration."
)
2023-01-16 16:50:35 -07:00
live : CameraLiveConfig = Field (
default_factory = CameraLiveConfig , title = "Live playback settings."
)
go2rtc : RestreamConfig = Field (
2022-11-02 05:36:09 -06:00
default_factory = RestreamConfig , title = "Global restream configuration."
)
2021-06-24 01:45:27 -04:00
birdseye : BirdseyeConfig = Field (
default_factory = BirdseyeConfig , title = "Birdseye configuration."
)
ffmpeg : FfmpegConfig = Field (
default_factory = FfmpegConfig , title = "Global FFmpeg configuration."
)
objects : ObjectConfig = Field (
default_factory = ObjectConfig , title = "Global object configuration."
)
motion : Optional [ MotionConfig ] = Field (
title = "Global motion detection configuration."
)
2021-08-28 09:14:00 -05:00
detect : DetectConfig = Field (
default_factory = DetectConfig , title = "Global object tracking configuration."
2021-06-24 01:45:27 -04:00
)
cameras : Dict [ str , CameraConfig ] = Field ( title = "Camera configuration." )
2021-09-03 05:03:36 -07:00
timestamp_style : TimestampStyleConfig = Field (
default_factory = TimestampStyleConfig ,
title = "Global timestamp style configuration." ,
)
2021-06-24 01:45:27 -04:00
@property
def runtime_config ( self ) -> FrigateConfig :
"""Merge camera config with globals."""
config = self . copy ( deep = True )
2021-06-24 16:53:01 -04:00
2023-01-07 18:10:48 -05:00
# MQTT user/password substitutions
if config . mqtt . user or config . mqtt . password :
config . mqtt . user = config . mqtt . user . format ( ** FRIGATE_ENV_VARS )
2021-06-24 16:53:01 -04:00
config . mqtt . password = config . mqtt . password . format ( ** FRIGATE_ENV_VARS )
2022-11-02 12:41:44 +01:00
# Global config to propagate down to camera level
2021-06-24 01:45:27 -04:00
global_config = config . dict (
include = {
2022-04-15 05:59:30 -06:00
"birdseye" : ... ,
2021-06-24 01:45:27 -04:00
"record" : ... ,
"snapshots" : ... ,
2021-09-03 05:03:36 -07:00
"rtmp" : ... ,
2023-01-16 16:50:35 -07:00
"live" : ... ,
2021-06-24 01:45:27 -04:00
"objects" : ... ,
"motion" : ... ,
"detect" : ... ,
"ffmpeg" : ... ,
2021-09-03 05:03:36 -07:00
"timestamp_style" : ... ,
2021-05-22 18:28:15 -04:00
},
2021-06-24 01:45:27 -04:00
exclude_unset = True ,
2021-05-22 18:28:15 -04:00
)
2021-06-24 01:45:27 -04:00
for name , camera in config . cameras . items ():
merged_config = deep_merge ( camera . dict ( exclude_unset = True ), global_config )
2021-07-09 16:14:16 -04:00
camera_config : CameraConfig = CameraConfig . parse_obj (
{ "name" : name , ** merged_config }
)
2021-05-22 18:28:15 -04:00
2021-08-28 09:14:00 -05:00
# Default max_disappeared configuration
2021-08-28 08:51:29 -05:00
max_disappeared = camera_config . detect . fps * 5
if camera_config . detect . max_disappeared is None :
camera_config . detect . max_disappeared = max_disappeared
2022-02-11 07:12:51 -06:00
# Default stationary_threshold configuration
2022-02-11 07:30:47 -06:00
stationary_threshold = camera_config . detect . fps * 10
2022-02-13 08:58:44 -06:00
if camera_config . detect . stationary . threshold is None :
camera_config . detect . stationary . threshold = stationary_threshold
2022-02-11 07:12:51 -06:00
2021-06-24 16:53:01 -04:00
# FFMPEG input substitution
for input in camera_config . ffmpeg . inputs :
input . path = input . path . format ( ** FRIGATE_ENV_VARS )
2021-06-24 01:45:27 -04:00
# Add default filters
object_keys = camera_config . objects . track
if camera_config . objects . filters is None :
camera_config . objects . filters = {}
object_keys = object_keys - camera_config . objects . filters . keys ()
for key in object_keys :
camera_config . objects . filters [ key ] = FilterConfig ()
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
# Apply global object masks and convert masks to numpy array
for object , filter in camera_config . objects . filters . items ():
if camera_config . objects . mask :
filter_mask = []
if filter . mask is not None :
filter_mask = (
filter . mask
if isinstance ( filter . mask , list )
else [ filter . mask ]
)
object_mask = (
camera_config . objects . mask
if isinstance ( camera_config . objects . mask , list )
else [ camera_config . objects . mask ]
)
filter . mask = filter_mask + object_mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
# Set runtime filter to create masks
camera_config . objects . filters [ object ] = RuntimeFilterConfig (
frame_shape = camera_config . frame_shape ,
** filter . dict ( exclude_unset = True ),
)
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
# Convert motion configuration
if camera_config . motion is None :
camera_config . motion = RuntimeMotionConfig (
frame_shape = camera_config . frame_shape
)
else :
camera_config . motion = RuntimeMotionConfig (
frame_shape = camera_config . frame_shape ,
raw_mask = camera_config . motion . mask ,
** camera_config . motion . dict ( exclude_unset = True ),
)
2021-05-22 18:28:15 -04:00
2023-01-16 16:50:35 -07:00
# Set live view stream if none is set
if not camera_config . live . stream_name :
camera_config . live . stream_name = name
2022-11-08 18:47:45 -07:00
verify_config_roles ( camera_config )
2023-01-16 16:50:35 -07:00
verify_valid_live_stream_name ( config , camera_config )
2022-11-08 18:47:45 -07:00
verify_old_retain_config ( camera_config )
verify_recording_retention ( camera_config )
2022-12-08 20:03:54 -07:00
verify_recording_segments_setup_with_reasonable_time ( camera_config )
2022-11-08 18:47:45 -07:00
verify_zone_objects_are_tracked ( camera_config )
2021-10-22 19:41:28 -05:00
2022-11-08 18:47:45 -07:00
if camera_config . rtmp . enabled :
2021-12-11 09:22:44 -06:00
logger . warning (
2022-11-08 18:47:45 -07:00
"RTMP restream is deprecated in favor of the restream role, recommend disabling RTMP."
2021-12-11 09:22:44 -06:00
)
2022-11-02 12:41:44 +01:00
# generate the ffmpeg commands
2022-01-14 16:31:25 +03:00
camera_config . create_ffmpeg_cmds ()
2021-10-24 13:33:13 -05:00
config . cameras [ name ] = camera_config
2022-12-15 07:12:52 -06:00
2023-01-06 18:31:54 -07:00
# get list of unique enabled labels for tracking
enabled_labels = set ( config . objects . track )
for _ , camera in config . cameras . items ():
enabled_labels . update ( camera . objects . track )
2023-01-07 11:05:11 -07:00
config . model . create_colormap ( sorted ( enabled_labels ))
2023-01-06 18:31:54 -07:00
2022-12-15 07:12:52 -06:00
for key , detector in config . detectors . items ():
detector_config : DetectorConfig = parse_obj_as ( DetectorConfig , detector )
if detector_config . model is None :
detector_config . model = config . model
else :
model = detector_config . model
schema = ModelConfig . schema ()[ "properties" ]
if (
model . width != schema [ "width" ][ "default" ]
or model . height != schema [ "height" ][ "default" ]
or model . labelmap_path is not None
or model . labelmap is not {}
or model . input_tensor != schema [ "input_tensor" ][ "default" ]
or model . input_pixel_format
!= schema [ "input_pixel_format" ][ "default" ]
):
logger . warning (
"Customizing more than a detector model path is unsupported."
)
merged_model = deep_merge (
detector_config . model . dict ( exclude_unset = True ),
config . model . dict ( exclude_unset = True ),
)
detector_config . model = ModelConfig . parse_obj ( merged_model )
config . detectors [ key ] = detector_config
2020-11-03 08:15:58 -06:00
return config
2021-06-24 01:45:27 -04:00
@validator ( "cameras" )
def ensure_zones_and_cameras_have_different_names ( cls , v : Dict [ str , CameraConfig ]):
zones = [ zone for camera in v . values () for zone in camera . zones . keys ()]
for zone in zones :
if zone in v . keys ():
raise ValueError ( "Zones cannot share names with cameras" )
return v
@classmethod
def parse_file ( cls , config_file ):
2020-11-03 08:15:58 -06:00
with open ( config_file ) as f :
raw_config = f . read ()
2021-01-09 09:26:46 -08:00
2021-12-12 10:27:05 -05:00
if config_file . endswith ( YAML_EXT ):
2022-11-08 18:47:45 -07:00
config = load_config_with_no_duplicates ( raw_config )
2020-11-03 08:15:58 -06:00
elif config_file . endswith ( ".json" ):
config = json . loads ( raw_config )
2021-01-09 09:26:46 -08:00
2021-06-24 01:45:27 -04:00
return cls . parse_obj ( config )
2022-12-07 06:36:56 -07:00
@classmethod
def parse_raw ( cls , raw_config ):
config = load_config_with_no_duplicates ( raw_config )
return cls . parse_obj ( config )