📁 File6. 스트리밍06 — Segmentation & Manifest 디자인

06 — Segmentation & Manifest 디자인

이 문서가 답하는 질문: segment는 몇 초가 정답인가? 그리고 GOP·키프레임 정렬은 왜 반드시 필요한가? 핵심 한 줄: segment 길이는 지연효율의 trade-off다. 그리고 segment 경계는 반드시 IDR이어야 한다 — 이 한 가정이 깨지면 모든 ABR 전환이 깨진다.


한 줄 답 (Pyramid Top)

Segment 길이는 짧을수록 지연 ↓ 오버헤드 ↑, *길수록 지연 ↑ 효율 ↑*의 단순한 trade-off다. 그리고 모든 segment의 첫 프레임은 IDR이어야 한다 — 그래야 다른 quality variant로 자유롭게 전환할 수 있다 (GOP-aligned).


Why — 왜 segment 길이가 결정 변수인가

스트리밍의 거의 모든 운영 결정이 segment 길이에서 갈린다.

영향짧은 segment (2s)긴 segment (10s)
라이브 지연6~10s25~40s
HTTP 요청 수 (1시간 영상)1800회360회
매니페스트 크기작음
GOP 압축 효율낮음 (IDR 자주)높음
시크 정밀도좋음거침
CDN 캐시 hit잘 됨 (작은 객체 → edge friendly)잘 됨 (요청 적음)
에러 발생 시 영향한 segment만 누락 (작음)한 segment 누락 = 10초 손실

짧으면 6배 더 많은 HTTP 요청, 길면 4배 더 많은 라이브 지연. 그래서 모두 중간(4~6초)에 수렴.


How — segment 길이별 실제 trade-off

2초 segment (라이브 빠름, VOD 흔치 않음)

  • 라이브 지연: 610초 (LL 없이도)
  • IDR 간격 = 2초 = 60 frames @30fps
  • H.264 비트레이트가 5~10% 증가 (IDR가 자주 → 압축 효율 ↓)

4초 segment

  • 라이브 지연: 1216초
  • 균형 — Twitch 등 빠른 라이브 OTT가 사용
  • IDR 간격 = 4초 = 120 frames

6초 segment (Apple HLS Authoring 권장)

  • 라이브 지연: 1824초
  • IDR 간격 = 6초 = 180 frames @30fps (또는 180~191 frame, 29.97fps)
  • VOD/일반 라이브 OTT의 default
  • HLS 기본값, 대다수의 인코더 preset

10초 segment (오래된 HLS, MPEG-TS)

  • 라이브 지연: 3040초
  • IDR 간격 = 10초 → 압축 효율 ↑
  • AES-128 키 회전과 잘 맞음 (10s 단위)
  • 신규 디자인에선 거의 사용 안 함

How — GOP-aligned segmentation (가장 중요한 contract)

모든 segment 첫 프레임이 IDR이어야 하나

비디오는 frame 사이의 예측으로 압축된다 (P-frame은 이전 frame을 참조, B-frame은 양방향). IDR(Instantaneous Decoder Refresh) 만이 자기 자신만으로 디코딩 가능한 frame.

ABR이 720p → 1080p로 전환할 때:

  1. 720p의 segment N까지 디코딩 완료.
  2. 1080p의 segment N+1을 받기 시작.
  3. 1080p segment N+1의 첫 frame이 IDR이 아니면 — 디코더는 없는 reference frame을 참조하려 한다 → 디코딩 실패 → 화면이 깜빡거나 멈춘다.

인코더 설정 (ffmpeg 예시)

ffmpeg -i input.mp4 \
  -c:v libx264 \
  -g 180                          # GOP size = 180 frames (= 6초 @ 30fps)
  -keyint_min 180                 # 최소 IDR 간격 = 180 (강제)
  -force_key_frames "expr:gte(t,n_forced*6)"  # 정확히 6초마다 IDR
  -sc_threshold 0                 # scene-change 자동 IDR 비활성 (의도치 않은 IDR 방지)
  -hls_time 6 \
  -hls_segment_type fmp4 \
  output/index.m3u8
옵션의미
-g 180GOP size 고정
-keyint_min 180IDR 사이 최소 간격 (보장)
-force_key_frames "expr:gte(t,n_forced*6)"6초 경계 시점마다 강제 IDR — segment 경계에 정확히 맞음
-sc_threshold 0scene change 감지 IDR 비활성화 (이게 켜져 있으면 임의 시점에 IDR이 추가되어 segment 경계와 어긋남)

