mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-09 04:35:25 +03:00
Implement backward preview jump and jump lockout
This commit is contained in:
parent
5864e6791b
commit
92116dcc6a
@ -18,6 +18,8 @@ import { Recording } from "@/types/record";
|
||||
import { Preview } from "@/types/preview";
|
||||
import { DynamicPlayback } from "@/types/playback";
|
||||
|
||||
type PlayerMode = "playback" | "scrubbing";
|
||||
|
||||
/**
|
||||
* Dynamically switches between video playback and scrubbing preview player.
|
||||
*/
|
||||
@ -26,6 +28,7 @@ type DynamicVideoPlayerProps = {
|
||||
camera: string;
|
||||
timeRange: { start: number; end: number };
|
||||
cameraPreviews: Preview[];
|
||||
defaultMode?: PlayerMode;
|
||||
onControllerReady?: (controller: DynamicVideoController) => void;
|
||||
};
|
||||
export default function DynamicVideoPlayer({
|
||||
@ -33,6 +36,7 @@ export default function DynamicVideoPlayer({
|
||||
camera,
|
||||
timeRange,
|
||||
cameraPreviews,
|
||||
defaultMode = "playback",
|
||||
onControllerReady,
|
||||
}: DynamicVideoPlayerProps) {
|
||||
const apiHost = useApiHost();
|
||||
@ -60,7 +64,7 @@ export default function DynamicVideoPlayer({
|
||||
|
||||
const playerRef = useRef<Player | undefined>(undefined);
|
||||
const previewRef = useRef<Player | undefined>(undefined);
|
||||
const [isScrubbing, setIsScrubbing] = useState(false);
|
||||
const [isScrubbing, setIsScrubbing] = useState(defaultMode == "scrubbing");
|
||||
const [hasPreview, setHasPreview] = useState(false);
|
||||
const [focusedItem, setFocusedItem] = useState<Timeline | undefined>(
|
||||
undefined,
|
||||
@ -74,10 +78,11 @@ export default function DynamicVideoPlayer({
|
||||
playerRef,
|
||||
previewRef,
|
||||
(config.cameras[camera]?.detect?.annotation_offset || 0) / 1000,
|
||||
defaultMode,
|
||||
setIsScrubbing,
|
||||
setFocusedItem,
|
||||
);
|
||||
}, [camera, config]);
|
||||
}, [camera, config, defaultMode]);
|
||||
|
||||
// keyboard control
|
||||
|
||||
@ -178,7 +183,7 @@ export default function DynamicVideoPlayer({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!controller || !recordings || recordings.length == 0) {
|
||||
if (!controller || !recordings) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -292,7 +297,7 @@ export class DynamicVideoController {
|
||||
private previewRef: MutableRefObject<Player | undefined>;
|
||||
private setScrubbing: (isScrubbing: boolean) => void;
|
||||
private setFocusedItem: (timeline: Timeline) => void;
|
||||
private playerMode: "playback" | "scrubbing" = "playback";
|
||||
private playerMode: PlayerMode = "playback";
|
||||
|
||||
// playback
|
||||
private recordings: Recording[] = [];
|
||||
@ -301,6 +306,7 @@ export class DynamicVideoController {
|
||||
undefined;
|
||||
private annotationOffset: number;
|
||||
private timeToStart: number | undefined = undefined;
|
||||
private clipChangeLockout: boolean = false;
|
||||
|
||||
// preview
|
||||
private preview: Preview | undefined = undefined;
|
||||
@ -312,12 +318,14 @@ export class DynamicVideoController {
|
||||
playerRef: MutableRefObject<Player | undefined>,
|
||||
previewRef: MutableRefObject<Player | undefined>,
|
||||
annotationOffset: number,
|
||||
defaultMode: PlayerMode,
|
||||
setScrubbing: (isScrubbing: boolean) => void,
|
||||
setFocusedItem: (timeline: Timeline) => void,
|
||||
) {
|
||||
this.playerRef = playerRef;
|
||||
this.previewRef = previewRef;
|
||||
this.annotationOffset = annotationOffset;
|
||||
this.playerMode = defaultMode;
|
||||
this.setScrubbing = setScrubbing;
|
||||
this.setFocusedItem = setFocusedItem;
|
||||
}
|
||||
@ -429,17 +437,39 @@ export class DynamicVideoController {
|
||||
}
|
||||
|
||||
if (time > this.preview.end) {
|
||||
if (this.clipChangeLockout) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.playerMode == "scrubbing") {
|
||||
this.playerMode = "playback";
|
||||
this.setScrubbing(false);
|
||||
this.timeToSeek = undefined;
|
||||
this.seeking = false;
|
||||
this.readyToScrub = false;
|
||||
this.clipChangeLockout = true;
|
||||
this.fireClipChangeEvent("forward");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (time < this.preview.start) {
|
||||
if (this.clipChangeLockout) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.playerMode == "scrubbing") {
|
||||
this.playerMode = "playback";
|
||||
this.setScrubbing(false);
|
||||
this.timeToSeek = undefined;
|
||||
this.seeking = false;
|
||||
this.readyToScrub = false;
|
||||
this.clipChangeLockout = true;
|
||||
this.fireClipChangeEvent("backward");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.playerMode != "scrubbing") {
|
||||
this.playerMode = "scrubbing";
|
||||
this.playerRef.current?.pause();
|
||||
@ -461,6 +491,8 @@ export class DynamicVideoController {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clipChangeLockout = false;
|
||||
|
||||
if (
|
||||
this.timeToSeek &&
|
||||
this.timeToSeek != this.previewRef.current?.currentTime()
|
||||
|
||||
@ -88,7 +88,7 @@ export default function VideoPlayer({
|
||||
|
||||
return (
|
||||
<div data-vjs-player>
|
||||
<div ref={videoRef} />
|
||||
<div className="rounded-2xl overflow-hidden" ref={videoRef} />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -533,9 +533,7 @@ function MotionReview({
|
||||
|
||||
let cameras;
|
||||
if (!filter || !filter.cameras) {
|
||||
cameras = Object.values(config.cameras).filter(
|
||||
(cam) => cam.name == "front_cam",
|
||||
);
|
||||
cameras = Object.values(config.cameras);
|
||||
} else {
|
||||
const filteredCams = filter.cameras;
|
||||
|
||||
@ -569,6 +567,26 @@ function MotionReview({
|
||||
timeRangeSegments.ranges[selectedRangeIdx].start,
|
||||
);
|
||||
|
||||
// move to next clip
|
||||
useEffect(() => {
|
||||
if (!videoPlayersRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.values(videoPlayersRef.current).forEach((controller) => {
|
||||
controller.onClipChangedEvent((dir) => {
|
||||
if (
|
||||
dir == "forward" &&
|
||||
selectedRangeIdx < timeRangeSegments.ranges.length - 1
|
||||
) {
|
||||
setSelectedRangeIdx(selectedRangeIdx + 1);
|
||||
} else if (selectedRangeIdx > 0) {
|
||||
setSelectedRangeIdx(selectedRangeIdx - 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [selectedRangeIdx, timeRangeSegments]);
|
||||
|
||||
useEffect(() => {
|
||||
Object.values(videoPlayersRef.current).forEach((controller) => {
|
||||
controller.scrubToTimestamp(currentTime);
|
||||
@ -579,7 +597,7 @@ function MotionReview({
|
||||
<>
|
||||
<div
|
||||
ref={contentRef}
|
||||
className={`size-full mt-4 grid sm:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4`}
|
||||
className={`size-full m-2 grid sm:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 gap-2 overflow-auto`}
|
||||
>
|
||||
{reviewCameras.map((camera) => {
|
||||
let grow;
|
||||
@ -597,9 +615,9 @@ function MotionReview({
|
||||
camera={camera.name}
|
||||
timeRange={timeRangeSegments.ranges[selectedRangeIdx]}
|
||||
cameraPreviews={relevantPreviews || []}
|
||||
defaultMode="scrubbing"
|
||||
onControllerReady={(controller) => {
|
||||
videoPlayersRef.current[camera.name] = controller;
|
||||
//setPlayerReady(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user