Demo Voiceover 技能
為展示影片生成並同步 AI 語音旁白。
定位
這是展示影片處理管道中的第三個階段:
/demo-script → /record-demo → /demo-voiceover
(規劃) (錄製) (旁白與交付)
亦可獨立使用,為任何現有影片加上時間戳日誌的旁白。
輸入
技能需要以下資料:
- Demo 腳本 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:時間對齊
生成音訊後,比對規劃與實際的持續時間:
- 音訊可放入可用時間窗口 -> 在規劃時間點開始播放
- 音訊較短 -> 多餘的靜音沒問題(展示需要呼吸空間)
- 音訊較長 -> 加速(最大 1.15 倍,透過
atempo),或縮短文字重新生成
速度調整上限為 1.15 倍 — 超過會聽起來很趕。
階段 4:音訊組裝
使用輔助腳本將旁白段落與影片合併:
bash scripts/merge_audio_video.sh demo_raw.mp4 narration_results.json final_demo.mp4
該腳本會建立一個包含 adelay(每段延遲)和 atempo(速度調整)的 ffmpeg filter_complex。
階段 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.15 倍 |
| 音訊太長 | 縮短旁白文字,重新生成 | 只重新生成該段落 |
| 音訊太短 | 加入靜音填補(自然) | 展示中的靜音沒問題 |
| 段落間間隔尷尬 | 延長停頓或加入過渡語 | 停頓保持在 3 秒內 |
輸出檔案
| 檔案 | 說明 |
|---|---|
<name>_final.mp4 | 影片 + 旁白合併 |
<name>_final_subtitled.mp4 | 影片 + 旁白 + 內嵌字幕 |
<name>.srt | 字幕檔(獨立) |
<name>_narration_full.mp3 | 完整旁白音軌(可重複使用) |
narration_NNN.mp3 | 單一段落(供重新生成) |
<name>_voiceover_plan.json | 時間規劃檔案(用於除錯/迭代) |
所有檔案儲存在 workspace/<date>/
迭代工作流程
技能支援重新生成個別段落,而無需重新處理全部內容:
- 使用者檢視影片:「場景 3 的旁白太快了」
- 代理調整:縮短文字,只重新生成段落 3
- 使用段落 1-2 的
previous_request_ids保持連續性 - 使用 ffmpeg 重新合併(僅音軌變更,影片不變)
這個流程很快,因為:
- 影片無需重新錄製
- 每個變更的段落只需一次 ElevenLabs API 呼叫
- ffmpeg 合併只需要幾秒鐘