mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-12 18:17:36 +03:00
use annotation offset
This commit is contained in:
parent
f673398053
commit
fcb28cf1c2
@ -2,6 +2,7 @@ import { useMemo, useCallback } from "react";
|
|||||||
import { ObjectLifecycleSequence, LifecycleClassType } from "@/types/timeline";
|
import { ObjectLifecycleSequence, LifecycleClassType } from "@/types/timeline";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import { useActivityStream } from "@/contexts/ActivityStreamContext";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@ -32,6 +33,10 @@ export default function ObjectTrackOverlay({
|
|||||||
objectTimeline,
|
objectTimeline,
|
||||||
}: ObjectTrackOverlayProps) {
|
}: ObjectTrackOverlayProps) {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
const { annotationOffset } = useActivityStream();
|
||||||
|
|
||||||
|
// Offset currentTime by annotation offset for rendering
|
||||||
|
const effectiveCurrentTime = currentTime - annotationOffset;
|
||||||
|
|
||||||
// Fetch the full event data to get saved path points
|
// Fetch the full event data to get saved path points
|
||||||
const { data: eventData } = useSWR(["event_ids", { ids: selectedObjectId }]);
|
const { data: eventData } = useSWR(["event_ids", { ids: selectedObjectId }]);
|
||||||
@ -76,14 +81,14 @@ export default function ObjectTrackOverlay({
|
|||||||
const currentObjectZones = useMemo(() => {
|
const currentObjectZones = useMemo(() => {
|
||||||
if (!objectTimeline) return [];
|
if (!objectTimeline) return [];
|
||||||
|
|
||||||
// Find the most recent timeline event at or before current time
|
// Find the most recent timeline event at or before effective current time
|
||||||
const relevantEvents = objectTimeline
|
const relevantEvents = objectTimeline
|
||||||
.filter((event) => event.timestamp <= currentTime)
|
.filter((event) => event.timestamp <= effectiveCurrentTime)
|
||||||
.sort((a, b) => b.timestamp - a.timestamp); // Most recent first
|
.sort((a, b) => b.timestamp - a.timestamp); // Most recent first
|
||||||
|
|
||||||
// Get zones from the most recent event
|
// Get zones from the most recent event
|
||||||
return relevantEvents[0]?.data?.zones || [];
|
return relevantEvents[0]?.data?.zones || [];
|
||||||
}, [objectTimeline, currentTime]);
|
}, [objectTimeline, effectiveCurrentTime]);
|
||||||
|
|
||||||
const zones = useMemo(() => {
|
const zones = useMemo(() => {
|
||||||
if (!config?.cameras?.[camera]?.zones || !currentObjectZones.length)
|
if (!config?.cameras?.[camera]?.zones || !currentObjectZones.length)
|
||||||
@ -320,9 +325,12 @@ export default function ObjectTrackOverlay({
|
|||||||
{(() => {
|
{(() => {
|
||||||
if (!objectTimeline) return null;
|
if (!objectTimeline) return null;
|
||||||
|
|
||||||
// Find the most recent timeline event at or before current time with a bounding box
|
// Find the most recent timeline event at or before effective current time with a bounding box
|
||||||
const relevantEvents = objectTimeline
|
const relevantEvents = objectTimeline
|
||||||
.filter((event) => event.timestamp <= currentTime && event.data.box)
|
.filter(
|
||||||
|
(event) =>
|
||||||
|
event.timestamp <= effectiveCurrentTime && event.data.box,
|
||||||
|
)
|
||||||
.sort((a, b) => b.timestamp - a.timestamp); // Most recent first
|
.sort((a, b) => b.timestamp - a.timestamp); // Most recent first
|
||||||
|
|
||||||
const currentEvent = relevantEvents[0];
|
const currentEvent = relevantEvents[0];
|
||||||
|
|||||||
@ -15,9 +15,11 @@ export default function ActivityStream({
|
|||||||
currentTime,
|
currentTime,
|
||||||
onSeek,
|
onSeek,
|
||||||
}: ActivityStreamProps) {
|
}: ActivityStreamProps) {
|
||||||
const { selectedObjectId } = useActivityStream();
|
const { selectedObjectId, annotationOffset } = useActivityStream();
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const effectiveTime = currentTime + annotationOffset;
|
||||||
|
|
||||||
// group activities by timestamp (within 1 second resolution window)
|
// group activities by timestamp (within 1 second resolution window)
|
||||||
const groupedActivities = useMemo(() => {
|
const groupedActivities = useMemo(() => {
|
||||||
const groups: { [key: number]: ObjectLifecycleSequence[] } = {};
|
const groups: { [key: number]: ObjectLifecycleSequence[] } = {};
|
||||||
@ -36,12 +38,13 @@ export default function ActivityStream({
|
|||||||
(a, b) => a.timestamp - b.timestamp,
|
(a, b) => a.timestamp - b.timestamp,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
timestamp: sortedActivities[0].timestamp, // use the earliest activity timestamp
|
timestamp: sortedActivities[0].timestamp, // Original timestamp for display
|
||||||
|
effectiveTimestamp: sortedActivities[0].timestamp + annotationOffset, // Adjusted for sorting/comparison
|
||||||
activities: sortedActivities,
|
activities: sortedActivities,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => a.timestamp - b.timestamp);
|
.sort((a, b) => a.timestamp - b.timestamp);
|
||||||
}, [timelineData]);
|
}, [timelineData, annotationOffset]);
|
||||||
|
|
||||||
// Filter activities if object is selected
|
// Filter activities if object is selected
|
||||||
const filteredGroups = useMemo(() => {
|
const filteredGroups = useMemo(() => {
|
||||||
@ -62,10 +65,10 @@ export default function ActivityStream({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!scrollRef.current) return;
|
if (!scrollRef.current) return;
|
||||||
|
|
||||||
// Find the last group where timestamp <= currentTime
|
// Find the last group where effectiveTimestamp <= currentTime + annotationOffset
|
||||||
let currentGroupIndex = -1;
|
let currentGroupIndex = -1;
|
||||||
for (let i = filteredGroups.length - 1; i >= 0; i--) {
|
for (let i = filteredGroups.length - 1; i >= 0; i--) {
|
||||||
if (filteredGroups[i].timestamp <= currentTime) {
|
if (filteredGroups[i].effectiveTimestamp <= effectiveTime) {
|
||||||
currentGroupIndex = i;
|
currentGroupIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -82,7 +85,7 @@ export default function ActivityStream({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [filteredGroups, currentTime]);
|
}, [filteredGroups, effectiveTime, annotationOffset]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -99,7 +102,7 @@ export default function ActivityStream({
|
|||||||
<ActivityGroup
|
<ActivityGroup
|
||||||
key={group.timestamp}
|
key={group.timestamp}
|
||||||
group={group}
|
group={group}
|
||||||
isCurrent={group.timestamp <= currentTime}
|
isCurrent={group.effectiveTimestamp <= currentTime}
|
||||||
onSeek={onSeek}
|
onSeek={onSeek}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
@ -112,6 +115,7 @@ export default function ActivityStream({
|
|||||||
type ActivityGroupProps = {
|
type ActivityGroupProps = {
|
||||||
group: {
|
group: {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
effectiveTimestamp: number;
|
||||||
activities: ObjectLifecycleSequence[];
|
activities: ObjectLifecycleSequence[];
|
||||||
};
|
};
|
||||||
isCurrent: boolean;
|
isCurrent: boolean;
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import React, { createContext, useContext, useState, useMemo } from "react";
|
import React, { createContext, useContext, useState, useMemo } from "react";
|
||||||
import { ObjectLifecycleSequence } from "@/types/timeline";
|
import { ObjectLifecycleSequence } from "@/types/timeline";
|
||||||
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
interface ActivityStreamContextType {
|
interface ActivityStreamContextType {
|
||||||
selectedObjectId: string | undefined;
|
selectedObjectId: string | undefined;
|
||||||
selectedObjectTimeline: ObjectLifecycleSequence[] | undefined;
|
selectedObjectTimeline: ObjectLifecycleSequence[] | undefined;
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
camera: string;
|
camera: string;
|
||||||
|
annotationOffset: number;
|
||||||
setSelectedObjectId: (id: string | undefined) => void;
|
setSelectedObjectId: (id: string | undefined) => void;
|
||||||
isActivityMode: boolean;
|
isActivityMode: boolean;
|
||||||
}
|
}
|
||||||
@ -33,6 +36,16 @@ export function ActivityStreamProvider({
|
|||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
|
const annotationOffset = useMemo(() => {
|
||||||
|
if (!config) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (config.cameras[camera]?.detect?.annotation_offset || 0) / 1000; // Convert to seconds
|
||||||
|
}, [config, camera]);
|
||||||
|
|
||||||
const selectedObjectTimeline = useMemo(() => {
|
const selectedObjectTimeline = useMemo(() => {
|
||||||
if (!selectedObjectId || !timelineData) return undefined;
|
if (!selectedObjectId || !timelineData) return undefined;
|
||||||
return timelineData.filter((item) => item.source_id === selectedObjectId);
|
return timelineData.filter((item) => item.source_id === selectedObjectId);
|
||||||
@ -43,6 +56,7 @@ export function ActivityStreamProvider({
|
|||||||
selectedObjectTimeline,
|
selectedObjectTimeline,
|
||||||
currentTime,
|
currentTime,
|
||||||
camera,
|
camera,
|
||||||
|
annotationOffset,
|
||||||
setSelectedObjectId,
|
setSelectedObjectId,
|
||||||
isActivityMode,
|
isActivityMode,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user