Include preferred startTime in source so that the playlist does not need to seek

This commit is contained in:
Nicolas Mowen 2025-08-14 09:06:31 -06:00
parent c2f8de94e8
commit 92fa46d6ea
2 changed files with 34 additions and 13 deletions

View File

@ -28,11 +28,16 @@ const unsupportedErrorCodes = [
MediaError.MEDIA_ERR_DECODE, MediaError.MEDIA_ERR_DECODE,
]; ];
export interface HlsSource {
playlist: string;
startPosition: number;
}
type HlsVideoPlayerProps = { type HlsVideoPlayerProps = {
videoRef: MutableRefObject<HTMLVideoElement | null>; videoRef: MutableRefObject<HTMLVideoElement | null>;
containerRef?: React.MutableRefObject<HTMLDivElement | null>; containerRef?: React.MutableRefObject<HTMLDivElement | null>;
visible: boolean; visible: boolean;
currentSource: string; currentSource: HlsSource;
hotKeys: boolean; hotKeys: boolean;
supportsFullscreen: boolean; supportsFullscreen: boolean;
fullscreen: boolean; fullscreen: boolean;
@ -113,17 +118,25 @@ export default function HlsVideoPlayer({
const currentPlaybackRate = videoRef.current.playbackRate; const currentPlaybackRate = videoRef.current.playbackRate;
if (!useHlsCompat) { if (!useHlsCompat) {
videoRef.current.src = currentSource; videoRef.current.src = currentSource.playlist;
videoRef.current.load(); videoRef.current.load();
return; return;
} }
if (!hlsRef.current) { // we must destroy the hlsRef every time the source changes
hlsRef.current = new Hls(); // so that we can create a new HLS instance with startPosition
hlsRef.current.attachMedia(videoRef.current); // 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.current.playbackRate = currentPlaybackRate;
}, [videoRef, hlsRef, useHlsCompat, currentSource]); }, [videoRef, hlsRef, useHlsCompat, currentSource]);

View File

@ -6,7 +6,7 @@ import { Recording } from "@/types/record";
import { Preview } from "@/types/preview"; import { Preview } from "@/types/preview";
import PreviewPlayer, { PreviewController } from "../PreviewPlayer"; import PreviewPlayer, { PreviewController } from "../PreviewPlayer";
import { DynamicVideoController } from "./DynamicVideoController"; import { DynamicVideoController } from "./DynamicVideoController";
import HlsVideoPlayer from "../HlsVideoPlayer"; import HlsVideoPlayer, { HlsSource } from "../HlsVideoPlayer";
import { TimeRange } from "@/types/timeline"; import { TimeRange } from "@/types/timeline";
import ActivityIndicator from "@/components/indicators/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
import { VideoResolutionType } from "@/types/live"; import { VideoResolutionType } from "@/types/live";
@ -98,9 +98,10 @@ export default function DynamicVideoPlayer({
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isBuffering, setIsBuffering] = useState(false); const [isBuffering, setIsBuffering] = useState(false);
const [loadingTimeout, setLoadingTimeout] = useState<NodeJS.Timeout>(); const [loadingTimeout, setLoadingTimeout] = useState<NodeJS.Timeout>();
const [source, setSource] = useState( const [source, setSource] = useState<HlsSource>({
`${apiHost}vod/${camera}/start/${timeRange.after}/end/${timeRange.before}/master.m3u8`, playlist: `${apiHost}vod/${camera}/start/${timeRange.after}/end/${timeRange.before}/master.m3u8`,
); startPosition: startTimestamp ? timeRange.after - startTimestamp : 0,
});
// start at correct time // start at correct time
@ -184,9 +185,16 @@ export default function DynamicVideoPlayer({
playerRef.current.autoplay = !isScrubbing; playerRef.current.autoplay = !isScrubbing;
} }
setSource( setSource({
`${apiHost}vod/${camera}/start/${recordingParams.after}/end/${recordingParams.before}/master.m3u8`, 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)); setLoadingTimeout(setTimeout(() => setIsLoading(true), 1000));
controller.newPlayback({ controller.newPlayback({