mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Live view tweaks and jsmpeg bugfix (#11584)
* live view tweaks and jsmpeg bugfix * use container aspect in check
This commit is contained in:
		
							parent
							
								
									6913cc6abc
								
							
						
					
					
						commit
						4165639308
					
				@ -77,7 +77,7 @@ export default function JSMpegPlayer({
 | 
				
			|||||||
    const video = new JSMpeg.VideoElement(
 | 
					    const video = new JSMpeg.VideoElement(
 | 
				
			||||||
      playerRef.current,
 | 
					      playerRef.current,
 | 
				
			||||||
      url,
 | 
					      url,
 | 
				
			||||||
      { canvas: "#video-canvas" },
 | 
					      { canvas: `#${camera}-canvas` },
 | 
				
			||||||
      { protocols: [], audio: false, videoBufferSize: 1024 * 1024 * 4 },
 | 
					      { protocols: [], audio: false, videoBufferSize: 1024 * 1024 * 4 },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -90,13 +90,13 @@ export default function JSMpegPlayer({
 | 
				
			|||||||
        playerRef.current = null;
 | 
					        playerRef.current = null;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }, [url]);
 | 
					  }, [url, camera]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className={className} ref={internalContainerRef}>
 | 
					    <div className={className} ref={internalContainerRef}>
 | 
				
			||||||
      <div ref={playerRef} className="jsmpeg">
 | 
					      <div ref={playerRef} className="jsmpeg">
 | 
				
			||||||
        <canvas
 | 
					        <canvas
 | 
				
			||||||
          id="video-canvas"
 | 
					          id={`${camera}-canvas`}
 | 
				
			||||||
          style={{
 | 
					          style={{
 | 
				
			||||||
            width: scaledWidth ?? width,
 | 
					            width: scaledWidth ?? width,
 | 
				
			||||||
            height: scaledHeight ?? height,
 | 
					            height: scaledHeight ?? height,
 | 
				
			||||||
 | 
				
			|||||||
@ -4,8 +4,9 @@ import BirdseyeLivePlayer from "@/components/player/BirdseyeLivePlayer";
 | 
				
			|||||||
import { Button } from "@/components/ui/button";
 | 
					import { Button } from "@/components/ui/button";
 | 
				
			||||||
import { TooltipProvider } from "@/components/ui/tooltip";
 | 
					import { TooltipProvider } from "@/components/ui/tooltip";
 | 
				
			||||||
import { useResizeObserver } from "@/hooks/resize-observer";
 | 
					import { useResizeObserver } from "@/hooks/resize-observer";
 | 
				
			||||||
 | 
					import { useFullscreen } from "@/hooks/use-fullscreen";
 | 
				
			||||||
import { FrigateConfig } from "@/types/frigateConfig";
 | 
					import { FrigateConfig } from "@/types/frigateConfig";
 | 
				
			||||||
import { useEffect, useMemo, useRef, useState } from "react";
 | 
					import { useMemo, useRef } from "react";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  isDesktop,
 | 
					  isDesktop,
 | 
				
			||||||
  isMobile,
 | 
					  isMobile,
 | 
				
			||||||
@ -29,25 +30,10 @@ export default function LiveBirdseyeView() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // fullscreen state
 | 
					  // fullscreen state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  const { fullscreen, toggleFullscreen } = useFullscreen(mainRef);
 | 
				
			||||||
    if (mainRef.current == null) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const listener = () => {
 | 
					 | 
				
			||||||
      setFullscreen(document.fullscreenElement != null);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    document.addEventListener("fullscreenchange", listener);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return () => {
 | 
					 | 
				
			||||||
      document.removeEventListener("fullscreenchange", listener);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }, [mainRef]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // playback state
 | 
					  // playback state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [fullscreen, setFullscreen] = useState(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const cameraAspectRatio = useMemo(() => {
 | 
					  const cameraAspectRatio = useMemo(() => {
 | 
				
			||||||
    if (!config) {
 | 
					    if (!config) {
 | 
				
			||||||
      return 16 / 9;
 | 
					      return 16 / 9;
 | 
				
			||||||
@ -96,15 +82,23 @@ export default function LiveBirdseyeView() {
 | 
				
			|||||||
    return windowWidth / windowHeight;
 | 
					    return windowWidth / windowHeight;
 | 
				
			||||||
  }, [windowWidth, windowHeight]);
 | 
					  }, [windowWidth, windowHeight]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const containerAspectRatio = useMemo(() => {
 | 
				
			||||||
 | 
					    if (!containerRef.current) {
 | 
				
			||||||
 | 
					      return windowAspectRatio;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return containerRef.current.clientWidth / containerRef.current.clientHeight;
 | 
				
			||||||
 | 
					  }, [windowAspectRatio, containerRef]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const aspectRatio = useMemo<number>(() => {
 | 
					  const aspectRatio = useMemo<number>(() => {
 | 
				
			||||||
    if (isMobile || fullscreen) {
 | 
					    if (isMobile || fullscreen) {
 | 
				
			||||||
      return cameraAspectRatio;
 | 
					      return cameraAspectRatio;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return windowAspectRatio < cameraAspectRatio
 | 
					      return containerAspectRatio < cameraAspectRatio
 | 
				
			||||||
        ? windowAspectRatio - 0.05
 | 
					        ? containerAspectRatio
 | 
				
			||||||
        : cameraAspectRatio - 0.03;
 | 
					        : cameraAspectRatio;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [cameraAspectRatio, windowAspectRatio, fullscreen]);
 | 
					  }, [cameraAspectRatio, containerAspectRatio, fullscreen]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!config) {
 | 
					  if (!config) {
 | 
				
			||||||
    return <ActivityIndicator />;
 | 
					    return <ActivityIndicator />;
 | 
				
			||||||
@ -149,13 +143,7 @@ export default function LiveBirdseyeView() {
 | 
				
			|||||||
                Icon={fullscreen ? FaCompress : FaExpand}
 | 
					                Icon={fullscreen ? FaCompress : FaExpand}
 | 
				
			||||||
                isActive={fullscreen}
 | 
					                isActive={fullscreen}
 | 
				
			||||||
                title={fullscreen ? "Close" : "Fullscreen"}
 | 
					                title={fullscreen ? "Close" : "Fullscreen"}
 | 
				
			||||||
                onClick={() => {
 | 
					                onClick={toggleFullscreen}
 | 
				
			||||||
                  if (fullscreen) {
 | 
					 | 
				
			||||||
                    document.exitFullscreen();
 | 
					 | 
				
			||||||
                  } else {
 | 
					 | 
				
			||||||
                    mainRef.current?.requestFullscreen();
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                }}
 | 
					 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </TooltipProvider>
 | 
					          </TooltipProvider>
 | 
				
			||||||
 | 
				
			|||||||
@ -73,6 +73,7 @@ import {
 | 
				
			|||||||
import { useNavigate } from "react-router-dom";
 | 
					import { useNavigate } from "react-router-dom";
 | 
				
			||||||
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
 | 
					import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
 | 
				
			||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
 | 
					import { useFullscreen } from "@/hooks/use-fullscreen";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type LiveCameraViewProps = {
 | 
					type LiveCameraViewProps = {
 | 
				
			||||||
  config?: FrigateConfig;
 | 
					  config?: FrigateConfig;
 | 
				
			||||||
@ -177,19 +178,7 @@ export default function LiveCameraView({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // fullscreen / pip state
 | 
					  // fullscreen / pip state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  const { fullscreen, toggleFullscreen } = useFullscreen(mainRef);
 | 
				
			||||||
    if (mainRef.current == null) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const fsListener = () => {
 | 
					 | 
				
			||||||
      setFullscreen(document.fullscreenElement != null);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    document.addEventListener("fullscreenchange", fsListener);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return () => {
 | 
					 | 
				
			||||||
      document.removeEventListener("fullscreenchange", fsListener);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }, [mainRef]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    setPip(document.pictureInPictureElement != null);
 | 
					    setPip(document.pictureInPictureElement != null);
 | 
				
			||||||
@ -201,7 +190,6 @@ export default function LiveCameraView({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const [audio, setAudio] = useState(false);
 | 
					  const [audio, setAudio] = useState(false);
 | 
				
			||||||
  const [mic, setMic] = useState(false);
 | 
					  const [mic, setMic] = useState(false);
 | 
				
			||||||
  const [fullscreen, setFullscreen] = useState(false);
 | 
					 | 
				
			||||||
  const [pip, setPip] = useState(false);
 | 
					  const [pip, setPip] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [fullResolution, setFullResolution] = useState<VideoResolutionType>({
 | 
					  const [fullResolution, setFullResolution] = useState<VideoResolutionType>({
 | 
				
			||||||
@ -209,37 +197,6 @@ export default function LiveCameraView({
 | 
				
			|||||||
    height: 0,
 | 
					    height: 0,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const growClassName = useMemo(() => {
 | 
					 | 
				
			||||||
    let aspect;
 | 
					 | 
				
			||||||
    if (fullResolution.width && fullResolution.height) {
 | 
					 | 
				
			||||||
      aspect = fullResolution.width / fullResolution.height;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      aspect = camera.detect.width / camera.detect.height;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (isMobile) {
 | 
					 | 
				
			||||||
      if (isPortrait) {
 | 
					 | 
				
			||||||
        return "absolute left-2 right-2 top-[50%] -translate-y-[50%]";
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        if (aspect > 1.5) {
 | 
					 | 
				
			||||||
          return "p-2 absolute left-0 top-[50%] -translate-y-[50%]";
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          return "p-2 absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (fullscreen) {
 | 
					 | 
				
			||||||
      if (aspect > 1.5) {
 | 
					 | 
				
			||||||
        return "absolute inset-x-2 top-[50%] -translate-y-[50%]";
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        return "absolute inset-y-2 left-[50%] -translate-x-[50%]";
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      return "absolute top-2 bottom-2 left-[50%] -translate-x-[50%]";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }, [camera, fullscreen, isPortrait, fullResolution]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const preferredLiveMode = useMemo(() => {
 | 
					  const preferredLiveMode = useMemo(() => {
 | 
				
			||||||
    if (isSafari || mic) {
 | 
					    if (isSafari || mic) {
 | 
				
			||||||
      return "webrtc";
 | 
					      return "webrtc";
 | 
				
			||||||
@ -252,6 +209,14 @@ export default function LiveCameraView({
 | 
				
			|||||||
    return windowWidth / windowHeight;
 | 
					    return windowWidth / windowHeight;
 | 
				
			||||||
  }, [windowWidth, windowHeight]);
 | 
					  }, [windowWidth, windowHeight]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const containerAspectRatio = useMemo(() => {
 | 
				
			||||||
 | 
					    if (!containerRef.current) {
 | 
				
			||||||
 | 
					      return windowAspectRatio;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return containerRef.current.clientWidth / containerRef.current.clientHeight;
 | 
				
			||||||
 | 
					  }, [windowAspectRatio, containerRef]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const cameraAspectRatio = useMemo(() => {
 | 
					  const cameraAspectRatio = useMemo(() => {
 | 
				
			||||||
    if (fullResolution.width && fullResolution.height) {
 | 
					    if (fullResolution.width && fullResolution.height) {
 | 
				
			||||||
      return fullResolution.width / fullResolution.height;
 | 
					      return fullResolution.width / fullResolution.height;
 | 
				
			||||||
@ -264,11 +229,42 @@ export default function LiveCameraView({
 | 
				
			|||||||
    if (isMobile || fullscreen) {
 | 
					    if (isMobile || fullscreen) {
 | 
				
			||||||
      return cameraAspectRatio;
 | 
					      return cameraAspectRatio;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return windowAspectRatio < cameraAspectRatio
 | 
					      return containerAspectRatio < cameraAspectRatio
 | 
				
			||||||
        ? windowAspectRatio - 0.05
 | 
					        ? containerAspectRatio
 | 
				
			||||||
        : cameraAspectRatio - 0.03;
 | 
					        : cameraAspectRatio;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [cameraAspectRatio, windowAspectRatio, fullscreen]);
 | 
					  }, [cameraAspectRatio, containerAspectRatio, fullscreen]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const growClassName = useMemo(() => {
 | 
				
			||||||
 | 
					    let aspect;
 | 
				
			||||||
 | 
					    if (fullResolution.width && fullResolution.height) {
 | 
				
			||||||
 | 
					      aspect = fullResolution.width / fullResolution.height;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      aspect = camera.detect.width / camera.detect.height;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isMobile) {
 | 
				
			||||||
 | 
					      if (isPortrait) {
 | 
				
			||||||
 | 
					        return "absolute left-0.5 right-0.5 top-[50%] -translate-y-[50%]";
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        if (aspect > containerAspectRatio) {
 | 
				
			||||||
 | 
					          return "p-2 absolute left-0 top-[50%] -translate-y-[50%]";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          return "p-2 absolute top-0.5 bottom-0.5 left-[50%] -translate-x-[50%]";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (fullscreen) {
 | 
				
			||||||
 | 
					      if (aspect > containerAspectRatio) {
 | 
				
			||||||
 | 
					        return "absolute inset-x-2 top-[50%] -translate-y-[50%]";
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return "absolute inset-y-2 left-[50%] -translate-x-[50%]";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return "absolute top-0.5 bottom-0.5 left-[50%] -translate-x-[50%]";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [camera, fullscreen, isPortrait, fullResolution, containerAspectRatio]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <TransformWrapper minScale={1.0}>
 | 
					    <TransformWrapper minScale={1.0}>
 | 
				
			||||||
@ -333,13 +329,7 @@ export default function LiveCameraView({
 | 
				
			|||||||
                  Icon={fullscreen ? FaCompress : FaExpand}
 | 
					                  Icon={fullscreen ? FaCompress : FaExpand}
 | 
				
			||||||
                  isActive={fullscreen}
 | 
					                  isActive={fullscreen}
 | 
				
			||||||
                  title={fullscreen ? "Close" : "Fullscreen"}
 | 
					                  title={fullscreen ? "Close" : "Fullscreen"}
 | 
				
			||||||
                  onClick={() => {
 | 
					                  onClick={toggleFullscreen}
 | 
				
			||||||
                    if (fullscreen) {
 | 
					 | 
				
			||||||
                      document.exitFullscreen();
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                      mainRef.current?.requestFullscreen();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                  }}
 | 
					 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
              {!isIOS && (
 | 
					              {!isIOS && (
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user