모든 variant가 같은 -force_key_frames를 써야 segment 경계가 정확히 같은 시점. 이게 segmentAlignment="true"(DASH) / EXT-X-INDEPENDENT-SEGMENTS(HLS)의 전제.

MediaConvert / Bento4 / Shaka Packager의 동치 설정

도구GOP-aligned segmentation 옵션
AWS MediaConvert”GOP Size” + “Segment Length” + “Cadence Adapt”
Bento4 (mp4fragment)--fragment-duration 6000
Shaka Packager--segment_duration 6 (그리고 인코더가 이미 IDR을 정렬했다고 가정)
ffmpeg HLS muxer-hls_time 6 (실제 GOP가 정렬되어야 정확하게 작동)

packager는 자르기만 한다. IDR 정렬은 인코더가 보장해야 한다.


What — Discontinuity (HLS의 segment 끊어내기)

서로 다른 인코딩 세션·codec·해상도의 segment를 한 매니페스트에 이어 붙여야 할 때 (광고 삽입, 재인코딩, 라이브 stitching).

#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-DISCONTINUITY-SEQUENCE:0

#EXTINF:6.0,
content_001.m4s
#EXTINF:6.0,
content_002.m4s

#EXT-X-DISCONTINUITY                ← 여기서부터 새 인코딩
#EXT-X-PROGRAM-DATE-TIME:2026-05-10T10:30:00Z
#EXT-X-MAP:URI="ad_init.mp4"        ← 새 init segment

#EXTINF:5.0,
ad_001.m4s
#EXTINF:5.0,
ad_002.m4s
#EXTINF:5.0,
ad_003.m4s

#EXT-X-DISCONTINUITY                ← 광고 끝 → 본편 복귀
#EXT-X-MAP:URI="content_init.mp4"

#EXTINF:6.0,
content_003.m4s
...
태그효과
#EXT-X-DISCONTINUITY다음 segment에서 디코더 완전 reset (codec/해상도 변경 가능)
#EXT-X-MAP새 init segment 지정
#EXT-X-PROGRAM-DATE-TIMEwall-clock 표시 (광고 추적용)

DASH의 Period 경계와 동일 개념. CMAF에서는 Period 경계 = HLS의 Discontinuity 경계가 자연스럽게 일치한다.

#EXT-X-DISCONTINUITY-SEQUENCE

라이브에서 sliding window가 굴러가다 과거 discontinuity가 잘려 나가면, 플레이어가 누적 discontinuity를 잃는다 → EXT-X-DISCONTINUITY-SEQUENCE가 그 누적 카운트.


What — segment 파일 명명과 운영 주의

명명 규칙 (운영 친화적)

private/{contentId}/hls/
├── master.m3u8
├── 1080p/
│   ├── index.m3u8
│   ├── init.mp4
│   ├── seg_00001.m4s
│   ├── seg_00002.m4s
│   └── ...
├── 720p/
│   ├── index.m3u8
│   ├── init.mp4
│   └── seg_*.m4s
└── ...

변형 1: master_variant_360p_00001.ts — 한 디렉터리에 평탄화 (AWS MediaConvert HLS group의 default). 변형 2: 위처럼 resolution별 디렉터리 — 운영 시 s3 ls 직관적.

핵심 contract (운영 측면)

  1. Variant naming convention은 후속 파이프라인의 contract가 된다. 예: thumbnail sprite를 360p variant에서만 추출한다면 — master_variant_360p_*.ts라는 prefix가 코드의 가정이 된다. 이름 규칙을 바꾸면 후속 단계가 깨진다.
  2. init segment URL은 한 영상의 평생 동안 불변이어야 한다. CDN 캐시 정책에 영구 캐시 + immutable 권장.
  3. segment URL은 한 영상의 평생 동안 불변. cache-control: max-age=31536000 (1년).
  4. 매니페스트 URL은 짧은 캐시. cache-control: max-age=210 (라이브) / max-age=3003600 (VOD).

What — 매니페스트 캐시 정책 (CDN 측)

