From b8a349d5e029414d778dcdf81077e9aed328a1ff Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sat, 22 Aug 2026 01:53:43 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=BB=98=E8=AE=A4=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=8E=9F=E7=94=9Fvideo=E6=92=AD=E6=94=BE=EF=BC=8C?= =?UTF-8?q?=E5=BD=BB=E5=BA=95=E8=A7=A3=E5=86=B3=E4=B8=BB=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E8=A7=A3=E7=A0=81=E5=8D=A1=E9=A1=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FrontendPreviewPlayer.tsx: - useWebCodecs 默认 false,走浏览器原生硬件解码(独立线程) - 删除 Canvas ResizeObserver useEffect(默认路径不需要) - video 元素只预加载当前+下一片段,其余 preload=none+不设src,减少并发连接 - 片段指示器统一显示片段序号,不再显示Canvas/降级提示 - isWebCodecsSupported 不再导入,清理 TS6133 useSegmentScheduler.ts: - tick 进度更新 200ms 节流(5fps),避免 60fps setCurrentTime 触发整树 re-render - 重播/seek 时重置节流 ref - switchToSegment 兜底:若 video 无 src 则动态设置并 load(),兼容 seek 到未预加载片段 useCanvasPlayer.ts 不删,保留作为兜底 --- .../components/FrontendPreviewPlayer.tsx | 90 +++++++------------ .../generate/hooks/useSegmentScheduler.ts | 20 ++++- 2 files changed, 51 insertions(+), 59 deletions(-) diff --git a/apps/web/src/pages/generate/components/FrontendPreviewPlayer.tsx b/apps/web/src/pages/generate/components/FrontendPreviewPlayer.tsx index 1acbb75b1..364abcc81 100644 --- a/apps/web/src/pages/generate/components/FrontendPreviewPlayer.tsx +++ b/apps/web/src/pages/generate/components/FrontendPreviewPlayer.tsx @@ -17,7 +17,7 @@ import { import type { AssetItem } from "@/api/assets" import type { EditingTemplate } from "@/api/editing-planner" import { useSegmentScheduler, type PlaybackSegment } from "../hooks/useSegmentScheduler" -import { useCanvasPlayer, isWebCodecsSupported } from "../hooks/useCanvasPlayer" +import { useCanvasPlayer } from "../hooks/useCanvasPlayer" interface FrontendPreviewPlayerProps { assets: AssetItem[] @@ -82,7 +82,9 @@ const FrontendPreviewPlayer: React.FC = ({ titleSettings, }) => { const segments = useMemo(() => buildPlaybackSegments(assets, template), [assets, template]) - const useWebCodecs = isWebCodecsSupported() + // 默认走原生 video 播放(浏览器硬件解码,独立线程,不阻塞 UI) + // WebCodecs 仅在明确需要时启用(保留代码作为兜底) + const useWebCodecs = false // ── 两条路径共用同一个 canvas ref(fallback 路径不使用) ── const canvasRef = useRef(null) @@ -265,32 +267,8 @@ const FrontendPreviewPlayer: React.FC = ({ const progressPercent = totalDuration > 0 ? (currentTime / totalDuration) * 100 : 0 - // ── Canvas ResizeObserver ── + // ── Canvas 容器 ref(保留声明,WebCodecs 兜底路径仍引用) ── const canvasContainerRef = useRef(null) - useEffect(() => { - if (!effectiveUseWebCodecs || !canPlay) return - const container = canvasContainerRef.current - const canvas = canvasRef.current - if (!container || !canvas) return - // 立即设置一次 canvas 像素分辨率,避免默认 300×150 导致首帧变形 - const initRect = container.getBoundingClientRect() - if (initRect.width > 0 && initRect.height > 0) { - const dpr = window.devicePixelRatio || 1 - canvas.width = initRect.width * dpr - canvas.height = initRect.height * dpr - } - const ro = new ResizeObserver((entries) => { - for (const entry of entries) { - const { width, height } = entry.contentRect - if (width > 0 && height > 0) { - canvas.width = width * window.devicePixelRatio - canvas.height = height * window.devicePixelRatio - } - } - }) - ro.observe(container) - return () => ro.disconnect() - }, [effectiveUseWebCodecs, canPlay]) // ── 未就绪 ── if (!ready || !assets.length) { @@ -385,31 +363,35 @@ const FrontendPreviewPlayer: React.FC = ({ )} - {/* ── Video 渲染层(fallback 路径,或 WebCodecs 解码失败时自动切换) ── */} + {/* ── Video 渲染层(默认路径,浏览器原生硬件解码) ── */} {!effectiveUseWebCodecs && - segments.map((seg, i) => ( -