forked from Github/frigate
Add jsmpeg support to new webUI and make birdseye default for live page (#8995)
* Add jsmpeg and make birdseye default for live view * Fix jsmpeg * Fix
This commit is contained in:
committed by
Blake Blackshear
parent
1a27c7db29
commit
2236ae5d3b
36
web/src/components/player/BirdseyeLivePlayer.tsx
Normal file
36
web/src/components/player/BirdseyeLivePlayer.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import WebRtcPlayer from "./WebRTCPlayer";
|
||||
import { BirdseyeConfig } 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 (
|
||||
<div className="max-w-5xl">
|
||||
<WebRtcPlayer camera="birdseye" />
|
||||
</div>
|
||||
);
|
||||
} else if (liveMode == "mse") {
|
||||
return <div className="max-w-5xl">Not yet implemented</div>;
|
||||
} else if (liveMode == "jsmpeg") {
|
||||
return (
|
||||
<div className={`max-w-[${birdseyeConfig.width}px]`}>
|
||||
<JSMpegPlayer
|
||||
camera="birdseye"
|
||||
width={birdseyeConfig.width}
|
||||
height={birdseyeConfig.height}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
<ActivityIndicator />;
|
||||
}
|
||||
}
|
||||
94
web/src/components/player/JSMpegPlayer.tsx
Normal file
94
web/src/components/player/JSMpegPlayer.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import { baseUrl } from "@/api/baseUrl";
|
||||
import { useResizeObserver } from "@/hooks/resize-observer";
|
||||
// @ts-ignore we know this doesn't have types
|
||||
import JSMpeg from "@cycjimmy/jsmpeg-player";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
type JSMpegPlayerProps = {
|
||||
camera: string;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export default function JSMpegPlayer({
|
||||
camera,
|
||||
width,
|
||||
height,
|
||||
}: JSMpegPlayerProps) {
|
||||
const url = `${baseUrl.replace(/^http/, "ws")}live/jsmpeg/${camera}`;
|
||||
const playerRef = useRef<HTMLDivElement | null>(null);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [{ width: containerWidth }] = useResizeObserver(containerRef);
|
||||
|
||||
// Add scrollbar width (when visible) to the available observer width to eliminate screen juddering.
|
||||
// https://github.com/blakeblackshear/frigate/issues/1657
|
||||
let scrollBarWidth = 0;
|
||||
if (window.innerWidth && document.body.offsetWidth) {
|
||||
scrollBarWidth = window.innerWidth - document.body.offsetWidth;
|
||||
}
|
||||
const availableWidth = scrollBarWidth
|
||||
? containerWidth + scrollBarWidth
|
||||
: containerWidth;
|
||||
const aspectRatio = width / height;
|
||||
|
||||
const scaledHeight = useMemo(() => {
|
||||
const scaledHeight = Math.floor(availableWidth / aspectRatio);
|
||||
const finalHeight = Math.min(scaledHeight, height);
|
||||
|
||||
if (finalHeight > 0) {
|
||||
return finalHeight;
|
||||
}
|
||||
|
||||
return 100;
|
||||
}, [availableWidth, aspectRatio, height]);
|
||||
const scaledWidth = useMemo(
|
||||
() => Math.ceil(scaledHeight * aspectRatio - scrollBarWidth),
|
||||
[scaledHeight, aspectRatio, scrollBarWidth]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!playerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("player ref exists and creating video");
|
||||
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 () => {
|
||||
if (playerRef.current) {
|
||||
try {
|
||||
video.destroy();
|
||||
} catch (e) {}
|
||||
playerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
<div
|
||||
ref={playerRef}
|
||||
className={`jsmpeg`}
|
||||
style={{
|
||||
height: `${scaledHeight}px`,
|
||||
width: `${scaledWidth}px`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={`max-w-[${cameraConfig.detect.width}px]`}>
|
||||
Not Yet Implemented
|
||||
<JSMpegPlayer
|
||||
camera={cameraConfig.name}
|
||||
width={cameraConfig.detect.width}
|
||||
height={cameraConfig.detect.height}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (liveMode == "debug") {
|
||||
|
||||
Reference in New Issue
Block a user