MMD 댄스 영상 자막: 종단간 워크플로우
0. 한 줄 원칙
작성/렌더링 후 반드시 키프레임을 추출하여 확인: ffprobe 길이가 일치한다고 해서 자막이 실제로 표시되는 것은 아닙니다; ASS 타임스탬프나 스타일 색상 형식이 잘못되면 '렌더링 성공했지만 자막 없음' 현상이 발생합니다.
이 프로젝트는 이미 여러 번 '수정했다고 생각했지만 실제로는 입혀지지 않은' 교훈을 얻었으며, 모든 검수는 키프레임 스크린샷을 기준으로 합니다.
1. 자산 및 경로 (이 워크스페이스)
| 항목 | 경로 |
|---|---|
| 자막 프로젝트 | 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. 7단계 메인 워크플로우
① 원본 조사 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.
한 줄에 하나의 큐; 행 내에서 다시 단어별로 여러 이벤트로 분할 (아래 참조). 마지막 이벤트는 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, 스타일 색상값 끝에&없음, 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.webmalpha 검증:
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분 소요; 두 하위 에이전트 병렬 시 ≈ 가장 느린 쪽 시간 소요.