import React, { useContext, useEffect, useRef, useState } from "react"; import Hls from "hls.js"; import { VideoPlayerContext } from "./VideoPlayer"; interface VideoDisplayProps { autoPlay?: boolean; } export const VideoDisplay: React.FC = ({ autoPlay = false, }) => { const { src, poster, onError, videoRef, setIsReady, setIsPlaying, setError, setBufferingState, isMuted, setLoadingProgress, setCurrentTime, setDuration, brightness, isDragging, setIsDragging, progressRef, } = useContext(VideoPlayerContext); // 处理进度条拖拽 const handleProgressDrag = (e: MouseEvent) => { if (!isDragging || !videoRef.current || !progressRef.current) return; const rect = progressRef.current.getBoundingClientRect(); const percent = Math.max( 0, Math.min(1, (e.clientX - rect.left) / rect.width) ); videoRef.current.currentTime = percent * videoRef.current.duration; }; // 添加拖拽事件监听 useEffect(() => { const handleMouseUp = () => setIsDragging(false); const handleMouseMove = (e: MouseEvent) => handleProgressDrag(e); if (isDragging) { document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); } return () => { document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; }, [isDragging]); // 初始化 HLS 和事件监听 useEffect(() => { let hls: Hls; const initializeHls = async () => { if (!videoRef.current) return; // Reset states setIsReady(false); setError(null); setLoadingProgress(0); setBufferingState(false); // Check for native HLS support (Safari) if (videoRef.current.canPlayType("application/vnd.apple.mpegurl")) { videoRef.current.src = src; setIsReady(true); // 设置视频时长 setDuration(videoRef.current.duration); if (autoPlay) { try { await videoRef.current.play(); setIsPlaying(true); } catch (error) { console.log("Auto-play prevented:", error); } } return; } if (!Hls.isSupported()) { const errorMessage = "您的浏览器不支持 HLS 视频播放"; setError(errorMessage); onError?.(errorMessage); return; } hls = new Hls({ maxBufferLength: 30, maxMaxBufferLength: 600, enableWorker: true, debug: false, }); hls.loadSource(src); hls.attachMedia(videoRef.current); hls.on(Hls.Events.MANIFEST_PARSED, async () => { setIsReady(true); // 设置视频时长 setDuration(videoRef.current?.duration || 0); if (autoPlay && videoRef.current) { try { await videoRef.current.play(); setIsPlaying(true); } catch (error) { console.log("Auto-play prevented:", error); } } }); hls.on(Hls.Events.BUFFER_APPENDING, () => { setBufferingState(true); }); hls.on(Hls.Events.FRAG_BUFFERED, (_, data) => { setBufferingState(false); if (data.stats) { const progress = (data.stats.loaded / data.stats.total) * 100; setLoadingProgress(Math.round(progress)); } }); let fatalError; let networkError; hls.on(Hls.Events.ERROR, (_, data) => { if (data.fatal) { switch (data.type) { case Hls.ErrorTypes.NETWORK_ERROR: networkError = `网络错误: ${data.details}`; console.error(networkError); setError(networkError); onError?.(networkError); hls.startLoad(); break; case Hls.ErrorTypes.MEDIA_ERROR: console.error("Media error, attempting to recover"); setError("视频解码错误,尝试恢复..."); hls.recoverMediaError(); break; default: fatalError = `加载失败: ${data.details}`; console.error(fatalError); setError(fatalError); onError?.(fatalError); hls.destroy(); break; } } }); }; // 事件处理 const handlePlay = () => setIsPlaying(true); const handlePause = () => setIsPlaying(false); const handleEnded = () => setIsPlaying(false); const handleWaiting = () => setBufferingState(true); const handlePlaying = () => setBufferingState(false); const handleLoadedMetadata = () => { if (videoRef.current) { // 设置视频时长 setDuration(videoRef.current.duration); } }; if (videoRef.current) { videoRef.current.addEventListener("play", handlePlay); videoRef.current.addEventListener("pause", handlePause); videoRef.current.addEventListener("ended", handleEnded); videoRef.current.addEventListener("waiting", handleWaiting); videoRef.current.addEventListener("playing", handlePlaying); videoRef.current.addEventListener( "loadedmetadata", handleLoadedMetadata ); } initializeHls(); return () => { if (videoRef.current) { videoRef.current.removeEventListener("play", handlePlay); videoRef.current.removeEventListener("pause", handlePause); videoRef.current.removeEventListener("ended", handleEnded); videoRef.current.removeEventListener("waiting", handleWaiting); videoRef.current.removeEventListener("playing", handlePlaying); videoRef.current.removeEventListener( "loadedmetadata", handleLoadedMetadata ); } if (hls) { hls.destroy(); } }; }, [src, onError, autoPlay]); return (
); };