📁 File3. 비디오06 · 컨테이너 (MP4 · MOV · MKV · WebM · MPEG-TS · FLV)

06 · 컨테이너 (MP4 · MOV · MKV · WebM · MPEG-TS · FLV)

이 문서가 답하는 질문: 코덱과 컨테이너는 어떻게 다르고, MP4와 MKV는 왜 둘 다 있는가? 선행: 01-pixel-resolution-aspect.md, 04-yuv-and-chroma-subsampling.md


한 줄 답

코덱은 비디오/오디오를 압축하는 알고리즘이고, 컨테이너는 그 압축된 스트림과 메타·자막·챕터·DRM 정보를 한 파일에 묶는 포장 형식이다. 하나의 코덱은 여러 컨테이너에 들어갈 수 있고, 하나의 컨테이너는 여러 코덱을 담을 수 있다.


Why — 왜 컨테이너가 따로 필요한가

비디오 한 편에는 동영상 하나만 들어가는 게 아니다:

  • 비디오 스트림 1개 (또는 여러 개 — angle, picture-in-picture)
  • 오디오 스트림 N개 (다국어·5.1 서라운드)
  • 자막 스트림 M개 (SRT·WebVTT·PGS)
  • 챕터 마커
  • 메타데이터 (제목·저작자·EXIF·Dolby Vision RPU·HDR mastering display)
  • DRM 보호 정보
  • 썸네일/포스터

이걸 한 파일에 어떻게 줄 세울지, 어떻게 빠르게 탐색할지(seek), 어떻게 스트리밍 단위로 쪼갤지 가 컨테이너의 일.


How — 컨테이너의 두 가지 모델

1) Atom/Box 모델 (ISO BMFF — MP4·MOV·HEIF)

전체 파일이 재귀적인 박스(box, atom) 트리.

[ftyp]                  ← File Type box (포맷 식별)
[moov]                  ← Movie box (메타 — 모든 트랙 정보)
  [trak]                ← Track box × N (비디오·오디오·자막)
    [mdia]
      [minf]
        [stbl]          ← Sample Table (어떤 sample이 어디 있나)
          [stsd]        ← codec configuration
          [stts]        ← time-to-sample (PTS)
          [stss]        ← sync sample (key frame index)
          [stsz]        ← sample size
          [stco]/[co64] ← chunk offset (파일 안 위치)
[mdat]                  ← Media Data (실제 압축된 비디오/오디오 바이트)

moov가 인덱스, mdat가 데이터. seek할 때 moov의 stbl을 읽어 mdat 안의 byte offset으로 점프.

2) Stream 모델 (MPEG-TS — 방송)

고정 188바이트 패킷의 무한 스트림. 인덱스가 따로 없고, 매 패킷에 Program Information 이 반복적으로 포함되어 있어 중간부터 받아도 디코드 가능.

[TS pkt 188B][TS pkt 188B][TS pkt 188B]...
   ↑ header(4B) + payload(184B)
   header: sync(0x47) | flags | PID | …

방송·라이브 스트리밍에 최적. HLS는 MPEG-TS 세그먼트를 그대로 사용 (현재는 fMP4 세그먼트도 표준).


What — 컨테이너 비교

주요 컨테이너 요약 표

컨테이너확장자모델비디오 코덱오디오 코덱자막DRM라이브
MP4 (ISO BMFF).mp4 / .m4vatom/boxH.264·HEVC·AV1·VP9·MPEG-4AAC·MP3·AC-3·Opus·ALACTX3G·WebVTT(fMP4)CENCfMP4 ✅
MOV (QuickTime).movatom/box (MP4 조상)ProRes·H.264·HEVC·DNxHDAAC·PCM·ALACQT text
MKV (Matroska).mkvEBML거의 모든 코덱모든 코덱SRT·SSA·VobSub·PGS자체
WebM.webmEBML (MKV 부분집합)VP8·VP9·AV1Vorbis·OpusWebVTTEME 가능
MPEG-TS.ts / .m2tsstream (188B 패킷)MPEG-2·H.264·HEVCAAC·AC-3·DTSDVB·CCCENC
FLV.flvRTMP 스트림 포장H.264·VP6·SorensonAAC·MP3·Speex(RTMP)
AVI.aviRIFF chunk(구식) DivX·Xvid·MJPEGMP3·AC-3
3GP.3gpatom/box (MP4 sub)H.263·H.264·MPEG-4AMR·AACTX3G
OGG.ogvOgg pageTheora·DaalaVorbis·Opus·FLACKate

