Add sub stream recording with adaptive quality playback (#24009)

* add sub stream recording with adaptive quality playback

Optionally record a second, lower bitrate stream alongside the main
recording stream via a `record_sub` input role and `record.sub` config block, with its own retention windows.
Recordings rows now carry the stream type plus the media details needed to serve both streams from one manifest: video codec, audio presence, audio codec and rate, and a record-time keyframe index.

Playback resolves coverage across both streams and merges them into a single VOD sequence, falling back to a discontinuity manifest with per-clip init segments when the media signatures differ. The player exposes a quality selector, and an auto governor picks the stream from stall time, bandwidth, codec support, and the save-data hint.

* fix tests and i18n
This commit is contained in:
Josh Hawkins
2026-08-22 11:40:42 -05:00
parent 8de6216c61
commit d67304a84d
68 changed files with 6813 additions and 602 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+25
View File
@@ -867,6 +867,31 @@ test.describe("Multi-Camera Export from History @high", () => {
await frigateApp.page.route("**/api/recordings/unavailable**", (route) =>
route.fulfill({ json: [] }),
);
// registered after the broad recordings route so it wins the match;
// coverage is an object, and the array above crashes the player
await frigateApp.page.route("**/api/*/recordings/coverage**", (route) =>
route.fulfill({
json: {
spans: [
{
start_time: playbackTime - 3600,
end_time: playbackTime + 600,
streams: ["main"],
},
],
codecs_compatible: true,
streams: {
main: {
video_codec: "h264",
audio_rate: null,
audio_codec: null,
has_audio: false,
bitrate: 2_000_000,
},
},
},
}),
);
await frigateApp.goto(`/review?timestamp=front_door_${playbackTime}`);
}