用 Python 和 FFmpeg 实现自动化 PPT 视频生成:一个后端开发者的实践记录
前阵子公司内容团队提了个需求:有几十份培训用的 PPT,每页备注里写好了讲解词,希望批量转成带配音的 MP4 视频,上传到内部学习平台。手动录屏肯定不现实——40 份 PPT 每份 30 页,录完人都废了。我的第一反应是看看能不能用技术手段把流程自动化掉。这篇文章记录了我踩坑和落地的过程。
需求拆解
内容团队的真实诉求其实很简单:给一份 PPT,输出的视频里每一页幻灯片停留几秒,同时有语音朗读当前页的备注文字。翻页、配音、拼接这三个环节如果能串起来,整个流程就通了。
具体来说要解决三个子问题:
- 从 PPT 中提取内容——每页的幻灯片图片和备注文字怎么拿到
- 文字转语音——备注文本怎么生成自然的配音音频
- 合成视频——图片 + 配音怎么拼成最终的 MP4 文件
第一步:从 PPT 里提取素材
直接从 .pptx 文件里提取每页的图片和备注文字,可以用 python-pptx 库。但问题是 .ppt(旧格式)这个库不支持。我的做法是先把 PPT 统一转为 PDF(Office 或者 WPS 都能批量转),然后用 PyMuPDF(也就是 fitz)来提取。
pip install pymupdf
import fitz
import os
from pathlib import Path
def extract_from_pdf(pdf_path, output_dir, dpi=150):
"""从 PDF 提取每页为图片,同时读取备注(备注需提前转为 PDF 注释)"""
doc = fitz.open(pdf_path)
os.makedirs(output_dir, exist_ok=True)
slides = []
for i in range(len(doc)):
page = doc[i]
# 导出为 PNG
pix = page.get_pixmap(matrix=fitz.Matrix(dpi/72, dpi/72))
img_path = os.path.join(output_dir, f"slide_{i+1:03d}.png")
pix.save(img_path)
slides.append(img_path)
doc.close()
return slides
# 使用
pages = extract_from_pdf("training_01.pdf", "./slides/")
print(f"提取了 {len(pages)} 页图片")
备注文字的处理有点绕。.pptx 的备注存在 notesSlide 里,如果用 python-pptx 读取的话要区分新旧格式。我图省事直接让内容团队把每页备注复制到一个 JSON 文件里对号入座,反而比程序自动提取更可靠——至少不会出现编码问题导致漏读的情况。
第二步:配音生成
配音方案我对比了两种:
方案 A:edge-tts(免费,在线)
微软 Edge 浏览器内置的 TTS 引擎可以通过 edge-tts 库直接调用,不需要申请 API Key,网络通就能用。
pip install edge-tts
import asyncio
import edge_tts
async def gen_audio(text, output_path, voice="zh-CN-XiaoxiaoNeural", rate="+0%"):
"""生成配音"""
communicate = edge_tts.Communicate(text, voice, rate=rate)
await communicate.save(output_path)
print(f"生成完成: {output_path}")
# 使用
async def batch_gen(notes_dict):
tasks = []
for idx, text in notes_dict.items():
path = f"audio_{idx:03d}.mp3"
tasks.append(gen_audio(text, path))
await asyncio.gather(*tasks)
notes = {1: "欢迎参加本次培训……", 2: "首先介绍公司组织架构……"}
asyncio.run(batch_gen(notes))
edge-tts 免费的代价是每次都要联网请求,而且并发量大了会被限流。我跑 40 份 PPT 时被限了两次,后来改成串行 + 每生成一页 sleep 0.5 秒才稳下来。
方案 B:VITS 本地模型(离线,效果更自然)
如果对配音自然度要求更高,或者需要在无网络环境处理,可以跑本地的 VITS 模型。GitHub 上有不少中文训练好的 checkpoint,加载后用 PyTorch 推理就行:
# 伪代码示意——实际需要加载具体的 VITS 模型
import torch
import soundfile as sf
def vits_tts(text, model, device="cuda"):
with torch.no_grad():
audio = model.infer(text)
sf.write("output.wav", audio, samplerate=22050)
VITS 的效果确实比 edge-tts 更接近真人,但部署成本高——至少需要一张显卡,模型文件几百 MB,第一次配置环境大概要半天。如果是团队长期使用,这个投入是值得的;如果只是临时做一批视频,用 edge-tts 就够了。
第三步:FFmpeg 视频合成
配音生成后,把每页图片 + 对应配音合成一小段视频,最后把所有片段拼接起来。
# 单页合成:图片显示 + 配音播放
ffmpeg -loop 1 -i slide_001.png -i audio_001.mp3 \
-c:v libx264 -tune stillimage -c:a aac \
-b:a 128k -pix_fmt yuv420p -shortest clip_001.mp4
要处理批量的话用 Python 包装一下:
import subprocess
import os
def make_clip(image, audio, output, fps=24):
"""图片+配音→视频片段"""
cmd = [
"ffmpeg", "-y",
"-loop", "1",
"-i", image,
"-i", audio,
"-c:v", "libx264",
"-tune", "stillimage",
"-c:a", "aac",
"-b:a", "128k",
"-r", str(fps),
"-pix_fmt", "yuv420p",
"-shortest",
output
]
subprocess.run(cmd, capture_output=True)
def concat_clips(clip_list, output_path):
"""拼接所有片段"""
list_file = "concat_list.txt"
with open(list_file, "w") as f:
for clip in clip_list:
f.write(f"file '{os.path.abspath(clip)}'\n")
subprocess.run([
"ffmpeg", "-y", "-f", "concat",
"-safe", "0",
"-i", list_file,
"-c", "copy",
output_path
])
os.remove(list_file)
最终的效果测试下来:一份 30 页的 PPT,从配完音的素材到输出完整视频,整个过程大约 40 秒。如果加上 TTS 生成的时间,总共大概 5-8 分钟一份。
几个踩过的坑
1. 图片比例不一致。 不同 PPT 的页面比例可能是 4:3 也可能是 16:9,混合拼接时 FFmpeg 默认拉伸,画面会变形。后来在合成命令里加了 scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2 的 filter,把不足的部分用黑边填充才解决。
2. 配音时长和图片停留时间不匹配。 如果某页备注只有一句话(3 秒读完),而图片默认停留 8 秒,画面就会在配音播完后干等着。我最后的处理方式是动态计算音频时长,然后用音频时长来决定 -shortest 的截断点,不用固定停留时间。
3. edge-tts 的代理问题。 公司内网访问微软 TTS 服务需要走代理,但 edge-tts 默认不读系统代理配置。后来在代码里显式设置了 export HTTPS_PROXY=http://proxy:port 才跑通。
方案对比
这次实践做下来,我的感受是:纯手撸这套流程,前期搭建环境大概花了两个下午(主要是调 FFmpeg filter 和排 TTS 的坑),但后面批量跑起来确实省时间。40 份 PPT,脚本跑了大概 3 个半小时(主要是 TTS 生成排队),人工只需要检查输出结果。
后来跟同行交流了解到也有现成的方案可以处理这个流程:上传 PPT 后自动完成图片提取、备注配音、视频合成这几个步骤,不需要自己搭环境。如果只是偶尔做几份,或者团队里没有能折腾这些的开发者,现成方案可能更实际。但如果你跟我一样需要批量处理、对输出参数有定制要求、或者想把这套能力集成到自己的系统里,自己搭这套流水线还是值得的。
以上代码基于 Python 3.10 + FFmpeg 6.0 测试通过,不同环境可能需要调整参数。
更多推荐



所有评论(0)