Add rotate camera feature to record option

This commit is contained in:
Galindo, Alex 2023-03-06 11:39:08 +01:00
parent fd941cd008
commit 826671c008
3 changed files with 199 additions and 107 deletions

View File

@ -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
) )

View File

@ -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 = {

View File

@ -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"