Files
ViGent2/Docs/DevLogs/Day27.md
Kevin Wong 0e3502c6f0 更新
2026-02-27 16:11:34 +08:00

10 KiB
Raw Blame History

Remotion 描边修复 + 字体样式扩展 + TypeScript 修复 (Day 27)

概述

修复标题/字幕描边渲染问题(描边过粗 + 副标题重影),扩展字体样式选项(标题 4→12、字幕 4→8修复 Remotion 项目 TypeScript 类型错误。


改动内容

1. 描边渲染修复(标题 + 字幕)

  • 问题: 标题黑色描边过粗,副标题出现重影/鬼影
  • 根因: buildTextShadow 用 4 方向 textShadow 模拟描边 — 对角线叠加导致描边视觉上比实际 stroke_size 更粗4 角方向在中间有间隙和叠加,造成重影
  • 修复: 改用 CSS 原生描边 -webkit-text-stroke + paint-order: stroke fillRemotion 用 Chromium 渲染,完美支持)
  • 旧方案:
    textShadow: `-8px -8px 0 #000, 8px -8px 0 #000, -8px 8px 0 #000, 8px 8px 0 #000, 0 0 16px rgba(0,0,0,0.5), 0 2px 4px rgba(0,0,0,0.3)`
    
  • 新方案:
    WebkitTextStroke: `5px #000000`,
    paintOrder: 'stroke fill',
    textShadow: `0 2px 4px rgba(0,0,0,0.3)`,
    
  • 同时将所有预设样式的 stroke_size 从 8 降到 5配合原生描边视觉更干净

2. 字体样式扩展

标题样式: 4 个 → 12 个(+8

ID 样式名 字体 配色
title_pangmen 庞门正道 庞门正道标题体3.0 白字黑描
title_round 优设标题圆 优设标题圆 白字紫描
title_alibaba 阿里数黑体 阿里巴巴数黑体 白字黑描
title_chaohei 文道潮黑 文道潮黑 青蓝字深蓝描
title_wujie 无界黑 标小智无界黑 白字深灰描
title_houdi 厚底黑 Aa厚底黑 红字深黑描
title_banyuan 寒蝉半圆体 寒蝉半圆体 白字黑描
title_jixiang 欣意吉祥宋 字体圈欣意吉祥宋 金字棕描

字幕样式: 4 个 → 8 个(+4

ID 样式名 字体 高亮色
subtitle_pink 少女粉 DingTalk JinBuTi 粉色 #FF69B4
subtitle_lime 清新绿 DingTalk Sans 荧光绿 #76FF03
subtitle_gold 金色隶书 阿里妈妈刀隶体 金色 #FDE68A
subtitle_kai 楷体红字 SimKai 红色 #FF4444

3. TypeScript 类型错误修复

  • Root.tsx: Composition 泛型类型与 calculateMetadata 参数类型不匹配 — 内联 calculateMetadata 并显式标注参数类型,defaultProps 使用 satisfies VideoProps 约束
  • Video.tsx: VideoProps 接口添加 [key: string]: unknown 索引签名,兼容 Remotion 要求的 Record<string, unknown> 约束
  • VideoLayer.tsx: OffthreadVideo 组件不支持 loop prop — 移除(该 prop 原本就被忽略)

4. 进度条文案还原

  • 问题: 进度条显示后端推送的详细阶段消息(如"正在合成唇型"),用户希望只显示"正在AI生成中..."
  • 修复: HomePage.tsx 进度条文案从 {currentTask.message || "正在AI生成中..."} 改为固定 正在AI生成中...

📁 修改文件清单

文件 改动
remotion/src/components/Title.tsx buildTextShadowbuildStrokeStyleCSS 原生描边),标题+副标题同时生效
remotion/src/components/Subtitles.tsx buildTextShadowbuildStrokeStyleCSS 原生描边)
remotion/src/Root.tsx 修复 Composition 泛型类型、calculateMetadata 参数类型
remotion/src/Video.tsx VideoProps 添加索引签名
remotion/src/components/VideoLayer.tsx 移除 OffthreadVideo 不支持的 loop prop
backend/assets/styles/title.json 标题样式从 4 个扩展到 12 个,stroke_size 8→5
backend/assets/styles/subtitle.json 字幕样式从 4 个扩展到 8 个
frontend/.../HomePage.tsx 进度条文案还原为固定"正在AI生成中..."