MP4 box 구조 깊이 보기

isom
└─ ftyp (major_brand="isom", compatible="iso2 avc1 mp41")
└─ moov
   ├─ mvhd (movie header — duration, timescale)
   ├─ trak (video)
   │  ├─ tkhd (track header — width, height)
   │  ├─ edts/elst (edit list — cut/offset)
   │  └─ mdia/minf/stbl/...
   ├─ trak (audio)
   ├─ trak (subtitle)
   ├─ udta (user data — iTunes metadata, Dolby Vision config)
   └─ mvex (movie extends — fragmented MP4 indicator)
└─ moof (fragment) × N    ← Fragmented MP4의 경우
   └─ traf (track fragment)
└─ mdat (media data) × N

Fragmented MP4 (fMP4) — 스트리밍의 표준

전통적 MP4는 moov(인덱스)가 파일 끝 또는 시작에 한 번. 라이브 스트리밍에는 부적합 — 끝나기 전엔 인덱스를 못 쓴다.

Fragmented MP4는 파일을 moof + mdat 쌍의 프래그먼트로 쪼갠다:

[ftyp][moov(empty placeholder)]
[moof][mdat]   ← fragment 1 (예: 6초)
[moof][mdat]   ← fragment 2
[moof][mdat]   ← ...

→ 각 fragment가 독립적으로 디코드 가능. HLS·DASH·LL-HLS의 세그먼트 가 fMP4 (CMAF).

# fMP4 fragment 길이를 6초로 나눠 인코딩 (HLS/DASH 공통)
ffmpeg -i in.mp4 -c copy \
  -movflags +frag_keyframe+empty_moov+default_base_moof \
  -frag_duration 6000000 fragmented.mp4

faststart — moov를 앞으로

# moov를 파일 끝에서 앞으로 이동 (web 점진적 스트리밍 가능)
ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4
 
# 검증 (moov가 mdat보다 앞에 있어야 함)
mp4dump out.mp4 | head -20

→ moov가 끝에 있으면 브라우저는 파일 전체를 받아야 재생 시작. 앞에 있으면 첫 청크만 받아도 재생.

MKV가 인기 있는 이유

MKV (Matroska)는 EBML(Extensible Binary Meta Language) 기반 — XML의 바이너리 친척. 모든 코덱·자막·다중 트랙·챕터를 자유롭게 담는다:

[EBML header]
[Segment]
  [SeekHead]      ← top-level element 위치 인덱스
  [Info]          ← timecode scale, duration
  [Tracks]        ← 트랙 메타 (비디오·오디오·자막 N개)
  [Chapters]
  [Attachments]   ← 폰트·이미지 첨부
  [Cluster] × N   ← 실제 데이터 (블록 단위)
  [Cues]          ← seek index
  [Tags]          ← 메타데이터

→ 사용자/팬섭(fansub) 커뮤니티의 사실상 표준. 영화에 5개 자막·3개 음성을 묶을 수 있다.

MIME 타입과 codecs 파라미터

video/mp4; codecs="avc1.640028,mp4a.40.2"
                    ↑ H.264 High@4.0    ↑ AAC-LC
video/mp4; codecs="hvc1.1.6.L120.90,mp4a.40.2"
                    ↑ HEVC Main 4.0
video/mp4; codecs="av01.0.05M.08,opus"
                    ↑ AV1 Main Profile 5.0  ↑ Opus
video/webm; codecs="vp9,opus"
video/mp2t                                  ← MPEG-TS
application/vnd.apple.mpegurl               ← .m3u8 (HLS playlist)
application/dash+xml                        ← .mpd (DASH manifest)

MediaSource.isTypeSupported() 가 이 문자열로 디코더 가용성을 확인한다.


What-if — 잘못 다루면 어떻게 깨지는가

❌ 함정 1 — moov 앞으로 안 옮긴 MP4를 웹 점진 다운로드

증상: 5분 영상에서 끝까지 다운로드된 뒤에야 재생 시작. 원인: moov가 mdat 뒤에 있어서 브라우저가 인덱스를 못 읽음.

ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4

❌ 함정 2 — MOV를 그대로 웹에 올림

