3장. HLS 구조와 Playlist(m3u8) 완전 해부
3.1 HLS의 3대 구성요소
2장에서 HLS는 영상을 조각으로 나눈다고 했다.
하지만 단순히 파일을 나누는 것만으로는 스트리밍이 동작하지 않는다.
플레이어는 다음을 알아야 한다.
- 어떤 화질이 존재하는가
- 어떤 순서로 재생해야 하는가
- 다음에 무엇을 가져와야 하는가
이를 해결하는 구조가 HLS의 3대 구성요소다.
Playlist + Segment + Player
↓ ↓ ↓
지도 실 영상 엔진
이 중 가장 중요한 것은 Playlist (m3u8)이다.
왜냐하면
플레이어는 실제 영상이 아니라 “playlist를 기준으로” 동작하기 때문이다
3.2 실제 HLS 디렉토리 구조
이론보다 실제 구조를 보는 것이 이해가 빠르다.
hls/
├── master.m3u8
├── 1080p/
│ ├── index.m3u8
│ ├── init.mp4 (fMP4 사용 시)
│ ├── seg100.ts
│ ├── seg101.ts
│ └── ...
├── 720p/
│ ├── index.m3u8
│ ├── seg100.ts
│ └── ...
├── 480p/
│ ├── index.m3u8
│ └── ...
├── audio/ (오디오 트랙 분리 시)
│ ├── ko/index.m3u8
│ └── en/index.m3u8
└── subtitle/
├── ko.m3u8
└── en.m3u8
구조 특징
- 화질별로 디렉토리가 분리됨
- 동일한 시간 구간이 여러 화질로 존재
- 오디오/자막은 별도 트랙으로 분리 가능
같은 번호의 segment는 같은 시간 구간을 의미한다.
1080p/seg100.ts
720p/seg100.ts
480p/seg100.ts
이 구조 덕분에:
플레이어는 같은 시점에서 화질을 자유롭게 변경할 수 있다 (ABR)
3.3 Master Playlist 완전 분석
Master Playlist는 “이 영상에 어떤 화질/트랙이 있는가“를 알려주는 카탈로그 역할을 한다.
(공식 명칭은 Multivariant Playlist다. 실무에서는 master playlist라고 더 많이 부른다.)
기본 예시
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
1080p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3000000,RESOLUTION=1280x720,CODECS="avc1.4d401f,mp4a.40.2"
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=640x360,CODECS="avc1.42c01e,mp4a.40.2"
480p/index.m3u8
플레이어는 이 파일을 보고 사용 가능한 화질 목록을 알게 된다.
3.3.1 EXT-X-STREAM-INF의 속성들
#EXT-X-STREAM-INF는
각 화질(variant)의 메타데이터를 담는다.
| 속성 | 의미 |
|---|---|
| BANDWIDTH | 최대 비트레이트 (bps), 필수 |
| AVERAGE-BANDWIDTH | 평균 비트레이트 |
| RESOLUTION | 해상도 (가로x세로) |
| CODECS | 코덱 식별자 |
| FRAME-RATE | 프레임레이트 |
| HDCP-LEVEL | DRM 출력 보호 레벨 |
| VIDEO-RANGE | SDR / HDR10 / PQ |
| AUDIO | 연결된 오디오 GROUP-ID |
| SUBTITLES | 연결된 자막 GROUP-ID |
| CLOSED-CAPTIONS | 연결된 캡션 GROUP-ID |
BANDWIDTH는 ABR이 화질을 고르는 핵심 기준이다.
플레이어는 현재 네트워크 속도와 BANDWIDTH를 비교해서 다음 화질을 선택한다.
3.3.2 EXT-X-MEDIA로 정의하는 대체 렌디션
영상에는 비디오 외에도 여러 오디오, 자막이 있을 수 있다.
이를 표현하는 태그가
#EXT-X-MEDIA다.
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",NAME="Korean",
LANGUAGE="ko",DEFAULT=YES,AUTOSELECT=YES,
URI="audio/ko/index.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",NAME="English",
LANGUAGE="en",DEFAULT=NO,AUTOSELECT=YES,
URI="audio/en/index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",NAME="Korean",
LANGUAGE="ko",DEFAULT=YES,
URI="subtitle/ko.m3u8"
주요 속성:
| 속성 | 의미 |
|---|---|
| TYPE | AUDIO / VIDEO / SUBTITLES / CLOSED-CAPTIONS |
| GROUP-ID | 같은 그룹으로 묶이는 식별자 |
| NAME | 사용자에게 보이는 이름 |
| LANGUAGE | 언어 코드 (BCP 47) |
| DEFAULT | 기본 선택 여부 |
| AUTOSELECT | 자동 선택 가능 여부 |
| URI | 실제 playlist 경로 |
3.3.3 GROUP-ID로 묶이는 트랙 구조
GROUP-ID는 매우 중요한 개념이다.
비디오 variant는 자신과 연결될 오디오/자막 그룹을 명시한다.
#EXT-X-STREAM-INF:BANDWIDTH=5000000,
RESOLUTION=1920x1080,
AUDIO="aud1", ← 오디오 그룹 연결
SUBTITLES="sub1" ← 자막 그룹 연결
1080p/index.m3u8
이 구조의 의미는 이렇다.
비디오는 한 줄, 오디오/자막은 별도 트랙으로 묶고, GROUP-ID로 연결한다.
이를 통해 같은 영상에 한국어/영어 오디오를 동시에 제공하거나 자막을 따로 토글할 수 있다.
3.3.4 EXT-X-I-FRAME-STREAM-INF
이 태그는 탐색/썸네일용 I-Frame 전용 playlist를 정의한다.
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=200000,
RESOLUTION=640x360,
CODECS="avc1.42c01e",
URI="iframe/index.m3u8"
왜 필요한가
영상에서 임의 위치로 점프할 때 플레이어는 keyframe(I-Frame)부터 디코딩해야 한다.
I-Frame만 모아둔 별도 playlist가 있으면:
- 탐색이 빠르다
- 썸네일 미리보기를 만들 수 있다
- trick play (배속, 역재생)에 활용된다
3.3.5 EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-INDEPENDENT-SEGMENTS
이 태그가 있으면 다음을 의미한다.
모든 segment는 자기 자신만으로 디코딩이 가능하다 (즉, 각 segment가 keyframe으로 시작한다)
이 보장이 있어야 ABR 전환이 어디서든 깨끗하게 일어난다.
3.4 Media Playlist의 주요 태그
Media Playlist는 실제 segment의 재생 순서를 정의하는 파일이다.
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:100
#EXT-X-MAP:URI="init.mp4"
#EXTINF:2.000,
seg100.m4s
#EXTINF:2.000,
seg101.m4s
#EXTINF:2.000,
seg102.m4s
3.4.1 EXT-X-VERSION
#EXT-X-VERSION:7
플레이어가 이 playlist를 해석하기 위해 필요한 최소 HLS 프로토콜 버전을 선언한다.
| 버전 | 추가된 기능 |
|---|---|
| 3 | 부동소수 EXTINF |
| 4 | EXT-X-BYTERANGE |
| 6 | EXT-X-MAP |
| 7 | fMP4 (mp4 segment) |
| 9 | EXT-X-PART (LL-HLS) |
3.4.2 EXT-X-TARGETDURATION
#EXT-X-TARGETDURATION:2
segment 최대 길이 (초).
- 짧을수록 지연 감소
- 길수록 요청 횟수 감소
Latency vs 효율의 트레이드오프
플레이어는 이 값을 기준으로 playlist를 얼마나 자주 다시 가져올지를 결정한다.
3.4.3 EXT-X-MEDIA-SEQUENCE
#EXT-X-MEDIA-SEQUENCE:100
현재 playlist의 첫 segment 번호.
라이브 스트리밍에서는 시간이 지나면서 이 값이 증가한다.
T+0초 : MEDIA-SEQUENCE=100, segments [100,101,102]
T+2초 : MEDIA-SEQUENCE=101, segments [101,102,103]
T+4초 : MEDIA-SEQUENCE=102, segments [102,103,104]
플레이어는 이 번호로 현재 재생 위치를 추적한다.
3.4.4 EXTINF
#EXTINF:2.000,
seg100.m4s
- 다음 segment의 재생 길이 (초)
- 그 다음 줄에 segment URI
길이는 TARGETDURATION 이하여야 한다.
3.4.5 EXT-X-MAP (Initialization Segment)
#EXT-X-MAP:URI="init.mp4"
fMP4를 사용할 때 등장하는 태그.
init.mp4는 실제 영상 데이터를 담고 있지 않다.
대신:
- 코덱 정보
- 트랙 메타데이터
- 디코더 초기화 데이터
가 들어있다.
플레이어는 init.mp4를 먼저 다운로드한 뒤 각 segment를 재생한다.
(TS 포맷은 init segment가 필요 없다. 각 TS segment에 PAT/PMT가 포함되어 있기 때문이다. 자세한 건 4.2절에서 다룬다.)
3.4.6 EXT-X-BYTERANGE
#EXTINF:2.000,
#EXT-X-BYTERANGE:1048576@0
big.mp4
#EXTINF:2.000,
#EXT-X-BYTERANGE:1048576@1048576
big.mp4
하나의 큰 파일을 바이트 범위로 잘라서 사용할 수 있다.
이렇게 하면:
- 파일 수가 줄어든다
- 서버 관리가 편해진다
- HTTP Range 요청으로 부분만 가져온다
3.5 VOD와 Live의 구분
같은 m3u8 형식이지만 VOD와 Live는 동작이 다르다.
이를 구분하는 태그들이 있다.
3.5.1 EXT-X-PLAYLIST-TYPE
#EXT-X-PLAYLIST-TYPE:VOD
값은 두 가지.
| 값 | 의미 |
|---|---|
| VOD | 완성된 영상. playlist 변경 없음 |
| EVENT | 추가만 가능 (라이브 녹화) |
이 태그가 없으면 일반 라이브로 간주된다. (playlist가 sliding window로 갱신됨)
3.5.2 EXT-X-ENDLIST
#EXT-X-ENDLIST
더 이상 segment가 추가되지 않음을 의미한다.
- VOD의 마지막
- 종료된 라이브 (생방송 끝)
이 태그를 보면 플레이어는 playlist를 더 이상 다시 요청하지 않는다.
3.5.3 EXT-X-DISCONTINUITY
#EXTINF:2.000,
seg100.ts
#EXT-X-DISCONTINUITY
#EXTINF:2.000,
ad_001.ts
다음 segment가 이전 segment와 연속성이 끊긴다는 표시.
주로 다음 상황에서 사용된다.
- 광고 삽입 (SSAI)
- 코덱/해상도 변경
- 타임스탬프 불연속
플레이어는 이 태그를 보면 디코더를 리셋한다.
3.6 플레이어 동작 흐름
플레이어는 다음 과정을 반복한다.
1. master.m3u8 요청
2. 화질/오디오/자막 선택
3. 각 트랙의 index.m3u8 요청
4. (필요시) init.mp4 요청
5. segment 다운로드
6. 디코딩 + 재생
7. playlist 재요청 (라이브의 경우)
8. 다음 segment 결정 → 5번으로 돌아감
핵심 특징:
플레이어는 playlist를 계속 다시 읽는다
이 polling 구조가 5장에서 다룰 지연의 근본 원인 중 하나다.
3.7 Live Streaming과 Sliding Window
라이브에서는 playlist가 계속 변경된다.
시점 T+0
#EXT-X-MEDIA-SEQUENCE:100
#EXTINF:2.0,
seg100.ts
#EXTINF:2.0,
seg101.ts
#EXTINF:2.0,
seg102.ts
시점 T+2초
#EXT-X-MEDIA-SEQUENCE:101
#EXTINF:2.0,
seg101.ts
#EXTINF:2.0,
seg102.ts
#EXTINF:2.0,
seg103.ts
특징
- 오래된 segment는 목록에서 제거된다
- 새로운 segment가 끝에 추가된다
- 총 segment 개수는 일정하게 유지된다
이 구조를 Sliding Window라고 한다.
항상 최신 영상만 유지한다
뒤로 무한정 되감기는 불가능하다. (라이브 DVR 기능은 별도 설계가 필요하다)
3.8 HLS 구조의 본질
이제 전체를 정리하면 이렇다.
서버의 역할
- playlist 제공
- segment 제공
- (필요시) init segment 제공
플레이어의 역할
- playlist 분석
- 다음 segment 계산
- 직접 요청
- 디코딩 및 재생
핵심:
HLS는 서버가 보내는 구조가 아니라 클라이언트가 가져오는 구조다 (pull-based)
3.9 이 구조가 만드는 장점과 단점
장점
- ABR이 자연스럽게 가능
- CDN 캐싱 활용 가능
- 네트워크 안정성
- 인프라 비용 절감
단점
- polling 구조 → 지연 발생
- segment 단위 → 즉시성 부족
- playlist 갱신 주기에 의존
3장 한 줄 정리
HLS는 playlist라는 지도와 segment라는 조각으로 이루어지며, 플레이어는 그 지도를 보고 스스로 길을 찾아가는 구조다.