Install
openclaw skills install ppt2videoConvert PowerPoint presentations into narrated videos with Chinese voiceover, synchronized subtitles, and page-by-page audio sync. Use this skill when the user uploads a PPT file and wants a video explainer, training video, or course video generated from slides.
openclaw skills install ppt2video将PPT课件自动转换为带中文旁白、同步字幕、音画精确对齐的讲解视频。
from pptx import Presentation
from pptx.enum.shapes import MSO_SHAPE_TYPE
import os
prs = Presentation("input.pptx")
output_dir = "public/slides"
os.makedirs(output_dir, exist_ok=True)
for i, slide in enumerate(prs.slides, 1):
for shape in slide.shapes:
if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
image = shape.image
filepath = os.path.join(output_dir, f"slide_{i:02d}.{image.ext}")
with open(filepath, "wb") as f:
f.write(image.blob)
print(f"Slide {i}: {filepath}")
break
注意:如果PPT是文字型(非图片型),需要额外截图或导出为图片。
原则:
格式:
页1:今天聊AI视频的双轨实践
页2:感性路线Seedance用AI画画,理性路线Remotion用代码控制
页3:Seedance四大能力,但单次只支持四到十五秒
...
关键:每页独立生成音频片段,不要生成一条全长音频
cd audio/pages
edge-tts --voice zh-CN-XiaoxiaoNeural --text "今天聊AI视频的双轨实践" --write-media p01.mp3
edge-tts --voice zh-CN-XiaoxiaoNeural --text "感性路线Seedance用AI画画" --write-media p02.mp3
# ... 每页一条
推荐语音:zh-CN-XiaoxiaoNeural(女声,专业清晰)
for f in p*.mp3; do
duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$f")
frames=$(python3 -c "print(int(float('$duration') * 24 + 0.5))")
printf "%s: %.3fs = %d frames\n" "$f" "$duration" "$frames"
done
计算公式:frames = int(duration_seconds * 24 + 0.5)
项目结构:
remotion-ppt-video/
├── src/
│ ├── index.tsx # registerRoot
│ └── PPTVideo.tsx # 主组件
├── public/
│ ├── slides/ # PPT图片
│ │ ├── slide_01.png
│ │ └── ...
│ └── audio/ # 逐页音频
│ ├── p01.mp3
│ └── ...
├── audio/
│ └── pages/ # 音频源文件
├── out/ # 输出目录
├── remotion.config.ts
└── tsconfig.json
PPTVideo.tsx 核心结构:
const SLIDES = [
{ img: 'slides/slide_01.png', text: '今天聊AI视频的双轨实践', audio: 'audio/p01.mp3', frames: 75 },
// ... 每页对应一条
];
// 计算累计起始帧
const starts: number[] = [];
let acc = 0;
for (const s of SLIDES) {
starts.push(acc);
acc += s.frames;
}
export const TOTAL_FRAMES = acc;
布局规范:
objectFit: 'contain'#0a0a14borderTop: '1px solid rgba(255,255,255,0.06)'#ff6b35Sequence使用:
<Sequence from={starts[index]} durationInFrames={slide.frames}>
<Audio src={staticFile(slide.audio)} volume={0.95} />
<SlideScene ... />
</Sequence>
npx remotion render src/index.tsx ppt-video out/video.mp4 --overwrite --concurrency=1
VPS优化参数:
| 问题 | 解决方案 |
|---|---|
| 旁白跨页 | 每页独立音频 |
| 画面切换与旁白不对齐 | durationInFrames = 音频秒数 × fps |
| 字幕与旁白不同步 | 每页字幕严格对应该页旁白 |
┌─────────────────────────┐
│ │
│ PPT画面 (85%) │
│ 完整显示,无遮挡 │
│ │
├─────────────────────────┤ ← 分隔线
│ 字幕安全区 (15%) │
│ 独立底色,不重叠 │
└─────────────────────────┘
objectFit: 'contain' 保持比例#0f0f1a(与字幕区 #0a0a14 区分)Q: PPT提取出来没有文字? A: PPT可能是图片型(文字已渲染为图像),需要用OCR识别或重新制作文字层。
Q: 音频总时长超过90秒? A: 精简讲解词,每页控制在5-8秒。关键信息优先,细节可省略。
Q: 渲染时内存不足? A: 降低分辨率到854×480,帧率24fps,并发设为1。
Q: 字幕和PPT底部文字重叠? A: 检查是否正确设置了85%/15%分区。PPT画面必须在85%区域内。
Q: 音画不同步?
A: 确认每页独立音频,且 durationInFrames 精确等于音频时长×fps。不要用单条全长音频。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| fps | 24 | 流畅且节省资源 |
| 分辨率 | 854×480 | VPS安全渲染 |
| 画面比例 | 85% | 上部PPT画面 |
| 字幕比例 | 15% | 底部字幕安全区 |
| 语音 | zh-CN-XiaoxiaoNeural | 女声,专业 |
| 淡入时长 | 0.3秒 | 画面自然过渡 |
| 字幕淡入延迟 | 0.2秒 | 画面先出现 |