QuickTime MOV는 ProRes·DNxHD 같은 마스터링 코덱을 자주 담는다. 100MB/min 비트레이트. 브라우저는 ProRes 디코더가 없어서 재생 불가. 반드시 H.264/HEVC/AV1로 트랜스코드.

❌ 함정 3 — MKV를 iOS Safari로 송출

iOS Safari는 MKV/WebM 미지원 (Safari 16+에서 일부 WebM AV1 지원 시작). <video src="movie.mkv"> 는 그냥 안 뜬다. MP4 또는 HLS 권장.

❌ 함정 4 — fMP4와 일반 MP4를 같은 endpoint에서 혼용

CDN 캐시 키가 같아 fMP4 fragment이 일반 MP4와 충돌. 잘못된 byte를 받아 디코더 깨짐. 별도 path 또는 별도 cache key로 분리.

❌ 함정 5 — codecs 파라미터 누락

<source src="video.mp4" type="video/mp4">

는 통과되긴 하지만, 정확한 디코더 capability 확인이 안 된다 — 일부 브라우저는 fallback 결정에 codecs 파라미터를 본다.

<source src="video.mp4" type='video/mp4; codecs="avc1.640028,mp4a.40.2"'>
<source src="video.webm" type='video/webm; codecs="vp9,opus"'>

❌ 함정 6 — MPEG-TS를 progressive download로 사용

TS는 stream-oriented. byte-range seek이 비효율적이고 인덱스가 없다. 파일 다운로드용은 MP4, 라이브/세그먼트 스트리밍용은 TS 또는 fMP4.


Insight — 흥미로운 이야기

“MP4는 MOV의 후손이다”

1991년 Apple QuickTime의 atom/box 모델 → 1998년 ISO/IEC 14496-12 (ISO BMFF) → 1999년 MPEG-4 Part 14 (MP4). ISO BMFF는 MOV의 atom 구조를 거의 그대로 표준화한 것이라, MP4와 MOV는 box 단위 호환 — ffmpeg는 두 포맷을 동일 코드 패스로 다룬다. HEIF·CMAF·Dolby Vision도 모두 ISO BMFF 위에 만들어졌다.

“FLV는 죽었지만 RTMP는 안 죽었다”

FLV 컨테이너는 Flash Player의 사망(2020)으로 사실상 죽었지만, RTMP 프로토콜 위의 tag 구조는 OBS·Twitch·YouTube Live의 송출 측 표준으로 남아있다. “OBS → RTMP(FLV tags) → ingest server → HLS(fMP4) → viewer” 가 현대 라이브 파이프라인. 즉 FLV는 송출 와이어 프로토콜, MP4/HLS는 시청 측 포맷으로 갈렸다.

“MKV가 ISO 표준이 아닌 이유”

MKV는 1990년대 후반 러시아 개발자가 시작한 오픈 프로젝트(Matroska). ISO 표준에 들어가지 못한 건 너무 자유롭기 때문어떤 코덱도 담을 수 있다 는 게 표준화 입장에선 약점. 그러나 그 자유로움이 팬섭·고화질 아카이브 커뮤니티의 마음을 잡았다. Google이 WebM(MKV 부분집합)을 만들면서 제한된 MKV가 다시 웹 표준에 합류.

“CMAF는 HLS와 DASH를 통일하려 했다”

2017년 Apple·MS·Netflix·Akamai가 CMAF (Common Media Application Format) 표준화. 같은 fMP4 세그먼트를 HLS와 DASH 둘 다 가리킬 수 있게 — 인코딩·CDN 비용을 한 번만 내자는 야심. 결과: CMAF는 기술적으로 잘 되지만 DRM 차이(FairPlay vs Widevine)와 manifest 차이로 완전 통일은 미완. 그래도 fMP4 세그먼트의 표준은 사실상 CMAF.


요약 + Mermaid

컨테이너는 코덱을 포장하는 형식이며, atom/box (MP4 계열)와 stream (MPEG-TS) 두 가지 모델이 있다. MP4는 웹·OTT의 표준, MKV는 자유도 챔피언이지만 iOS 비호환, MPEG-TS는 라이브, WebM은 Google 친화. faststart로 moov를 앞으로, fMP4로 fragment 쪼개기, codecs 파라미터로 capability 명시 — 이 셋이 모던 컨테이너 운용의 90%.