Skip to content

Commit

Permalink
Live camera aspect ratio fixes (#10266)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hawkeye217 authored Mar 5, 2024
1 parent 7be2923 commit ed99be0
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 15 deletions.
4 changes: 2 additions & 2 deletions web/src/components/player/LivePlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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}
Expand Down
16 changes: 12 additions & 4 deletions web/src/hooks/resize-observer.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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);
}
});
Expand Down
37 changes: 29 additions & 8 deletions web/src/views/live/LiveCameraView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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%]";
}
Expand All @@ -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}
Expand Down Expand Up @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion web/src/views/live/LiveDashboardView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down

0 comments on commit ed99be0

Please sign in to comment.