From b0527df3c7d6b9a419c97f30d0241ff6900c6472 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Thu, 20 Nov 2025 15:58:58 -0700 Subject: [PATCH] HLS adjustments (#20983) * Revert "Fix HLS jumping to end of timeChunk (#20982)" This reverts commit 301e0a1a3a247cf2a86ad6db287f2fe29bfb3c4e. * Never use native HLS * Fix inverse operation --- web/src/components/player/HlsVideoPlayer.tsx | 21 ++----------------- .../player/dynamic/DynamicVideoPlayer.tsx | 19 +++++++++++++++++ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index 2ac9196ba..20d3dd4d7 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -6,7 +6,7 @@ import { useState, } from "react"; import Hls from "hls.js"; -import { isAndroid, isDesktop, isMobile } from "react-device-detect"; +import { isDesktop, isMobile } from "react-device-detect"; import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch"; import VideoControls from "./VideoControls"; import { VideoResolutionType } from "@/types/live"; @@ -22,7 +22,7 @@ import { useTranslation } from "react-i18next"; import ObjectTrackOverlay from "@/components/overlay/ObjectTrackOverlay"; // Android native hls does not seek correctly -const USE_NATIVE_HLS = !isAndroid; +const USE_NATIVE_HLS = false; const HLS_MIME_TYPE = "application/vnd.apple.mpegurl" as const; const unsupportedErrorCodes = [ MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, @@ -135,23 +135,6 @@ export default function HlsVideoPlayer({ if (!useHlsCompat) { videoRef.current.src = currentSource.playlist; videoRef.current.load(); - // For native HLS, we need to seek after metadata loads since startPosition isn't supported - if (currentSource.startPosition !== undefined) { - videoRef.current.addEventListener( - "loadedmetadata", - () => { - if (videoRef.current && currentSource.startPosition !== undefined) { - // Clamp startPosition to video duration to prevent seeking beyond the end - const clampedPosition = Math.min( - currentSource.startPosition, - videoRef.current.duration || Infinity, - ); - videoRef.current.currentTime = clampedPosition; - } - }, - { once: true }, - ); - } return; } diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index 252d39c2b..f26826fa7 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -111,6 +111,7 @@ export default function DynamicVideoPlayer({ const [loadingTimeout, setLoadingTimeout] = useState(); const [source, setSource] = useState({ playlist: `${apiHost}vod/${camera}/start/${timeRange.after}/end/${timeRange.before}/master.m3u8`, + startPosition: startTimestamp ? startTimestamp - timeRange.after : 0, }); // start at correct time @@ -195,8 +196,26 @@ export default function DynamicVideoPlayer({ playerRef.current.autoplay = !isScrubbing; } + let startPosition = undefined; + + if (startTimestamp) { + const inpointOffset = calculateInpointOffset( + recordingParams.after, + (recordings || [])[0], + ); + const idealStartPosition = Math.max( + 0, + startTimestamp - timeRange.after - inpointOffset, + ); + + if (idealStartPosition >= recordings[0].start_time - timeRange.after) { + startPosition = idealStartPosition; + } + } + setSource({ playlist: `${apiHost}vod/${camera}/start/${recordingParams.after}/end/${recordingParams.before}/master.m3u8`, + startPosition, }); setLoadingTimeout(setTimeout(() => setIsLoading(true), 1000));