🔍 验证

  • npx tsc --noEmit — 零错误
  • npm run build:render — 渲染脚本编译成功
  • npm run build(前端)— 零报错
  • 描边:标题/副标题/字幕使用 CSS 原生描边,无重影、无虚胖
  • 样式选择:前端下拉可加载全部 12 个标题 + 8 个字幕样式

视频生成流水线性能优化

概述

针对视频生成流水线进行全面性能优化,涵盖 FFmpeg 编码参数、LatentSync 推理参数、多素材并行化、以及后处理阶段并行化。预估 15s 单素材视频从 ~280s 降至 ~190s (32%)30s 双素材从 ~400s 降至 ~240s (40%)。

服务器配置: 2x RTX 3090 (24GB), 2x Xeon E5-2680 v4 (56核), 192GB RAM

第一阶段FFmpeg 编码优化

最终合成 preset slowmedium

  • 合成阶段从 ~50s 降到 ~25s质量几乎无变化

中间文件 CRF 18 → 23

  • 中间产物trim、prepare_segment、concat、loop、normalize_orientation不是最终输出不需要高质量编码
  • 每个中间步骤快 3-8 秒

最终合成 CRF 18 → 20

  • 15 秒口播视频 CRF 18 vs 20 肉眼无法区分

第二阶段LatentSync 推理参数调优

inference_steps 20 → 16

  • 推理时间线性减少 20%~180s → ~144s

guidance_scale 2.0 → 1.5

  • classifier-free guidance 权重降低每步计算量微降5-10%

⚠️ 两项需重启 LatentSync 服务后测试唇形质量,确认可接受再保留。如质量不佳可回退 .env 参数。

第三阶段:多素材流水线并行化

素材下载 + 归一化并行

  • 串行 for 循环改为 asyncio.gather()normalize_orientation 通过 run_in_executor 在线程池执行
  • N 个素材从串行 N×5s → ~5s

片段预处理并行

  • 逐个 prepare_segment 改为 asyncio.gather() + run_in_executor
  • 2 素材 ~90s → ~50s4 素材 ~180s → ~60s

第四阶段:流水线交叠

