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:
Josh Hawkins 2024-03-05 14:19:56 -06:00 committed by GitHub
parent 7be2923d2d
commit ed99be0856
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 44 additions and 15 deletions

View File

@ -93,7 +93,7 @@ export default function LivePlayer({
if (liveMode == "webrtc") {
player = (
<WebRtcPlayer
className={`rounded-2xl h-full ${liveReady ? "" : "hidden"}`}
className={`rounded-2xl size-full ${liveReady ? "" : "hidden"}`}
camera={cameraConfig.live.stream_name}
playbackEnabled={cameraActive}
audioEnabled={playAudio}
@ -104,7 +104,7 @@ export default function LivePlayer({
if ("MediaSource" in window || "ManagedMediaSource" in window) {
player = (
<MSEPlayer
className={`rounded-2xl h-full ${liveReady ? "" : "hidden"}`}
className={`rounded-2xl size-full ${liveReady ? "" : "hidden"}`}
camera={cameraConfig.name}
playbackEnabled={cameraActive}
audioEnabled={playAudio}

View File

@ -1,7 +1,11 @@
import { MutableRefObject, useEffect, useMemo, useState } from "react";
export function useResizeObserver(...refs: MutableRefObject<Element | null>[]) {
const [dimensions, setDimensions] = useState(
type RefType = MutableRefObject<Element | null> | Window;
export function useResizeObserver(...refs: RefType[]) {
const [dimensions, setDimensions] = useState<
{ width: number; height: number; x: number; y: number }[]
>(
new Array(refs.length).fill({
width: 0,
height: 0,
@ -21,14 +25,18 @@ export function useResizeObserver(...refs: MutableRefObject<Element | null>[]) {
useEffect(() => {
refs.forEach((ref) => {
if (ref.current) {
if (ref instanceof Window) {
resizeObserver.observe(document.body);
} else if (ref.current) {
resizeObserver.observe(ref.current);
}
});
return () => {
refs.forEach((ref) => {
if (ref.current) {
if (ref instanceof Window) {
resizeObserver.unobserve(document.body);
} else if (ref.current) {
resizeObserver.unobserve(ref.current);
}
});

View File

@ -15,6 +15,7 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { TooltipProvider } from "@/components/ui/tooltip";
import { useResizeObserver } from "@/hooks/resize-observer";
import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { CameraConfig } from "@/types/frigateConfig";
import { CameraPtzInfo } from "@/types/ptz";
@ -61,6 +62,8 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
const navigate = useNavigate();
const { isPortrait } = useMobileOrientation();
const mainRef = useRef<HTMLDivElement | null>(null);
const [{ width: windowWidth, height: windowHeight }] =
useResizeObserver(window);
// camera features
@ -105,7 +108,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
return "absolute left-2 right-2 top-[50%] -translate-y-[50%]";
} else {
if (aspect > 16 / 9) {
return "absolute left-12 top-[50%] -translate-y-[50%]";
return "absolute left-0 top-[50%] -translate-y-[50%]";
} else {
return "absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
}
@ -114,17 +117,33 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
if (fullscreen) {
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 {
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 {
return "absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
}
}, [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 (
<div
ref={mainRef}
@ -216,14 +235,16 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
</div>
</TooltipProvider>
</div>
<div className="relative size-full">
<div className="relative size-full p-2">
<div
className={growClassName}
style={{ aspectRatio: camera.detect.width / camera.detect.height }}
style={{
aspectRatio: aspectRatio,
}}
>
<LivePlayer
key={camera.name}
className={`size-full ${fullscreen ? "*:rounded-none" : ""}`}
className={`${fullscreen ? "*:rounded-none" : ""}`}
windowVisible
showStillWithoutActivity={false}
cameraConfig={camera}

View File

@ -75,7 +75,7 @@ export default function LiveDashboardView({
}, [visibilityListener]);
return (
<div className="size-full overflow-y-scroll px-2">
<div className="size-full overflow-y-auto px-2">
{isMobile && (
<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" />