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:
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
@ -119,14 +119,38 @@ PRESETS_HW_ACCEL_SCALE = {
|
||||
}
|
||||
|
||||
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}",
|
||||
"preset-rpi-32-h264": {
|
||||
"detect": " -vf transpose={0}",
|
||||
"record": " -vf transpose={0}",
|
||||
},
|
||||
"preset-rpi-64-h264": {
|
||||
"detect": " -vf transpose={0}",
|
||||
"record": " -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 = {
|
||||
@ -151,6 +175,7 @@ def parse_preset_hardware_acceleration_decode(arg: Any) -> list[str]:
|
||||
|
||||
def _parse_rotation_scale(
|
||||
arg: Any,
|
||||
mode: str,
|
||||
rotate: int,
|
||||
) -> str:
|
||||
"""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
|
||||
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(
|
||||
@ -186,7 +211,7 @@ def parse_preset_hardware_acceleration_scale(
|
||||
scale.extend(detect_args)
|
||||
return scale
|
||||
|
||||
transpose =_parse_rotation_scale(arg, rotate)
|
||||
transpose =_parse_rotation_scale(arg, "detect", rotate)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
PRESETS_RECORD_OUTPUT = {
|
||||
"preset-record-generic": [
|
||||
"-f",
|
||||
"segment",
|
||||
"-segment_time",
|
||||
"10",
|
||||
"-segment_format",
|
||||
"mp4",
|
||||
"-reset_timestamps",
|
||||
"1",
|
||||
"-strftime",
|
||||
"1",
|
||||
"-c",
|
||||
"copy",
|
||||
"-an",
|
||||
],
|
||||
"preset-record-generic-audio-aac": [
|
||||
"-f",
|
||||
"segment",
|
||||
"-segment_time",
|
||||
"10",
|
||||
"-segment_format",
|
||||
"mp4",
|
||||
"-reset_timestamps",
|
||||
"1",
|
||||
"-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",
|
||||
],
|
||||
PRESETS_RECORD_OUTPUT = "-f segment -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1"
|
||||
PRESETS_RECORD_VIDEO_AUDIO = {
|
||||
"preset-record-generic": {
|
||||
"video": " -c:v copy",
|
||||
"audio": " -an",
|
||||
},
|
||||
"preset-record-generic-audio-aac": {
|
||||
"video": " -c:v copy",
|
||||
"audio": " -c:a aac",
|
||||
},
|
||||
"preset-record-generic-audio-copy": {
|
||||
"video": " -c:v copy",
|
||||
"audio": " -c:a copy",
|
||||
},
|
||||
"preset-record-mjpeg": {
|
||||
"video": " -c:v libx264",
|
||||
"audio": " -an",
|
||||
},
|
||||
"preset-record-jpeg": {
|
||||
"video": " -c:v libx264",
|
||||
"audio": " -an",
|
||||
},
|
||||
"preset-record-ubiquiti": {
|
||||
"video": " -c:v copy",
|
||||
"audio": " -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."""
|
||||
if not isinstance(arg, str):
|
||||
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 = {
|
||||
|
||||
@ -291,6 +291,126 @@ class TestFfmpegPresets(unittest.TestCase):
|
||||
" ".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):
|
||||
self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["output_args"][
|
||||
"rtmp"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user