mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Respect motion only when playing back (#10632)
* Respect motion only when playing back motion * Increase efficiency * Fix import
This commit is contained in:
		
							parent
							
								
									c2a32bd6c1
								
							
						
					
					
						commit
						bb50b2b6f4
					
				| @ -1,3 +1,4 @@ | |||||||
|  | import { Timeline } from "@/types/timeline"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| 
 | 
 | ||||||
| type TimelineEventOverlayProps = { | type TimelineEventOverlayProps = { | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ import { useEffect, useMemo, useState } from "react"; | |||||||
| import MSEPlayer from "./MsePlayer"; | import MSEPlayer from "./MsePlayer"; | ||||||
| import JSMpegPlayer from "./JSMpegPlayer"; | import JSMpegPlayer from "./JSMpegPlayer"; | ||||||
| import { MdCircle } from "react-icons/md"; | import { MdCircle } from "react-icons/md"; | ||||||
| import useCameraActivity from "@/hooks/use-camera-activity"; | import { useCameraActivity } from "@/hooks/use-camera-activity"; | ||||||
| import { useRecordingsState } from "@/api/ws"; | import { useRecordingsState } from "@/api/ws"; | ||||||
| import { LivePlayerMode } from "@/types/live"; | import { LivePlayerMode } from "@/types/live"; | ||||||
| import useCameraLiveMode from "@/hooks/use-camera-live-mode"; | import useCameraLiveMode from "@/hooks/use-camera-live-mode"; | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import { Recording } from "@/types/record"; | import { Recording } from "@/types/record"; | ||||||
| import { DynamicPlayback } from "@/types/playback"; | import { DynamicPlayback } from "@/types/playback"; | ||||||
| import { PreviewController } from "../PreviewPlayer"; | import { PreviewController } from "../PreviewPlayer"; | ||||||
|  | import { Timeline } from "@/types/timeline"; | ||||||
| 
 | 
 | ||||||
| type PlayerMode = "playback" | "scrubbing"; | type PlayerMode = "playback" | "scrubbing"; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import { Preview } from "@/types/preview"; | |||||||
| import PreviewPlayer, { PreviewController } from "../PreviewPlayer"; | import PreviewPlayer, { PreviewController } from "../PreviewPlayer"; | ||||||
| import { DynamicVideoController } from "./DynamicVideoController"; | import { DynamicVideoController } from "./DynamicVideoController"; | ||||||
| import HlsVideoPlayer from "../HlsVideoPlayer"; | import HlsVideoPlayer from "../HlsVideoPlayer"; | ||||||
|  | import { Timeline } from "@/types/timeline"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Dynamically switches between video playback and scrubbing preview player. |  * Dynamically switches between video playback and scrubbing preview player. | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ import { | |||||||
|   useMotionActivity, |   useMotionActivity, | ||||||
| } from "@/api/ws"; | } from "@/api/ws"; | ||||||
| import { CameraConfig } from "@/types/frigateConfig"; | import { CameraConfig } from "@/types/frigateConfig"; | ||||||
|  | import { MotionData, ReviewSegment } from "@/types/review"; | ||||||
|  | import { TimeRange } from "@/types/timeline"; | ||||||
| import { useEffect, useMemo, useState } from "react"; | import { useEffect, useMemo, useState } from "react"; | ||||||
| 
 | 
 | ||||||
| type useCameraActivityReturn = { | type useCameraActivityReturn = { | ||||||
| @ -12,7 +14,7 @@ type useCameraActivityReturn = { | |||||||
|   activeAudio: boolean; |   activeAudio: boolean; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default function useCameraActivity( | export function useCameraActivity( | ||||||
|   camera: CameraConfig, |   camera: CameraConfig, | ||||||
| ): useCameraActivityReturn { | ): useCameraActivityReturn { | ||||||
|   const [activeObjects, setActiveObjects] = useState<string[]>([]); |   const [activeObjects, setActiveObjects] = useState<string[]>([]); | ||||||
| @ -66,3 +68,57 @@ export default function useCameraActivity( | |||||||
|       : false, |       : false, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export function useCameraMotionTimestamps( | ||||||
|  |   timeRange: TimeRange, | ||||||
|  |   motionOnly: boolean, | ||||||
|  |   events: ReviewSegment[], | ||||||
|  |   motion: MotionData[], | ||||||
|  | ) { | ||||||
|  |   const timestamps = useMemo(() => { | ||||||
|  |     const seekableTimestamps = []; | ||||||
|  |     let lastEventIdx = 0; | ||||||
|  |     let lastMotionIdx = 0; | ||||||
|  | 
 | ||||||
|  |     for (let i = timeRange.after; i <= timeRange.before; i += 0.5) { | ||||||
|  |       if (!motionOnly) { | ||||||
|  |         seekableTimestamps.push(i); | ||||||
|  |       } else { | ||||||
|  |         const relevantEventIdx = events.findIndex((seg, segIdx) => { | ||||||
|  |           if (segIdx < lastEventIdx) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           return seg.start_time <= i && seg.end_time >= i; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (relevantEventIdx != -1) { | ||||||
|  |           lastEventIdx = relevantEventIdx; | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const relevantMotionIdx = motion.findIndex((mot, motIdx) => { | ||||||
|  |           if (motIdx < lastMotionIdx) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           return mot.start_time <= i && mot.start_time + 15 >= i; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (relevantMotionIdx == -1 || motion[relevantMotionIdx].motion == 0) { | ||||||
|  |           if (relevantMotionIdx != -1) { | ||||||
|  |             lastMotionIdx = relevantMotionIdx; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         seekableTimestamps.push(i); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return seekableTimestamps; | ||||||
|  |   }, [timeRange, motionOnly, events, motion]); | ||||||
|  | 
 | ||||||
|  |   return timestamps; | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| type Timeline = { | export type Timeline = { | ||||||
|   camera: string; |   camera: string; | ||||||
|   timestamp: number; |   timestamp: number; | ||||||
|   data: { |   data: { | ||||||
| @ -23,11 +23,4 @@ type Timeline = { | |||||||
|   source: string; |   source: string; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // may be used in the future, keep for now for reference
 | export type TimeRange = { before: number; after: number }; | ||||||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |  | ||||||
| type HourlyTimeline = { |  | ||||||
|   start: number; |  | ||||||
|   end: number; |  | ||||||
|   count: number; |  | ||||||
|   hours: { [key: string]: Timeline[] }; |  | ||||||
| }; |  | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ import { | |||||||
| } from "react-icons/md"; | } from "react-icons/md"; | ||||||
| import { FaBicycle } from "react-icons/fa"; | import { FaBicycle } from "react-icons/fa"; | ||||||
| import { endOfHourOrCurrentTime } from "./dateUtil"; | import { endOfHourOrCurrentTime } from "./dateUtil"; | ||||||
|  | import { Timeline } from "@/types/timeline"; | ||||||
| 
 | 
 | ||||||
| export function getTimelineIcon(timelineItem: Timeline) { | export function getTimelineIcon(timelineItem: Timeline) { | ||||||
|   switch (timelineItem.class_type) { |   switch (timelineItem.class_type) { | ||||||
|  | |||||||
| @ -39,6 +39,8 @@ import PreviewPlayer, { | |||||||
| import SummaryTimeline from "@/components/timeline/SummaryTimeline"; | import SummaryTimeline from "@/components/timeline/SummaryTimeline"; | ||||||
| import { RecordingStartingPoint } from "@/types/record"; | import { RecordingStartingPoint } from "@/types/record"; | ||||||
| import VideoControls from "@/components/player/VideoControls"; | import VideoControls from "@/components/player/VideoControls"; | ||||||
|  | import { TimeRange } from "@/types/timeline"; | ||||||
|  | import { useCameraMotionTimestamps } from "@/hooks/use-camera-activity"; | ||||||
| 
 | 
 | ||||||
| type EventViewProps = { | type EventViewProps = { | ||||||
|   reviews?: ReviewSegment[]; |   reviews?: ReviewSegment[]; | ||||||
| @ -606,7 +608,7 @@ type MotionReviewProps = { | |||||||
|     significant_motion: ReviewSegment[]; |     significant_motion: ReviewSegment[]; | ||||||
|   }; |   }; | ||||||
|   relevantPreviews?: Preview[]; |   relevantPreviews?: Preview[]; | ||||||
|   timeRange: { before: number; after: number }; |   timeRange: TimeRange; | ||||||
|   startTime?: number; |   startTime?: number; | ||||||
|   filter?: ReviewFilter; |   filter?: ReviewFilter; | ||||||
|   motionOnly?: boolean; |   motionOnly?: boolean; | ||||||
| @ -718,6 +720,12 @@ function MotionReview({ | |||||||
| 
 | 
 | ||||||
|   const [playbackRate, setPlaybackRate] = useState(8); |   const [playbackRate, setPlaybackRate] = useState(8); | ||||||
|   const [controlsOpen, setControlsOpen] = useState(false); |   const [controlsOpen, setControlsOpen] = useState(false); | ||||||
|  |   const seekTimestamps = useCameraMotionTimestamps( | ||||||
|  |     timeRange, | ||||||
|  |     motionOnly, | ||||||
|  |     reviewItems?.all ?? [], | ||||||
|  |     motionData ?? [], | ||||||
|  |   ); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (!playing) { |     if (!playing) { | ||||||
| @ -725,17 +733,22 @@ function MotionReview({ | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const interval = 500 / playbackRate; |     const interval = 500 / playbackRate; | ||||||
|     const startTime = currentTime; |     const startIdx = seekTimestamps.findIndex((time) => time > currentTime); | ||||||
|  | 
 | ||||||
|  |     if (!startIdx) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     let counter = 0; |     let counter = 0; | ||||||
|     const intervalId = setInterval(() => { |     const intervalId = setInterval(() => { | ||||||
|       counter += 0.5; |       counter += 1; | ||||||
| 
 | 
 | ||||||
|       if (startTime + counter >= timeRange.before) { |       if (startIdx + counter >= seekTimestamps.length) { | ||||||
|         setPlaying(false); |         setPlaying(false); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       setCurrentTime(startTime + counter); |       setCurrentTime(seekTimestamps[startIdx + counter]); | ||||||
|     }, interval); |     }, interval); | ||||||
| 
 | 
 | ||||||
|     return () => { |     return () => { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user