diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index 7a147939f..4df6f16e4 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -28,11 +28,16 @@ const unsupportedErrorCodes = [ MediaError.MEDIA_ERR_DECODE, ]; +export interface HlsSource { + playlist: string; + startPosition: number; +} + type HlsVideoPlayerProps = { videoRef: MutableRefObject; containerRef?: React.MutableRefObject; visible: boolean; - currentSource: string; + currentSource: HlsSource; hotKeys: boolean; supportsFullscreen: boolean; fullscreen: boolean; @@ -113,17 +118,25 @@ export default function HlsVideoPlayer({ const currentPlaybackRate = videoRef.current.playbackRate; if (!useHlsCompat) { - videoRef.current.src = currentSource; + videoRef.current.src = currentSource.playlist; videoRef.current.load(); return; } - if (!hlsRef.current) { - hlsRef.current = new Hls(); - hlsRef.current.attachMedia(videoRef.current); + // we must destroy the hlsRef every time the source changes + // so that we can create a new HLS instance with startPosition + // set at the optimal point in time + if (hlsRef.current) { + hlsRef.current.destroy(); } - hlsRef.current.loadSource(currentSource); + hlsRef.current = new Hls({ + maxBufferLength: 10, + maxBufferSize: 20 * 1000 * 1000, + startPosition: currentSource.startPosition, + }); + hlsRef.current.attachMedia(videoRef.current); + hlsRef.current.loadSource(currentSource.playlist); videoRef.current.playbackRate = currentPlaybackRate; }, [videoRef, hlsRef, useHlsCompat, currentSource]); diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index 450dd1a17..336338660 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -6,7 +6,7 @@ import { Recording } from "@/types/record"; import { Preview } from "@/types/preview"; import PreviewPlayer, { PreviewController } from "../PreviewPlayer"; import { DynamicVideoController } from "./DynamicVideoController"; -import HlsVideoPlayer from "../HlsVideoPlayer"; +import HlsVideoPlayer, { HlsSource } from "../HlsVideoPlayer"; import { TimeRange } from "@/types/timeline"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { VideoResolutionType } from "@/types/live"; @@ -98,9 +98,10 @@ export default function DynamicVideoPlayer({ const [isLoading, setIsLoading] = useState(false); const [isBuffering, setIsBuffering] = useState(false); const [loadingTimeout, setLoadingTimeout] = useState(); - const [source, setSource] = useState( - `${apiHost}vod/${camera}/start/${timeRange.after}/end/${timeRange.before}/master.m3u8`, - ); + const [source, setSource] = useState({ + playlist: `${apiHost}vod/${camera}/start/${timeRange.after}/end/${timeRange.before}/master.m3u8`, + startPosition: startTimestamp ? timeRange.after - startTimestamp : 0, + }); // start at correct time @@ -184,9 +185,16 @@ export default function DynamicVideoPlayer({ playerRef.current.autoplay = !isScrubbing; } - setSource( - `${apiHost}vod/${camera}/start/${recordingParams.after}/end/${recordingParams.before}/master.m3u8`, - ); + setSource({ + playlist: `${apiHost}vod/${camera}/start/${recordingParams.after}/end/${recordingParams.before}/master.m3u8`, + startPosition: startTimestamp ? startTimestamp - timeRange.after : 0, + }); + if (startTimestamp) { + console.log( + `start timestamp is ${startTimestamp} which is ${startTimestamp - recordingParams.after} seconds from start of playlist`, + ); + } + setLoadingTimeout(setTimeout(() => setIsLoading(true), 1000)); controller.newPlayback({