mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-07 15:22:48 +03:00
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:
committed by
Nicolas Mowen
parent
f7c5500ea8
commit
1498231eb9
@@ -615,16 +615,16 @@ export default function MotionSearchView({
|
||||
}, [selectedRangeIdx, chunkedTimeRange]);
|
||||
|
||||
const updateSelectedSegment = useCallback(
|
||||
(nextTime: number, updateStartTime: boolean) => {
|
||||
(nextTime: number) => {
|
||||
const index = chunkedTimeRange.findIndex(
|
||||
(segment) => segment.after <= nextTime && segment.before >= nextTime,
|
||||
);
|
||||
|
||||
if (index != -1) {
|
||||
if (updateStartTime) {
|
||||
setPlaybackStart(nextTime);
|
||||
}
|
||||
|
||||
setPlaybackStart(nextTime);
|
||||
// the outgoing chunk's player runs until the new source replaces
|
||||
// it, reporting old positions while the new chunk loads
|
||||
mainControllerRef.current?.pause();
|
||||
setSelectedRangeIdx(index);
|
||||
}
|
||||
},
|
||||
@@ -638,7 +638,10 @@ export default function MotionSearchView({
|
||||
currentTime > currentTimeRange.before + 60 ||
|
||||
currentTime < currentTimeRange.after - 60
|
||||
) {
|
||||
updateSelectedSegment(currentTime, false);
|
||||
// the player rebuilds its source against playbackStart, and a
|
||||
// stale anchor resolves to no startPosition, dropping playback
|
||||
// at the start of the hour instead of the drag target
|
||||
updateSelectedSegment(currentTime);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -678,9 +681,12 @@ export default function MotionSearchView({
|
||||
currentTimeRange.after <= currentTime &&
|
||||
currentTimeRange.before >= currentTime
|
||||
) {
|
||||
// a source reload mid-seek resumes from playbackStart, so the
|
||||
// anchor has to follow explicit seeks
|
||||
setPlaybackStart(currentTime);
|
||||
mainControllerRef.current?.seekToTimestamp(currentTime, true);
|
||||
} else {
|
||||
updateSelectedSegment(currentTime, true);
|
||||
updateSelectedSegment(currentTime);
|
||||
}
|
||||
} else if (playerTime != currentTime) {
|
||||
mainControllerRef.current?.play();
|
||||
@@ -700,9 +706,12 @@ export default function MotionSearchView({
|
||||
setCurrentTime(time);
|
||||
|
||||
if (currentTimeRange.after <= time && currentTimeRange.before >= time) {
|
||||
// a source reload mid-seek resumes from playbackStart, so the
|
||||
// anchor has to follow explicit seeks
|
||||
setPlaybackStart(time);
|
||||
mainControllerRef.current?.seekToTimestamp(time, play);
|
||||
} else {
|
||||
updateSelectedSegment(time, true);
|
||||
updateSelectedSegment(time);
|
||||
}
|
||||
},
|
||||
[currentTimeRange, updateSelectedSegment],
|
||||
|
||||
Reference in New Issue
Block a user