mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +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