Allow segment length override

This commit is contained in:
Ilya Bursov 2025-12-30 13:59:12 -05:00
parent fb9604fbcc
commit a2e010e220
9 changed files with 103 additions and 13 deletions

View File

@ -263,6 +263,8 @@ ffmpeg:
detect: -threads 2 -f rawvideo -pix_fmt yuv420p
# Optional: output args for record streams (default: shown below)
record: preset-record-generic
# Optional: Set segment length for recording stream. (default: shown below)
segment_time : 10
# Optional: Time in seconds to wait before ffmpeg retries connecting to the camera. (default: shown below)
# If set too low, frigate will retry a connection to the camera's stream too frequently, using up the limited streams some cameras can allow at once
# If set too high, then if a ffmpeg crash or camera stream timeout occurs, you could potentially lose up to a maximum of retry_interval second(s) of footage

View File

@ -214,6 +214,7 @@ class CameraConfig(FrigateBaseModel):
parse_preset_output_record(
self.ffmpeg.output_args.record,
self.ffmpeg.apple_compatibility,
self.ffmpeg.output_args.segment_time,
)
or self.ffmpeg.output_args.record
)

View File

@ -41,6 +41,7 @@ class FfmpegOutputArgsConfig(FrigateBaseModel):
default=RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT,
title="Record role FFmpeg output arguments.",
)
segment_time: int = Field(default=10, title="Segment length for recording stream.")
class FfmpegConfig(FrigateBaseModel):

View File

@ -76,6 +76,8 @@ FFMPEG_HWACCEL_VULKAN = "preset-vulkan"
FFMPEG_HWACCEL_RKMPP = "preset-rkmpp"
FFMPEG_HWACCEL_AMF = "preset-amd-amf"
FFMPEG_HVC1_ARGS = ["-tag:v", "hvc1"]
FFMPEG_SEGMENT_TIME_PARAM = "-segment_time"
FFMPEG_SEGMENT_TIME_VALUE = "10"
# RKNN constants
SUPPORTED_RK_SOCS = ["rk3562", "rk3566", "rk3568", "rk3576", "rk3588"]

View File

