fix(ai-avatar): 全盘修复 FFmpeg 渲染滤镜链路(exit 234 P0) #1868

Merged
frontend-dev merged 2 commits from fix/ffmpeg-filter-comprehensive-fix into develop 2026-09-11 23:40:22 +08:00
Collaborator

问题

AI 数字人渲染失败,FFmpeg 退出码 234,错误 Invalid argument for option filter_complex。

经全盘梳理,发现 6 处根因(不是单点补丁,是整条滤镜链路的系统性问题):

  1. font=bold 是无效 drawtext 参数——直接导致 filter_complex 解析失败(exit 234)
  2. 字体路径错误:Dockerfile COPY 的 NotoSansSC-VF.ttf 不在 DRAWTEXT_FONT_SEARCH_PATHS 中,CJK .ttc 被 Dockerfile 删除,fallback 到不支持中文的 DejaVuSans
  3. B-roll 硬编码 1280x720 横屏:AI 数字人是 9:16 竖屏,B-roll 片段缩放尺寸错误
  4. PIP 输入索引错误 + 标签链断裂:broll_input_idx 用 sorted 长度算而非原始下标;overlay 输出标签是 [vout0]/[vout1] 而非最终 [vout]
  5. 音频丢失:_build_ffmpeg_command 缺 -map 0:a? 和 -c:a aac,合成后视频静音
  6. 滤镜拼接末尾分号处理脆弱

修复

packages/domain/video_filter_builder.py

  • DRAWTEXT_FONT_SEARCH_PATHS:VF 字体置顶,移除 DejaVuSans(不支持 CJK),加 Bold.ttc 路径
  • DRAWTEXT_FONT_MAP:关键字改 NotoSansSC 匹配 VF 字体文件名
  • 删除 font=bold 无效参数;bold 无显式描边时自动用 borderw=3 + 同色描边 模拟粗体视觉效果
  • build_broll_overlay_filter 返回 (filter_str, final_label) 元组,标签契约清晰
  • 重写 fullscreen/PIP 拆分:原始列表下标决定 FFmpeg -i 输入序号,sorted 仅用于时序
  • PIP overlay 正确基于 fullscreen 输出([vout_fs])或主视频([0:v])链接
  • output_width/output_height 贯穿所有 scale/pad/overlay

apps/api/app/services/ai_avatar_render_service.py

  • 新增 _probe_video_resolution() 用 ffprobe 探测实际分辨率
  • AI 数字人默认竖屏 720x1280,探测失败兜底不阻断
  • 滤镜拼接四分支(broll+title / broll-only / title-only / 无滤镜)清晰处理
  • 补 -map 0:a? + -c:a aac -b:a 128k,音频不再丢
  • 清理冗余内联 import

验证

  • 本地 ffmpeg 实测:中文标题 + B-roll fullscreen + 竖屏 720x1280 合成成功,输出 117KB
  • 标题-only 场景(bold + 描边)合成成功
  • 3524 个相关单测全部通过(video/filter/avatar/render/lipsync/drawtext/title)
  • 新增 test_bold_true_does_not_use_font_bold_param 防回归

关联:#1798 AI 数字人渲染合成

## 问题 AI 数字人渲染失败,FFmpeg 退出码 234,错误 `Invalid argument for option filter_complex`。 经全盘梳理,发现 6 处根因(不是单点补丁,是整条滤镜链路的系统性问题): 1. **`font=bold` 是无效 drawtext 参数**——直接导致 filter_complex 解析失败(exit 234) 2. **字体路径错误**:Dockerfile COPY 的 NotoSansSC-VF.ttf 不在 DRAWTEXT_FONT_SEARCH_PATHS 中,CJK .ttc 被 Dockerfile 删除,fallback 到不支持中文的 DejaVuSans 3. **B-roll 硬编码 1280x720 横屏**:AI 数字人是 9:16 竖屏,B-roll 片段缩放尺寸错误 4. **PIP 输入索引错误 + 标签链断裂**:broll_input_idx 用 sorted 长度算而非原始下标;overlay 输出标签是 [vout0]/[vout1] 而非最终 [vout] 5. **音频丢失**:_build_ffmpeg_command 缺 -map 0:a? 和 -c:a aac,合成后视频静音 6. **滤镜拼接末尾分号处理脆弱** ## 修复 ### packages/domain/video_filter_builder.py - DRAWTEXT_FONT_SEARCH_PATHS:VF 字体置顶,移除 DejaVuSans(不支持 CJK),加 Bold.ttc 路径 - DRAWTEXT_FONT_MAP:关键字改 NotoSansSC 匹配 VF 字体文件名 - 删除 `font=bold` 无效参数;bold 无显式描边时自动用 `borderw=3 + 同色描边` 模拟粗体视觉效果 - build_broll_overlay_filter 返回 `(filter_str, final_label)` 元组,标签契约清晰 - 重写 fullscreen/PIP 拆分:原始列表下标决定 FFmpeg -i 输入序号,sorted 仅用于时序 - PIP overlay 正确基于 fullscreen 输出([vout_fs])或主视频([0:v])链接 - output_width/output_height 贯穿所有 scale/pad/overlay ### apps/api/app/services/ai_avatar_render_service.py - 新增 `_probe_video_resolution()` 用 ffprobe 探测实际分辨率 - AI 数字人默认竖屏 720x1280,探测失败兜底不阻断 - 滤镜拼接四分支(broll+title / broll-only / title-only / 无滤镜)清晰处理 - 补 `-map 0:a?` + `-c:a aac -b:a 128k`,音频不再丢 - 清理冗余内联 import ## 验证 - 本地 ffmpeg 实测:中文标题 + B-roll fullscreen + 竖屏 720x1280 合成成功,输出 117KB - 标题-only 场景(bold + 描边)合成成功 - 3524 个相关单测全部通过(video/filter/avatar/render/lipsync/drawtext/title) - 新增 `test_bold_true_does_not_use_font_bold_param` 防回归 关联:#1798 AI 数字人渲染合成
frontend-dev added 1 commit 2026-09-11 23:09:56 +08:00
fix(ai-avatar): 全盘修复 FFmpeg 渲染滤镜链路(exit 234 P0)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 27s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m43s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m55s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m44s
AI Code Review / AI Code Review (pull_request) Successful in 6m34s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 8m17s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 8m45s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 112h13m34s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 112h13m49s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 112h13m21s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 112h13m26s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 112h13m27s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 112h13m27s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 112h13m32s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 112h13m32s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 112h13m16s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 112h13m15s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 112h13m32s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 112h13m33s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 112h13m36s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 112h48m43s
32c3d2f263
根因分析(退出码 234 = Invalid argument):
1) drawtext 使用了无效参数 font=bold,导致整个 filter_complex 解析失败
   —— FFmpeg drawtext 没有 bold 参数;改为通过 borderw 模拟粗体视觉效果
