基于PPT生成对应讲解视频技能
把 PPTX + 逐页讲解稿 → 讲解视频(MP4)。核心思路:
文本稿解析 → TTS 配音 → PPT 原生渲染为图片 → 逐页成片 → 拼接。
成片效果等同"打开 PPT 逐页讲解的录屏",但完全可控、可重出。
> 说明:AI 无法在真实桌面上用真人声卡实时录屏。本技能用"PowerPoint 原生渲染 + TTS 配音 + 视频合成"实现等效且更可控的"边翻页边讲解"视频。交付时应向用户说明这一点。
何时使用
- 用户给出一个 PPTX 和一个"讲解稿/解说稿/培训稿",要求做成"讲解视频/录屏讲解/配音视频"。
- 需要每页 PPT 与讲解内容一一对应、带配音的视频。
推荐用法:一键流程(优先)
技能提供 pipeline.py,把解析→配音→渲染→合成串成一条命令:
python pipeline.py \
--pptx "课件.pptx" \
--script "讲解稿.docx" \
--output "讲解视频.mp4" \
--voice zh-CN-YunyangNeural # 男声;默认女声 Xiaoxiao
可选参数:
- --slides <目录>:已有渲染好的 PNG 目录,可跳过渲染步骤(复用素材重出不同声线/角标时很有用)。
- --force:强制重新合成配音(换声线时必须加,否则已存在的 mp3 会被跳过)。
- --ffmpeg <路径> / --codec auto|qsv|mf|libx264|mpeg4:默认自动探测。
- --no-badge:不加页码角标。
- --intro-title / --intro-subtitle / --intro-hint:自定义开场卡片文案。
- --workdir <目录>:中间产物目录(默认 <output>_build)。
pipeline.py 内部:Windows 优先用 PowerPoint COM 原生导出(最保真),失败或非 Windows 自动退回 render_fallback.py(python-pptx 近似渲染)。中文 pptx 会先复制到 ASCII 临时目录再交给 PowerPoint,规避 COM 乱码。
分步用法(需要精细控制时)
第1步 解析讲解稿 → 页码映射
python parse_script.py --docx 讲解稿.docx --out narration.json
- 按
第N页 讲解稿正则切分,得到{页码: 讲解词},页数=讲解词条数。 - 已修复数据丢失 bug:某页讲解词跨多个段落时,全部段落会被保留(不再只取第一段)。
- 无页码标记 → 明确报错退出(不再静默产出空映射);可用
--pattern自定义正则。
第2步 TTS 逐页配音
python make_tts.py --narration narration.json --audio-dir audio --voice zh-CN-YunyangNeural
- 输出
audio/slide_N.mp3。已存在的有效 mp3 会跳过(幂等);换声线加--force。 - 无需精确测时长:合成时用 ffmpeg
-shortest让画面长度自动等于音频长度(故无durations.json之类死代码)。
第3步 PPT 渲染为高清 PNG(关键,保真)
- Windows 首选:先复制 pptx 到纯 ASCII 路径,再
powershell -NoProfile -ExecutionPolicy Bypass -File ppt_export.ps1 -Src <ascii.pptx> -OutDir <slides>(1920×1080 原生导出)。 - 兜底:
python render_fallback.py --pptx 课件.pptx --out-dir slides(python-pptx 近似,保真度低)。
第4步 预处理图片(页码角标 + 页码→画面映射)
由 build_video.py 内部调用 Pillow 把"第 N / 总页"角标烧进 PNG(因自带 ffmpeg 常缺 drawtext/subtitles 滤镜,不依赖 ffmpeg 字幕滤镜)。
关键映射规则(已修复系统性错位 bug):讲解稿标注的"第 N 页"严格对应第 N 张幻灯片(slide_%02d.png 的第 N 个文件),而非按"连续序号"重排。这样即使讲解稿页码不连续(如只标注正文页、跳过封面/目录),也绝不会把第5页的讲解词贴到第2张画面上。PowerPoint / 兜底渲染均按物理顺序导出 slide_01…slide_N.png,与 PPT 页码天然一致。
> 跨平台字体:角标/开场标题的中文依赖系统中文字体。Windows 自带;Linux 已预置 Noto CJK / 文泉驿路径,macOS 预置 PingFang。若系统无任何中文字体,中文会显示异常,需先安装中文字体(技能脚本 build_video.py / render_fallback.py 的字体候选列表可继续扩充)。
第5步 逐页成片 + 拼接(核心)
python build_video.py --slides slides --audio-dir audio \
--narration narration.json --output 讲解视频.mp4
- ffmpeg 与编码器 自动探测(见下);缺页素材会告警跳过而不静默丢页。
第6步 校验(务必做)
python verify.py --video 讲解视频.mp4 # 流信息 + 时长 + 音频电平(确认非静音)
python verify.py --video 讲解视频.mp4 \
--clips-dir <build>/clips --frames-dir _frames # 可选:累计片段时长交叉核对 + 抽每页帧人工核对映射
ffmpeg -i 讲解视频.mp4 -af volumedetect -f null - # 也可直接看 mean_volume 是否非静音
verify.py已参数化(不再写死路径):--video必填,--ffmpeg/--clips-dir/--frames-dir可选。- 重点确认
mean_volume在 -60 dB 以上(非静音),否则多半是拼接方式问题(回看坑4)。
自动探测(已去除硬编码)
- ffmpeg:优先 PATH → 剪映 JianyingPro 自带 → 全盘兜底(
find_ffmpeg())。 - 视频编码器(
detect_codec()):依次探测h264_qsv(Intel硬编) →h264_mf(Win媒体基础) →libx264→mpeg4,取第一个可用者,不再写死 Intel QSV 或剪映路径。无可用编码器才会报错退出。 - 这样技能在 AMD/NVIDIA/Apple/无集显机器、或剪映未装的机器上也能跑(自动退到 libx264/mpeg4)。
环境准备(先探测,缺什么补什么)
- PPT 渲染:Windows 本机 Microsoft PowerPoint COM(最优);次选 LibreOffice;再退
python-pptx + Pillow(render_fallback.py兜底)。 - ffmpeg:系统已有或剪映自带均可;本技能自动探测。不要依赖需联网下载的静态构建(网络可能极慢,实测 ~1MB/分钟)。
- Python 库:
python-docx、edge-tts、pillow、numpy(仅部分脚本用到)。装在隔离 venv,网络慢时关沙箱/-i https://pypi.org/simple。 - TTS 声线(edge-tts,需联网):
- 女声(默认/清晰专业):zh-CN-XiaoxiaoNeural
- 男声(新闻播报、沉稳专业,适合培训讲解):zh-CN-YunyangNeural
- 男声(年轻活泼):zh-CN-YunxiNeural;更成熟稳重:zh-CN-YunjianNeural
踩坑与解决全记录(实战沉淀,按发生顺序)
坑1:环境缺 ffmpeg / LibreOffice,且网络下载极慢
渲染改走本机 PowerPoint COM 原生导出;编码用剪映自带 ffmpeg(现改为自动探测);Python 库用 venv --clear 重建后 pip install。
坑2:中文路径导致 COM / 工具乱码
pptx 复制到纯 ASCII 临时目录中转;pipeline.py 已内置此逻辑。
坑3:自带 ffmpeg 缺少 libx264 / drawtext / subtitles
编码改 QSV/MF/libx264(自动探测);字幕/角标用 Pillow 烧进 PNG,不依赖 ffmpeg 字幕滤镜。
坑4(最致命):concat demuxer + -c copy 直拼 → 多数播放器无声/爆音
- 现象:VLC 正常,但 Windows 播放器、QuickTime、手机播放器整段没声音。
- 根因:
-f concat -c copy保留各片段独立时间戳/编辑列表,严格播放器在拼接点丢弃音频。 - 解决(铁律):一律用 concat filter 重编码为单一连续流(见
build_video.py的concat_merge())。已生成视频若发现无声,用原片段按此法重编码即可修复,无需重渲重配。
坑5:覆盖桌面原文件被进程占用
输出用新文件名(如 _修复版.mp4 / _男声版.mp4),待用户关闭占用进程再替换。
坑6:没有真人声卡,无法真正"实时录屏"
用 TTS + 渲染 + 合成实现等效"边翻页边讲解"录屏,交付时说明。
坑7:换声线需整链重跑
只改 --voice(或加 --force 重合成音频),画面/角标不变,再合成即可。可先抽 30 秒音频样本试听确认。
坑8(审计修复):脚本全路径硬编码 + 讲解稿多段落截断
- 原版 7 个脚本写死用户/工程/ffmpeg/桌面路径,换用户换项目即失效 → 现全部 argparse 参数化,并新增
pipeline.py一键串联。 - 原
parse_script.py跨段讲解词只取第一段(数据丢失)→ 已改为多段累加;无页码标记时明确报错。 - 原三个合成脚本(build_video/male_rebuild/build_repair)大量重复 → 已合并为单一参数化
build_video.py。
坑9(再审计修复):讲解词↔画面系统性错位 + 跨平台中文字体 + verify 硬编码
- 错位 bug(P1,已实测):
build_video.py原循环用for i, page in enumerate(pages, start=1)找slide_%02d.png % i,仅在讲解稿页码"1..N 连续"时凑巧对齐;一旦页码跳号(封面/目录无讲解),讲解词会系统性贴错画面(实测:讲解稿第3页→slide_01、第5页→slide_02)。修复:按讲解稿页码page严格索引slide_%02d.png % int(page),约定"讲解稿第 N 页 = 第 N 张幻灯片"。 - 跨平台中文字体(P2):
font()/load_font()候选列表仅 Windows;Linux/macOS fallback 到英文点阵字体 → 中文角标变方块。修复:补充 Noto CJK(Linux)、文泉驿(Linux)、PingFang/STHeiti(macOS)路径;若系统无任何中文字体需先安装。 verify.py硬编码(P3):原版完全不可复用。修复:argparse 化,--video必填 + 可选--clips-dir/--frames-dir/--ffmpeg。
常见坑速查(一句话版)
- 拼接千万别用
-c copy:多片段 MP4 直拼保留各自时间戳/编辑列表,大量播放器无声。一律 concat filter 重编码。 - 中文路径:交给 PowerPoint/COM 前复制到 ASCII 临时目录。
- 编码器/ffmpeg:不要写死,用技能的自动探测;无集显也能跑(退回 libx264/mpeg4)。
- 自带 ffmpeg 缺 libx264/drawtext → 自动选 QSV/MF,字幕用 Pillow 烧。
- 编码耗时约 1× 实时,放后台跑;交付前用
volumedetect验证音频非静音。
技能脚本清单(scripts/ 目录)
| 脚本 | 作用 |
|---|---|
pipeline.py |
一键流程:解析→配音→渲染→合成(推荐入口) |
parse_script.py |
解析讲解稿 docx → narration.json(页码→讲解词,多段累加、无标记报错) |
make_tts.py |
edge-tts 逐页配音 → audio/slide_N.mp3(幂等,支持 --force/--voice) |
ppt_export.ps1 |
PowerPoint COM 把 pptx 原生导出 1920×1080 PNG(param 参数化) |
render_fallback.py |
兜底:python-pptx + Pillow 近似渲染(无 PowerPoint/LibreOffice 时) |
build_video.py |
规范合成:自动探测 ffmpeg/编码器 + 预处理加角标 + concat filter 重编码拼接 |
verify.py |
校验:流信息 + 时长 + volumedetect(确认非静音)+ 可选抽帧/累计片段时长(已 argparse 化,--video 必填) |
交付
最终 MP4 用 present_files 展示;告知时长、分辨率、大小、保存路径,并附"为何用合成而非真录屏"与"已验证音频非静音"的说明。