7장. Low Latency를 가능하게 하는 데이터 구조 — CMAF
7.1 왜 데이터 구조까지 바뀌어야 했는가
6장에서 LL-HLS의 핵심은 “기다림을 줄이는 것“이었다.
- segment를 part로 나누고
- part는 만들어지는 즉시 전달
- polling 대신 blocking reload
이 중 가장 본질적인 변화는 이것이다.
완성된 파일이 아니라, 생성 중인 데이터를 전달한다
여기서 문제가 생긴다.
기존 HLS에서 사용하던 .ts 구조로는
이걸 깔끔하게 구현할 수 없다.
동작 방식의 변화는 데이터 형식의 변화를 요구한다.
7.2 TS는 왜 중간 전송이 어려운가
4장에서 이미 다뤘지만 다시 떠올려보자.
TS는 본래 방송 송출을 위해 만들어진 포맷이다.
- 188바이트 패킷이 끝없이 이어진다
- 한 프레임이 여러 패킷에 분산된다
- 디코딩 정보(PAT/PMT)는 주기적으로 반복된다
[PAT][PMT][V][V][A][V][A][PAT][PMT][V][V]...
이 구조에서 다음 시도를 한다고 해보자.
“지금 0.4초까지 만든 데이터를 일단 보내자”
문제는 이렇다.
- 마지막 프레임이 잘려 있을 수 있다
- PES 헤더와 본문이 분리되어 있을 수 있다
- PAT/PMT가 아직 이 부분에 안 왔을 수 있다
결국 받는 쪽은 디코딩에 실패한다.
그래서 TS에서는 완성된 segment 단위로만 안전하게 전송할 수 있었다.
7.3 fMP4가 LL-HLS를 가능하게 한 이유
4장에서 본 fMP4 구조를 다시 보자.
init.mp4 (ftyp + moov + mvex)
↑ 초기 정보
moof + mdat ← fragment 1
moof + mdat ← fragment 2
moof + mdat ← fragment 3
...
여기서 중요한 포인트가 두 개 있다.
7.3.1 moof + mdat의 독립성
moof + mdat 하나의 짝은
그 자체로 완성된 작은 영상이다.
각 fragment에는:
- 어떤 시간 구간인지 (
tfdt) - 몇 개의 샘플(프레임)이 들어있는지 (
trun) - 각 샘플의 크기와 오프셋
- 코덱은 init에서 받았으므로 별도 정보 불필요
즉 앞 fragment를 몰라도 이 짝만으로 재생이 가능하다.
이 구조 때문에 다음이 가능해진다.
- 전체 segment를 기다릴 필요 없음
- fragment 하나가 완성되면 즉시 전송 가능
- 받는 쪽도 받자마자 즉시 디코딩
7.3.2 init.mp4와 본체의 분리
fMP4는 초기 정보와 본체를 분리한다.
init.mp4 (한 번만 다운로드)
↓
moof + mdat
moof + mdat
moof + mdat ← 계속 추가
이 분리가 가져오는 효과:
- segment마다 코덱 정보 반복 불필요
- fragment 크기가 작아진다
- 새 segment를 처음부터 받아도 init만 있으면 즉시 재생
이건 HLS의 EXT-X-MAP 태그가 가리키는 것과 정확히 일치한다.
TS vs fMP4 한 줄 요약
TS: 전체가 하나의 흐름 fMP4: 완전한 조각들이 이어진 구조
이 차이 덕분에 “생성 중 전송“이 자연스럽게 가능해진다.
7.4 fMP4만으로 부족했던 이유
여기까지 보면 fMP4로 모든 문제가 풀린 것처럼 보인다.
하지만 실제 서비스에서는 또 다른 문제가 생긴다.
표준화의 부재
각자 방식대로 쪼개기 시작하면 호환이 깨진다.
- 어떤 인코더는 0.5초 단위
- 어떤 인코더는 1초 단위
- 어떤 인코더는 비트레이트가 다름
게다가 같은 fMP4여도
- HLS용 fMP4
- DASH용 fMP4
가 서로 미묘하게 달랐다.
예:
| 항목 | HLS fMP4 | DASH fMP4 |
|---|---|---|
| 박스 순서 | 약간 다름 | 약간 다름 |
| brand 코드 | mp42 등 | iso6 등 |
| 메타데이터 위치 | 차이 있음 | 차이 있음 |
이 차이 때문에
하나의 fMP4 파일을 두 프로토콜에서 그대로 쓸 수 없었다.
서비스 입장에서는 같은 영상을 두 번 인코딩하고 두 번 저장해야 했다.
저장 비용과 CDN 비용이 두 배가 된다.
7.5 CMAF가 해결한 것 ①: 쪼개는 단위
이 문제를 해결하기 위해 등장한 것이 CMAF (Common Media Application Format)이다.
ISO 표준으로 2018년 발표되었다.
CMAF는 새로운 구조가 아니다.
CMAF는 fMP4를 어떻게 사용할지 정의한 규칙이다.
세 가지를 표준화한다.
- 쪼개는 단위
- 전송 방식
- 호환성
데이터의 계층
CMAF는 데이터를 다음 계층으로 정의한다.
Track (예: 1080p 비디오 트랙)
└── Segment (플레이어가 인식하는 단위)
└── Fragment (내부 논리 단위)
└── Chunk (실제 전송 단위)
| 단위 | 역할 |
|---|---|
| Segment | URL로 요청되는 단위 |
| Fragment | moof+mdat 한 쌍 |
| Chunk | 가장 작은 전송 단위 |
용어 정리 (헷갈리는 부분)
| HLS 용어 | CMAF 용어 |
|---|---|
| Part | Chunk |
| Segment | Segment |
| (없음) | Fragment |
HLS의 Part와 CMAF의 Chunk는 사실상 같은 것이다. 서로 다른 표준이라 이름이 다를 뿐이다.
핵심 규칙
CMAF는 chunk에 강한 제약을 둔다.
각 chunk는 독립적으로 디코딩 가능해야 한다.
즉 chunk는 자기 안에 완전한 moof + mdat 쌍을 포함한다.
이 규칙 덕분에 중간부터 받아도 재생이 가능하다.
7.6 CMAF가 해결한 것 ②: 전송 방식
과거에는 fMP4를 써도 segment 전체를 다 만든 뒤 보내는 경우가 많았다.
CMAF는 이렇게 정의한다.
chunk가 만들어지면 즉시 전송해야 한다.
이때 사용하는 방식이 6장에서 본 HTTP Chunked Transfer Encoding이다.
[CMAF chunk 1 만들어짐] → 즉시 chunk 응답
[CMAF chunk 2 만들어짐] → 즉시 chunk 응답
[CMAF chunk 3 만들어짐] → 즉시 chunk 응답
...
[Segment 종료] → 응답 종료
데이터가 파일처럼 한 번에 내려오는 것이 아니라 스트림처럼 계속 흘러오게 된다.
LL-HLS와의 관계
LL-HLS는 CMAF의 chunk를 Part 단위로 m3u8에 노출한다.
#EXT-X-PART:DURATION=0.200,URI="seg100.0.m4s"
#EXT-X-PART:DURATION=0.200,URI="seg100.1.m4s"
같은 chunk를
DASH 측에서는 다른 방식으로 노출한다.
(LL-DASH의 availabilityTimeOffset)
데이터는 같고, 안내 방식만 다르다.
7.7 CMAF가 해결한 것 ③: 호환성
가장 큰 효과는 여기서 나온다.
CMAF는 fMP4 구조를 HLS와 DASH가 공유하도록 통일한다.
[ CMAF segment ]
↓
┌───┴────┐
HLS DASH
용 용
동일한 파일이 두 프로토콜에서 그대로 재생 가능하다.
서비스 입장에서의 이득
CMAF 이전:
원본 → HLS용 fMP4 (1080p, 720p, 480p)
→ DASH용 fMP4 (1080p, 720p, 480p)
[저장 2배, CDN 2배]
CMAF 이후:
원본 → CMAF (1080p, 720p, 480p)
↑ 한 벌만 생성
→ HLS playlist (m3u8)
→ DASH playlist (MPD)
↑ 매니페스트만 두 종류
저장 비용과 CDN 비용이 거의 절반으로 줄어든다.
7.8 Common Encryption (CENC)과 CMAF
CMAF의 또 다른 중요한 측면은 암호화 호환성이다.
DRM은 9장에서 자세히 다루지만, 여기서는 CMAF와의 관계만 짚는다.
과거의 문제
DRM 시스템마다 암호화 방식이 달랐다.
| 시스템 | 암호화 방식 |
|---|---|
| FairPlay (Apple) | cbcs (AES-CBC + samples) |
| Widevine (Google) | cenc 또는 cbcs |
| PlayReady (Microsoft) | cenc 또는 cbcs |
암호화 방식이 다르면 동일한 파일을 여러 DRM에서 못 쓴다.
CENC의 등장
Common Encryption (CENC)는 암호화 방식을 표준화한 ISO 규격이다.
CENC가 정의하는 두 방식:
cenc (AES-CTR 모드)
cbcs (AES-CBC + sample 단위)
CMAF는 이 중 cbcs를 권장한다.
모든 주요 DRM이 cbcs를 지원하기 때문이다.
CMAF + CENC의 효과
같은 CMAF 파일을 FairPlay, Widevine, PlayReady에서 모두 재생 가능하다.
원본 영상
→ CMAF + cbcs 암호화 (한 벌)
→ 라이선스만 DRM별로 발급
이전에는 DRM마다 별도 인코딩 + 별도 저장. 이제는 한 벌 + 라이선스 분리.
7.9 전체 흐름 정리
지금까지의 흐름을 정리하면 이렇다.
[HLS]
안정성을 위해 설계됨
→ 구조 때문에 지연 발생
[5장: 지연 분석]
encoder + segment + polling + buffer
[6장: LL-HLS]
→ "만들면서 전달하자"
→ Part, Blocking Reload, Preload Hint
[하지만 TS로는 구현 불가]
→ 데이터가 흐름 형태
[7장: fMP4 도입]
→ 독립 디코딩 가능한 조각 구조
→ 그러나 호환성 문제
[CMAF 표준화]
→ 쪼개는 단위 통일
→ 전송 방식 표준화
→ HLS와 DASH 호환
→ CENC로 DRM까지 통합
이 흐름의 끝에 우리가 있다.
비교표
| 구분 | 기존 HLS | LL-HLS + CMAF |
|---|---|---|
| 컨테이너 | MPEG-TS | fMP4 (CMAF) |
| 전송 단위 | Segment 전체 | Chunk 즉시 |
| 응답 방식 | 일반 HTTP | Chunked Transfer |
| 호환 표준 | HLS 전용 | HLS + DASH 공통 |
| 암호화 | AES-128 | CENC (cbcs) |
| 일반적 지연 | 6~10초 | 1~3초 |
7장 한 줄 정리
CMAF는 새로운 기술이 아니라 fMP4를 “어떻게 쓸지“를 약속한 규칙이며, 그 덕분에 데이터가 HLS와 DASH의 경계를 넘는다.