From aabfe5f615c0806bfd7ef3004c03d5d95127374b Mon Sep 17 00:00:00 2001 From: Nick Mowen Date: Sun, 17 Dec 2023 08:12:09 -0700 Subject: [PATCH] Add jsmpeg and make birdseye default for live view --- .../components/player/BirdseyeLivePlayer.tsx | 36 +++++++++++++ web/src/components/player/JSMpegPlayer.tsx | 52 +++++++++++++++++++ web/src/components/player/LivePlayer.tsx | 7 ++- web/src/pages/Live.tsx | 20 ++++--- web/src/types/frigateConfig.ts | 26 +++++----- 5 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 web/src/components/player/BirdseyeLivePlayer.tsx create mode 100644 web/src/components/player/JSMpegPlayer.tsx diff --git a/web/src/components/player/BirdseyeLivePlayer.tsx b/web/src/components/player/BirdseyeLivePlayer.tsx new file mode 100644 index 000000000..4f50c3034 --- /dev/null +++ b/web/src/components/player/BirdseyeLivePlayer.tsx @@ -0,0 +1,36 @@ +import WebRtcPlayer from "./WebRTCPlayer"; +import { BirdseyeConfig, CameraConfig } from "@/types/frigateConfig"; +import ActivityIndicator from "../ui/activity-indicator"; +import JSMpegPlayer from "./JSMpegPlayer"; + +type LivePlayerProps = { + birdseyeConfig: BirdseyeConfig; + liveMode: string; +}; + +export default function BirdseyeLivePlayer({ + birdseyeConfig, + liveMode, +}: LivePlayerProps) { + if (liveMode == "webrtc") { + return ( +
+ +
+ ); + } else if (liveMode == "mse") { + return
Not yet implemented
; + } else if (liveMode == "jsmpeg") { + return ( +
+ +
+ ); + } else { + ; + } +} diff --git a/web/src/components/player/JSMpegPlayer.tsx b/web/src/components/player/JSMpegPlayer.tsx new file mode 100644 index 000000000..90772960a --- /dev/null +++ b/web/src/components/player/JSMpegPlayer.tsx @@ -0,0 +1,52 @@ +import { baseUrl } from "@/api/baseUrl"; +import JSMpeg from "@cycjimmy/jsmpeg-player"; +import { useEffect, useRef } from "react"; + +type JSMpegPlayerProps = { + camera: string; + width: number; + height: number; +}; + +export default function JSMpegPlayer({ + camera, + width, + height, +}: JSMpegPlayerProps) { + const playerRef = useRef(null); + const url = `${baseUrl.replace(/^http/, "ws")}live/jsmpeg/${camera}`; + + useEffect(() => { + const video = new JSMpeg.VideoElement( + playerRef.current, + url, + {}, + { protocols: [], audio: false, videoBufferSize: 1024 * 1024 * 4 } + ); + + const fullscreen = () => { + if (video.els.canvas.webkitRequestFullScreen) { + video.els.canvas.webkitRequestFullScreen(); + } else { + video.els.canvas.mozRequestFullScreen(); + } + }; + + video.els.canvas.addEventListener("click", fullscreen); + + return () => { + video.destroy(); + }; + }, [url]); + + return ( +
+ ); +} diff --git a/web/src/components/player/LivePlayer.tsx b/web/src/components/player/LivePlayer.tsx index a6e61c03c..a850d575a 100644 --- a/web/src/components/player/LivePlayer.tsx +++ b/web/src/components/player/LivePlayer.tsx @@ -9,6 +9,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; import { Switch } from "../ui/switch"; import { Label } from "../ui/label"; import { usePersistence } from "@/hooks/use-persistence"; +import JSMpegPlayer from "./JSMpegPlayer"; const emptyObject = Object.freeze({}); @@ -66,7 +67,11 @@ export default function LivePlayer({ } else if (liveMode == "jsmpeg") { return (
- Not Yet Implemented +
); } else if (liveMode == "debug") { diff --git a/web/src/pages/Live.tsx b/web/src/pages/Live.tsx index 264bb2b93..bc7d35fe3 100644 --- a/web/src/pages/Live.tsx +++ b/web/src/pages/Live.tsx @@ -1,3 +1,4 @@ +import BirdseyeLivePlayer from "@/components/player/BirdseyeLivePlayer"; import LivePlayer from "@/components/player/LivePlayer"; import { Button } from "@/components/ui/button"; import { @@ -21,18 +22,19 @@ function Live() { const { camera: openedCamera } = useParams(); const [camera, setCamera] = useState( - openedCamera ?? "Select A Camera" + openedCamera ?? (config?.birdseye.enabled ? "birdseye" : "Select A Camera") ); const cameraConfig = useMemo(() => { - return config?.cameras[camera]; + return camera == "birdseye" ? undefined : config?.cameras[camera]; }, [camera, config]); const sortedCameras = useMemo(() => { if (!config) { return []; } - return Object.values(config.cameras) - .sort((aConf, bConf) => aConf.ui.order - bConf.ui.order); + return Object.values(config.cameras).sort( + (aConf, bConf) => aConf.ui.order - bConf.ui.order + ); }, [config]); const restreamEnabled = useMemo(() => { return ( @@ -56,7 +58,7 @@ function Live() { }, [cameraConfig, restreamEnabled]); const [viewSource, setViewSource, sourceIsLoaded] = usePersistence( `${camera}-source`, - defaultLiveMode + camera == "birdseye" ? "jsmpeg" : defaultLiveMode ); return ( @@ -74,7 +76,7 @@ function Live() { Select A Camera - {(sortedCameras).map((item) => ( + {sortedCameras.map((item) => (
+ {config && camera == "birdseye" && sourceIsLoaded && ( + + )} {cameraConfig && sourceIsLoaded && (