mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-01-21 00:06:44 +01:00
Fix android/chrome seeking on previews (#10512)
This commit is contained in:
parent
bb6f153e2e
commit
880bae1eb2
@ -12,7 +12,7 @@ import { Preview } from "@/types/preview";
|
||||
import { PreviewPlayback } from "@/types/playback";
|
||||
import { isCurrentHour } from "@/utils/dateUtil";
|
||||
import { baseUrl } from "@/api/baseUrl";
|
||||
import { isAndroid } from "react-device-detect";
|
||||
import { isAndroid, isChrome, isMobile } from "react-device-detect";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
|
||||
type PreviewPlayerProps = {
|
||||
@ -21,6 +21,7 @@ type PreviewPlayerProps = {
|
||||
timeRange: { start: number; end: number };
|
||||
cameraPreviews: Preview[];
|
||||
startTime?: number;
|
||||
isScrubbing: boolean;
|
||||
onControllerReady: (controller: PreviewController) => void;
|
||||
onClick?: () => void;
|
||||
};
|
||||
@ -30,6 +31,7 @@ export default function PreviewPlayer({
|
||||
timeRange,
|
||||
cameraPreviews,
|
||||
startTime,
|
||||
isScrubbing,
|
||||
onControllerReady,
|
||||
onClick,
|
||||
}: PreviewPlayerProps) {
|
||||
@ -53,6 +55,7 @@ export default function PreviewPlayer({
|
||||
timeRange={timeRange}
|
||||
cameraPreviews={cameraPreviews}
|
||||
startTime={startTime}
|
||||
isScrubbing={isScrubbing}
|
||||
onControllerReady={onControllerReady}
|
||||
onClick={onClick}
|
||||
/>
|
||||
@ -79,6 +82,7 @@ type PreviewVideoPlayerProps = {
|
||||
timeRange: { start: number; end: number };
|
||||
cameraPreviews: Preview[];
|
||||
startTime?: number;
|
||||
isScrubbing: boolean;
|
||||
onControllerReady: (controller: PreviewVideoController) => void;
|
||||
onClick?: () => void;
|
||||
};
|
||||
@ -88,6 +92,7 @@ function PreviewVideoPlayer({
|
||||
timeRange,
|
||||
cameraPreviews,
|
||||
startTime,
|
||||
isScrubbing,
|
||||
onControllerReady,
|
||||
onClick,
|
||||
}: PreviewVideoPlayerProps) {
|
||||
@ -118,6 +123,14 @@ function PreviewVideoPlayer({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [controller]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
controller.scrubbing = isScrubbing;
|
||||
}, [controller, isScrubbing]);
|
||||
|
||||
// initial state
|
||||
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
@ -140,7 +153,12 @@ function PreviewVideoPlayer({
|
||||
return;
|
||||
}
|
||||
|
||||
controller.finishedSeeking();
|
||||
if (isAndroid && isChrome) {
|
||||
// android/chrome glitches when setting currentTime at the same time as onSeeked
|
||||
setTimeout(() => controller.finishedSeeking(), 25);
|
||||
} else {
|
||||
controller.finishedSeeking();
|
||||
}
|
||||
}, [controller]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -227,6 +245,7 @@ class PreviewVideoController extends PreviewController {
|
||||
// preview
|
||||
private preview: Preview | undefined = undefined;
|
||||
private timeToSeek: number | undefined = undefined;
|
||||
public scrubbing = false;
|
||||
private seeking = false;
|
||||
|
||||
constructor(
|
||||
@ -253,6 +272,18 @@ class PreviewVideoController extends PreviewController {
|
||||
return false;
|
||||
}
|
||||
|
||||
const seekTime = time - this.preview.start;
|
||||
|
||||
if (
|
||||
isAndroid &&
|
||||
isChrome &&
|
||||
this.scrubbing &&
|
||||
Math.abs(seekTime - this.previewRef.current.currentTime) > 400
|
||||
) {
|
||||
// android/chrome has incorrect timestamps sent that are before the expected seek time
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.seeking) {
|
||||
this.timeToSeek = time;
|
||||
} else {
|
||||
@ -276,21 +307,12 @@ class PreviewVideoController extends PreviewController {
|
||||
Math.round(this.timeToSeek) -
|
||||
Math.round(this.previewRef.current.currentTime + this.preview.start);
|
||||
|
||||
if (Math.abs(diff) > 1) {
|
||||
let seekTime;
|
||||
if (isAndroid) {
|
||||
if (diff < 30) {
|
||||
seekTime = Math.round(
|
||||
this.previewRef.current.currentTime + diff / 2,
|
||||
);
|
||||
} else {
|
||||
seekTime = Math.round(this.timeToSeek - this.preview.start);
|
||||
}
|
||||
} else {
|
||||
seekTime = this.timeToSeek - this.preview.start;
|
||||
}
|
||||
const scrubLimit = isMobile ? 1 : 0.5;
|
||||
|
||||
this.previewRef.current.currentTime = seekTime;
|
||||
if (Math.abs(diff) >= scrubLimit) {
|
||||
// only seek if there is an appropriate amount of time difference
|
||||
this.previewRef.current.currentTime =
|
||||
this.timeToSeek - this.preview.start;
|
||||
} else {
|
||||
this.seeking = false;
|
||||
this.timeToSeek = undefined;
|
||||
|
@ -18,10 +18,10 @@ type DynamicVideoPlayerProps = {
|
||||
timeRange: { start: number; end: number };
|
||||
cameraPreviews: Preview[];
|
||||
startTimestamp?: number;
|
||||
isScrubbing: boolean;
|
||||
onControllerReady: (controller: DynamicVideoController) => void;
|
||||
onTimestampUpdate?: (timestamp: number) => void;
|
||||
onClipEnded?: () => void;
|
||||
isScrubbing: boolean;
|
||||
};
|
||||
export default function DynamicVideoPlayer({
|
||||
className,
|
||||
@ -29,10 +29,10 @@ export default function DynamicVideoPlayer({
|
||||
timeRange,
|
||||
cameraPreviews,
|
||||
startTimestamp,
|
||||
isScrubbing,
|
||||
onControllerReady,
|
||||
onTimestampUpdate,
|
||||
onClipEnded,
|
||||
isScrubbing,
|
||||
}: DynamicVideoPlayerProps) {
|
||||
const apiHost = useApiHost();
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
@ -192,6 +192,7 @@ export default function DynamicVideoPlayer({
|
||||
timeRange={timeRange}
|
||||
cameraPreviews={cameraPreviews}
|
||||
startTime={startTimestamp}
|
||||
isScrubbing={isScrubbing}
|
||||
onControllerReady={(previewController) => {
|
||||
setPreviewController(previewController);
|
||||
}}
|
||||
|
@ -616,6 +616,8 @@ function MotionReview({
|
||||
[selectedRangeIdx, timeRangeSegments],
|
||||
);
|
||||
|
||||
const [scrubbing, setScrubbing] = useState(false);
|
||||
|
||||
// move to next clip
|
||||
|
||||
useEffect(() => {
|
||||
@ -670,6 +672,7 @@ function MotionReview({
|
||||
timeRange={currentTimeRange}
|
||||
startTime={startTime}
|
||||
cameraPreviews={relevantPreviews || []}
|
||||
isScrubbing={scrubbing}
|
||||
onControllerReady={(controller) => {
|
||||
videoPlayersRef.current[camera.name] = controller;
|
||||
}}
|
||||
@ -694,6 +697,7 @@ function MotionReview({
|
||||
motion_events={motionData ?? []}
|
||||
severityType="significant_motion"
|
||||
contentRef={contentRef}
|
||||
onHandlebarDraggingChange={(scrubbing) => setScrubbing(scrubbing)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
@ -192,7 +192,7 @@ export function RecordingView({
|
||||
const grow = useMemo(() => {
|
||||
if (mainCameraAspect == "wide") {
|
||||
return "w-full aspect-wide";
|
||||
} else if (mainCameraAspect == "tall") {
|
||||
} else if (isDesktop && mainCameraAspect == "tall") {
|
||||
return "h-full aspect-tall";
|
||||
} else {
|
||||
return "w-full aspect-video";
|
||||
@ -288,6 +288,7 @@ export function RecordingView({
|
||||
timeRange={currentTimeRange}
|
||||
cameraPreviews={allPreviews ?? []}
|
||||
startTime={startTime}
|
||||
isScrubbing={scrubbing}
|
||||
onControllerReady={(controller) => {
|
||||
previewRefs.current[cam] = controller;
|
||||
controller.scrubToTimestamp(startTime);
|
||||
|
Loading…
Reference in New Issue
Block a user