diff --git a/frigate/test/test_recording_coverage.py b/frigate/test/test_recording_coverage.py index bb502f333a..b264a31f8a 100644 --- a/frigate/test/test_recording_coverage.py +++ b/frigate/test/test_recording_coverage.py @@ -13,6 +13,7 @@ from frigate.models import Recordings from frigate.util.recording_coverage import ( _rows_query, coverage_spans, + null_audio_glitches, plan_clip, realized_timeline, resolve_coverage, @@ -166,6 +167,22 @@ class TestRecordingCoverage(CoverageDbTestCase): expected = int(5 * 1024 * 1024 * 8 / 10) assert summary["main"]["bitrate"] == expected + def test_unknown_audio_row_keeps_video_only_stream(self): + self._insert("s1", 1000.0, 1010.0, "sub", has_audio=False) + self._insert("s2", 1010.0, 1020.0, "sub", has_audio=None) + self._insert("s3", 1020.0, 1030.0, "sub", has_audio=False) + kept = null_audio_glitches(resolve_coverage("front_door", 1000.0, 1030.0)) + self.assertEqual( + [i.sub.path for i in kept], [f"/tmp/s{n}.mp4" for n in (1, 2, 3)] + ) + + def test_video_only_glitch_dropped_on_audio_stream(self): + self._insert("s1", 1000.0, 1010.0, "sub", has_audio=True) + self._insert("s2", 1010.0, 1020.0, "sub", has_audio=False) + self._insert("s3", 1020.0, 1030.0, "sub", has_audio=None) + kept = null_audio_glitches(resolve_coverage("front_door", 1000.0, 1030.0)) + self.assertEqual([i.sub.path for i in kept], ["/tmp/s1.mp4", "/tmp/s3.mp4"]) + def test_other_camera_rows_excluded(self): self._insert("m1", 1000.0, 1010.0, "main") self._insert("o1", 1000.0, 1010.0, "main", camera="back_yard") diff --git a/frigate/util/recording_coverage.py b/frigate/util/recording_coverage.py index 96a3ec5292..55ca2449a9 100644 --- a/frigate/util/recording_coverage.py +++ b/frigate/util/recording_coverage.py @@ -204,11 +204,12 @@ def coverage_spans(intervals: list[CoverageInterval]) -> list[dict[str, Any]]: def stream_has_audio(intervals: list[CoverageInterval], main: bool) -> bool: """Whether a stream is audio-bearing over a coverage window. - A stream counts as audio-bearing unless EVERY one of its rows reports - has_audio False; NULL (legacy or undetermined) counts as audio. + A stream counts as audio-bearing only when one of its rows is known + to carry audio. A NULL row (legacy, or a segment ffprobe could not + read) proves nothing either way. """ return any( - row is not None and row.has_audio is not False + row is not None and row.has_audio is True for row in ((interval.main if main else interval.sub) for interval in intervals) )