Files
frigate/web/src/components/player/VideoPlayer.tsx
Nicolas Mowen c2a537ce22 Motion review (#10221)
* Break detection grid into separate function

* Implement backward preview jump and jump lockout

* ensure lockout is engaged when starting

* Add preview only mode to make loading more efficeint

* remove scrollbar and match gaps/margins with live view

* Rewrite dynamic player to use html video for preview and fix grid gaps

* consistent check for aspect ratio for tall cameras

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
2024-03-03 22:21:30 -06:00

96 lines
2.5 KiB
TypeScript

import { useEffect, useRef, ReactElement } from "react";
import videojs from "video.js";
import "videojs-playlist";
import "video.js/dist/video-js.css";
import Player from "video.js/dist/types/player";
type VideoPlayerProps = {
children?: ReactElement | ReactElement[];
options?: {
[key: string]: unknown;
};
seekOptions?: {
forward?: number;
backward?: number;
};
remotePlayback?: boolean;
onReady?: (player: Player) => void;
onDispose?: () => void;
};
export default function VideoPlayer({
children,
options,
seekOptions = { forward: 30, backward: 10 },
remotePlayback = false,
onReady = () => {},
onDispose = () => {},
}: VideoPlayerProps) {
const videoRef = useRef<HTMLDivElement | null>(null);
const playerRef = useRef<Player | null>(null);
useEffect(() => {
const defaultOptions = {
controls: true,
controlBar: {
skipButtons: seekOptions,
},
playbackRates: [0.5, 1, 2, 4, 8],
fluid: true,
};
if (!videojs.browser.IS_FIREFOX) {
defaultOptions.playbackRates.push(16);
}
// Make sure Video.js player is only initialized once
if (!playerRef.current) {
// The Video.js player needs to be _inside_ the component el for React 18 Strict Mode.
const videoElement = document.createElement(
"video-js",
) as HTMLVideoElement;
videoElement.controls = true;
videoElement.playsInline = true;
videoElement.disableRemotePlayback = !remotePlayback;
videoElement.classList.add("small-player");
videoElement.classList.add("video-js");
videoElement.classList.add("vjs-default-skin");
videoRef.current?.appendChild(videoElement);
const player = (playerRef.current = videojs(
videoElement,
{ ...defaultOptions, ...options },
() => {
onReady && onReady(player);
},
));
}
// we know that these deps are correct
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [options, videoRef]);
// Dispose the Video.js player when the functional component unmounts
useEffect(() => {
const player = playerRef.current;
return () => {
if (player && !player.isDisposed()) {
player.dispose();
playerRef.current = null;
onDispose();
}
};
// we know that these deps are correct
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playerRef]);
return (
<div data-vjs-player>
<div className="rounded-2xl overflow-hidden" ref={videoRef} />
{children}
</div>
);
}