mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-04 18:25:22 +03:00
Add rotate camera feature to detect option
This commit is contained in:
parent
318240c14c
commit
fd941cd008
@ -576,6 +576,7 @@ class CameraUiConfig(FrigateBaseModel):
|
|||||||
class CameraConfig(FrigateBaseModel):
|
class CameraConfig(FrigateBaseModel):
|
||||||
name: Optional[str] = Field(title="Camera name.", regex=REGEX_CAMERA_NAME)
|
name: Optional[str] = Field(title="Camera name.", regex=REGEX_CAMERA_NAME)
|
||||||
enabled: bool = Field(default=True, title="Enable camera.")
|
enabled: bool = Field(default=True, title="Enable camera.")
|
||||||
|
rotate: int = Field(default=0, title="Rotate camera: 0º, 90º, 180º or 270º(-90º)")
|
||||||
ffmpeg: CameraFfmpegConfig = Field(title="FFmpeg configuration for the camera.")
|
ffmpeg: CameraFfmpegConfig = Field(title="FFmpeg configuration for the camera.")
|
||||||
best_image_timeout: int = Field(
|
best_image_timeout: int = Field(
|
||||||
default=60,
|
default=60,
|
||||||
@ -674,6 +675,7 @@ class CameraConfig(FrigateBaseModel):
|
|||||||
self.detect.fps,
|
self.detect.fps,
|
||||||
self.detect.width,
|
self.detect.width,
|
||||||
self.detect.height,
|
self.detect.height,
|
||||||
|
self.rotate,
|
||||||
)
|
)
|
||||||
|
|
||||||
ffmpeg_output_args = scale_detect_args + ffmpeg_output_args + ["pipe:"]
|
ffmpeg_output_args = scale_detect_args + ffmpeg_output_args + ["pipe:"]
|
||||||
|
|||||||
@ -108,16 +108,27 @@ PRESETS_HW_ACCEL_DECODE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PRESETS_HW_ACCEL_SCALE = {
|
PRESETS_HW_ACCEL_SCALE = {
|
||||||
"preset-rpi-32-h264": "-r {0} -s {1}x{2}",
|
"preset-rpi-32-h264": "-r {0}{3} -s {1}x{2}",
|
||||||
"preset-rpi-64-h264": "-r {0} -s {1}x{2}",
|
"preset-rpi-64-h264": "-r {0}{3} -s {1}x{2}",
|
||||||
"preset-vaapi": "-r {0} -vf fps={0},scale_vaapi=w={1}:h={2},hwdownload,format=yuv420p",
|
"preset-vaapi": "-r {0} -vf fps={0},{3}scale_vaapi=w={1}:h={2},hwdownload,format=yuv420p",
|
||||||
"preset-intel-qsv-h264": "-r {0} -vf vpp_qsv=framerate={0}:w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
"preset-intel-qsv-h264": "-r {0} -vf vpp_qsv=framerate={0}:{3}w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||||
"preset-intel-qsv-h265": "-r {0} -vf vpp_qsv=framerate={0}:w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
"preset-intel-qsv-h265": "-r {0} -vf vpp_qsv=framerate={0}:{3}w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||||
"preset-nvidia-h264": "-r {0} -vf fps={0},scale_cuda=w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
"preset-nvidia-h264": "-r {0} -vf fps={0},{3}scale_cuda=w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||||
"preset-nvidia-h265": "-r {0} -vf fps={0},scale_cuda=w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
"preset-nvidia-h265": "-r {0} -vf fps={0},{3}scale_cuda=w={1}:h={2}:format=nv12,hwdownload,format=nv12,format=yuv420p",
|
||||||
"default": "-r {0} -s {1}x{2}",
|
"default": "-r {0} -s {1}x{2}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRESETS_HW_ACCEL_SCALE_ROTATION = {
|
||||||
|
"preset-rpi-32-h264": " -vf transpose={0}",
|
||||||
|
"preset-rpi-64-h264": " -vf transpose={0}",
|
||||||
|
"preset-vaapi": "transpose_vaapi={0},",
|
||||||
|
"preset-intel-qsv-h264": "transpose={0}:",
|
||||||
|
"preset-intel-qsv-h265": "transpose={0}:",
|
||||||
|
"preset-nvidia-h264": "transpose={0},",
|
||||||
|
"preset-nvidia-h265": "transpose={0},",
|
||||||
|
"default": " -vf transpose={0}",
|
||||||
|
}
|
||||||
|
|
||||||
PRESETS_HW_ACCEL_ENCODE = {
|
PRESETS_HW_ACCEL_ENCODE = {
|
||||||
"preset-rpi-32-h264": "ffmpeg -hide_banner {0} -c:v h264_v4l2m2m {1}",
|
"preset-rpi-32-h264": "ffmpeg -hide_banner {0} -c:v h264_v4l2m2m {1}",
|
||||||
"preset-rpi-64-h264": "ffmpeg -hide_banner {0} -c:v h264_v4l2m2m {1}",
|
"preset-rpi-64-h264": "ffmpeg -hide_banner {0} -c:v h264_v4l2m2m {1}",
|
||||||
@ -138,12 +149,36 @@ def parse_preset_hardware_acceleration_decode(arg: Any) -> list[str]:
|
|||||||
return PRESETS_HW_ACCEL_DECODE.get(arg, None)
|
return PRESETS_HW_ACCEL_DECODE.get(arg, None)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_rotation_scale(
|
||||||
|
arg: Any,
|
||||||
|
rotate: int,
|
||||||
|
) -> str:
|
||||||
|
"""Return the correct rotation scale or "" if preset none is set."""
|
||||||
|
if not isinstance(arg, str) or " " in arg:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if rotate == 90:
|
||||||
|
transpose = "clock"
|
||||||
|
elif rotate == 180:
|
||||||
|
if arg.startswith("preset-vaapi") or arg.startswith("preset-intel-qsv"):
|
||||||
|
transpose = "reverse"
|
||||||
|
else: # No 'reverse' option suported, then 2 'clocks' rotations
|
||||||
|
transpose = "clock,transpose=clock"
|
||||||
|
elif rotate == 270:
|
||||||
|
transpose = "cclock"
|
||||||
|
else : # Rotation not need or not supported
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return PRESETS_HW_ACCEL_SCALE_ROTATION.get(arg, "").format(transpose)
|
||||||
|
|
||||||
|
|
||||||
def parse_preset_hardware_acceleration_scale(
|
def parse_preset_hardware_acceleration_scale(
|
||||||
arg: Any,
|
arg: Any,
|
||||||
detect_args: list[str],
|
detect_args: list[str],
|
||||||
fps: int,
|
fps: int,
|
||||||
width: int,
|
width: int,
|
||||||
height: int,
|
height: int,
|
||||||
|
rotate: int,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
"""Return the correct scaling preset or default preset if none is set."""
|
"""Return the correct scaling preset or default preset if none is set."""
|
||||||
if not isinstance(arg, str) or " " in arg:
|
if not isinstance(arg, str) or " " in arg:
|
||||||
@ -151,14 +186,16 @@ def parse_preset_hardware_acceleration_scale(
|
|||||||
scale.extend(detect_args)
|
scale.extend(detect_args)
|
||||||
return scale
|
return scale
|
||||||
|
|
||||||
|
transpose =_parse_rotation_scale(arg, rotate)
|
||||||
|
|
||||||
scale = PRESETS_HW_ACCEL_SCALE.get(arg, "")
|
scale = PRESETS_HW_ACCEL_SCALE.get(arg, "")
|
||||||
|
|
||||||
if scale:
|
if scale:
|
||||||
scale = scale.format(fps, width, height).split(" ")
|
scale = scale.format(fps, width, height, transpose).split(" ")
|
||||||
scale.extend(detect_args)
|
scale.extend(detect_args)
|
||||||
return scale
|
return scale
|
||||||
else:
|
else:
|
||||||
scale = scale.format(fps, width, height).split(" ")
|
scale = scale.format(fps, width, height, transpose).split(" ")
|
||||||
scale.extend(detect_args)
|
scale.extend(detect_args)
|
||||||
return scale
|
return scale
|
||||||
|
|
||||||
|
|||||||
@ -85,6 +85,141 @@ class TestFfmpegPresets(unittest.TestCase):
|
|||||||
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_hwaccel_rotate_90_preset(self):
|
||||||
|
self.default_ffmpeg["cameras"]["back"][
|
||||||
|
"rotate"
|
||||||
|
] = 90
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["ffmpeg"][
|
||||||
|
"hwaccel_args"
|
||||||
|
] = "preset-nvidia-h264"
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["detect"] = {
|
||||||
|
"height": 1920,
|
||||||
|
"width": 2560,
|
||||||
|
"fps": 10,
|
||||||
|
}
|
||||||
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||||
|
assert "preset-nvidia-h264" not in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"fps=10,transpose=clock,scale_cuda=w=2560:h=1920:format=nv12,hwdownload,format=nv12,format=yuv420p"
|
||||||
|
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_hwaccel_rotate_180_preset(self):
|
||||||
|
self.default_ffmpeg["cameras"]["back"][
|
||||||
|
"rotate"
|
||||||
|
] = 180
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["ffmpeg"][
|
||||||
|
"hwaccel_args"
|
||||||
|
] = "preset-rpi-64-h264"
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["detect"] = {
|
||||||
|
"height": 1920,
|
||||||
|
"width": 2560,
|
||||||
|
"fps": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||||
|
assert "preset-rpi-64-h264" not in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"-r 10 -vf transpose=clock,transpose=clock -s 2560x1920"
|
||||||
|
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_hwaccel_rotate_180_vaapi_preset(self):
|
||||||
|
self.default_ffmpeg["cameras"]["back"][
|
||||||
|
"rotate"
|
||||||
|
] = 180
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["ffmpeg"][
|
||||||
|
"hwaccel_args"
|
||||||
|
] = "preset-vaapi"
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["detect"] = {
|
||||||
|
"height": 1920,
|
||||||
|
"width": 2560,
|
||||||
|
"fps": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||||
|
assert "preset-vaapi" not in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"-r 10 -vf fps=10,transpose_vaapi=reverse,scale_vaapi=w=2560:h=1920,hwdownload,format=yuv420p"
|
||||||
|
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_hwaccel_rotate_180_qsv_preset(self):
|
||||||
|
self.default_ffmpeg["cameras"]["back"][
|
||||||
|
"rotate"
|
||||||
|
] = 180
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["ffmpeg"][
|
||||||
|
"hwaccel_args"
|
||||||
|
] = "preset-intel-qsv-h264"
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["detect"] = {
|
||||||
|
"height": 1920,
|
||||||
|
"width": 2560,
|
||||||
|
"fps": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||||
|
assert "preset-intel-qsv-h264" not in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"-r 10 -vf vpp_qsv=framerate=10:transpose=reverse:w=2560:h=1920:format=nv12,hwdownload,format=nv12,format=yuv420p"
|
||||||
|
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_hwaccel_rotate_270_preset(self):
|
||||||
|
self.default_ffmpeg["cameras"]["back"][
|
||||||
|
"rotate"
|
||||||
|
] = 270
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["ffmpeg"][
|
||||||
|
"hwaccel_args"
|
||||||
|
] = "preset-nvidia-h264"
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["detect"] = {
|
||||||
|
"height": 1920,
|
||||||
|
"width": 2560,
|
||||||
|
"fps": 10,
|
||||||
|
}
|
||||||
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||||
|
assert "preset-nvidia-h264" not in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"fps=10,transpose=cclock,scale_cuda=w=2560:h=1920:format=nv12,hwdownload,format=nv12,format=yuv420p"
|
||||||
|
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_hwaccel_rotate_wrong_preset(self):
|
||||||
|
self.default_ffmpeg["cameras"]["back"][
|
||||||
|
"rotate"
|
||||||
|
] = 20
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["ffmpeg"][
|
||||||
|
"hwaccel_args"
|
||||||
|
] = "preset-nvidia-h264"
|
||||||
|
self.default_ffmpeg["cameras"]["back"]["detect"] = {
|
||||||
|
"height": 1920,
|
||||||
|
"width": 2560,
|
||||||
|
"fps": 10,
|
||||||
|
}
|
||||||
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||||
|
assert "preset-nvidia-h264" not in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"fps=10,scale_cuda=w=2560:h=1920:format=nv12,hwdownload,format=nv12,format=yuv420p"
|
||||||
|
in (" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]))
|
||||||
|
)
|
||||||
|
|
||||||
def test_default_ffmpeg_input_arg_preset(self):
|
def test_default_ffmpeg_input_arg_preset(self):
|
||||||
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user