@ -12,6 +12,8 @@ from frigate.const import (
FFMPEG_HWACCEL_RKMPP,
FFMPEG_HWACCEL_VAAPI,
FFMPEG_HWACCEL_VULKAN,
FFMPEG_SEGMENT_TIME_PARAM,
FFMPEG_SEGMENT_TIME_VALUE,
LIBAVFORMAT_VERSION_MAJOR,
)
from frigate.util.services import vainfo_hwaccel
@ -446,8 +448,8 @@ PRESETS_RECORD_OUTPUT = {
"preset-record-generic": [
"-f",
"segment",
"-segment_time",
"10",
FFMPEG_SEGMENT_TIME_PARAM,
FFMPEG_SEGMENT_TIME_VALUE,
"-segment_format",
"mp4",
"-reset_timestamps",
@ -461,8 +463,8 @@ PRESETS_RECORD_OUTPUT = {
"preset-record-generic-audio-aac": [
"-f",
"segment",
"-segment_time",
"10",
FFMPEG_SEGMENT_TIME_PARAM,
FFMPEG_SEGMENT_TIME_VALUE,
"-segment_format",
"mp4",
"-reset_timestamps",
@ -477,8 +479,8 @@ PRESETS_RECORD_OUTPUT = {
"preset-record-generic-audio-copy": [
"-f",
"segment",
"-segment_time",
"10",
FFMPEG_SEGMENT_TIME_PARAM,
FFMPEG_SEGMENT_TIME_VALUE,
"-segment_format",
"mp4",
"-reset_timestamps",
@ -491,8 +493,8 @@ PRESETS_RECORD_OUTPUT = {
"preset-record-mjpeg": [
"-f",
"segment",
"-segment_time",
"10",
FFMPEG_SEGMENT_TIME_PARAM,
FFMPEG_SEGMENT_TIME_VALUE,
"-segment_format",
"mp4",
"-reset_timestamps",
@ -506,8 +508,8 @@ PRESETS_RECORD_OUTPUT = {
"preset-record-jpeg": [
"-f",
"segment",
"-segment_time",
"10",
FFMPEG_SEGMENT_TIME_PARAM,
FFMPEG_SEGMENT_TIME_VALUE,
"-segment_format",
"mp4",
"-reset_timestamps",
@ -521,8 +523,8 @@ PRESETS_RECORD_OUTPUT = {
"preset-record-ubiquiti": [
"-f",
"segment",
"-segment_time",
"10",
FFMPEG_SEGMENT_TIME_PARAM,
FFMPEG_SEGMENT_TIME_VALUE,
"-segment_format",
"mp4",
"-reset_timestamps",
@ -539,7 +541,7 @@ PRESETS_RECORD_OUTPUT = {
}
def parse_preset_output_record(arg: Any, force_record_hvc1: bool) -> list[str]:
def parse_preset_output_record(arg: Any, force_record_hvc1: bool, segment_time: int = -1) -> list[str] | None:
"""Return the correct preset if in preset format otherwise return None."""
if not isinstance(arg, str):
return None
@ -549,6 +551,11 @@ def parse_preset_output_record(arg: Any, force_record_hvc1: bool) -> list[str]:
if not preset:
return None
if 0 < segment_time <= 60 and FFMPEG_SEGMENT_TIME_PARAM in preset:
idx = preset.index(FFMPEG_SEGMENT_TIME_PARAM)
if idx + 1 < len(preset):
preset[idx + 1] = str(segment_time)
if force_record_hvc1:
# Apple only supports HEVC if it is hvc1 (vs. hev1)
return preset + FFMPEG_HVC1_ARGS

View File

@ -128,6 +128,75 @@ class TestConfig(unittest.TestCase):
}
self.assertRaises(ValidationError, lambda: FrigateConfig(**config))
def test_default_segment_length(self):
config = {
"mqtt": {"host": "mqtt"},
"cameras": {
"back": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
]
},
"detect": {
"height": 1080,
"width": 1920,
"fps": 5,
},
}
},
}
frigate_config = FrigateConfig(**config)
assert frigate_config.cameras["back"].ffmpeg.output_args.segment_time == 10
def test_inherit_segment_length(self):
config = {
"mqtt": {"host": "mqtt"},
"ffmpeg": {"output_args": {"segment_time": 15}},
"cameras": {
"back": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
]
},
"detect": {
"height": 1080,
"width": 1920,
"fps": 5,
},
}
},
}
frigate_config = FrigateConfig(**config)
assert frigate_config.cameras["back"].ffmpeg.output_args.segment_time == 15
def test_override_segment_length(self):
config = {
"mqtt": {"host": "mqtt"},
"ffmpeg": {"output_args": {"segment_time": 15}},
"cameras": {
"back": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
],
"output_args": {"segment_time": 25}
},
"detect": {
"height": 1080,
"width": 1920,
"fps": 5,
},
}
},
}
frigate_config = FrigateConfig(**config)
assert frigate_config.cameras["back"].ffmpeg.output_args.segment_time == 25
def test_inherit_tracked_objects(self):
config = {
"mqtt": {"host": "mqtt"},

View File

@ -158,6 +158,9 @@
},
"record": {
"label": "Record role FFmpeg output arguments."
},
"segment_time": {
"label": "Segment length for recording stream."
}
}
},

View File

@ -21,6 +21,9 @@
},
"record": {
"label": "Record role FFmpeg output arguments."
},
"segment_time": {
"label": "Segment length for recording stream."
}
}
},

View File

@ -87,6 +87,7 @@ export interface CameraConfig {
detect: string[];
record: string;
rtmp: string;
segment_time: number;
};
retry_interval: number;
};
@ -418,6 +419,7 @@ export interface FrigateConfig {
detect: string[];
record: string;
rtmp: string;
segment_time: number;
};
retry_interval: number;
};