From d0ad840ef40aea9575aa89904bc77a08d51e7de8 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Fri, 20 Dec 2024 08:17:51 -0600 Subject: [PATCH] Enable temporary caching of camera images to improve responsiveness of UI (#15614) --- .../api/defs/query/media_query_parameters.py | 1 + frigate/api/media.py | 18 ++++++++++---- .../camera/AutoUpdatingCameraImage.tsx | 24 +++++++++++++++++-- web/src/components/player/LivePlayer.tsx | 3 ++- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/frigate/api/defs/query/media_query_parameters.py b/frigate/api/defs/query/media_query_parameters.py index b7df85d30..bcccb99bf 100644 --- a/frigate/api/defs/query/media_query_parameters.py +++ b/frigate/api/defs/query/media_query_parameters.py @@ -20,6 +20,7 @@ class MediaLatestFrameQueryParams(BaseModel): regions: Optional[int] = None quality: Optional[int] = 70 height: Optional[int] = None + store: Optional[int] = None class MediaEventsSnapshotQueryParams(BaseModel): diff --git a/frigate/api/media.py b/frigate/api/media.py index b5f3ba703..6cd11b30e 100644 --- a/frigate/api/media.py +++ b/frigate/api/media.py @@ -182,11 +182,16 @@ def latest_frame( frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA) - ret, img = cv2.imencode(f".{extension}", frame, quality_params) + _, img = cv2.imencode(f".{extension}", frame, quality_params) return Response( content=img.tobytes(), media_type=f"image/{mime_type}", - headers={"Content-Type": f"image/{mime_type}", "Cache-Control": "no-store"}, + headers={ + "Content-Type": f"image/{mime_type}", + "Cache-Control": "no-store" + if not params.store + else "private, max-age=60", + }, ) elif camera_name == "birdseye" and request.app.frigate_config.birdseye.restream: frame = cv2.cvtColor( @@ -199,11 +204,16 @@ def latest_frame( frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA) - ret, img = cv2.imencode(f".{extension}", frame, quality_params) + _, img = cv2.imencode(f".{extension}", frame, quality_params) return Response( content=img.tobytes(), media_type=f"image/{mime_type}", - headers={"Content-Type": f"image/{mime_type}", "Cache-Control": "no-store"}, + headers={ + "Content-Type": f"image/{mime_type}", + "Cache-Control": "no-store" + if not params.store + else "private, max-age=60", + }, ) else: return JSONResponse( diff --git a/web/src/components/camera/AutoUpdatingCameraImage.tsx b/web/src/components/camera/AutoUpdatingCameraImage.tsx index ee0f6eccc..d97a9214a 100644 --- a/web/src/components/camera/AutoUpdatingCameraImage.tsx +++ b/web/src/components/camera/AutoUpdatingCameraImage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import CameraImage from "./CameraImage"; type AutoUpdatingCameraImageProps = { @@ -8,6 +8,7 @@ type AutoUpdatingCameraImageProps = { className?: string; cameraClasses?: string; reloadInterval?: number; + periodicCache?: boolean; }; const MIN_LOAD_TIMEOUT_MS = 200; @@ -19,6 +20,7 @@ export default function AutoUpdatingCameraImage({ className, cameraClasses, reloadInterval = MIN_LOAD_TIMEOUT_MS, + periodicCache = false, }: AutoUpdatingCameraImageProps) { const [key, setKey] = useState(Date.now()); const [fps, setFps] = useState("0"); @@ -42,6 +44,8 @@ export default function AutoUpdatingCameraImage({ }, [reloadInterval]); const handleLoad = useCallback(() => { + setIsCached(true); + if (reloadInterval == -1) { return; } @@ -66,12 +70,28 @@ export default function AutoUpdatingCameraImage({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [key, setFps]); + // periodic cache to reduce loading indicator + + const [isCached, setIsCached] = useState(false); + + const cacheKey = useMemo(() => { + let baseParam = ""; + + if (periodicCache && !isCached) { + baseParam = "store=1"; + } else { + baseParam = `cache=${key}`; + } + + return `${baseParam}${searchParams ? `&${searchParams}` : ""}`; + }, [isCached, periodicCache, key, searchParams]); + return (
{showFps ? Displaying at {fps}fps : null} diff --git a/web/src/components/player/LivePlayer.tsx b/web/src/components/player/LivePlayer.tsx index 8038812db..abf908baa 100644 --- a/web/src/components/player/LivePlayer.tsx +++ b/web/src/components/player/LivePlayer.tsx @@ -294,10 +294,11 @@ export default function LivePlayer({ >