mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-04-01 01:17:00 +02:00
Live camera aspect ratio fixes (#10266)
* dynamically manage aspect ratio * full size * always use camera aspect ratio for mobile * no need for different handling for pano cams * don't set aspect ratio on fullscreen
This commit is contained in:
parent
7be2923d2d
commit
ed99be0856
@ -93,7 +93,7 @@ export default function LivePlayer({
|
|||||||
if (liveMode == "webrtc") {
|
if (liveMode == "webrtc") {
|
||||||
player = (
|
player = (
|
||||||
<WebRtcPlayer
|
<WebRtcPlayer
|
||||||
className={`rounded-2xl h-full ${liveReady ? "" : "hidden"}`}
|
className={`rounded-2xl size-full ${liveReady ? "" : "hidden"}`}
|
||||||
camera={cameraConfig.live.stream_name}
|
camera={cameraConfig.live.stream_name}
|
||||||
playbackEnabled={cameraActive}
|
playbackEnabled={cameraActive}
|
||||||
audioEnabled={playAudio}
|
audioEnabled={playAudio}
|
||||||
@ -104,7 +104,7 @@ export default function LivePlayer({
|
|||||||
if ("MediaSource" in window || "ManagedMediaSource" in window) {
|
if ("MediaSource" in window || "ManagedMediaSource" in window) {
|
||||||
player = (
|
player = (
|
||||||
<MSEPlayer
|
<MSEPlayer
|
||||||
className={`rounded-2xl h-full ${liveReady ? "" : "hidden"}`}
|
className={`rounded-2xl size-full ${liveReady ? "" : "hidden"}`}
|
||||||
camera={cameraConfig.name}
|
camera={cameraConfig.name}
|
||||||
playbackEnabled={cameraActive}
|
playbackEnabled={cameraActive}
|
||||||
audioEnabled={playAudio}
|
audioEnabled={playAudio}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { MutableRefObject, useEffect, useMemo, useState } from "react";
|
import { MutableRefObject, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
export function useResizeObserver(...refs: MutableRefObject<Element | null>[]) {
|
type RefType = MutableRefObject<Element | null> | Window;
|
||||||
const [dimensions, setDimensions] = useState(
|
|
||||||
|
export function useResizeObserver(...refs: RefType[]) {
|
||||||
|
const [dimensions, setDimensions] = useState<
|
||||||
|
{ width: number; height: number; x: number; y: number }[]
|
||||||
|
>(
|
||||||
new Array(refs.length).fill({
|
new Array(refs.length).fill({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@ -21,14 +25,18 @@ export function useResizeObserver(...refs: MutableRefObject<Element | null>[]) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refs.forEach((ref) => {
|
refs.forEach((ref) => {
|
||||||
if (ref.current) {
|
if (ref instanceof Window) {
|
||||||
|
resizeObserver.observe(document.body);
|
||||||
|
} else if (ref.current) {
|
||||||
resizeObserver.observe(ref.current);
|
resizeObserver.observe(ref.current);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
refs.forEach((ref) => {
|
refs.forEach((ref) => {
|
||||||
if (ref.current) {
|
if (ref instanceof Window) {
|
||||||
|
resizeObserver.unobserve(document.body);
|
||||||
|
} else if (ref.current) {
|
||||||
resizeObserver.unobserve(ref.current);
|
resizeObserver.unobserve(ref.current);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
|
import { useResizeObserver } from "@/hooks/resize-observer";
|
||||||
import useKeyboardListener from "@/hooks/use-keyboard-listener";
|
import useKeyboardListener from "@/hooks/use-keyboard-listener";
|
||||||
import { CameraConfig } from "@/types/frigateConfig";
|
import { CameraConfig } from "@/types/frigateConfig";
|
||||||
import { CameraPtzInfo } from "@/types/ptz";
|
import { CameraPtzInfo } from "@/types/ptz";
|
||||||
@ -61,6 +62,8 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { isPortrait } = useMobileOrientation();
|
const { isPortrait } = useMobileOrientation();
|
||||||
const mainRef = useRef<HTMLDivElement | null>(null);
|
const mainRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [{ width: windowWidth, height: windowHeight }] =
|
||||||
|
useResizeObserver(window);
|
||||||
|
|
||||||
// camera features
|
// camera features
|
||||||
|
|
||||||
@ -105,7 +108,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
|||||||
return "absolute left-2 right-2 top-[50%] -translate-y-[50%]";
|
return "absolute left-2 right-2 top-[50%] -translate-y-[50%]";
|
||||||
} else {
|
} else {
|
||||||
if (aspect > 16 / 9) {
|
if (aspect > 16 / 9) {
|
||||||
return "absolute left-12 top-[50%] -translate-y-[50%]";
|
return "absolute left-0 top-[50%] -translate-y-[50%]";
|
||||||
} else {
|
} else {
|
||||||
return "absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
|
return "absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
|
||||||
}
|
}
|
||||||
@ -114,17 +117,33 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
|||||||
|
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
if (aspect > 16 / 9) {
|
if (aspect > 16 / 9) {
|
||||||
return "absolute inset-x-0 top-[50%] -translate-y-[50%]";
|
return "absolute inset-x-2 top-[50%] -translate-y-[50%]";
|
||||||
} else {
|
} else {
|
||||||
return "absolute inset-y-0 left-[50%] -translate-x-[50%]";
|
return "absolute inset-y-2 left-[50%] -translate-x-[50%]";
|
||||||
}
|
}
|
||||||
} else if (aspect > 2) {
|
|
||||||
return "absolute left-2 right-2 top-[50%] -translate-y-[50%]";
|
|
||||||
} else {
|
} else {
|
||||||
return "absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
|
return "absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
|
||||||
}
|
}
|
||||||
}, [camera, fullscreen, isPortrait]);
|
}, [camera, fullscreen, isPortrait]);
|
||||||
|
|
||||||
|
const windowAspectRatio = useMemo(() => {
|
||||||
|
return windowWidth / windowHeight;
|
||||||
|
}, [windowWidth, windowHeight]);
|
||||||
|
|
||||||
|
const cameraAspectRatio = useMemo(() => {
|
||||||
|
return camera.detect.width / camera.detect.height;
|
||||||
|
}, [camera]);
|
||||||
|
|
||||||
|
const aspectRatio = useMemo<number>(() => {
|
||||||
|
if (isMobile || fullscreen) {
|
||||||
|
return cameraAspectRatio;
|
||||||
|
} else {
|
||||||
|
return windowAspectRatio < cameraAspectRatio
|
||||||
|
? windowAspectRatio - 0.05
|
||||||
|
: cameraAspectRatio - 0.03;
|
||||||
|
}
|
||||||
|
}, [cameraAspectRatio, windowAspectRatio, fullscreen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={mainRef}
|
ref={mainRef}
|
||||||
@ -216,14 +235,16 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
|||||||
</div>
|
</div>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative size-full">
|
<div className="relative size-full p-2">
|
||||||
<div
|
<div
|
||||||
className={growClassName}
|
className={growClassName}
|
||||||
style={{ aspectRatio: camera.detect.width / camera.detect.height }}
|
style={{
|
||||||
|
aspectRatio: aspectRatio,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<LivePlayer
|
<LivePlayer
|
||||||
key={camera.name}
|
key={camera.name}
|
||||||
className={`size-full ${fullscreen ? "*:rounded-none" : ""}`}
|
className={`${fullscreen ? "*:rounded-none" : ""}`}
|
||||||
windowVisible
|
windowVisible
|
||||||
showStillWithoutActivity={false}
|
showStillWithoutActivity={false}
|
||||||
cameraConfig={camera}
|
cameraConfig={camera}
|
||||||
|
@ -75,7 +75,7 @@ export default function LiveDashboardView({
|
|||||||
}, [visibilityListener]);
|
}, [visibilityListener]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="size-full overflow-y-scroll px-2">
|
<div className="size-full overflow-y-auto px-2">
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<div className="relative h-9 flex items-center justify-between">
|
<div className="relative h-9 flex items-center justify-between">
|
||||||
<Logo className="absolute inset-y-0 inset-x-1/2 -translate-x-1/2 h-8" />
|
<Logo className="absolute inset-y-0 inset-x-1/2 -translate-x-1/2 h-8" />
|
||||||
|
Loading…
Reference in New Issue
Block a user