import WebRtcPlayer from "./WebRTCPlayer"; import { CameraConfig } from "@/types/frigateConfig"; import AutoUpdatingCameraImage from "../camera/AutoUpdatingCameraImage"; import ActivityIndicator from "../ui/activity-indicator"; import { Button } from "../ui/button"; import { LuSettings } from "react-icons/lu"; import { useCallback, useEffect, useMemo, useState } from "react"; 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 MSEPlayer from "./MsePlayer"; import JSMpegPlayer from "./JSMpegPlayer"; import { MdCircle, MdLeakAdd } from "react-icons/md"; import { BsSoundwave } from "react-icons/bs"; import Chip from "../Chip"; import useCameraActivity from "@/hooks/use-camera-activity"; import { useRecordingsState } from "@/api/ws"; import { LivePlayerMode } from "@/types/live"; import useCameraLiveMode from "@/hooks/use-camera-live-mode"; const emptyObject = Object.freeze({}); type LivePlayerProps = { className?: string; cameraConfig: CameraConfig; preferredLiveMode?: LivePlayerMode; showStillWithoutActivity?: boolean; }; type Options = { [key: string]: boolean }; export default function LivePlayer({ className, cameraConfig, preferredLiveMode, showStillWithoutActivity = true, }: LivePlayerProps) { // camera activity const { activeMotion, activeAudio, activeTracking } = useCameraActivity(cameraConfig); const liveMode = useCameraLiveMode(cameraConfig, preferredLiveMode); const [liveReady, setLiveReady] = useState(false); useEffect(() => { if (!liveReady) { if (activeMotion && liveMode == "jsmpeg") { setLiveReady(true); } return; } if (!activeMotion && !activeTracking) { setLiveReady(false); } }, [activeMotion, activeTracking, liveReady]); const { payload: recording } = useRecordingsState(cameraConfig.name); // debug view settings const [showSettings, setShowSettings] = useState(false); const [options, setOptions] = usePersistence( `${cameraConfig?.name}-feed`, emptyObject ); const handleSetOption = useCallback( (id: string, value: boolean) => { const newOptions = { ...options, [id]: value }; setOptions(newOptions); }, [options] ); const searchParams = useMemo( () => new URLSearchParams( Object.keys(options).reduce((memo, key) => { //@ts-ignore we know this is correct memo.push([key, options[key] === true ? "1" : "0"]); return memo; }, []) ), [options] ); const handleToggleSettings = useCallback(() => { setShowSettings(!showSettings); }, [showSettings]); if (!cameraConfig) { return ; } let player; if (liveMode == "webrtc") { player = ( setLiveReady(true)} /> ); } else if (liveMode == "mse") { if ("MediaSource" in window || "ManagedMediaSource" in window) { player = ( setLiveReady(true)} /> ); } else { player = (
MSE is only supported on iOS 17.1+. You'll need to update if available or use jsmpeg / webRTC streams. See the docs for more info.
); } } else if (liveMode == "jsmpeg") { player = ( ); } else if (liveMode == "debug") { player = ( <> {showSettings ? ( Options ) : null} ); } else { player = ; } return (
{(showStillWithoutActivity == false || activeMotion || activeTracking) && player}
Motion
{cameraConfig.audio.enabled_in_config && (
Sound
)}
{recording == "ON" && ( )}
{cameraConfig.name.replaceAll("_", " ")}
); } type DebugSettingsProps = { handleSetOption: (id: string, value: boolean) => void; options: Options; }; function DebugSettings({ handleSetOption, options }: DebugSettingsProps) { return (
{ handleSetOption("bbox", isChecked); }} />
{ handleSetOption("timestamp", isChecked); }} />
{ handleSetOption("zones", isChecked); }} />
{ handleSetOption("mask", isChecked); }} />
{ handleSetOption("motion", isChecked); }} />
{ handleSetOption("regions", isChecked); }} />
); }