import WebRtcPlayer from "./WebRTCPlayer"; import { CameraConfig } from "@/types/frigateConfig"; import AutoUpdatingCameraImage from "../camera/AutoUpdatingCameraImage"; import ActivityIndicator from "../indicators/activity-indicator"; import { useEffect, useMemo, useState } from "react"; import MSEPlayer from "./MsePlayer"; import JSMpegPlayer from "./JSMpegPlayer"; import { MdCircle } from "react-icons/md"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { useCameraActivity } from "@/hooks/use-camera-activity"; import { LivePlayerMode, VideoResolutionType } from "@/types/live"; import useCameraLiveMode from "@/hooks/use-camera-live-mode"; import { getIconForLabel } from "@/utils/iconUtil"; import Chip from "../indicators/Chip"; import { capitalizeFirstLetter } from "@/utils/stringUtil"; import { cn } from "@/lib/utils"; type LivePlayerProps = { cameraRef?: (ref: HTMLDivElement | null) => void; className?: string; cameraConfig: CameraConfig; preferredLiveMode?: LivePlayerMode; showStillWithoutActivity?: boolean; windowVisible?: boolean; playAudio?: boolean; micEnabled?: boolean; // only webrtc supports mic iOSCompatFullScreen?: boolean; pip?: boolean; onClick?: () => void; setFullResolution?: React.Dispatch>; }; export default function LivePlayer({ cameraRef = undefined, className, cameraConfig, preferredLiveMode, showStillWithoutActivity = true, windowVisible = true, playAudio = false, micEnabled = false, iOSCompatFullScreen = false, pip, onClick, setFullResolution, }: LivePlayerProps) { // camera activity const { activeMotion, activeTracking, objects } = useCameraActivity(cameraConfig); const cameraActive = useMemo( () => !showStillWithoutActivity || (windowVisible && (activeMotion || activeTracking)), [activeMotion, activeTracking, showStillWithoutActivity, windowVisible], ); // camera live state const liveMode = useCameraLiveMode(cameraConfig, preferredLiveMode); const [liveReady, setLiveReady] = useState(false); useEffect(() => { if (!liveReady) { if (cameraActive && liveMode == "jsmpeg") { setLiveReady(true); } return; } if (!cameraActive) { setLiveReady(false); } // live mode won't change // eslint-disable-next-line react-hooks/exhaustive-deps }, [cameraActive, liveReady]); // camera still state const stillReloadInterval = useMemo(() => { if (!windowVisible) { return -1; // no reason to update the image when the window is not visible } if (liveReady) { return 60000; } if (cameraActive) { return 200; } return 30000; }, [liveReady, cameraActive, windowVisible]); if (!cameraConfig) { return ; } let player; if (liveMode == "webrtc") { player = ( setLiveReady(true)} pip={pip} /> ); } else if (liveMode == "mse") { if ("MediaSource" in window || "ManagedMediaSource" in window) { player = ( setLiveReady(true)} pip={pip} setFullResolution={setFullResolution} /> ); } 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 { player = ; } return (
{player} {objects.length > 0 && (
{[ ...new Set([ ...(objects || []).map(({ label }) => label), ]), ] .map((label) => { return getIconForLabel(label, "size-3 text-white"); }) .sort()}
{[ ...new Set([ ...(objects || []).map(({ label, sub_label }) => label.endsWith("verified") ? sub_label : label, ), ]), ] .filter( (label) => label !== undefined && !label.includes("-verified"), ) .map((label) => capitalizeFirstLetter(label)) .sort() .join(", ") .replaceAll("-verified", "")}
)}
{activeMotion && ( )}
); }