시네마틱 릴 편집기
원본 클립 폴더와 음악 트랙을 15~45초 길이의 9:16 비트 동기화 시네마틱 릴로 만듭니다. 모든 편집은 명령줄에서 FFmpeg를 사용하여 이루어집니다. 편집 계획은 편집 가능한 편집 목록(edl.json)에 저장되므로 처음부터 다시 시작하지 않고도 변경 사항을 다시 렌더링할 수 있습니다.
0. 확실한 규칙
raw/안의 파일을 절대 수정하거나 삭제하지 마세요. 모든 출력물은work/와out/에 작성하세요.- 음악은 사용자가 제공해야 합니다(라이선스 / 로열티 프리).
music/이 비어 있으면 트랙을 요청하세요. 저작권이 있는 음악을 다운로드하지 마세요. - 샷 계획(4단계)을 사용자에게 보여주고 최종 렌더링 전에 승인을 받으세요.
- 비트에 맞춰 하드 컷을 선호하세요. 트랜지션은 절제해서 사용하세요(릴당 최대 2~3개).
1. 사전 요구 사항 확인
ffmpeg -version && ffprobe -version
python3 -c "import librosa, numpy" || pip install librosa numpy soundfile
FFmpeg가 없으면 사용자에게 설치를 알리고(Windows: winget install ffmpeg, Mac: brew install ffmpeg, Linux: sudo apt install ffmpeg) 중단하세요.
2. 폴더 구조
project/
raw/ # 카메라 클립 (읽기 전용)
music/ # 하나의 음악 트랙
luts/ # 선택 사항인 .cube LUT 색보정용
work/ # 썸네일, 세그먼트, edl.json, beats.json
out/ # 최종 렌더링 (reel_v1.mp4, reel_v2.mp4 ...)
3. 푸티지 목록 확인 및 살펴보기
모든 클립에 대해 지속 시간, 해상도, fps 및 방향을 기록합니다:
ffprobe -v error -show_entries format=duration:stream=width,height,r_frame_rate,codec_type -of json raw/CLIP.mp4
클립당 연락처 시트를 만듭니다(2초마다 한 프레임). Read 도구로 확인하여 각 샷에 무엇이 있는지 파악하세요:
ffmpeg -y -i raw/CLIP.mp4 -vf "fps=1/2,scale=320:-1,tile=4x3" -frames:v 1 work/thumbs/CLIP.jpg
각 사용 가능한 순간에 태그를 지정합니다: 샷 유형(와이드 / 미디엄 / 클로즈업 / 디테일), 피사체, 움직임, 품질(선명함, 안정적임, 조명 좋음), 슬로우 모션 가능 여부(소스 ≥ 50 fps).
4. 스토리 구성 (샷 계획)
모든 릴을 다음과 같이 구성합니다:
- 훅 (0~2초) — 가장 인상적인 샷. 로고나 텍스트 인트로 없음.
- 빌드업 — 디테일과 기대감(클로즈업, 슬로우 모션).
- 히어로 순간 — 가장 큰 비트/음악 드롭에 맞춤.
- 감정 / 보상 — 반응, 사람들, 축하.
- 아웃트로 (마지막 2~3초) — 깔끔한 와이드 샷, 선택 사항으로 텍스트 또는 로고, 음악 페이드.
차량 인도 템플릿(푸티지에 맞게 조정): 외부 와이드 → 로고 클로즈업 → 휠/헤드라이트 디테일 → 가족 걸어오기(슬로우 모션) → 커버 공개(드롭에 맞춰) → 키 인계 → 반응/미소 → 실내 + 스티어링 → 케이크/화환/푸자 → 첫 드라이브 어웨이(와이드, 아웃트로).
규칙: 샷 크기를 다양화하세요(연속으로 두 개의 와이드 샷 금지), 각 샷을 0.5~2.5초로 유지하세요, 감정적이거나 공개 샷에만 슬로우 모션을 사용하세요, 가능하면 움직임에 맞춰 컷하세요.
5. 비트 감지
# work/beats.py
import librosa, json, sys
y, sr = librosa.load(sys.argv[1])
tempo, frames = librosa.beat.beat_track(y=y, sr=sr)
beats = librosa.frames_to_time(frames, sr=sr).tolist()
onset = librosa.onset.onset_strength(y=y, sr=sr)
json.dump({"tempo": float(tempo), "beats": beats}, open("work/beats.json", "w"), indent=1)
print(f"tempo {float(tempo):.1f} BPM, {len(beats)} beats")
비트에 맞춰 컷하세요(빠른 구간은 모든 비트, 느린 구간은 2번째 또는 4번째 비트마다). 드롭(가장 큰 에너지 점프)을 찾아 그곳에 히어로 샷을 배치하세요.
6. 편집 목록 work/edl.json 작성
{
"output": {"w": 1080, "h": 1920, "fps": 30},
"music": {"file": "music/track.mp3", "start": 12.0, "fade_out": 1.5},
"grade": {"lut": "luts/teal_orange.cube", "vignette": true},
"shots": [
{"id": 1, "src": "raw/C0001.mp4", "in": 4.20, "dur": 1.40, "speed": 1.0, "crop_x": "center", "note": "훅 – 차량 와이드"},
{"id": 2, "src": "raw/C0007.mp4", "in": 10.0, "dur": 1.00, "speed": 0.5, "crop_x": "center", "note": "휠 슬로우 모션"}
],
"transitions": [{"after_shot": 4, "type": "fade", "dur": 0.3}],
"text": [{"text": "어서 오세요", "start": 18.0, "end": 21.0}]
}
dur은 타임라인의 길이입니다. speed가 0.5이면 사용되는 소스 범위는 dur × speed초입니다. 렌더링 전에 이 계획을 간단한 표(샷, 시간, 설명)로 사용자에게 보여주세요.
7. 각 샷을 균일한 세그먼트로 렌더링
모든 세그먼트는 동일한 해상도, fps, 픽셀 형식을 가져야 하며 오디오가 없어야 깔끔하게 결합할 수 있습니다:
# 가로 소스 → 9:16 크롭, 스피드, 그레이딩
ffmpeg -y -ss IN -t SRC_DUR -i raw/CLIP.mp4 -an -vf "\
setpts=PTS/SPEED,\
scale=-2:1920,crop=1080:1920,\
fps=30,\
lut3d=luts/teal_orange.cube,\
eq=contrast=1.06:saturation=1.10,\
vignette=PI/5,\
format=yuv420p" \
-c:v libx264 -crf 16 -preset medium work/seg_01.mp4
SRC_DUR = dur × speed. 슬로우 모션에는 고프레임 소스를 사용하세요. 요청하지 않는 한minterpolate는 피하세요(느리고 인공물 발생).- 세로 소스:
scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920을 사용하세요. - LUT를 사용할 수 없는 경우: 영화 같은 느낌을 위해
eq=contrast=1.08:saturation=1.15:gamma=0.98과curves=preset=increase_contrast를 사용하세요. - 선택 사항인 시네마틱 바는 9:16 릴에서 사용되지 않습니다.
8. 세그먼트 결합
트랜지션 없음 – concat 디먹서:
printf "file 'seg_%02d.mp4'\n" 1 2 3 ... > work/list.txt
ffmpeg -y -f concat -safe 0 -i work/list.txt -c copy work/video.mp4
두 세그먼트 사이에 트랜지션이 있는 경우 xfade를 사용하세요(오프셋 = 첫 번째 클립 길이 − 트랜지션 지속 시간):
ffmpeg -y -i work/seg_04.mp4 -i work/seg_05.mp4 -filter_complex \
"[0][1]xfade=transition=fade:duration=0.3:offset=1.2,format=yuv420p" work/seg_04_05.mp4
각 트랜지션은 타임라인을 해당 지속 시간만큼 단축시킵니다. 비트 정렬을 다시 확인하세요.
9. 음악, 텍스트, 최종 내보내기
ffmpeg -y -i work/video.mp4 -ss MUSIC_START -i music/track.mp3 -filter_complex "\
[1:a]afade=t=in:d=0.3,afade=t=out:st=FADE_START:d=1.5,loudnorm=I=-14:TP=-1:LRA=11[a];\
[0:v]drawtext=fontfile=FONT.ttf:text='어서 오세요':fontsize=72:fontcolor=white:\
x=(w-text_w)/2:y=h*0.72:enable='between(t,18,21)':alpha='if(lt(t,18.4),(t-18)/0.4,1)'[v]" \
-map "[v]" -map "[a]" -shortest \
-c:v libx264 -crf 18 -preset slow -profile:v high -pix_fmt yuv420p -r 30 \
-c:a aac -b:a 192k -movflags +faststart out/reel_v1.mp4
drawtext에 사용할 글꼴 파일을 시스템에서 찾으세요(또는 물어보세요). 텍스트를 최소화하고 안전 영역 내에 유지하세요(상단 15%와 하단 25%는 피하세요. Instagram UI가 있는 곳입니다).
10. 품질 확인 (항상)
ffprobe out/reel_vN.mp4→ 1080×1920, 30 fps, H.264, AAC, 계획된 지속 시간.- 최종 릴의 연락처 시트(
fps=2,scale=270:-1,tile=6x5)를 보고 확인하세요: 검은 프레임 없음, 잘못된 크롭(머리 잘림) 없음, 일관된 색상. - 음량을 확인하고 히어로 샷이 드롭에 맞는지 확인하세요.
- 사용자에게 보고하세요: 지속 시간, 샷 개수, 파일 경로, 그리고 확실하지 않은 사항.
11. 수정
처음부터 다시 시작하지 마세요. work/edl.json을 편집하고(샷 교체, in/dur, 속도, 텍스트, 그레이딩 변경), 변경된 세그먼트만 다시 렌더링하고, 결합하여 다음 버전(reel_v2.mp4)으로 내보내세요. 사용자가 비교할 수 있도록 이전 버전을 유지하세요.