mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-08 20:25:26 +03:00
Rewrite dynamic player to use html video for preview and fix grid gaps
This commit is contained in:
parent
deb43e126a
commit
60cd8e7ad8
@ -63,9 +63,8 @@ export default function DynamicVideoPlayer({
|
|||||||
// controlling playback
|
// controlling playback
|
||||||
|
|
||||||
const playerRef = useRef<Player | undefined>(undefined);
|
const playerRef = useRef<Player | undefined>(undefined);
|
||||||
const previewRef = useRef<Player | undefined>(undefined);
|
const previewRef = useRef<HTMLVideoElement | null>(null);
|
||||||
const [isScrubbing, setIsScrubbing] = useState(previewOnly);
|
const [isScrubbing, setIsScrubbing] = useState(previewOnly);
|
||||||
const [hasPreview, setHasPreview] = useState(false);
|
|
||||||
const [focusedItem, setFocusedItem] = useState<Timeline | undefined>(
|
const [focusedItem, setFocusedItem] = useState<Timeline | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
@ -155,13 +154,11 @@ export default function DynamicVideoPlayer({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (preview) {
|
if (preview) {
|
||||||
setHasPreview(true);
|
|
||||||
return {
|
return {
|
||||||
src: preview.src,
|
src: preview.src,
|
||||||
type: preview.type,
|
type: preview.type,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
setHasPreview(false);
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,6 +166,8 @@ export default function DynamicVideoPlayer({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [currentPreview, setCurrentPreview] = useState(initialPreviewSource);
|
||||||
|
|
||||||
// state of playback player
|
// state of playback player
|
||||||
|
|
||||||
const recordingParams = useMemo(() => {
|
const recordingParams = useMemo(() => {
|
||||||
@ -201,7 +200,7 @@ export default function DynamicVideoPlayer({
|
|||||||
Math.round(preview.start) >= timeRange.start &&
|
Math.round(preview.start) >= timeRange.start &&
|
||||||
Math.floor(preview.end) <= timeRange.end,
|
Math.floor(preview.end) <= timeRange.end,
|
||||||
);
|
);
|
||||||
setHasPreview(preview != undefined);
|
setCurrentPreview(preview);
|
||||||
|
|
||||||
controller.newPlayback({
|
controller.newPlayback({
|
||||||
recordings: recordings ?? [],
|
recordings: recordings ?? [],
|
||||||
@ -222,7 +221,7 @@ export default function DynamicVideoPlayer({
|
|||||||
{!previewOnly && (
|
{!previewOnly && (
|
||||||
<div
|
<div
|
||||||
className={`w-full relative ${
|
className={`w-full relative ${
|
||||||
hasPreview && isScrubbing ? "hidden" : "visible"
|
currentPreview != undefined && isScrubbing ? "hidden" : "visible"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
@ -266,35 +265,27 @@ export default function DynamicVideoPlayer({
|
|||||||
</VideoPlayer>
|
</VideoPlayer>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<video
|
||||||
className={`w-full ${hasPreview && isScrubbing ? "visible" : "hidden"}`}
|
ref={previewRef}
|
||||||
|
className={`size-full rounded-2xl ${currentPreview != undefined && isScrubbing ? "visible" : "hidden"} ${tallVideo ? "aspect-video" : ""} bg-black`}
|
||||||
|
preload="auto"
|
||||||
|
autoPlay
|
||||||
|
playsInline
|
||||||
|
muted
|
||||||
|
onSeeked={() => controller.finishedSeeking()}
|
||||||
|
onLoadedData={() => controller.previewReady()}
|
||||||
|
onLoadStart={
|
||||||
|
previewOnly && onControllerReady
|
||||||
|
? () => {
|
||||||
|
onControllerReady(controller);
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<VideoPlayer
|
{currentPreview != undefined && (
|
||||||
options={{
|
<source src={currentPreview.src} type={currentPreview.type} />
|
||||||
preload: "auto",
|
)}
|
||||||
autoplay: true,
|
</video>
|
||||||
controls: false,
|
|
||||||
muted: true,
|
|
||||||
loadingSpinner: false,
|
|
||||||
sources: hasPreview ? initialPreviewSource : null,
|
|
||||||
aspectRatio: tallVideo ? "16:9" : undefined,
|
|
||||||
}}
|
|
||||||
seekOptions={{}}
|
|
||||||
onReady={(player) => {
|
|
||||||
previewRef.current = player;
|
|
||||||
player.pause();
|
|
||||||
player.on("seeked", () => controller.finishedSeeking());
|
|
||||||
player.on("loadeddata", () => controller.previewReady());
|
|
||||||
|
|
||||||
if (previewOnly && onControllerReady) {
|
|
||||||
onControllerReady(controller);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onDispose={() => {
|
|
||||||
previewRef.current = undefined;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -302,7 +293,7 @@ export default function DynamicVideoPlayer({
|
|||||||
export class DynamicVideoController {
|
export class DynamicVideoController {
|
||||||
// main state
|
// main state
|
||||||
private playerRef: MutableRefObject<Player | undefined>;
|
private playerRef: MutableRefObject<Player | undefined>;
|
||||||
private previewRef: MutableRefObject<Player | undefined>;
|
private previewRef: MutableRefObject<HTMLVideoElement | null>;
|
||||||
private setScrubbing: (isScrubbing: boolean) => void;
|
private setScrubbing: (isScrubbing: boolean) => void;
|
||||||
private setFocusedItem: (timeline: Timeline) => void;
|
private setFocusedItem: (timeline: Timeline) => void;
|
||||||
private playerMode: PlayerMode = "playback";
|
private playerMode: PlayerMode = "playback";
|
||||||
@ -324,7 +315,7 @@ export class DynamicVideoController {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
playerRef: MutableRefObject<Player | undefined>,
|
playerRef: MutableRefObject<Player | undefined>,
|
||||||
previewRef: MutableRefObject<Player | undefined>,
|
previewRef: MutableRefObject<HTMLVideoElement | null>,
|
||||||
annotationOffset: number,
|
annotationOffset: number,
|
||||||
defaultMode: PlayerMode,
|
defaultMode: PlayerMode,
|
||||||
setScrubbing: (isScrubbing: boolean) => void,
|
setScrubbing: (isScrubbing: boolean) => void,
|
||||||
@ -352,12 +343,6 @@ export class DynamicVideoController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.preview = newPlayback.preview;
|
this.preview = newPlayback.preview;
|
||||||
if (this.preview && this.previewRef.current) {
|
|
||||||
this.previewRef.current.src({
|
|
||||||
src: this.preview.src,
|
|
||||||
type: this.preview.type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
seekToTimestamp(time: number, play: boolean = false) {
|
seekToTimestamp(time: number, play: boolean = false) {
|
||||||
@ -487,15 +472,22 @@ export class DynamicVideoController {
|
|||||||
if (this.seeking) {
|
if (this.seeking) {
|
||||||
this.timeToSeek = time;
|
this.timeToSeek = time;
|
||||||
} else {
|
} else {
|
||||||
this.previewRef.current?.currentTime(
|
if (this.previewRef.current) {
|
||||||
Math.max(0, time - this.preview.start),
|
this.previewRef.current.currentTime = Math.max(
|
||||||
);
|
0,
|
||||||
this.seeking = true;
|
time - this.preview.start,
|
||||||
|
);
|
||||||
|
this.seeking = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finishedSeeking() {
|
finishedSeeking() {
|
||||||
if (!this.preview || this.playerMode == "playback") {
|
if (
|
||||||
|
!this.previewRef.current ||
|
||||||
|
!this.preview ||
|
||||||
|
this.playerMode == "playback"
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,11 +495,10 @@ export class DynamicVideoController {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
this.timeToSeek &&
|
this.timeToSeek &&
|
||||||
this.timeToSeek != this.previewRef.current?.currentTime()
|
this.timeToSeek != this.previewRef.current?.currentTime
|
||||||
) {
|
) {
|
||||||
this.previewRef.current?.currentTime(
|
this.previewRef.current.currentTime =
|
||||||
this.timeToSeek - this.preview.start,
|
this.timeToSeek - this.preview.start;
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
this.seeking = false;
|
this.seeking = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -608,7 +608,7 @@ function MotionReview({
|
|||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
className="size-full m-2 grid sm:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4 overflow-auto no-scrollbar"
|
className="w-full h-min m-2 grid sm:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4 overflow-auto no-scrollbar"
|
||||||
>
|
>
|
||||||
{reviewCameras.map((camera) => {
|
{reviewCameras.map((camera) => {
|
||||||
let grow;
|
let grow;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user