mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-04-24 01:16:47 +02:00
Enable temporary caching of camera images to improve responsiveness of UI (#15614)
This commit is contained in:
parent
edab4efa42
commit
d0ad840ef4
@ -20,6 +20,7 @@ class MediaLatestFrameQueryParams(BaseModel):
|
|||||||
regions: Optional[int] = None
|
regions: Optional[int] = None
|
||||||
quality: Optional[int] = 70
|
quality: Optional[int] = 70
|
||||||
height: Optional[int] = None
|
height: Optional[int] = None
|
||||||
|
store: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
class MediaEventsSnapshotQueryParams(BaseModel):
|
class MediaEventsSnapshotQueryParams(BaseModel):
|
||||||
|
@ -182,11 +182,16 @@ def latest_frame(
|
|||||||
|
|
||||||
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
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(
|
return Response(
|
||||||
content=img.tobytes(),
|
content=img.tobytes(),
|
||||||
media_type=f"image/{mime_type}",
|
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:
|
elif camera_name == "birdseye" and request.app.frigate_config.birdseye.restream:
|
||||||
frame = cv2.cvtColor(
|
frame = cv2.cvtColor(
|
||||||
@ -199,11 +204,16 @@ def latest_frame(
|
|||||||
|
|
||||||
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
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(
|
return Response(
|
||||||
content=img.tobytes(),
|
content=img.tobytes(),
|
||||||
media_type=f"image/{mime_type}",
|
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:
|
else:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import CameraImage from "./CameraImage";
|
import CameraImage from "./CameraImage";
|
||||||
|
|
||||||
type AutoUpdatingCameraImageProps = {
|
type AutoUpdatingCameraImageProps = {
|
||||||
@ -8,6 +8,7 @@ type AutoUpdatingCameraImageProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
cameraClasses?: string;
|
cameraClasses?: string;
|
||||||
reloadInterval?: number;
|
reloadInterval?: number;
|
||||||
|
periodicCache?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MIN_LOAD_TIMEOUT_MS = 200;
|
const MIN_LOAD_TIMEOUT_MS = 200;
|
||||||
@ -19,6 +20,7 @@ export default function AutoUpdatingCameraImage({
|
|||||||
className,
|
className,
|
||||||
cameraClasses,
|
cameraClasses,
|
||||||
reloadInterval = MIN_LOAD_TIMEOUT_MS,
|
reloadInterval = MIN_LOAD_TIMEOUT_MS,
|
||||||
|
periodicCache = false,
|
||||||
}: AutoUpdatingCameraImageProps) {
|
}: AutoUpdatingCameraImageProps) {
|
||||||
const [key, setKey] = useState(Date.now());
|
const [key, setKey] = useState(Date.now());
|
||||||
const [fps, setFps] = useState<string>("0");
|
const [fps, setFps] = useState<string>("0");
|
||||||
@ -42,6 +44,8 @@ export default function AutoUpdatingCameraImage({
|
|||||||
}, [reloadInterval]);
|
}, [reloadInterval]);
|
||||||
|
|
||||||
const handleLoad = useCallback(() => {
|
const handleLoad = useCallback(() => {
|
||||||
|
setIsCached(true);
|
||||||
|
|
||||||
if (reloadInterval == -1) {
|
if (reloadInterval == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,12 +70,28 @@ export default function AutoUpdatingCameraImage({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [key, setFps]);
|
}, [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 (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<CameraImage
|
<CameraImage
|
||||||
camera={camera}
|
camera={camera}
|
||||||
onload={handleLoad}
|
onload={handleLoad}
|
||||||
searchParams={`cache=${key}${searchParams ? `&${searchParams}` : ""}`}
|
searchParams={cacheKey}
|
||||||
className={cameraClasses}
|
className={cameraClasses}
|
||||||
/>
|
/>
|
||||||
{showFps ? <span className="text-xs">Displaying at {fps}fps</span> : null}
|
{showFps ? <span className="text-xs">Displaying at {fps}fps</span> : null}
|
||||||
|
@ -294,10 +294,11 @@ export default function LivePlayer({
|
|||||||
>
|
>
|
||||||
<AutoUpdatingCameraImage
|
<AutoUpdatingCameraImage
|
||||||
className="size-full"
|
className="size-full"
|
||||||
|
cameraClasses="relative size-full flex justify-center"
|
||||||
camera={cameraConfig.name}
|
camera={cameraConfig.name}
|
||||||
showFps={false}
|
showFps={false}
|
||||||
reloadInterval={stillReloadInterval}
|
reloadInterval={stillReloadInterval}
|
||||||
cameraClasses="relative size-full flex justify-center"
|
periodicCache
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user