mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-28 19:06:52 +03:00
reject record_sub on the same input as record and document the role
This commit is contained in:
@@ -83,11 +83,12 @@ A camera is enabled by default but can be disabled by using `enabled: False`. Ca
|
||||
|
||||
Each role can only be assigned to one input per camera. The options for roles are as follows:
|
||||
|
||||
| Role | Description |
|
||||
| -------- | ----------------------------------------------------------------------------------- |
|
||||
| `detect` | Main feed for object detection. [docs](object_detectors.md) |
|
||||
| `record` | Saves segments of the video feed based on configuration settings. [docs](record.md) |
|
||||
| `audio` | Feed for audio based detection. [docs](audio_detectors.md) |
|
||||
| Role | Description |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `detect` | Main feed for object detection. [docs](object_detectors.md) |
|
||||
| `record` | Saves segments of the video feed based on configuration settings. [docs](record.md) |
|
||||
| `record_sub` | Saves segments of a second, lower quality stream with its own retention. [docs](record.md#sub-stream-recording) |
|
||||
| `audio` | Feed for audio based detection. [docs](audio_detectors.md) |
|
||||
|
||||
<ConfigTabs>
|
||||
<TabItem value="ui">
|
||||
|
||||
@@ -427,7 +427,7 @@ This table covers only features that read recordings from disk. Tracked object s
|
||||
|
||||
### Trade-offs
|
||||
|
||||
- Recording a second stream increases overall storage use. The increase is typically small relative to the main recordings, since the low quality stream is much smaller.
|
||||
- Recording a second stream increases overall storage use. The increase is typically small relative to the main recordings, since the low quality stream is much smaller. Both streams are cached before being written to disk, so cache use goes up as well. See [the `/tmp/cache` area is separate](#the-tmpcache-area-is-separate) if you start seeing `No space left on device` errors after enabling it.
|
||||
- The go2rtc transcode approach continuously encodes the low quality stream, which uses CPU or GPU resources. This cost only applies to the transcode path; recording the camera's native sub stream does not re-encode. See the [go2rtc hardware acceleration documentation](https://github.com/AlexxIT/go2rtc?tab=readme-ov-file#source-ffmpeg) for accelerating the transcode.
|
||||
- Many camera sub streams do not include audio. If the source stream has no audio, the low quality recordings will not have audio.
|
||||
- **Matching video codecs and audio settings between the two streams gives the smoothest playback.** When playback combines both qualities on one timeline (the default `Auto` behavior: for example original quality during events with low quality in between, or low quality history after the original recordings expire) and the streams use different video codecs or audio settings, for example H.265 on the main stream and H.264 on the sub stream, or 16 kHz audio on one and 8 kHz on the other, playback still works: Frigate inserts a decoder reset at each quality transition, which can cause a barely-perceptible pause there. Configuring both streams in the camera's firmware to use the same video codec, audio codec, and sample rate makes transitions fully seamless, and a mismatched audio sample rate can also be corrected with [sub stream output args](#sub-stream-output-args). If one stream has audio and the other does not, combined time ranges play **without audio**; selecting a single quality with the playback selector always keeps that stream's audio.
|
||||
|
||||
@@ -220,16 +220,21 @@ class CameraConfig(FrigateBaseModel):
|
||||
|
||||
# add roles to the input if there is only one
|
||||
if len(config["ffmpeg"]["inputs"]) == 1:
|
||||
has_audio = "audio" in config["ffmpeg"]["inputs"][0].get("roles", [])
|
||||
existing_roles = config["ffmpeg"]["inputs"][0].get("roles", [])
|
||||
|
||||
config["ffmpeg"]["inputs"][0]["roles"] = [
|
||||
"record",
|
||||
"detect",
|
||||
]
|
||||
|
||||
if has_audio:
|
||||
if "audio" in existing_roles:
|
||||
config["ffmpeg"]["inputs"][0]["roles"].append("audio")
|
||||
|
||||
# kept so role validation can report the real problem rather than
|
||||
# claiming the role was never assigned
|
||||
if "record_sub" in existing_roles:
|
||||
config["ffmpeg"]["inputs"][0]["roles"].append("record_sub")
|
||||
|
||||
super().__init__(**config)
|
||||
|
||||
@property
|
||||
|
||||
@@ -269,6 +269,12 @@ def verify_config_roles(camera_config: CameraConfig) -> None:
|
||||
f"Camera {camera_config.name} has sub stream recording enabled, but record_sub is not assigned to an input."
|
||||
)
|
||||
|
||||
for ffmpeg_input in camera_config.ffmpeg.inputs:
|
||||
if "record" in ffmpeg_input.roles and "record_sub" in ffmpeg_input.roles:
|
||||
raise ValueError(
|
||||
f"Camera {camera_config.name} has record and record_sub assigned to the same input, which would record the same stream twice."
|
||||
)
|
||||
|
||||
if camera_config.audio.enabled and "audio" not in assigned_roles:
|
||||
raise ValueError(
|
||||
f"Camera {camera_config.name} has audio events enabled, but audio is not assigned to an input."
|
||||
|
||||
@@ -1229,6 +1229,36 @@ class TestConfig(unittest.TestCase):
|
||||
lambda: FrigateConfig(**config).cameras,
|
||||
)
|
||||
|
||||
def test_fails_on_record_and_record_sub_on_same_input(self):
|
||||
config = self._sub_record_config()
|
||||
config["cameras"]["back"]["ffmpeg"]["inputs"] = [
|
||||
{
|
||||
"path": "rtsp://10.0.0.1:554/video",
|
||||
"roles": ["detect", "record", "record_sub"],
|
||||
},
|
||||
{"path": "rtsp://10.0.0.1:554/video2", "roles": ["audio"]},
|
||||
]
|
||||
|
||||
self.assertRaisesRegex(
|
||||
ValueError,
|
||||
"record and record_sub assigned to the same input",
|
||||
lambda: FrigateConfig(**config).cameras,
|
||||
)
|
||||
|
||||
def test_fails_on_record_sub_with_a_single_input(self):
|
||||
# the single input case has record forced onto it, so record_sub can
|
||||
# only ever duplicate that same stream
|
||||
config = self._sub_record_config()
|
||||
config["cameras"]["back"]["ffmpeg"]["inputs"] = [
|
||||
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect", "record_sub"]},
|
||||
]
|
||||
|
||||
self.assertRaisesRegex(
|
||||
ValueError,
|
||||
"record and record_sub assigned to the same input",
|
||||
lambda: FrigateConfig(**config).cameras,
|
||||
)
|
||||
|
||||
def test_record_sub_segment_time_not_checked_when_disabled(self):
|
||||
config = self._sub_record_config(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user