MMD 舞蹈影片字幕:端到端流程
0. 一句話原則
寫完/渲染完必須抽幀看一眼:ffprobe 時長對 ≠ 字幕真的出現;ASS 時間戳或 Style 顏色格式錯都會「渲染成功但沒字」。
本專案已有多次「以為改好了,其實沒燒進去」的教訓,驗收一律以抽幀截圖為準。
1. 資產與路徑(本 workspace)
| 東西 | 路徑 |
|---|---|
| 字幕專案 | D:\yingyong2\論項目\字幕\ |
| LRC 歌詞 | 字幕\[by維爾日記].txt — KISS OF LIFE《Who is she (Japanese Ver.)》,36 句 |
| 舊 MMD 成片 | 字幕\0001-4956 - 副本.mkv(2560×1080 / 30fps;檔案不完整,標稱 165s 實際只能解到 ~137s) |
| 新 MMD 成片 | D:\mmd.mp4\0002-4956.mkv(2560×1080 / 30fps / 165.166s,完整) |
| 字型 | 字幕\build\fonts\(Anton, ArchivoBlack, BebasNeue, Bungee, Monoton, PermanentMarker, RubikGlitch, Silkscreen, PirataOne, UnifrakturCook)+ 系統 Georgia / Microsoft YaHei |
| 產生腳本 | 字幕\build\*.py |
| 成片輸出 | 字幕\輸出\<任務名>\(每個任務一個資料夾,交付名帶中文可讀名) |
| ffmpeg | C:\Users\35530\AppData\Local\Microsoft\WinGet\Packages\Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe\ffmpeg-9.0-full_build\bin\ffmpeg.exe |
| python | C:\Users\35530\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\python\python.exe(PIL 12.3 / numpy 2.3,無 cv2) |
2. 七步主流程
① 探測素材 ffprobe 解析度/幀率/時長/色彩;解碼測試判斷檔案是否截斷
② 選片段 歌曲段 vs 舞蹈動作;小樣 5s / 30s / 全片
③ 對歌詞 LRC 絕對時間 → 減去片段起點 = ASS 相對時間
④ 定風格 參考 S1–S6(見 style-frames-compare);先出靜態幀,再出動態
⑤ 寫 ASS Python 產生;遵守 ass-render-playbook 的格式硬規則
⑥ 渲染 ffmpeg 燒錄(或透明疊加層);先出 5–10s 驗證,再全段
⑦ 驗收 抽幀(每句至少 1 幀)+ 拼關鍵幀表;看構圖、無中文、不擋臉、收尾淡出
① 探測素材
$ff='...\ffmpeg.exe'; $fp='...\ffprobe.exe'
& $fp -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate,nb_frames,pix_fmt,color_space -show_entries format=duration -of default=nw=1 $v
& $ff -v error -i $v -map 0:v:0 -c copy -f null NUL # 報 "File ended prematurely" = 截斷
- 本專案素材都是 2560×1080 / 30fps / h264 yuv420p / bt709,超寬 64:27。
- 165s 影片 ≈ 4956 幀;30s 成片 = 900 幀。
② 選片段
- LRC 副歌第一段:
36.892 / 40.366 / 44.003 / 47.577 / 51.179 / 54.698 / 58.309 / 61.926,到 ~66s 收。 - 5s 對比樣片常用
0:58.2–1:03.2;30s 樣片常用0:36.5–1:06.5;全片 0–154.6s(舊檔案只能到 137s)。 - 裁片:
-ss 36.5 -t 30 -i src -vf "eq=brightness=-0.05:contrast=1.04:saturation=1.02" -c:v libx264 -crf 16 -pix_fmt yuv420p(輕微壓暗是為了讓白/奶油字有對比;完整調色另見 subject-color-grade)
③ 對歌詞
LRC 是絕對時間;ASS 用相對片段起點的時間。例:片段從 36.5s 開始,則 36.892 → 0.392s。
一句一個 cue;句內再按詞拆多個事件(見下)。結尾事件要提前 0.14–0.4s 收掉,避免跨句黏連。
④ 定風格
- 先讀
style-frames-compare技能,按目標氣質選 S1–S6,先出靜態幀給用戶看。 - 重要約束:用戶明確說「不要中文」時,只保留原歌詞英文/日文;人物在中間,大字放左右安全欄與上下角。
- 已有可複用產生器:
styles_anim.py(S2/S4/S6 的 5s 動畫)、flare_wide.py(炸場)、neon_wide.py(霓虹)、avoid_side2.py(完全避讓)、kpop_styles.py(靜態稿)、art_frames.py(藝術字靜態稿)。
⑤ 寫 ASS
- 用 Python 產生(比手寫穩):定義
Ass類別 +word()/rect()輔助,最後write()。 - 硬規則、模板、坑見
ass-render-playbook(時間格式H:MM:SS.cc、Style 色值無尾&、fontsdir 相對路徑等)。
⑥ 渲染
- 工作目錄 = 輸出目錄;ASS 用 ASCII 檔名;字型目錄相對:
cd "字幕\輸出\S4S6_30s"
& $ff -v error -y -i _base_30s.mp4 -vf "ass=_s4.ass:fontsdir=fonts" `
-c:v libx264 -preset medium -crf 16 -pix_fmt yuv420p -movflags +faststart S4_30s.mp4
- 透明疊加層(後期用):
-f lavfi -i "[email protected]:s=2560x1080:r=30,format=rgba" -vf "ass=x.ass:alpha=1"+ VP9yuva420p(詳見 ass-render-playbook)。
⑦ 驗收清單
-
ffprobeduration 與設計一致(5.000 / 30.000 / 154.6) - 抽幀:每句至少 1 幀,確認字真的出現、位置對、無亂碼
- 用戶要求無中文 → 抽 2–3 幀確認只有英文/日文
- 人物臉/身體未被遮擋(近景/特寫幀尤其檢查)
- 尾句在結束前淡出(如 29.86s 前),不留殘字
- 拼
關鍵幀.jpg(2×2 或 2×3,每格 1280×540,帶標籤)方便用戶看 - 檔名可讀 + 說明 md 一併交付
3. 任務類型 → 交付物
| 任務 | 交付 |
|---|---|
| 靜態幀方案 | N 張 2560×1080 jpg + 總覽.jpg + 說明 md |
| 5s 小樣 | 1–3 條 5s mp4 + 對比影片 + 關鍵幀表 |
| 30s 小樣 | 1–2 條 30s mp4(900 幀)+ 對比版 + 關鍵幀表 + 說明 md |
| 成對交付(常見) | 每個風格 2 條:X_有背景_30s.mp4(H.264 燒錄)+ X_純字幕_透明_30s.webm(VP9 alpha);N 風格 × 2 條影片,附 純字幕_透明預覽.jpg(棋盤格合成預覽) |
| 全片 | 字幕 ASS + 燒錄 mp4 / 透明 webm + 說明 |
| 調色 | 調色 mp4 + 對比幀 + 參數說明 / LUT |
透明版渲染(與有背景版同一個 ASS,幀對幀一致):
& $ff -v error -y -f lavfi -i "[email protected]:s=2560x1080:r=30,format=rgba" ` -vf "ass=_s4.ass:alpha=1:fontsdir=fonts" -t 30 -c:v libvpx-vp9 -pix_fmt yuva420p ` -b:v 0 -crf 30 -row-mt 1 -auto-alt-ref 0 -metadata:s:v:0 alpha_mode=1 OUT.webm驗證 alpha:
ffmpeg -c:v libvpx-vp9 -ss 12.4 -i OUT.webm -frames:v 1 -pix_fmt rgba f.png,PIL 檢查 alpha min=0 / max=255、覆蓋率>0;抽幀必須顯式-c:v libvpx-vp9,否則丟 alpha 全變 255。
4. 並行(子代理)約定
用戶說「子代理分開做」時的標準玩法:
- 主代理先準備共享底片(裁好、統一壓暗)與
fonts/副本、cue sheet(相對時間表); - 每個風格一個子代理,提示詞裡給死:輸入路徑、輸出檔名(
S4_30s.mp4等)、ASS 硬規則、時間軸表、驗收要求; - 子代理各自只寫自己的前置檔案,互不覆蓋;
- 主代理收齊後做對比影片/關鍵幀表/說明,並統一驗收抽幀。 實測:單個 30s 樣式(2560×1080、8 句、約 40–60 個 Dialogue)從設計到成片約 10–20 分鐘;兩子代理並行≈最慢那個的時間。