Whisper 字幕对齐 与 BGM 混音 并行

  • 两者互不依赖(都只依赖 audio_pathasyncio.gather() 并行执行
  • 单素材模式下 Whisper 从 LatentSync 之后的串行步骤移至与 BGM 并行
  • 不开 BGM 或不开字幕时行为不变,只有同时启用时才并行

修改文件

文件 改动
backend/app/services/video_service.py compose: preset slow→medium, CRF 18→20; normalize_orientation/prepare_segment/concat: CRF 18→23
backend/app/services/lipsync_service.py _loop_video_to_duration: CRF 18→23
backend/.env LATENTSYNC_INFERENCE_STEPS=16, LATENTSYNC_GUIDANCE_SCALE=1.5
backend/app/modules/videos/workflow.py import asyncio; 素材下载/归一化并行; 片段预处理并行; Whisper+BGM 并行

回退方案

  • FFmpeg 参数:如画质不满意,将最终 CRF 改回 18、preset 改回 slow
  • LatentSync如唇形质量下降将 .env 中 INFERENCE_STEPS 改回 20、GUIDANCE_SCALE 改回 2.0
  • 并行化:纯架构优化,无质量影响,无需回退

MuseTalk + LatentSync 混合唇形同步方案

概述

LatentSync 1.6 质量高但推理极慢(~78% 总时长),长视频(>=2min耗时 20-60 分钟不可接受。MuseTalk 1.5 是单步潜空间修复非扩散模型逐帧推理速度接近实时30fps+ on V100适合长视频。混合方案按音频时长自动路由短视频用 LatentSync 保质量,长视频用 MuseTalk 保速度。

架构

  • 路由阈值: LIPSYNC_DURATION_THRESHOLD (默认 120s)
  • 短视频 (<120s): LatentSync 1.6 (GPU1, 端口 8007)
  • 长视频 (>=120s): MuseTalk 1.5 (GPU0, 端口 8011)
  • 回退: MuseTalk 不可用时自动 fallback 到 LatentSync

改动文件

文件 改动
models/MuseTalk/ 从 Temp/MuseTalk 复制代码 + 下载权重
models/MuseTalk/scripts/server.py 新建 FastAPI 常驻服务 (端口 8011, GPU0)
backend/app/core/config.py 新增 MUSETALK_* 和 LIPSYNC_DURATION_THRESHOLD
backend/.env 新增对应环境变量
backend/app/services/lipsync_service.py 新增 _call_musetalk_server() + 混合路由逻辑 + 扩展 check_health()

MuseTalk 推理性能优化 (server.py v2)

概述

MuseTalk 首次长视频测试 (136s, 3404 帧) 耗时 1799s (~30 分钟),分析发现瓶颈集中在人脸检测 (28%)、BiSeNet 合成 (22%)、I/O (17%),而非 UNet 推理本身 (17%)。通过 6 项优化预估降至 8-10 分钟 (~3x 加速)。

性能瓶颈分析 (优化前, 1799s)

阶段 耗时 占比 瓶颈原因
DWPose + 人脸检测 ~510s 28% batch_size_fa=1, 每帧跑 2 个 NN, 完全串行
合成 + BiSeNet 人脸解析 ~400s 22% 每帧都跑 BiSeNet + PNG 写盘
UNet 推理 ~300s 17% batch_size=8 太小
I/O (PNG 读写 + FFmpeg) ~300s 17% PNG 压缩慢, ffmpeg→PNG→imread 链路
VAE 编码 ~100s 6% 逐帧编码, 未批处理

6 项优化

# 优化项 详情
1 batch_size 8→32 .env 修改, RTX 3090 显存充裕
2 cv2.VideoCapture 直读帧 跳过 ffmpeg→PNG→imread 链路, 省去 3404 次 PNG 编解码
3 人脸检测降频 (每5帧) 每 5 帧运行 DWPose + FaceAlignment, 中间帧线性插值 bbox
4 BiSeNet mask 缓存 (每5帧) 每 5 帧运行 get_image_prepare_material, 中间帧用 get_image_blending 复用缓存 mask
5 cv2.VideoWriter 直写 跳过逐帧 PNG 写盘 + ffmpeg 重编码, 用 VideoWriter 直写 mp4
6 每阶段计时 7 个阶段精确计时, 方便后续进一步调优

修改文件

文件 改动
models/MuseTalk/scripts/server.py 完全重写 _run_inference(), 新增 _detect_faces_subsampled()
backend/.env MUSETALK_BATCH_SIZE 8→32

Remotion 并发渲染优化

概述

Remotion 渲染在 56 核服务器上默认只用 8 并发 (min(8, cores/2)),改为 16 并发,预估从 ~5 分钟降到 ~2-3 分钟。

改动

  • remotion/render.ts: renderMedia() 新增 concurrency 参数 (默认 16), 支持 --concurrency CLI 参数覆盖
  • remotion/dist/render.js: 重新编译

修改文件

文件 改动
remotion/render.ts RenderOptions 新增 concurrency 字段, renderMedia() 传入 concurrency
remotion/dist/render.js TypeScript 重新编译