mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-04 18:25:22 +03:00
Add rotate camera feature to record option
This commit is contained in:
parent
fd941cd008
commit
826671c008
@ -690,7 +690,11 @@ class CameraConfig(FrigateBaseModel):
|
|||||||
)
|
)
|
||||||
if "record" in ffmpeg_input.roles and self.record.enabled:
|
if "record" in ffmpeg_input.roles and self.record.enabled:
|
||||||
record_args = get_ffmpeg_arg_list(
|
record_args = get_ffmpeg_arg_list(
|
||||||
parse_preset_output_record(self.ffmpeg.output_args.record)
|
parse_preset_output_record(
|
||||||
|
self.ffmpeg.output_args.record,
|
||||||
|
ffmpeg_input.hwaccel_args or self.ffmpeg.hwaccel_args,
|
||||||
|
self.rotate,
|
||||||
|
)
|
||||||
or self.ffmpeg.output_args.record
|
or self.ffmpeg.output_args.record
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -119,14 +119,38 @@ PRESETS_HW_ACCEL_SCALE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PRESETS_HW_ACCEL_SCALE_ROTATION = {
|
PRESETS_HW_ACCEL_SCALE_ROTATION = {
|
||||||
"preset-rpi-32-h264": " -vf transpose={0}",
|
"preset-rpi-32-h264": {
|
||||||
"preset-rpi-64-h264": " -vf transpose={0}",
|
"detect": " -vf transpose={0}",
|
||||||
"preset-vaapi": "transpose_vaapi={0},",
|
"record": " -vf transpose={0}",
|
||||||
"preset-intel-qsv-h264": "transpose={0}:",
|
},
|
||||||
"preset-intel-qsv-h265": "transpose={0}:",
|
"preset-rpi-64-h264": {
|
||||||
"preset-nvidia-h264": "transpose={0},",
|
"detect": " -vf transpose={0}",
|
||||||
"preset-nvidia-h265": "transpose={0},",
|
"record": " -vf transpose={0}",
|
||||||
"default": " -vf transpose={0}",
|
},
|
||||||
|
"preset-vaapi": {
|
||||||
|
"detect": "transpose_vaapi={0},",
|
||||||
|
"record": " -vf transpose_vaapi={0}",
|
||||||
|
},
|
||||||
|
"preset-intel-qsv-h264": {
|
||||||
|
"detect": "transpose={0}:",
|
||||||
|
"record": " -vf vpp_qsv=transpose={0}",
|
||||||
|
},
|
||||||
|
"preset-intel-qsv-h265": {
|
||||||
|
"detect": "transpose={0}:",
|
||||||
|
"record": " -vf vpp_qsv=transpose={0}",
|
||||||
|
},
|
||||||
|
"preset-nvidia-h264": {
|
||||||
|
"detect": "transpose={0},",
|
||||||
|
"record": " -vf transpose={0}",
|
||||||
|
},
|
||||||
|
"preset-nvidia-h265": {
|
||||||
|
"detect": "transpose={0},",
|
||||||
|
"record": " -vf transpose={0}",
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"detect": " -vf transpose={0}",
|
||||||
|
"record": " -vf transpose={0}",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
PRESETS_HW_ACCEL_ENCODE = {
|
PRESETS_HW_ACCEL_ENCODE = {
|
||||||
@ -151,6 +175,7 @@ def parse_preset_hardware_acceleration_decode(arg: Any) -> list[str]:
|
|||||||
|
|
||||||
def _parse_rotation_scale(
|
def _parse_rotation_scale(
|
||||||
arg: Any,
|
arg: Any,
|
||||||
|
mode: str,
|
||||||
rotate: int,
|
rotate: int,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Return the correct rotation scale or "" if preset none is set."""
|
"""Return the correct rotation scale or "" if preset none is set."""
|
||||||
@ -169,7 +194,7 @@ def _parse_rotation_scale(
|
|||||||
else : # Rotation not need or not supported
|
else : # Rotation not need or not supported
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return PRESETS_HW_ACCEL_SCALE_ROTATION.get(arg, "").format(transpose)
|
return PRESETS_HW_ACCEL_SCALE_ROTATION.get(arg, "").get(mode).format(transpose)
|
||||||
|
|
||||||
|
|
||||||
def parse_preset_hardware_acceleration_scale(
|
def parse_preset_hardware_acceleration_scale(
|
||||||
@ -186,7 +211,7 @@ def parse_preset_hardware_acceleration_scale(
|
|||||||
scale.extend(detect_args)
|
scale.extend(detect_args)
|
||||||
return scale
|
return scale
|
||||||
|
|
||||||
transpose =_parse_rotation_scale(arg, rotate)
|
transpose =_parse_rotation_scale(arg, "detect", rotate)
|
||||||
|
|
||||||
scale = PRESETS_HW_ACCEL_SCALE.get(arg, "")
|
scale = PRESETS_HW_ACCEL_SCALE.get(arg, "")
|
||||||
|
|
||||||
@ -363,109 +388,52 @@ def parse_preset_input(arg: Any, detect_fps: int) -> list[str]:
|
|||||||
return PRESETS_INPUT.get(arg, None)
|
return PRESETS_INPUT.get(arg, None)
|
||||||
|
|
||||||
|
|
||||||
PRESETS_RECORD_OUTPUT = {
|
PRESETS_RECORD_OUTPUT = "-f segment -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1"
|
||||||
"preset-record-generic": [
|
PRESETS_RECORD_VIDEO_AUDIO = {
|
||||||
"-f",
|
"preset-record-generic": {
|
||||||
"segment",
|
"video": " -c:v copy",
|
||||||
"-segment_time",
|
"audio": " -an",
|
||||||
"10",
|
},
|
||||||
"-segment_format",
|
"preset-record-generic-audio-aac": {
|
||||||
"mp4",
|
"video": " -c:v copy",
|
||||||
"-reset_timestamps",
|
"audio": " -c:a aac",
|
||||||
"1",
|
},
|
||||||
"-strftime",
|
"preset-record-generic-audio-copy": {
|
||||||
"1",
|
"video": " -c:v copy",
|
||||||
"-c",
|
"audio": " -c:a copy",
|
||||||
"copy",
|
},
|
||||||
"-an",
|
"preset-record-mjpeg": {
|
||||||
],
|
"video": " -c:v libx264",
|
||||||
"preset-record-generic-audio-aac": [
|
"audio": " -an",
|
||||||
"-f",
|
},
|
||||||
"segment",
|
"preset-record-jpeg": {
|
||||||
"-segment_time",
|
"video": " -c:v libx264",
|
||||||
"10",
|
"audio": " -an",
|
||||||
"-segment_format",
|
},
|
||||||
"mp4",
|
"preset-record-ubiquiti": {
|
||||||
"-reset_timestamps",
|
"video": " -c:v copy",
|
||||||
"1",
|
"audio": " -ar 44100 -c:a aac",
|
||||||
"-strftime",
|
},
|
||||||
"1",
|
|
||||||
"-c:v",
|
|
||||||
"copy",
|
|
||||||
"-c:a",
|
|
||||||
"aac",
|
|
||||||
],
|
|
||||||
"preset-record-generic-audio-copy": [
|
|
||||||
"-f",
|
|
||||||
"segment",
|
|
||||||
"-segment_time",
|
|
||||||
"10",
|
|
||||||
"-segment_format",
|
|
||||||
"mp4",
|
|
||||||
"-reset_timestamps",
|
|
||||||
"1",
|
|
||||||
"-strftime",
|
|
||||||
"1",
|
|
||||||
"-c",
|
|
||||||
"copy",
|
|
||||||
],
|
|
||||||
"preset-record-mjpeg": [
|
|
||||||
"-f",
|
|
||||||
"segment",
|
|
||||||
"-segment_time",
|
|
||||||
"10",
|
|
||||||
"-segment_format",
|
|
||||||
"mp4",
|
|
||||||
"-reset_timestamps",
|
|
||||||
"1",
|
|
||||||
"-strftime",
|
|
||||||
"1",
|
|
||||||
"-c:v",
|
|
||||||
"libx264",
|
|
||||||
"-an",
|
|
||||||
],
|
|
||||||
"preset-record-jpeg": [
|
|
||||||
"-f",
|
|
||||||
"segment",
|
|
||||||
"-segment_time",
|
|
||||||
"10",
|
|
||||||
"-segment_format",
|
|
||||||
"mp4",
|
|
||||||
"-reset_timestamps",
|
|
||||||
"1",
|
|
||||||
"-strftime",
|
|
||||||
"1",
|
|
||||||
"-c:v",
|
|
||||||
"libx264",
|
|
||||||
"-an",
|
|
||||||
],
|
|
||||||
"preset-record-ubiquiti": [
|
|
||||||
"-f",
|
|
||||||
"segment",
|
|
||||||
"-segment_time",
|
|
||||||
"10",
|
|
||||||
"-segment_format",
|
|
||||||
"mp4",
|
|
||||||
"-reset_timestamps",
|
|
||||||
"1",
|
|
||||||
"-strftime",
|
|
||||||
"1",
|
|
||||||
"-c:v",
|
|
||||||
"copy",
|
|
||||||
"-ar",
|
|
||||||
"44100",
|
|
||||||
"-c:a",
|
|
||||||
"aac",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def parse_preset_output_record(arg: Any) -> list[str]:
|
def parse_preset_output_record(arg: Any, hw_acc: Any, rotate: int) -> list[str]:
|
||||||
"""Return the correct preset if in preset format otherwise return None."""
|
"""Return the correct preset if in preset format otherwise return None."""
|
||||||
if not isinstance(arg, str):
|
if not isinstance(arg, str):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return PRESETS_RECORD_OUTPUT.get(arg, None)
|
preset_record_video_audio = PRESETS_RECORD_VIDEO_AUDIO.get(arg, None)
|
||||||
|
if not preset_record_video_audio:
|
||||||
|
return None
|
||||||
|
|
||||||
|
audio = preset_record_video_audio["audio"]
|
||||||
|
|
||||||
|
video = preset_record_video_audio["video"]
|
||||||
|
transpose =_parse_rotation_scale(hw_acc, "record", rotate)
|
||||||
|
if transpose != "" or not "copy" in video:
|
||||||
|
video = transpose + " -c:v libx264"
|
||||||
|
|
||||||
|
return (PRESETS_RECORD_OUTPUT + video + audio).split(" ")
|
||||||
|
|
||||||
|
|
||||||
PRESETS_RTMP_OUTPUT = {
|
PRESETS_RTMP_OUTPUT = {
|
||||||
|
|||||||
@ -291,6 +291,126 @@ class TestFfmpegPresets(unittest.TestCase):
|
|||||||
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_output_record_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"]["ffmpeg"]["output_args"][
|
||||||
|
"record"
|
||||||
|
] = "preset-record-generic-audio-aac"
|
||||||
|
|
||||||
|
frigate_config = FrigateConfig(**self.default_ffmpeg)
|
||||||
|
frigate_config.cameras["back"].create_ffmpeg_cmds()
|
||||||
|
assert "preset-record-generic-audio-aac" not in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
assert "-vf transpose=clock -c:v libx264 -c:a aac" in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_output_record_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"]["ffmpeg"]["output_args"][
|
||||||
|
"record"
|
||||||
|
] = "preset-record-generic-audio-aac"
|
||||||
|
|
||||||
|
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 "-vf transpose=clock,transpose=clock -c:v libx264 -c:a aac" in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_output_record_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"]["ffmpeg"]["output_args"][
|
||||||
|
"record"
|
||||||
|
] = "preset-record-generic-audio-aac"
|
||||||
|
|
||||||
|
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 "-vf transpose_vaapi=reverse -c:v libx264 -c:a aac" in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_output_record_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"]["ffmpeg"]["output_args"][
|
||||||
|
"record"
|
||||||
|
] = "preset-record-generic-audio-aac"
|
||||||
|
|
||||||
|
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 "-vf vpp_qsv=transpose=reverse -c:v libx264 -c:a aac" in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_output_record_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"]["ffmpeg"]["output_args"][
|
||||||
|
"record"
|
||||||
|
] = "preset-record-generic-audio-aac"
|
||||||
|
|
||||||
|
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 "-vf transpose=cclock -c:v libx264 -c:a aac" in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ffmpeg_output_record_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"]["ffmpeg"]["output_args"][
|
||||||
|
"record"
|
||||||
|
] = "preset-record-generic-audio-aac"
|
||||||
|
|
||||||
|
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 "-c:v copy -c:a aac" in (
|
||||||
|
" ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"])
|
||||||
|
)
|
||||||
|
|
||||||
def test_ffmpeg_output_rtmp_preset(self):
|
def test_ffmpeg_output_rtmp_preset(self):
|
||||||
self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["output_args"][
|
self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["output_args"][
|
||||||
"rtmp"
|
"rtmp"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user