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

This commit is contained in:
Nicolas Mowen 2024-03-03 21:05:11 -07:00
parent deb43e126a
commit 60cd8e7ad8
2 changed files with 43 additions and 52 deletions

View File

@ -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`}
<VideoPlayer preload="auto"
options={{ autoPlay
preload: "auto", playsInline
autoplay: true, muted
controls: false, onSeeked={() => controller.finishedSeeking()}
muted: true, onLoadedData={() => controller.previewReady()}
loadingSpinner: false, onLoadStart={
sources: hasPreview ? initialPreviewSource : null, previewOnly && onControllerReady
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); onControllerReady(controller);
} }
}} : undefined
onDispose={() => { }
previewRef.current = undefined; >
}} {currentPreview != undefined && (
/> <source src={currentPreview.src} type={currentPreview.type} />
</div> )}
</video>
</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,
time - this.preview.start,
); );
this.seeking = true; 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;
} }

View File

@ -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;