forked from Github/frigate
Live player fixes (#13143)
* Jump to live when exceeding buffer time threshold in MSE player * clean up * Try adjusting playback rate instead of jumping to live * clean up * fallback to webrtc if enabled before jsmpeg * baseline * clean up * remove comments * adaptive playback rate and intelligent switching improvements * increase logging and reset live mode after camera is no longer active on dashboard only * jump to live on safari/iOS * clean up * clean up * refactor camera live mode hook * remove key listener * resolve conflicts
This commit is contained in:
@@ -41,6 +41,7 @@ import {
|
||||
TooltipContent,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import useCameraLiveMode from "@/hooks/use-camera-live-mode";
|
||||
|
||||
type DraggableGridLayoutProps = {
|
||||
cameras: CameraConfig[];
|
||||
@@ -75,36 +76,8 @@ export default function DraggableGridLayout({
|
||||
|
||||
// preferred live modes per camera
|
||||
|
||||
const [preferredLiveModes, setPreferredLiveModes] = useState<{
|
||||
[key: string]: LivePlayerMode;
|
||||
}>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (!cameras) return;
|
||||
|
||||
const mseSupported =
|
||||
"MediaSource" in window || "ManagedMediaSource" in window;
|
||||
|
||||
const newPreferredLiveModes = cameras.reduce(
|
||||
(acc, camera) => {
|
||||
const isRestreamed =
|
||||
config &&
|
||||
Object.keys(config.go2rtc.streams || {}).includes(
|
||||
camera.live.stream_name,
|
||||
);
|
||||
|
||||
if (!mseSupported) {
|
||||
acc[camera.name] = isRestreamed ? "webrtc" : "jsmpeg";
|
||||
} else {
|
||||
acc[camera.name] = isRestreamed ? "mse" : "jsmpeg";
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: LivePlayerMode },
|
||||
);
|
||||
|
||||
setPreferredLiveModes(newPreferredLiveModes);
|
||||
}, [cameras, config, windowVisible]);
|
||||
const { preferredLiveModes, setPreferredLiveModes, resetPreferredLiveMode } =
|
||||
useCameraLiveMode(cameras, windowVisible);
|
||||
|
||||
const ResponsiveGridLayout = useMemo(() => WidthProvider(Responsive), []);
|
||||
|
||||
@@ -477,6 +450,7 @@ export default function DraggableGridLayout({
|
||||
return newModes;
|
||||
});
|
||||
}}
|
||||
onResetLiveMode={() => resetPreferredLiveMode(camera.name)}
|
||||
>
|
||||
{isEditMode && showCircles && <CornerCircles />}
|
||||
</LivePlayerGridItem>
|
||||
@@ -635,6 +609,7 @@ type LivePlayerGridItemProps = {
|
||||
preferredLiveMode: LivePlayerMode;
|
||||
onClick: () => void;
|
||||
onError: (e: LivePlayerError) => void;
|
||||
onResetLiveMode: () => void;
|
||||
};
|
||||
|
||||
const LivePlayerGridItem = React.forwardRef<
|
||||
@@ -655,6 +630,7 @@ const LivePlayerGridItem = React.forwardRef<
|
||||
preferredLiveMode,
|
||||
onClick,
|
||||
onError,
|
||||
onResetLiveMode,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@@ -676,6 +652,7 @@ const LivePlayerGridItem = React.forwardRef<
|
||||
preferredLiveMode={preferredLiveMode}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
onResetLiveMode={onResetLiveMode}
|
||||
containerRef={ref as React.RefObject<HTMLDivElement>}
|
||||
/>
|
||||
{children}
|
||||
|
||||
@@ -227,6 +227,10 @@ export default function LiveCameraView({
|
||||
return "webrtc";
|
||||
}
|
||||
|
||||
if (!isRestreamed) {
|
||||
return "jsmpeg";
|
||||
}
|
||||
|
||||
return "mse";
|
||||
}, [lowBandwidth, mic, webRTC, isRestreamed]);
|
||||
|
||||
@@ -286,14 +290,23 @@ export default function LiveCameraView({
|
||||
}
|
||||
}, [fullscreen, isPortrait, cameraAspectRatio, containerAspectRatio]);
|
||||
|
||||
const handleError = useCallback((e: LivePlayerError) => {
|
||||
if (e == "mse-decode") {
|
||||
setWebRTC(true);
|
||||
} else {
|
||||
setWebRTC(false);
|
||||
setLowBandwidth(true);
|
||||
}
|
||||
}, []);
|
||||
const handleError = useCallback(
|
||||
(e: LivePlayerError) => {
|
||||
if (e) {
|
||||
if (
|
||||
!webRTC &&
|
||||
config &&
|
||||
config.go2rtc?.webrtc?.candidates?.length > 0
|
||||
) {
|
||||
setWebRTC(true);
|
||||
} else {
|
||||
setWebRTC(false);
|
||||
setLowBandwidth(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
[config, webRTC],
|
||||
);
|
||||
|
||||
return (
|
||||
<TransformWrapper minScale={1.0} wheel={{ smoothStep: 0.005 }}>
|
||||
|
||||
@@ -28,8 +28,9 @@ import DraggableGridLayout from "./DraggableGridLayout";
|
||||
import { IoClose } from "react-icons/io5";
|
||||
import { LuLayoutDashboard } from "react-icons/lu";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { LivePlayerError, LivePlayerMode } from "@/types/live";
|
||||
import { LivePlayerError } from "@/types/live";
|
||||
import { FaCompress, FaExpand } from "react-icons/fa";
|
||||
import useCameraLiveMode from "@/hooks/use-camera-live-mode";
|
||||
import { useResizeObserver } from "@/hooks/resize-observer";
|
||||
|
||||
type LiveDashboardViewProps = {
|
||||
@@ -129,9 +130,6 @@ export default function LiveDashboardView({
|
||||
// camera live views
|
||||
|
||||
const [autoLiveView] = usePersistence("autoLiveView", true);
|
||||
const [preferredLiveModes, setPreferredLiveModes] = useState<{
|
||||
[key: string]: LivePlayerMode;
|
||||
}>({});
|
||||
|
||||
const [{ height: containerHeight }] = useResizeObserver(containerRef);
|
||||
|
||||
@@ -186,32 +184,8 @@ export default function LiveDashboardView({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!cameras) return;
|
||||
|
||||
const mseSupported =
|
||||
"MediaSource" in window || "ManagedMediaSource" in window;
|
||||
|
||||
const newPreferredLiveModes = cameras.reduce(
|
||||
(acc, camera) => {
|
||||
const isRestreamed =
|
||||
config &&
|
||||
Object.keys(config.go2rtc.streams || {}).includes(
|
||||
camera.live.stream_name,
|
||||
);
|
||||
|
||||
if (!mseSupported) {
|
||||
acc[camera.name] = isRestreamed ? "webrtc" : "jsmpeg";
|
||||
} else {
|
||||
acc[camera.name] = isRestreamed ? "mse" : "jsmpeg";
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: LivePlayerMode },
|
||||
);
|
||||
|
||||
setPreferredLiveModes(newPreferredLiveModes);
|
||||
}, [cameras, config, windowVisible]);
|
||||
const { preferredLiveModes, setPreferredLiveModes, resetPreferredLiveMode } =
|
||||
useCameraLiveMode(cameras, windowVisible);
|
||||
|
||||
const cameraRef = useCallback(
|
||||
(node: HTMLElement | null) => {
|
||||
@@ -381,6 +355,7 @@ export default function LiveDashboardView({
|
||||
autoLive={autoLiveView}
|
||||
onClick={() => onSelectCamera(camera.name)}
|
||||
onError={(e) => handleError(camera.name, e)}
|
||||
onResetLiveMode={() => resetPreferredLiveMode(camera.name)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user