import { MutableRefObject, ReactNode, useEffect, useRef, useState, } from "react"; import Hls from "hls.js"; import { isDesktop, isMobile } from "react-device-detect"; import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch"; import VideoControls from "./VideoControls"; const HLS_MIME_TYPE = "application/vnd.apple.mpegurl" as const; const unsupportedErrorCodes = [ MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, MediaError.MEDIA_ERR_DECODE, ]; type HlsVideoPlayerProps = { className: string; children?: ReactNode; videoRef: MutableRefObject; visible: boolean; currentSource: string; hotKeys: boolean; onClipEnded?: () => void; onPlayerLoaded?: () => void; onTimeUpdate?: (time: number) => void; onPlaying?: () => void; }; export default function HlsVideoPlayer({ className, children, videoRef, visible, currentSource, hotKeys, onClipEnded, onPlayerLoaded, onTimeUpdate, onPlaying, }: HlsVideoPlayerProps) { // playback const hlsRef = useRef(); const [useHlsCompat, setUseHlsCompat] = useState(false); const [loadedMetadata, setLoadedMetadata] = useState(false); useEffect(() => { if (!videoRef.current) { return; } if (videoRef.current.canPlayType(HLS_MIME_TYPE)) { return; } else if (Hls.isSupported()) { setUseHlsCompat(true); } }, [videoRef]); useEffect(() => { if (!videoRef.current) { return; } const currentPlaybackRate = videoRef.current.playbackRate; if (!useHlsCompat) { videoRef.current.src = currentSource; videoRef.current.load(); return; } if (!hlsRef.current) { hlsRef.current = new Hls(); hlsRef.current.attachMedia(videoRef.current); } hlsRef.current.loadSource(currentSource); videoRef.current.playbackRate = currentPlaybackRate; }, [videoRef, hlsRef, useHlsCompat, currentSource]); // controls const [isPlaying, setIsPlaying] = useState(true); const [mobileCtrlTimeout, setMobileCtrlTimeout] = useState(); const [controls, setControls] = useState(isMobile); const [controlsOpen, setControlsOpen] = useState(false); return (
{ setControls(true); } : undefined } onMouseOut={ isDesktop ? () => { setControls(controlsOpen); } : undefined } onClick={isDesktop ? undefined : () => setControls(!controls)} > { if (!videoRef.current) { return; } if (play) { videoRef.current.play(); } else { videoRef.current.pause(); } }} onSeek={(diff) => { const currentTime = videoRef.current?.currentTime; if (!videoRef.current || !currentTime) { return; } videoRef.current.currentTime = Math.max(0, currentTime + diff); }} onSetPlaybackRate={(rate) => videoRef.current ? (videoRef.current.playbackRate = rate) : null } /> {children}
); }