03 · Frame Rate · Timing · Timecode
이 문서가 답하는 질문: 23.976fps는 왜 존재하며, drop-frame timecode란 무엇인가? 선행:
02-progressive-vs-interlaced.md
한 줄 답
프레임레이트는 “초당 프레임 수”이지만, 실제 표준은 모두 분수다(24/1.001, 30/1.001, …). 1953년 NTSC 컬러TV 호환성 결정의 결과로 0.1% 만큼 느려진 시간 — 그 0.1%를 보정하는 게 drop-frame timecode다.
Why — 왜 23.976이 존재하는가
1953년, NTSC 컬러TV 표준을 만들 때 흑백 TV와의 호환 이 강제됐다. 컬러 부반송파를 추가하면서 30 fps를 그대로 유지하면 음성 반송파와 비트가 섞이는 간섭이 생긴다는 것이 RCA의 측정 결과.
해결책: frame rate를 0.1% 줄여 29.97 fps로. 정확히는:
30 / 1.001 ≈ 29.9700299... fps이 결정이 60년이 지난 지금 모든 미디어 시스템에 그림자를 드리운다.
| ”이름” | 실제 값 | 출처 |
|---|---|---|
| 24p | 24.000 | 영화 (35mm 필름 표준) |
| 23.976p | 24/1.001 ≈ 23.976 | 영화→NTSC 변환용 (텔레시네 베이스) |
| 25p | 25.000 | PAL/SECAM (유럽 50Hz) |
| 29.97p / 29.97i | 30/1.001 ≈ 29.970 | NTSC 비디오 |
| 30p | 30.000 | 디지털 (게임·웹) |
| 50p / 50i | 50.000 | PAL HD |
| 59.94p / 59.94i | 60/1.001 ≈ 59.940 | NTSC HD |
| 60p | 60.000 | 디지털 (게임·웹) |
| 120p | 120.000 | 8K NHK·고프레임 캡처 |
→ NTSC 계열은 모두 1/1.001 = 0.999000999... 만큼 느리다. 이걸 무시하면 24시간에 약 86초 어긋난다.
How — 어떻게 동작하는가
Drop-frame Timecode (DF) — 0.1%의 청구서
Timecode는 영상의 시간 좌표(HH:MM:SS:FF, FF는 프레임 029). 29.97 fps인데 timecode는 029 라벨을 쓴다 — 즉 30 fps의 라벨을 29.97 fps에 붙이는 모순.
해결: 매 분마다 2개 프레임 라벨을 건너뛴다 (단, 매 10분째는 건너뛰지 않음).
00:00:59:29 → 00:01:00:02 ← 00:01:00:00, 00:01:00:01 라벨 스킵
00:09:59:29 → 00:10:00:00 ← 매 10분은 스킵 안 함이 패턴으로 60분 동안 정확히 (60-6) × 2 = 108 프레임 만큼 라벨을 건너뛰면서 wall-clock 시간과 일치하게 만든다.
프레임은 그대로 있다 — 라벨만 바뀐다는 점이 핵심. 그래서 “drop frame”은 사실 drop label.
| 표기 | 의미 |
|---|---|
01:00:00:00 (NDF, non-drop-frame) | 라벨 건너뛰기 없음 → 실시간보다 0.1% 길어짐 |
01:00:00;00 (DF, semicolon) | 라벨 건너뛰기 있음 → 실시간 일치 |
→ 24fps 영화나 25fps PAL은 정수 fps라 DF가 필요 없다. NTSC 계열에만 존재하는 트릭.
VFR (Variable Frame Rate) vs CFR (Constant Frame Rate)
| 모드 | 설명 | 사용처 |
|---|---|---|
| CFR | 모든 프레임 간격이 같음 | 방송·전통적 비디오 표준 |
| VFR | 프레임 간격이 가변 (PTS만 정확) | 화면 녹화 (OBS·QuickTime), 모바일 카메라 |
화면 녹화기는 변경된 프레임만 기록해 디스크/CPU를 아낀다 — 이게 VFR. 문제는 NLE(Premiere·Resolve)·일부 인코더가 CFR을 가정해서, VFR 자산을 그대로 임포트하면 오디오 sync drift가 생긴다.
# VFR 진단
ffprobe -select_streams v:0 -show_entries packet=pts_time \
-of csv input.mp4 | head -20
# → pts_time이 균일 간격이면 CFR, 불규칙이면 VFR
# VFR을 CFR로 정규화 (편집 전 필수)
ffmpeg -i screen.mp4 -vsync cfr -r 60 -c:v libx264 cfr.mp4
# fps 필터로 확정
ffmpeg -i in.mp4 -vf "fps=30" -c:v libx264 out.mp4PTS / DTS — 컨테이너의 시간 좌표
| 약어 | 풀이 | 역할 |
|---|---|---|
| PTS (Presentation TS) | 프레임을 언제 표시 할지 | 디스플레이 타임스탬프 |
| DTS (Decode TS) | 프레임을 언제 디코딩 할지 | B-frame 때문에 PTS와 다름 |
B-frame이 있으면 디코딩 순서 ≠ 표시 순서 → DTS와 PTS가 분리된다 (08-codec-h264.md GOP).
표시 순서: I B B P B B P
디코딩 순서: I P B B P B BWhat — 자주 쓰는 fps 가이드와 명령어
콘텐츠별 권장 frame rate
| 콘텐츠 | 권장 fps | 이유 |
|---|---|---|
| 영화 (시네마) | 24p | 100년 표준. cinematic motion blur 와 일치 |
| 드라마/방송 | 23.976p / 29.97p | NTSC 호환 |
| 스포츠/뉴스 | 50p / 59.94p | 빠른 모션·실시간 |
| 게임 스트리밍 | 60p | 게임 자체가 60+ |
| 인터뷰·토크 | 30p | 자연스럽고 가벼움 |
| 슬로모션 캡처 | 120p / 240p / 480p | 고프레임으로 찍어 30/60p로 재생 |
| TikTok/Reels | 30p / 60p | 모바일 디스플레이 표준 |
| VR / AR | 90p+ | 멀미 방지 |
ffmpeg fps 변환 패턴
# 1. 단순 fps 변경 (프레임 drop/duplicate)
ffmpeg -i in.mp4 -r 30 out.mp4
# 2. fps 필터 (더 정확)
ffmpeg -i in.mp4 -vf "fps=30" out.mp4
# 3. 60p → 30p 안전 변환
ffmpeg -i 60p.mp4 -vf "fps=30" -c:v libx264 30p.mp4
# 4. 24p → 60p 모션 보간 (minterpolate)
ffmpeg -i 24p.mp4 -vf "minterpolate=fps=60:mi_mode=mci" smooth60p.mp4
# 5. 슬로모션 (240p 캡처 → 30p로 재생 = 8배 슬로)
ffmpeg -i 240p_capture.mp4 -filter:v "setpts=8*PTS" -r 30 slow.mp4
# 6. 23.976 ↔ 24 변환
ffmpeg -i 23976.mp4 -vf "fps=24" -af "atempo=1.001" out24.mp4Timecode 다루기
# 입력 timecode 확인
ffprobe -show_entries stream_tags=timecode in.mov
# timecode 삽입
ffmpeg -i in.mp4 -timecode 01:00:00:00 -c copy out.mp4
# drop-frame 명시 (29.97)
ffmpeg -i in.mov -timecode "01:00:00;00" -r 30000/1001 -c copy out.movWhat-if — 잘못 다루면 어떻게 깨지는가
❌ 함정 1 — 23.976을 24로 라운딩
비디오 fps를 24.000으로 잡고 오디오는 그대로 두면, 80분 짜리 영화에서 ~5초 누적 sync drift. → 시청자가 입모양이 안 맞는다고 보고하는 가장 흔한 케이스.
# 잘못
ffmpeg -i in.mov -r 24 -c:a copy out.mp4
# 올바름 (1.001 비율 보존)
ffmpeg -i in.mov -r 24000/1001 -c:a copy out.mp4❌ 함정 2 — VFR을 CFR로 인식해 인코딩
OBS 녹화·iPhone 캡처는 VFR. 그대로 NLE에 넣으면 timeline이 prefer “30 fps CFR”로 해석 → 장면 전환 후 점진적 oudio drift.
# 진단
ffprobe -select_streams v:0 -show_entries stream=avg_frame_rate,r_frame_rate in.mp4
# avg_frame_rate=30/1, r_frame_rate=600/1 → VFR 의심
# (r_frame_rate은 timebase 분모, 값이 매우 크면 VFR)
# CFR 정규화 후 편집
ffmpeg -i screen.mp4 -vsync cfr -r 30 -c:v libx264 -crf 18 cfr.mp4❌ 함정 3 — drop-frame timecode 오해
01:00:00;00 (DF) 와 01:00:00:00 (NDF)를 같은 시각으로 착각해서
편집실에서 1시간 마크에 광고를 넣었더니 방송 송출 시 광고가 3.6초 뒤에 시작.
→ NDF에서 1시간 라벨은 wall-clock 1시간 + 3.6초.
❌ 함정 4 — 60p → 30p drop만으로 처리
# bad: 단순 frame drop → 모션이 끊김
ffmpeg -i 60p.mp4 -r 30 -c:v libx264 out.mp4
# good: fps 필터 (motion-aware blending 옵션 가능)
ffmpeg -i 60p.mp4 -vf "fps=30" -c:v libx264 out.mp4
# better: 모션 블러 합성으로 영화 같은 셔터
ffmpeg -i 60p.mp4 -vf "tblend=all_mode=average,fps=30" -c:v libx264 out.mp4❌ 함정 5 — 모션 보간(minterpolate)을 영화에 적용
24p 영화를 60p로 minterpolate하면 soap opera effect — 영화가 드라마처럼 너무 매끄럽게 보이는 현상. 극장의 24p는 cinematic. 일부러 그 느낌을 위한 것이고 보간하면 영화감이 사라진다.
Insight — 흥미로운 이야기
“24fps는 사람이 보기 편한 숫자가 아니다”
24 fps는 1920년대 사운드 영화 등장 시 광학 사운드 트랙을 읽기 위한 최저 필름 속도. 이전 무성영화는 16~22 fps가 흔했고, 24는 사운드 동기화의 최소 속도. 1927년 The Jazz Singer 이후 표준이 됐다 — 기술적 한계가 미적 표준이 된 사례.
“NTSC = Never Twice Same Color”
NTSC의 0.1% drift는 50년 동안 방송업계의 농담이었다. 1953년 라디오·흑백TV·신생 컬러TV의 호환성을 위해 도입됐고, 그 결과로 23.976/29.97/59.94가 만들어졌다. 1990년대 ATSC 디지털 방송이 와도 호환을 위해 그대로 끌고 왔고, 2025년 OTT까지 영향이 남는다.
“PAL/SECAM은 깔끔한 25를 가졌지만…”
유럽은 50Hz 전력망 → 25fps. 영화 24를 PAL로 변환할 때는 4% 빠르게 재생 (“PAL speedup”). 영화는 24 → 25, 약 4분 단축. 음악도 같은 4% 올라가서 A=440Hz가 A♯ 가까이 올라간다. 비틀스 LP 영국판과 미국판이 음높이가 다른 게 이 때문 — 프레임레이트가 음악의 키를 바꾼 사건.
요약 + Mermaid
24·25·30이 아니라 24/1.001·25·30/1.001 — 1953년 NTSC 컬러TV의 0.1%가 모든 디지털 미디어에 박혀있다. Drop-frame timecode는 라벨을 건너뛰어 실시간과 맞추는 트릭이고, VFR/CFR 혼동은 sync drift의 1순위. 영화는 24p, 방송은 23.976/29.97, 게임/스포츠는 60p가 정석이며, fps 변환은 단순 drop이 아니라 fps 필터로 한다.