mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-07 07:12:50 +03:00
report null instead of 0 for a stream with no cached bandwidth sample
This commit is contained in:
+4
-2
@@ -156,11 +156,13 @@ class StorageMaintainer(threading.Thread):
|
|||||||
"bandwidth", 0
|
"bandwidth", 0
|
||||||
),
|
),
|
||||||
# only streams with segments on disk are reported, so a camera
|
# only streams with segments on disk are reported, so a camera
|
||||||
# keeps its sub entry until sub retention expires those segments
|
# keeps its sub entry until sub retention expires those segments.
|
||||||
|
# bandwidth is null rather than 0 when the cache holds no sample
|
||||||
|
# for the stream, since 0 would claim it writes nothing
|
||||||
"streams": {
|
"streams": {
|
||||||
stream_type: {
|
stream_type: {
|
||||||
"usage": stream_usages[stream_type],
|
"usage": stream_usages[stream_type],
|
||||||
"bandwidth": stream_bandwidths.get(stream_type, 0),
|
"bandwidth": stream_bandwidths.get(stream_type),
|
||||||
}
|
}
|
||||||
for stream_type in (STREAM_TYPE_MAIN, STREAM_TYPE_SUB)
|
for stream_type in (STREAM_TYPE_MAIN, STREAM_TYPE_SUB)
|
||||||
if stream_usages.get(stream_type)
|
if stream_usages.get(stream_type)
|
||||||
|
|||||||
@@ -283,6 +283,49 @@ class TestHttp(unittest.TestCase):
|
|||||||
== MAX_CALCULATED_BANDWIDTH
|
== MAX_CALCULATED_BANDWIDTH
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_stream_bandwidth_is_none_without_a_cached_sample(self):
|
||||||
|
"""A stream that appears after the bandwidth cache freezes has no estimate.
|
||||||
|
|
||||||
|
Sub stream recording can be toggled on at runtime, so the cache can hold
|
||||||
|
a main-only sample while sub segments are already landing on disk.
|
||||||
|
Reporting 0 there would claim the sub stream costs nothing.
|
||||||
|
"""
|
||||||
|
config = FrigateConfig(**self.minimal_config)
|
||||||
|
storage = StorageMaintainer(config, MagicMock())
|
||||||
|
|
||||||
|
time_keep = datetime.datetime.now().timestamp()
|
||||||
|
for i in range(60):
|
||||||
|
_insert_mock_recording(
|
||||||
|
f"main_{i}.frontdoor",
|
||||||
|
os.path.join(self.test_dir, f"main_{i}.tmp"),
|
||||||
|
time_keep + i * 10,
|
||||||
|
time_keep + i * 10 + 10,
|
||||||
|
seg_size=20,
|
||||||
|
seg_dur=10,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 50 or more segments flips needs_refresh off, freezing the cache
|
||||||
|
storage.calculate_camera_bandwidth()
|
||||||
|
assert storage.camera_storage_stats["front_door"]["needs_refresh"] is False
|
||||||
|
|
||||||
|
for i in range(60):
|
||||||
|
_insert_mock_recording(
|
||||||
|
f"sub_{i}.frontdoor",
|
||||||
|
os.path.join(self.test_dir, f"sub_{i}.tmp"),
|
||||||
|
time_keep + 5000 + i * 10,
|
||||||
|
time_keep + 5000 + i * 10 + 10,
|
||||||
|
seg_size=2,
|
||||||
|
seg_dur=10,
|
||||||
|
stream_type=STREAM_TYPE_SUB,
|
||||||
|
)
|
||||||
|
|
||||||
|
storage.calculate_camera_bandwidth()
|
||||||
|
streams = storage.calculate_camera_usages()["front_door"]["streams"]
|
||||||
|
|
||||||
|
assert streams[STREAM_TYPE_SUB]["usage"] == 120
|
||||||
|
assert streams[STREAM_TYPE_SUB]["bandwidth"] is None
|
||||||
|
assert streams[STREAM_TYPE_MAIN]["bandwidth"] == 7200
|
||||||
|
|
||||||
def test_camera_usages_with_no_recordings(self):
|
def test_camera_usages_with_no_recordings(self):
|
||||||
"""A camera with no segments reports zero usage and no streams."""
|
"""A camera with no segments reports zero usage and no streams."""
|
||||||
config = FrigateConfig(**self.minimal_config)
|
config = FrigateConfig(**self.minimal_config)
|
||||||
|
|||||||
@@ -204,9 +204,12 @@ export function CombinedStorageGraph({
|
|||||||
|
|
||||||
const getStreamSplit = useCallback(
|
const getStreamSplit = useCallback(
|
||||||
(row: StorageRow, field: keyof StreamStorage) => {
|
(row: StorageRow, field: keyof StreamStorage) => {
|
||||||
const sub = row.streams?.sub;
|
const mainValue = row.streams?.main?.[field];
|
||||||
|
const subValue = row.streams?.sub?.[field];
|
||||||
|
|
||||||
if (!sub) {
|
// omit the split entirely when either side is unknown, rather than
|
||||||
|
// showing a zero that would read as "this stream costs nothing"
|
||||||
|
if (mainValue == null || subValue == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,13 +219,13 @@ export function CombinedStorageGraph({
|
|||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
{t("quality.main", { ns: "components/player" })}
|
{t("quality.main", { ns: "components/player" })}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
{getUnitSize(row.streams?.main?.[field] ?? 0)}
|
{getUnitSize(mainValue)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
{t("quality.sub", { ns: "components/player" })}
|
{t("quality.sub", { ns: "components/player" })}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
{getUnitSize(sub[field])}
|
{getUnitSize(subValue)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export type StorageStats = {
|
|||||||
|
|
||||||
export type StreamStorage = {
|
export type StreamStorage = {
|
||||||
usage: number;
|
usage: number;
|
||||||
bandwidth: number;
|
bandwidth: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CameraStorage = {
|
export type CameraStorage = {
|
||||||
|
|||||||
Reference in New Issue
Block a user