mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-12-23 19:11:14 +01:00
128 lines
4.0 KiB
Python
128 lines
4.0 KiB
Python
|
import shutil
|
||
|
from enum import Enum
|
||
|
from typing import Union
|
||
|
|
||
|
from pydantic import Field, PrivateAttr, field_validator
|
||
|
|
||
|
from frigate.const import DEFAULT_FFMPEG_VERSION, INCLUDED_FFMPEG_VERSIONS
|
||
|
|
||
|
from ..base import FrigateBaseModel
|
||
|
from ..env import EnvString
|
||
|
|
||
|
__all__ = [
|
||
|
"CameraFfmpegConfig",
|
||
|
"CameraInput",
|
||
|
"CameraRoleEnum",
|
||
|
"FfmpegConfig",
|
||
|
"FfmpegOutputArgsConfig",
|
||
|
]
|
||
|
|
||
|
# Note: Setting threads to less than 2 caused several issues with recording segments
|
||
|
# https://github.com/blakeblackshear/frigate/issues/5659
|
||
|
FFMPEG_GLOBAL_ARGS_DEFAULT = ["-hide_banner", "-loglevel", "warning", "-threads", "2"]
|
||
|
FFMPEG_INPUT_ARGS_DEFAULT = "preset-rtsp-generic"
|
||
|
|
||
|
RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT = "preset-record-generic"
|
||
|
DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT = [
|
||
|
"-threads",
|
||
|
"2",
|
||
|
"-f",
|
||
|
"rawvideo",
|
||
|
"-pix_fmt",
|
||
|
"yuv420p",
|
||
|
]
|
||
|
|
||
|
|
||
|
class FfmpegOutputArgsConfig(FrigateBaseModel):
|
||
|
detect: Union[str, list[str]] = Field(
|
||
|
default=DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT,
|
||
|
title="Detect role FFmpeg output arguments.",
|
||
|
)
|
||
|
record: Union[str, list[str]] = Field(
|
||
|
default=RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT,
|
||
|
title="Record role FFmpeg output arguments.",
|
||
|
)
|
||
|
_force_record_hvc1: bool = PrivateAttr(default=False)
|
||
|
|
||
|
|
||
|
class FfmpegConfig(FrigateBaseModel):
|
||
|
path: str = Field(default="default", title="FFmpeg path")
|
||
|
global_args: Union[str, list[str]] = Field(
|
||
|
default=FFMPEG_GLOBAL_ARGS_DEFAULT, title="Global FFmpeg arguments."
|
||
|
)
|
||
|
hwaccel_args: Union[str, list[str]] = Field(
|
||
|
default="auto", 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.",
|
||
|
)
|
||
|
retry_interval: float = Field(
|
||
|
default=10.0,
|
||
|
title="Time in seconds to wait before FFmpeg retries connecting to the camera.",
|
||
|
)
|
||
|
|
||
|
@property
|
||
|
def ffmpeg_path(self) -> str:
|
||
|
if self.path == "default":
|
||
|
if shutil.which("ffmpeg") is None:
|
||
|
return f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg"
|
||
|
else:
|
||
|
return "ffmpeg"
|
||
|
elif self.path in INCLUDED_FFMPEG_VERSIONS:
|
||
|
return f"/usr/lib/ffmpeg/{self.path}/bin/ffmpeg"
|
||
|
else:
|
||
|
return f"{self.path}/bin/ffmpeg"
|
||
|
|
||
|
@property
|
||
|
def ffprobe_path(self) -> str:
|
||
|
if self.path == "default":
|
||
|
if shutil.which("ffprobe") is None:
|
||
|
return f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffprobe"
|
||
|
else:
|
||
|
return "ffprobe"
|
||
|
elif self.path in INCLUDED_FFMPEG_VERSIONS:
|
||
|
return f"/usr/lib/ffmpeg/{self.path}/bin/ffprobe"
|
||
|
else:
|
||
|
return f"{self.path}/bin/ffprobe"
|
||
|
|
||
|
|
||
|
class CameraRoleEnum(str, Enum):
|
||
|
audio = "audio"
|
||
|
record = "record"
|
||
|
detect = "detect"
|
||
|
|
||
|
|
||
|
class CameraInput(FrigateBaseModel):
|
||
|
path: EnvString = Field(title="Camera input path.")
|
||
|
roles: list[CameraRoleEnum] = Field(title="Roles assigned to this input.")
|
||
|
global_args: Union[str, list[str]] = Field(
|
||
|
default_factory=list, title="FFmpeg global arguments."
|
||
|
)
|
||
|
hwaccel_args: Union[str, list[str]] = Field(
|
||
|
default_factory=list, title="FFmpeg hardware acceleration arguments."
|
||
|
)
|
||
|
input_args: Union[str, list[str]] = Field(
|
||
|
default_factory=list, title="FFmpeg input arguments."
|
||
|
)
|
||
|
|
||
|
|
||
|
class CameraFfmpegConfig(FfmpegConfig):
|
||
|
inputs: list[CameraInput] = Field(title="Camera inputs.")
|
||
|
|
||
|
@field_validator("inputs")
|
||
|
@classmethod
|
||
|
def validate_roles(cls, v):
|
||
|
roles = [role for input in v for role in input.roles]
|
||
|
|
||
|
if len(roles) != len(set(roles)):
|
||
|
raise ValueError("Each input role may only be used once.")
|
||
|
|
||
|
if "detect" not in roles:
|
||
|
raise ValueError("The detect role is required.")
|
||
|
|
||
|
return v
|