Files
CamTalk/frontend/src/hooks/useObservationMode.ts

89 lines
2.6 KiB
TypeScript
Raw Normal View History

// ============================================================
// useObservationMode — 观察模式 Hook
// 职责:定时采帧 → 关键帧检测 → 画面变化时触发回调
// 来源docs/05-用户故事.md US-05持续场景监控
// ============================================================
import { useCallback, useEffect, useRef, useState } from "react";
import { sampleFrame, compareFrames } from "../components/EdgeProcessor";
/** 画面变化显著阈值 */
const CHANGE_THRESHOLD = 0.85;
/** 采样间隔ms */
const SAMPLE_INTERVAL = 5000;
export interface ObservationOptions {
/** 画面变化回调,携带当前帧的 DataURL */
onChange?: (frameDataUrl: string) => void;
}
export function useObservationMode(options?: ObservationOptions) {
const [isObserving, setIsObserving] = useState(false);
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const prevFrameRef = useRef<Uint8ClampedArray | null>(null);
const optionsRef = useRef(options);
useEffect(() => {
optionsRef.current = options;
}, [options]);
/**
*
* @param video video
* @param captureFrame video DataURL
*/
const startObserving = useCallback(
(video: HTMLVideoElement | null, captureFrame: () => string | null) => {
if (!video) return;
// 立即采一帧作为基准
prevFrameRef.current = sampleFrame(video);
intervalRef.current = setInterval(() => {
const current = sampleFrame(video);
if (!current) return;
if (prevFrameRef.current) {
const { similarity } = compareFrames(prevFrameRef.current, current);
if (similarity < CHANGE_THRESHOLD) {
console.log(
`[Observation] 画面变化 (similarity=${similarity.toFixed(2)})`,
);
const frameDataUrl = captureFrame();
if (frameDataUrl) {
optionsRef.current?.onChange?.(frameDataUrl);
}
}
}
prevFrameRef.current = current;
}, SAMPLE_INTERVAL);
setIsObserving(true);
},
[],
);
/** 停止观察模式 */
const stopObserving = useCallback(() => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
prevFrameRef.current = null;
setIsObserving(false);
}, []);
// 组件卸载时清理
useEffect(() => {
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);
return { isObserving, startObserving, stopObserving };
}