mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Add ability to set playback speed on motion playback (#10628)
* Allow control of playback rate on motion page * Apply playback rate
This commit is contained in:
		
							parent
							
								
									76a114a3cd
								
							
						
					
					
						commit
						63bf986e08
					
				@ -160,6 +160,7 @@ export default function HlsVideoPlayer({
 | 
				
			|||||||
        show={controls}
 | 
					        show={controls}
 | 
				
			||||||
        controlsOpen={controlsOpen}
 | 
					        controlsOpen={controlsOpen}
 | 
				
			||||||
        setControlsOpen={setControlsOpen}
 | 
					        setControlsOpen={setControlsOpen}
 | 
				
			||||||
 | 
					        playbackRate={videoRef.current?.playbackRate ?? 1}
 | 
				
			||||||
        onPlayPause={(play) => {
 | 
					        onPlayPause={(play) => {
 | 
				
			||||||
          if (!videoRef.current) {
 | 
					          if (!videoRef.current) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
@ -180,6 +181,9 @@ export default function HlsVideoPlayer({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          videoRef.current.currentTime = Math.max(0, currentTime + diff);
 | 
					          videoRef.current.currentTime = Math.max(0, currentTime + diff);
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
 | 
					        onSetPlaybackRate={(rate) =>
 | 
				
			||||||
 | 
					          videoRef.current ? (videoRef.current.playbackRate = rate) : null
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      {children}
 | 
					      {children}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,7 @@ const CONTROLS_DEFAULT: VideoControls = {
 | 
				
			|||||||
  seek: true,
 | 
					  seek: true,
 | 
				
			||||||
  playbackRate: true,
 | 
					  playbackRate: true,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					const PLAYBACK_RATE_DEFAULT = isSafari ? [0.5, 1, 2] : [0.5, 1, 2, 4, 8, 16];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type VideoControlsProps = {
 | 
					type VideoControlsProps = {
 | 
				
			||||||
  className?: string;
 | 
					  className?: string;
 | 
				
			||||||
@ -38,9 +39,12 @@ type VideoControlsProps = {
 | 
				
			|||||||
  isPlaying: boolean;
 | 
					  isPlaying: boolean;
 | 
				
			||||||
  show: boolean;
 | 
					  show: boolean;
 | 
				
			||||||
  controlsOpen?: boolean;
 | 
					  controlsOpen?: boolean;
 | 
				
			||||||
 | 
					  playbackRates?: number[];
 | 
				
			||||||
 | 
					  playbackRate: number;
 | 
				
			||||||
  setControlsOpen?: (open: boolean) => void;
 | 
					  setControlsOpen?: (open: boolean) => void;
 | 
				
			||||||
  onPlayPause: (play: boolean) => void;
 | 
					  onPlayPause: (play: boolean) => void;
 | 
				
			||||||
  onSeek: (diff: number) => void;
 | 
					  onSeek: (diff: number) => void;
 | 
				
			||||||
 | 
					  onSetPlaybackRate: (rate: number) => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export default function VideoControls({
 | 
					export default function VideoControls({
 | 
				
			||||||
  className,
 | 
					  className,
 | 
				
			||||||
@ -49,18 +53,13 @@ export default function VideoControls({
 | 
				
			|||||||
  isPlaying,
 | 
					  isPlaying,
 | 
				
			||||||
  show,
 | 
					  show,
 | 
				
			||||||
  controlsOpen,
 | 
					  controlsOpen,
 | 
				
			||||||
 | 
					  playbackRates = PLAYBACK_RATE_DEFAULT,
 | 
				
			||||||
 | 
					  playbackRate,
 | 
				
			||||||
  setControlsOpen,
 | 
					  setControlsOpen,
 | 
				
			||||||
  onPlayPause,
 | 
					  onPlayPause,
 | 
				
			||||||
  onSeek,
 | 
					  onSeek,
 | 
				
			||||||
 | 
					  onSetPlaybackRate,
 | 
				
			||||||
}: VideoControlsProps) {
 | 
					}: VideoControlsProps) {
 | 
				
			||||||
  const playbackRates = useMemo(() => {
 | 
					 | 
				
			||||||
    if (isSafari) {
 | 
					 | 
				
			||||||
      return [0.5, 1, 2];
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      return [0.5, 1, 2, 4, 8, 16];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }, []);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const onReplay = useCallback(
 | 
					  const onReplay = useCallback(
 | 
				
			||||||
    (e: React.MouseEvent<SVGElement>) => {
 | 
					    (e: React.MouseEvent<SVGElement>) => {
 | 
				
			||||||
      e.stopPropagation();
 | 
					      e.stopPropagation();
 | 
				
			||||||
@ -177,7 +176,7 @@ export default function VideoControls({
 | 
				
			|||||||
      {features.seek && (
 | 
					      {features.seek && (
 | 
				
			||||||
        <MdForward10 className="size-5 cursor-pointer" onClick={onSkip} />
 | 
					        <MdForward10 className="size-5 cursor-pointer" onClick={onSkip} />
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
      {video && features.playbackRate && (
 | 
					      {features.playbackRate && (
 | 
				
			||||||
        <DropdownMenu
 | 
					        <DropdownMenu
 | 
				
			||||||
          open={controlsOpen == true}
 | 
					          open={controlsOpen == true}
 | 
				
			||||||
          onOpenChange={(open) => {
 | 
					          onOpenChange={(open) => {
 | 
				
			||||||
@ -186,10 +185,10 @@ export default function VideoControls({
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <DropdownMenuTrigger>{`${video.playbackRate}x`}</DropdownMenuTrigger>
 | 
					          <DropdownMenuTrigger>{`${playbackRate}x`}</DropdownMenuTrigger>
 | 
				
			||||||
          <DropdownMenuContent>
 | 
					          <DropdownMenuContent>
 | 
				
			||||||
            <DropdownMenuRadioGroup
 | 
					            <DropdownMenuRadioGroup
 | 
				
			||||||
              onValueChange={(rate) => (video.playbackRate = parseFloat(rate))}
 | 
					              onValueChange={(rate) => onSetPlaybackRate(parseFloat(rate))}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              {playbackRates.map((rate) => (
 | 
					              {playbackRates.map((rate) => (
 | 
				
			||||||
                <DropdownMenuRadioItem key={rate} value={rate.toString()}>
 | 
					                <DropdownMenuRadioItem key={rate} value={rate.toString()}>
 | 
				
			||||||
 | 
				
			|||||||
@ -716,11 +716,15 @@ function MotionReview({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // playback
 | 
					  // playback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [playbackRate, setPlaybackRate] = useState(8);
 | 
				
			||||||
 | 
					  const [controlsOpen, setControlsOpen] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (!playing) {
 | 
					    if (!playing) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const interval = 500 / playbackRate;
 | 
				
			||||||
    const startTime = currentTime;
 | 
					    const startTime = currentTime;
 | 
				
			||||||
    let counter = 0;
 | 
					    let counter = 0;
 | 
				
			||||||
    const intervalId = setInterval(() => {
 | 
					    const intervalId = setInterval(() => {
 | 
				
			||||||
@ -732,14 +736,14 @@ function MotionReview({
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      setCurrentTime(startTime + counter);
 | 
					      setCurrentTime(startTime + counter);
 | 
				
			||||||
    }, 60);
 | 
					    }, interval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return () => {
 | 
					    return () => {
 | 
				
			||||||
      clearInterval(intervalId);
 | 
					      clearInterval(intervalId);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    // do not render when current time changes
 | 
					    // do not render when current time changes
 | 
				
			||||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
					    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
				
			||||||
  }, [playing]);
 | 
					  }, [playing, playbackRate]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!relevantPreviews) {
 | 
					  if (!relevantPreviews) {
 | 
				
			||||||
    return <ActivityIndicator />;
 | 
					    return <ActivityIndicator />;
 | 
				
			||||||
@ -815,9 +819,13 @@ function MotionReview({
 | 
				
			|||||||
        features={{
 | 
					        features={{
 | 
				
			||||||
          volume: false,
 | 
					          volume: false,
 | 
				
			||||||
          seek: true,
 | 
					          seek: true,
 | 
				
			||||||
          playbackRate: false,
 | 
					          playbackRate: true,
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
        isPlaying={playing}
 | 
					        isPlaying={playing}
 | 
				
			||||||
 | 
					        playbackRates={[4, 8, 12, 16]}
 | 
				
			||||||
 | 
					        playbackRate={playbackRate}
 | 
				
			||||||
 | 
					        controlsOpen={controlsOpen}
 | 
				
			||||||
 | 
					        setControlsOpen={setControlsOpen}
 | 
				
			||||||
        onPlayPause={setPlaying}
 | 
					        onPlayPause={setPlaying}
 | 
				
			||||||
        onSeek={(diff) => {
 | 
					        onSeek={(diff) => {
 | 
				
			||||||
          const wasPlaying = playing;
 | 
					          const wasPlaying = playing;
 | 
				
			||||||
@ -832,6 +840,7 @@ function MotionReview({
 | 
				
			|||||||
            setTimeout(() => setPlaying(true), 100);
 | 
					            setTimeout(() => setPlaying(true), 100);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
 | 
					        onSetPlaybackRate={setPlaybackRate}
 | 
				
			||||||
        show={currentTime < timeRange.before - 4}
 | 
					        show={currentTime < timeRange.before - 4}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    </>
 | 
					    </>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user