자원TTL이유
master.m3u8 (VOD)1h ~ 1d한 번 만들어지면 거의 안 바뀜
master.m3u8 (Live)2~5ssliding window
index.m3u8 (VOD variant)1h ~ 1d동일
index.m3u8 (Live variant)2~5ssliding window
init.mp4immutable, 1y평생 불변
seg_*.m4s (VOD)immutable, 1y한 번 발행 후 불변
seg_*.m4s (Live)immutable, 1y발행 후 불변 (window에서 빠질 뿐)

CloudFront/Akamai 모두 매니페스트만 짧은 TTL, segment는 영원에 가까운 TTL.


What-if — 잘못 만들면 어떻게 깨지는가

증상원인처방
ABR 전환 시 화면 깜빡GOP 미정렬 (variant마다 IDR 위치 다름)모든 variant에 동일한 force_key_frames
시크가 segment 경계로 튐INDEPENDENT-SEGMENTS 플래그 없음매니페스트에 #EXT-X-INDEPENDENT-SEGMENTS 추가
마지막 segment가 짧아 가끔 stallEXTINF 정수 반올림EXTINF를 소수점 6자리까지 정확히
광고 삽입 후 디코더 죽음Discontinuity 누락#EXT-X-DISCONTINUITY + 새 #EXT-X-MAP
라이브 sliding window 갱신 시 segment URL 충돌sequence 카운터 reset절대 reset 금지, monotonic 유지
segment 길이 다양 → buffer 예측 어려움가변 GOP (scene change IDR)-sc_threshold 0
CDN miss 폭주매니페스트 TTL이 너무 짧고 변경이 잦음매니페스트 갱신 주기 = TTL 일치
light variant init이 재발행되어 4xx 폭주인코딩 재시작 시 init MD5 변경init은 한 번 발행 후 재인코딩하지 않음

Insight — 흥미로운 이야기

“왜 6초가 default인가”

Apple HLS Authoring Spec(2009 초기)이 10초로 시작 → 2016년 6초로 권장 변경. 이유는 셋:

  1. 모바일 기기의 디코더 성능이 좋아져 IDR을 자주 박아도 압축 비용 부담이 줄었다.
  2. 시크 응답이 더 중요해졌다 (모바일 short-form의 시크 빈도 ↑).
  3. 라이브 지연이 더 중요해졌다 (스포츠 OTT 대두). 4초 가도 되지만 압축 효율 손실이 6초 대비 명백.

“GOP-aligned는 단순한 contract지만 깨면 우주가 무너진다”

GOP 정렬 한 번 잘못되면 — ABR 전환 깜빡, 시크 실패, 광고 삽입 디코더 크래시, 자막 sync 어긋남 — 모든 증상이 한 원인에서 나온다. 운영 incident의 근본 원인 중 가장 흔한 것.

“광고 삽입은 사실 Discontinuity로 짠 곡예다”

SCTE-35 marker가 광고 삽입 시점을 알려주면, manifest manipulator(server-side)가 그 시점에 #EXT-X-DISCONTINUITY를 박고 광고 segment를 끼워넣는다. 이게 server-side ad insertion (SSAI). 반대로 client-side ad insertion (CSAI)은 플레이어가 영상 일시정지하고 광고 따로 재생. SSAI가 광고 차단 우회에 강한 이유는 광고와 본편이 같은 stream에 섞여 있어 구분이 어렵기 때문.

“Frame-accurate seek는 단순한 게 아니다”

segment 경계는 IDR이지만 그 사이는 P/B-frame이다. 플레이어가 segment 중간 시각에 시크하려면 segment를 처음부터 디코딩해서 그 시각까지 progress해야 한다. 그래서 시크의 최악 지연은 segment 길이만큼. 6초 segment면 최악 6초 디코딩이 필요. 빠른 시크가 중요하면 segment를 짧게 — 또는 trick play용 I-frame only playlist(#EXT-X-I-FRAME-STREAM-INF)를 추가.


한 단락 요약 + Mermaid

Segmentation은 길이경계 정렬의 두 결정이다. 길이는 4~6초가 default — 짧으면 지연/오버헤드, 길면 효율/지연 trade-off. 경계는 반드시 모든 variant에서 IDR로 정렬 — 이 한 가정이 ABR/시크/광고 삽입 전체의 contract. 그리고 segment URL은 영원히 불변, 매니페스트만 짧은 TTL — 캐시 정책의 핵심.

→ 다음 파일 07-drm.md: 만들어진 segment를 누가 풀어주는가.