mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Use full resolution aspect ratio when available (#11173)
* base recordings and live views off of actual video resolution * don't set for jsmpeg * reset when changing main cam * rename * Only use resolution for main camera * fix lint --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
		
							parent
							
								
									1c9626ecff
								
							
						
					
					
						commit
						11ff7cb2b7
					
				@ -1,8 +1,15 @@
 | 
				
			|||||||
import { MutableRefObject, useEffect, useRef, useState } from "react";
 | 
					import {
 | 
				
			||||||
 | 
					  MutableRefObject,
 | 
				
			||||||
 | 
					  useCallback,
 | 
				
			||||||
 | 
					  useEffect,
 | 
				
			||||||
 | 
					  useRef,
 | 
				
			||||||
 | 
					  useState,
 | 
				
			||||||
 | 
					} from "react";
 | 
				
			||||||
import Hls from "hls.js";
 | 
					import Hls from "hls.js";
 | 
				
			||||||
import { isAndroid, isDesktop, isMobile } from "react-device-detect";
 | 
					import { isAndroid, isDesktop, isMobile } from "react-device-detect";
 | 
				
			||||||
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
 | 
					import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
 | 
				
			||||||
import VideoControls from "./VideoControls";
 | 
					import VideoControls from "./VideoControls";
 | 
				
			||||||
 | 
					import { VideoResolutionType } from "@/types/live";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Android native hls does not seek correctly
 | 
					// Android native hls does not seek correctly
 | 
				
			||||||
const USE_NATIVE_HLS = !isAndroid;
 | 
					const USE_NATIVE_HLS = !isAndroid;
 | 
				
			||||||
@ -21,6 +28,7 @@ type HlsVideoPlayerProps = {
 | 
				
			|||||||
  onPlayerLoaded?: () => void;
 | 
					  onPlayerLoaded?: () => void;
 | 
				
			||||||
  onTimeUpdate?: (time: number) => void;
 | 
					  onTimeUpdate?: (time: number) => void;
 | 
				
			||||||
  onPlaying?: () => void;
 | 
					  onPlaying?: () => void;
 | 
				
			||||||
 | 
					  setFullResolution?: React.Dispatch<React.SetStateAction<VideoResolutionType>>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export default function HlsVideoPlayer({
 | 
					export default function HlsVideoPlayer({
 | 
				
			||||||
  videoRef,
 | 
					  videoRef,
 | 
				
			||||||
@ -31,6 +39,7 @@ export default function HlsVideoPlayer({
 | 
				
			|||||||
  onPlayerLoaded,
 | 
					  onPlayerLoaded,
 | 
				
			||||||
  onTimeUpdate,
 | 
					  onTimeUpdate,
 | 
				
			||||||
  onPlaying,
 | 
					  onPlaying,
 | 
				
			||||||
 | 
					  setFullResolution,
 | 
				
			||||||
}: HlsVideoPlayerProps) {
 | 
					}: HlsVideoPlayerProps) {
 | 
				
			||||||
  // playback
 | 
					  // playback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,6 +47,18 @@ export default function HlsVideoPlayer({
 | 
				
			|||||||
  const [useHlsCompat, setUseHlsCompat] = useState(false);
 | 
					  const [useHlsCompat, setUseHlsCompat] = useState(false);
 | 
				
			||||||
  const [loadedMetadata, setLoadedMetadata] = useState(false);
 | 
					  const [loadedMetadata, setLoadedMetadata] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleLoadedMetadata = useCallback(() => {
 | 
				
			||||||
 | 
					    setLoadedMetadata(true);
 | 
				
			||||||
 | 
					    if (videoRef.current) {
 | 
				
			||||||
 | 
					      if (setFullResolution) {
 | 
				
			||||||
 | 
					        setFullResolution({
 | 
				
			||||||
 | 
					          width: videoRef.current.videoWidth,
 | 
				
			||||||
 | 
					          height: videoRef.current.videoHeight,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [videoRef, setFullResolution]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (!videoRef.current) {
 | 
					    if (!videoRef.current) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
@ -193,7 +214,7 @@ export default function HlsVideoPlayer({
 | 
				
			|||||||
              : undefined
 | 
					              : undefined
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          onLoadedData={onPlayerLoaded}
 | 
					          onLoadedData={onPlayerLoaded}
 | 
				
			||||||
          onLoadedMetadata={() => setLoadedMetadata(true)}
 | 
					          onLoadedMetadata={handleLoadedMetadata}
 | 
				
			||||||
          onEnded={onClipEnded}
 | 
					          onEnded={onClipEnded}
 | 
				
			||||||
          onError={(e) => {
 | 
					          onError={(e) => {
 | 
				
			||||||
            if (
 | 
					            if (
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import JSMpegPlayer from "./JSMpegPlayer";
 | 
				
			|||||||
import { MdCircle } from "react-icons/md";
 | 
					import { MdCircle } from "react-icons/md";
 | 
				
			||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
 | 
					import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
 | 
				
			||||||
import { useCameraActivity } from "@/hooks/use-camera-activity";
 | 
					import { useCameraActivity } from "@/hooks/use-camera-activity";
 | 
				
			||||||
import { LivePlayerMode } from "@/types/live";
 | 
					import { LivePlayerMode, VideoResolutionType } from "@/types/live";
 | 
				
			||||||
import useCameraLiveMode from "@/hooks/use-camera-live-mode";
 | 
					import useCameraLiveMode from "@/hooks/use-camera-live-mode";
 | 
				
			||||||
import { getIconForLabel } from "@/utils/iconUtil";
 | 
					import { getIconForLabel } from "@/utils/iconUtil";
 | 
				
			||||||
import Chip from "../indicators/Chip";
 | 
					import Chip from "../indicators/Chip";
 | 
				
			||||||
@ -27,6 +27,7 @@ type LivePlayerProps = {
 | 
				
			|||||||
  iOSCompatFullScreen?: boolean;
 | 
					  iOSCompatFullScreen?: boolean;
 | 
				
			||||||
  pip?: boolean;
 | 
					  pip?: boolean;
 | 
				
			||||||
  onClick?: () => void;
 | 
					  onClick?: () => void;
 | 
				
			||||||
 | 
					  setFullResolution?: React.Dispatch<React.SetStateAction<VideoResolutionType>>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function LivePlayer({
 | 
					export default function LivePlayer({
 | 
				
			||||||
@ -41,6 +42,7 @@ export default function LivePlayer({
 | 
				
			|||||||
  iOSCompatFullScreen = false,
 | 
					  iOSCompatFullScreen = false,
 | 
				
			||||||
  pip,
 | 
					  pip,
 | 
				
			||||||
  onClick,
 | 
					  onClick,
 | 
				
			||||||
 | 
					  setFullResolution,
 | 
				
			||||||
}: LivePlayerProps) {
 | 
					}: LivePlayerProps) {
 | 
				
			||||||
  const [cameraHovered, setCameraHovered] = useState(false);
 | 
					  const [cameraHovered, setCameraHovered] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -123,6 +125,7 @@ export default function LivePlayer({
 | 
				
			|||||||
          audioEnabled={playAudio}
 | 
					          audioEnabled={playAudio}
 | 
				
			||||||
          onPlaying={() => setLiveReady(true)}
 | 
					          onPlaying={() => setLiveReady(true)}
 | 
				
			||||||
          pip={pip}
 | 
					          pip={pip}
 | 
				
			||||||
 | 
					          setFullResolution={setFullResolution}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,13 @@
 | 
				
			|||||||
import { baseUrl } from "@/api/baseUrl";
 | 
					import { baseUrl } from "@/api/baseUrl";
 | 
				
			||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
 | 
					import { VideoResolutionType } from "@/types/live";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  SetStateAction,
 | 
				
			||||||
 | 
					  useCallback,
 | 
				
			||||||
 | 
					  useEffect,
 | 
				
			||||||
 | 
					  useMemo,
 | 
				
			||||||
 | 
					  useRef,
 | 
				
			||||||
 | 
					  useState,
 | 
				
			||||||
 | 
					} from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MSEPlayerProps = {
 | 
					type MSEPlayerProps = {
 | 
				
			||||||
  camera: string;
 | 
					  camera: string;
 | 
				
			||||||
@ -8,6 +16,7 @@ type MSEPlayerProps = {
 | 
				
			|||||||
  audioEnabled?: boolean;
 | 
					  audioEnabled?: boolean;
 | 
				
			||||||
  pip?: boolean;
 | 
					  pip?: boolean;
 | 
				
			||||||
  onPlaying?: () => void;
 | 
					  onPlaying?: () => void;
 | 
				
			||||||
 | 
					  setFullResolution?: React.Dispatch<SetStateAction<VideoResolutionType>>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function MSEPlayer({
 | 
					function MSEPlayer({
 | 
				
			||||||
@ -17,6 +26,7 @@ function MSEPlayer({
 | 
				
			|||||||
  audioEnabled = false,
 | 
					  audioEnabled = false,
 | 
				
			||||||
  pip = false,
 | 
					  pip = false,
 | 
				
			||||||
  onPlaying,
 | 
					  onPlaying,
 | 
				
			||||||
 | 
					  setFullResolution,
 | 
				
			||||||
}: MSEPlayerProps) {
 | 
					}: MSEPlayerProps) {
 | 
				
			||||||
  let connectTS: number = 0;
 | 
					  let connectTS: number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,6 +60,15 @@ function MSEPlayer({
 | 
				
			|||||||
    return `${baseUrl.replace(/^http/, "ws")}live/mse/api/ws?src=${camera}`;
 | 
					    return `${baseUrl.replace(/^http/, "ws")}live/mse/api/ws?src=${camera}`;
 | 
				
			||||||
  }, [camera]);
 | 
					  }, [camera]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleLoadedMetadata = useCallback(() => {
 | 
				
			||||||
 | 
					    if (videoRef.current && setFullResolution) {
 | 
				
			||||||
 | 
					      setFullResolution({
 | 
				
			||||||
 | 
					        width: videoRef.current.videoWidth,
 | 
				
			||||||
 | 
					        height: videoRef.current.videoHeight,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [setFullResolution]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const play = () => {
 | 
					  const play = () => {
 | 
				
			||||||
    const currentVideo = videoRef.current;
 | 
					    const currentVideo = videoRef.current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -286,6 +305,7 @@ function MSEPlayer({
 | 
				
			|||||||
      playsInline
 | 
					      playsInline
 | 
				
			||||||
      preload="auto"
 | 
					      preload="auto"
 | 
				
			||||||
      onLoadedData={onPlaying}
 | 
					      onLoadedData={onPlaying}
 | 
				
			||||||
 | 
					      onLoadedMetadata={handleLoadedMetadata}
 | 
				
			||||||
      muted={!audioEnabled}
 | 
					      muted={!audioEnabled}
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ import { DynamicVideoController } from "./DynamicVideoController";
 | 
				
			|||||||
import HlsVideoPlayer from "../HlsVideoPlayer";
 | 
					import HlsVideoPlayer from "../HlsVideoPlayer";
 | 
				
			||||||
import { TimeRange } from "@/types/timeline";
 | 
					import { TimeRange } from "@/types/timeline";
 | 
				
			||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
 | 
					import ActivityIndicator from "@/components/indicators/activity-indicator";
 | 
				
			||||||
 | 
					import { VideoResolutionType } from "@/types/live";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Dynamically switches between video playback and scrubbing preview player.
 | 
					 * Dynamically switches between video playback and scrubbing preview player.
 | 
				
			||||||
@ -24,6 +25,7 @@ type DynamicVideoPlayerProps = {
 | 
				
			|||||||
  onControllerReady: (controller: DynamicVideoController) => void;
 | 
					  onControllerReady: (controller: DynamicVideoController) => void;
 | 
				
			||||||
  onTimestampUpdate?: (timestamp: number) => void;
 | 
					  onTimestampUpdate?: (timestamp: number) => void;
 | 
				
			||||||
  onClipEnded?: () => void;
 | 
					  onClipEnded?: () => void;
 | 
				
			||||||
 | 
					  setFullResolution: React.Dispatch<React.SetStateAction<VideoResolutionType>>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export default function DynamicVideoPlayer({
 | 
					export default function DynamicVideoPlayer({
 | 
				
			||||||
  className,
 | 
					  className,
 | 
				
			||||||
@ -36,6 +38,7 @@ export default function DynamicVideoPlayer({
 | 
				
			|||||||
  onControllerReady,
 | 
					  onControllerReady,
 | 
				
			||||||
  onTimestampUpdate,
 | 
					  onTimestampUpdate,
 | 
				
			||||||
  onClipEnded,
 | 
					  onClipEnded,
 | 
				
			||||||
 | 
					  setFullResolution,
 | 
				
			||||||
}: DynamicVideoPlayerProps) {
 | 
					}: DynamicVideoPlayerProps) {
 | 
				
			||||||
  const apiHost = useApiHost();
 | 
					  const apiHost = useApiHost();
 | 
				
			||||||
  const { data: config } = useSWR<FrigateConfig>("config");
 | 
					  const { data: config } = useSWR<FrigateConfig>("config");
 | 
				
			||||||
@ -182,6 +185,7 @@ export default function DynamicVideoPlayer({
 | 
				
			|||||||
          setIsLoading(false);
 | 
					          setIsLoading(false);
 | 
				
			||||||
          setNoRecording(false);
 | 
					          setNoRecording(false);
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
 | 
					        setFullResolution={setFullResolution}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      <PreviewPlayer
 | 
					      <PreviewPlayer
 | 
				
			||||||
        className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${className}`}
 | 
					        className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${className}`}
 | 
				
			||||||
 | 
				
			|||||||
@ -1 +1,5 @@
 | 
				
			|||||||
export type LivePlayerMode = "webrtc" | "mse" | "jsmpeg" | "debug";
 | 
					export type LivePlayerMode = "webrtc" | "mse" | "jsmpeg" | "debug";
 | 
				
			||||||
 | 
					export type VideoResolutionType = {
 | 
				
			||||||
 | 
					  width: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -38,3 +38,6 @@ export type RecordingStartingPoint = {
 | 
				
			|||||||
  startTime: number;
 | 
					  startTime: number;
 | 
				
			||||||
  severity: ReviewSeverity;
 | 
					  severity: ReviewSeverity;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ASPECT_VERTICAL_LAYOUT = 1.5;
 | 
				
			||||||
 | 
					export const ASPECT_WIDE_LAYOUT = 2;
 | 
				
			||||||
 | 
				
			|||||||
@ -41,6 +41,8 @@ import MobileReviewSettingsDrawer from "@/components/overlay/MobileReviewSetting
 | 
				
			|||||||
import Logo from "@/components/Logo";
 | 
					import Logo from "@/components/Logo";
 | 
				
			||||||
import { Skeleton } from "@/components/ui/skeleton";
 | 
					import { Skeleton } from "@/components/ui/skeleton";
 | 
				
			||||||
import { FaVideo } from "react-icons/fa";
 | 
					import { FaVideo } from "react-icons/fa";
 | 
				
			||||||
 | 
					import { VideoResolutionType } from "@/types/live";
 | 
				
			||||||
 | 
					import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SEGMENT_DURATION = 30;
 | 
					const SEGMENT_DURATION = 30;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -189,9 +191,18 @@ export function RecordingView({
 | 
				
			|||||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
					    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
				
			||||||
  }, [currentTime, scrubbing]);
 | 
					  }, [currentTime, scrubbing]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [fullResolution, setFullResolution] = useState<VideoResolutionType>({
 | 
				
			||||||
 | 
					    width: 0,
 | 
				
			||||||
 | 
					    height: 0,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onSelectCamera = useCallback(
 | 
					  const onSelectCamera = useCallback(
 | 
				
			||||||
    (newCam: string) => {
 | 
					    (newCam: string) => {
 | 
				
			||||||
      setMainCamera(newCam);
 | 
					      setMainCamera(newCam);
 | 
				
			||||||
 | 
					      setFullResolution({
 | 
				
			||||||
 | 
					        width: 0,
 | 
				
			||||||
 | 
					        height: 0,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
      setPlaybackStart(currentTime);
 | 
					      setPlaybackStart(currentTime);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    [currentTime],
 | 
					    [currentTime],
 | 
				
			||||||
@ -205,6 +216,10 @@ export function RecordingView({
 | 
				
			|||||||
        return undefined;
 | 
					        return undefined;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (cam == mainCamera && fullResolution.width && fullResolution.height) {
 | 
				
			||||||
 | 
					        return fullResolution.width / fullResolution.height;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const camera = config.cameras[cam];
 | 
					      const camera = config.cameras[cam];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!camera) {
 | 
					      if (!camera) {
 | 
				
			||||||
@ -213,7 +228,7 @@ export function RecordingView({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return camera.detect.width / camera.detect.height;
 | 
					      return camera.detect.width / camera.detect.height;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    [config],
 | 
					    [config, fullResolution, mainCamera],
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const mainCameraAspect = useMemo(() => {
 | 
					  const mainCameraAspect = useMemo(() => {
 | 
				
			||||||
@ -221,9 +236,9 @@ export function RecordingView({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (!aspectRatio) {
 | 
					    if (!aspectRatio) {
 | 
				
			||||||
      return "normal";
 | 
					      return "normal";
 | 
				
			||||||
    } else if (aspectRatio > 2) {
 | 
					    } else if (aspectRatio > ASPECT_WIDE_LAYOUT) {
 | 
				
			||||||
      return "wide";
 | 
					      return "wide";
 | 
				
			||||||
    } else if (aspectRatio < 16 / 9) {
 | 
					    } else if (aspectRatio < ASPECT_VERTICAL_LAYOUT) {
 | 
				
			||||||
      return "tall";
 | 
					      return "tall";
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return "normal";
 | 
					      return "normal";
 | 
				
			||||||
@ -397,6 +412,7 @@ export function RecordingView({
 | 
				
			|||||||
                  mainControllerRef.current = controller;
 | 
					                  mainControllerRef.current = controller;
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
                isScrubbing={scrubbing || exportMode == "timeline"}
 | 
					                isScrubbing={scrubbing || exportMode == "timeline"}
 | 
				
			||||||
 | 
					                setFullResolution={setFullResolution}
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            {isDesktop && (
 | 
					            {isDesktop && (
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ import { TooltipProvider } from "@/components/ui/tooltip";
 | 
				
			|||||||
import { useResizeObserver } from "@/hooks/resize-observer";
 | 
					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 { VideoResolutionType } from "@/types/live";
 | 
				
			||||||
import { CameraPtzInfo } from "@/types/ptz";
 | 
					import { CameraPtzInfo } from "@/types/ptz";
 | 
				
			||||||
import { RecordingStartingPoint } from "@/types/record";
 | 
					import { RecordingStartingPoint } from "@/types/record";
 | 
				
			||||||
import React, {
 | 
					import React, {
 | 
				
			||||||
@ -149,14 +150,24 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
 | 
				
			|||||||
  const [fullscreen, setFullscreen] = useState(false);
 | 
					  const [fullscreen, setFullscreen] = useState(false);
 | 
				
			||||||
  const [pip, setPip] = useState(false);
 | 
					  const [pip, setPip] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [fullResolution, setFullResolution] = useState<VideoResolutionType>({
 | 
				
			||||||
 | 
					    width: 0,
 | 
				
			||||||
 | 
					    height: 0,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const growClassName = useMemo(() => {
 | 
					  const growClassName = useMemo(() => {
 | 
				
			||||||
    const aspect = camera.detect.width / camera.detect.height;
 | 
					    let aspect;
 | 
				
			||||||
 | 
					    if (fullResolution.width && fullResolution.height) {
 | 
				
			||||||
 | 
					      aspect = fullResolution.width / fullResolution.height;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      aspect = camera.detect.width / camera.detect.height;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (isMobile) {
 | 
					    if (isMobile) {
 | 
				
			||||||
      if (isPortrait) {
 | 
					      if (isPortrait) {
 | 
				
			||||||
        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 > 1.5) {
 | 
				
			||||||
          return "p-2 absolute left-0 top-[50%] -translate-y-[50%]";
 | 
					          return "p-2 absolute left-0 top-[50%] -translate-y-[50%]";
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          return "p-2 absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
 | 
					          return "p-2 absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
 | 
				
			||||||
@ -165,7 +176,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (fullscreen) {
 | 
					    if (fullscreen) {
 | 
				
			||||||
      if (aspect > 16 / 9) {
 | 
					      if (aspect > 1.5) {
 | 
				
			||||||
        return "absolute inset-x-2 top-[50%] -translate-y-[50%]";
 | 
					        return "absolute inset-x-2 top-[50%] -translate-y-[50%]";
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        return "absolute inset-y-2 left-[50%] -translate-x-[50%]";
 | 
					        return "absolute inset-y-2 left-[50%] -translate-x-[50%]";
 | 
				
			||||||
@ -173,7 +184,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
 | 
				
			|||||||
    } 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, fullResolution]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const preferredLiveMode = useMemo(() => {
 | 
					  const preferredLiveMode = useMemo(() => {
 | 
				
			||||||
    if (isSafari || mic) {
 | 
					    if (isSafari || mic) {
 | 
				
			||||||
@ -188,8 +199,12 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
 | 
				
			|||||||
  }, [windowWidth, windowHeight]);
 | 
					  }, [windowWidth, windowHeight]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const cameraAspectRatio = useMemo(() => {
 | 
					  const cameraAspectRatio = useMemo(() => {
 | 
				
			||||||
    return camera.detect.width / camera.detect.height;
 | 
					    if (fullResolution.width && fullResolution.height) {
 | 
				
			||||||
  }, [camera]);
 | 
					      return fullResolution.width / fullResolution.height;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return camera.detect.width / camera.detect.height;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [camera, fullResolution]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const aspectRatio = useMemo<number>(() => {
 | 
					  const aspectRatio = useMemo<number>(() => {
 | 
				
			||||||
    if (isMobile || fullscreen) {
 | 
					    if (isMobile || fullscreen) {
 | 
				
			||||||
@ -347,6 +362,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
 | 
				
			|||||||
              iOSCompatFullScreen={isIOS}
 | 
					              iOSCompatFullScreen={isIOS}
 | 
				
			||||||
              preferredLiveMode={preferredLiveMode}
 | 
					              preferredLiveMode={preferredLiveMode}
 | 
				
			||||||
              pip={pip}
 | 
					              pip={pip}
 | 
				
			||||||
 | 
					              setFullResolution={setFullResolution}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          {camera.onvif.host != "" && (
 | 
					          {camera.onvif.host != "" && (
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user