Demo Voiceover 技能
生成并同步 AI 画外音到演示视频中。
定位
这是演示视频管道的第三阶段:
/demo-script → /record-demo → /demo-voiceover
(规划) (录制) (配音 & 交付)
也可独立使用,为任何现有视频 + 时间戳日志添加旁白。
输入
该技能需要以下内容:
- 演示脚本 JSON — 来自
/demo-script(workspace/<date>/demo-script-<name>.json) - 原始视频文件 — 来自
/record-demo(workspace/<date>/<name>_raw.mp4) - 时间戳日志 — 来自
/record-demo(workspace/<date>/<name>_timestamps.json)
时间戳日志格式(由 /record-demo 生成):
{
"video_file": "demo_raw.mp4",
"start_epoch": 1739545200.0,
"steps": [
{
"scene_id": "intro",
"step_index": 0,
"action": "navigate",
"narration": "欢迎使用 OneTest。让我们创建第一个测试用例。",
"timestamp_sec": 0.0,
"completed_sec": 1.2
},
{
"scene_id": "navigate-to-test-cases",
"step_index": 0,
"action": "click",
"narration": "从侧边栏打开测试用例部分。",
"timestamp_sec": 3.2,
"completed_sec": 3.8
}
]
}
管线
阶段 1:旁白规划
确定每个旁白段在视频中的播放时机:
时机策略选项:
| 策略 | 旁白开始时机 | 最佳用途 |
|---|---|---|
on_action | 动作开始时刻 (timestamp_sec) | 解释即将发生什么 |
after_action | 动作完成之后 (completed_sec) | 描述刚刚发生了什么 |
before_action | 动作开始前 0.5 秒 | 营造期待感 |
fill_pause | 在 pause_after_ms 窗口期间 | 在人为停顿期间配音 |
默认:fill_pause — 旁白在每个动作之后的停顿期间播放,让观众先看到操作,然后在画面稳定时听到解释。这感觉最自然。
规划输出:
{
"segments": [
{
"index": 0,
"text": "欢迎使用 OneTest。让我们创建第一个测试用例。",
"video_start_sec": 1.2,
"max_duration_sec": 2.0
},
{
"index": 1,
"text": "从侧边栏打开测试用例部分。",
"video_start_sec": 3.8,
"max_duration_sec": 1.5
}
]
}
阶段 2:音频生成(ElevenLabs)
使用 ElevenLabs 带时间戳 的端点生成每个旁白段:
- 返回字符级对齐信息(精确到毫秒)
- 可免费生成字幕
- 通过
previous_request_ids支持连续性链式调用(最多 3 个)
运行 python scripts/generate_narration.py --plan voiceover_plan.json --voice rachel --output-dir .
有关 API 详细信息,请参考 references/elevenlabs-api.md。
阶段 3:时间协调
生成音频后,对比计划时长与实际时长:
- 音频适合可用窗口 -> 放在计划开始位置
- 音频更短 -> 多余的静音没问题(演示需要呼吸感)
- 音频更长 -> 加速(通过
atempo,最高 1.15 倍),或缩短文本并重新生成
速度调整上限为 1.15x — 超过此值听起来会很急促。
阶段 4:音频合成
使用辅助脚本将旁白段与视频合并:
bash scripts/merge_audio_video.sh demo_raw.mp4 narration_results.json final_demo.mp4
该脚本构建一个 ffmpeg filter_complex,为每个段使用 adelay,并使用 atempo 进行速度调整。
阶段 5:字幕生成
ElevenLabs 提供的字符级时间戳可实现免费 SRT 生成:
- 将字符分组为单词,单词分组为字幕行(最多 8 个单词)
- 为每个段偏移
video_start_sec - 输出为标准 SRT 格式
要将字幕烧录到视频中:
ffmpeg -i final_demo.mp4 -vf subtitles=demo.srt \
-c:v libx264 -crf 18 -c:a copy \
final_demo_subtitled.mp4
语音配置
根据受众选择语音,草稿阶段使用 eleven_turbo_v2,最终版本使用 eleven_multilingual_v2。
有关语音 ID、设置指南、模型比较和连续性链式调用的详细信息,请参考 references/elevenlabs-api.md。
节奏调整
| 问题 | 解决方案 | 限制 |
|---|---|---|
| 音频稍微过长 | 使用 atempo 滤镜加速 | 最高 1.15x |
| 音频过长过多 | 缩短旁白文本,重新生成 | 仅重新生成该段 |
| 音频太短 | 添加静音填充(自然) | 演示中的静音没问题 |
| 段之间尴尬停顿 | 延长停顿或添加过渡短语 | 停顿保持在 3 秒以内 |
输出文件
| 文件 | 描述 |
|---|---|
<name>_final.mp4 | 视频 + 旁白合并 |
<name>_final_subtitled.mp4 | 视频 + 旁白 + 烧录字幕 |
<name>.srt | 字幕文件(独立) |
<name>_narration_full.mp3 | 完整旁白音轨(可重用) |
narration_NNN.mp3 | 单个段(用于重新生成) |
<name>_voiceover_plan.json | 时间规划(调试/迭代用) |
所有文件保存至 workspace/<date>/
迭代工作流
该技能支持重做单个段而无需重新生成所有内容:
- 用户审核视频:“场景 3 的旁白太快了”
- Agent 调整:缩短文本,仅重新生成段 3
- 使用段 1-2 的
previous_request_ids保持连续性 - 使用 ffmpeg 重新合并(仅音轨变化,视频不变)
这样做的优势:
- 视频无需重新录制
- 每个更改的段只需一次 ElevenLabs API 调用
- ffmpeg 合并仅需数秒