2) DRAWTEXT_FONT_SEARCH_PATHS 未包含 Dockerfile 中 COPY 的
   NotoSansSC-VF.ttf 路径,且把不支持中文的 DejaVuSans 放在 fallback 首位,
   导致字体 fallback 到拉丁字体,中文渲染乱码/方框
3) build_broll_overlay_filter 硬编码 1280:720 横屏尺寸,AI 数字人是 9:16 竖屏
4) B-roll PIP 输入索引错误(用 len(sorted_segments) 而非原始下标映射),
   PIP 标签链断裂([pip0]→[vout0] 而非 [vout])
5) 渲染命令缺 -map 0:a?,合成后音频丢失
6) 标题滤镜与 B-roll 输出标签拼接用分号有缺陷,末尾分号处理偶发问题

修复内容(packages/domain/video_filter_builder.py):
- DRAWTEXT_FONT_SEARCH_PATHS: VF 字体置顶、移除 DejaVuSans、加 Bold.ttc 路径
- DRAWTEXT_FONT_MAP: 思源黑体等关键字改为 NotoSansSC(匹配 VF 文件名)
- 删除 font=bold 无效参数;bold 无显式描边时自动用 borderw=3+同色描边模拟粗体
- build_broll_overlay_filter 返回 (filter_str, final_label) 元组,解决标签问题
- 重写 fullscreen/PIP 拆分:原始列表下标决定 -i 输入序号,sorted 只用于时序处理
- PIP overlay 正确基于 fullscreen 输出([vout_fs])或主视频([0:v])链接
- output_width/output_height 贯穿所有 scale/pad/overlay,默认 1280x720 兼容旧调用

修复内容(apps/api/app/services/ai_avatar_render_service.py):
- 新增 _probe_video_resolution() 用 ffprobe 探测输入视频实际分辨率
- AI 数字人默认竖屏 720x1280,探测失败兜底不阻断渲染
- build_broll_overlay_filter 调用传实际 output_width/output_height
- 标题滤镜传实际尺寸,保证位置/坐标计算正确
- filter_complex 拼接重写:broll+title/broll-only/title-only/无滤镜四分支清晰
- _build_ffmpeg_command 补 -map 0:a? + -c:a aac,音频不再丢失
- 清理冗余内联 import

测试:
- 更新 test_ai_avatar_render_routes.py / test_video_filter_builder.py 适配新元组签名
- 新增 test_bold_true_does_not_use_font_bold_param 防回归
- 本地 ffmpeg 实测中文标题+B-roll+竖屏720x1280合成成功
- 相关 3524 个单测全通过

🚀 预览环境已部署

项目 详情
PR号 #1868
预览链接 https://pr-1868.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1868 | | 预览链接 | [https://pr-1868.preview.xiaoxiajianji.com](https://pr-1868.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-09-11 23:20:36 +08:00
style: auto-format with black + isort + ruff + prettier [skip ci-format-check]
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 32s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 33s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m48s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m42s
AI Code Review / AI Code Review (pull_request) Successful in 6m37s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 7m55s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 8m12s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 8m22s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 10m50s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 11m0s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 5s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 2m55s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 28m58s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Failing after 111h34m7s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 112h3m9s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 112h2m53s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 112h2m53s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 112h2m54s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 111h33m54s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 112h2m42s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 112h2m47s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 112h2m47s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 112h2m52s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 112h2m53s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 112h2m37s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 111h33m50s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 111h33m54s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 111h33m54s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 112h2m35s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 112h2m53s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 112h2m56s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 112h37m58s
a7067c8171
frontend-dev merged commit 982daac6e5 into develop 2026-09-11 23:40:22 +08:00

🗑️ 预览环境已清理

PR #1868 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1868 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.