H.264 코덱 완벽 가이드 | NAL·SPS·PPS·프로파일·레벨 심층 분석
이 글의 핵심
H.264 코덱 완벽 가이드. NAL Unit, SPS, PPS 구조부터 프로파일, 레벨, 비트스트림 분석, 실전 인코딩까지 기초부터 실무까지 완벽 정리.
들어가며
H.264 (AVC, MPEG-4 Part 10)는 가장 널리 사용되는 비디오 코덱입니다. YouTube, Netflix, Zoom 등 대부분의 영상 서비스가 H.264를 사용합니다.
비유로 말씀드리면, H.264는 영상 압축의 만능 열쇠입니다. H.265가 더 효율적이지만, H.264는 모든 기기에서 작동하고 특허 문제가 없어 여전히 표준입니다.
이 글을 읽으면
- H.264의 구조와 원리를 이해합니다
- NAL Unit, SPS, PPS 등 헤더 정보를 파악합니다
- 프로파일과 레벨을 선택할 수 있습니다
- 실전 인코딩과 디코딩을 구현합니다
목차
- H.264 기초
- NAL Unit 구조
- SPS (Sequence Parameter Set)
- PPS (Picture Parameter Set)
- Slice와 Macroblock
- 프로파일과 레벨
- 비트스트림 분석
- 실전 인코딩
- 왜 아직도 H.264인가
- 트러블슈팅
- 마무리
H.264 기초
H.264란?
H.264 (Advanced Video Coding)는 2003년 ITU-T와 ISO/IEC가 공동 개발한 비디오 압축 표준입니다.
별칭:
- H.264 (ITU-T 명칭)
- AVC (Advanced Video Coding)
- MPEG-4 Part 10
H.264의 압축 원리
1. 공간적 중복 제거 (Intra Prediction)
원본 프레임:
■■■■■■■■
■■■■■■■■
■■■■■■■■
인접 픽셀이 비슷함 → 차이만 저장
→ 압축!
2. 시간적 중복 제거 (Inter Prediction)
프레임 1: 공이 왼쪽에
프레임 2: 공이 오른쪽으로 이동
전체 프레임 저장 X
→ "공이 오른쪽으로 10픽셀 이동" 만 저장
→ 압축!
3. 변환 및 양자화 (DCT + Quantization)
픽셀 값 → 주파수 변환 (DCT)
→ 중요하지 않은 고주파 제거 (Quantization)
→ 압축!
H.264 프레임 타입
I-Frame (Intra Frame)
- 독립적인 완전한 프레임
- 압축률 낮음, 용량 큼
- 시크(Seek) 포인트
P-Frame (Predicted Frame)
- 이전 프레임 참조
- 압축률 중간
- I-Frame보다 작음
B-Frame (Bi-directional Frame)
- 이전 + 이후 프레임 참조
- 압축률 높음, 용량 작음
- 인코딩 복잡
GOP (Group of Pictures):
I P P P P P P P I P P P P P P P
I: 키프레임 (독립적)
P: 이전 프레임 참조
B: 양방향 참조 (선택적)
NAL Unit 구조
NAL Unit이란?
NAL (Network Abstraction Layer) Unit은 H.264 비트스트림의 기본 단위입니다.
구조:
[Start Code] [NAL Header] [NAL Payload]
3-4 bytes 1 byte 가변 길이
Start Code
Annex B 포맷:
0x00 0x00 0x00 0x01 (4 bytes, 일반적)
또는
0x00 0x00 0x01 (3 bytes, 짧은 형식)
AVCC 포맷 (MP4):
[Length] [NAL Data]
4 bytes 가변 길이
Length: NAL Unit 크기 (빅 엔디안)
NAL Header (1 byte)
7 6 5 4 3 2 1 0
┌─┬─────┬─────────┐
│F│ NRI │ Type │
└─┴─────┴─────────┘
F (Forbidden Zero Bit): 항상 0
NRI (NAL Ref IDC): 중요도 (0-3)
Type (NAL Unit Type): NAL 타입 (0-31)
NAL Unit 타입
| 타입 | 이름 | 설명 |
|---|---|---|
| 0 | Unspecified | 미정의 |
| 1 | Coded Slice (Non-IDR) | P/B 프레임 슬라이스 |
| 2 | Coded Slice (Partition A) | 데이터 분할 A |
| 3 | Coded Slice (Partition B) | 데이터 분할 B |
| 4 | Coded Slice (Partition C) | 데이터 분할 C |
| 5 | Coded Slice (IDR) | I 프레임 (키프레임) |
| 6 | SEI (Supplemental Enhancement) | 부가 정보 |
| 7 | SPS | 시퀀스 파라미터 세트 |
| 8 | PPS | 픽처 파라미터 세트 |
| 9 | Access Unit Delimiter | AU 구분자 |
| 10 | End of Sequence | 시퀀스 종료 |
| 11 | End of Stream | 스트림 종료 |
| 12 | Filler Data | 채우기 데이터 |
예시:
0x67: NAL Type 7 (SPS)
Binary: 0110 0111
F: 0
NRI: 11 (중요도 3, 최고)
Type: 00111 (7, SPS)
0x68: NAL Type 8 (PPS)
Binary: 0110 1000
F: 0
NRI: 11 (중요도 3)
Type: 01000 (8, PPS)
0x65: NAL Type 5 (IDR)
Binary: 0110 0101
F: 0
NRI: 11 (중요도 3)
Type: 00101 (5, IDR 프레임)
0x41: NAL Type 1 (Non-IDR)
Binary: 0100 0001
F: 0
NRI: 10 (중요도 2)
Type: 00001 (1, P/B 프레임)
SPS (Sequence Parameter Set)
SPS란?
SPS는 전체 비디오 시퀀스의 설정을 담고 있습니다. 디코더가 영상을 해석하는 데 필수적입니다.
포함 정보:
- 프로파일 (Profile)
- 레벨 (Level)
- 해상도 (Width, Height)
- 프레임 레이트
- 색상 정보
- 비트 깊이
SPS 구조
SPS NAL Unit:
[Start Code] [NAL Header (0x67)] [SPS Data]
SPS Data (비트 단위):
- profile_idc (8 bits)
- constraint_set_flags (8 bits)
- level_idc (8 bits)
- seq_parameter_set_id (ue(v))
- chroma_format_idc (ue(v))
- bit_depth_luma_minus8 (ue(v))
- bit_depth_chroma_minus8 (ue(v))
- log2_max_frame_num_minus4 (ue(v))
- pic_order_cnt_type (ue(v))
- max_num_ref_frames (ue(v))
- pic_width_in_mbs_minus1 (ue(v))
- pic_height_in_map_units_minus1 (ue(v))
- frame_mbs_only_flag (1 bit)
- ...
ue(v): Exp-Golomb 가변 길이 인코딩
SPS 예시 분석
SPS 바이트:
00 00 00 01 67 64 00 1F AC D9 40 50 05 BB 01 6A 02 02 02 80 00 00 03 00 80 00 00 19 47 8C 18 CB
분석:
00 00 00 01: Start Code
67: NAL Header (Type 7, SPS)
64: profile_idc = 100 (High Profile)
00: constraint_set_flags
1F: level_idc = 31 (Level 3.1)
AC D9 40 50 05 BB 01 6A 02 02 02 80 ...
→ 비트 단위로 파싱:
seq_parameter_set_id = 0
chroma_format_idc = 1 (4:2:0)
bit_depth_luma_minus8 = 0 (8-bit)
bit_depth_chroma_minus8 = 0 (8-bit)
log2_max_frame_num_minus4 = 0 (max_frame_num = 16)
pic_order_cnt_type = 0
max_num_ref_frames = 4
pic_width_in_mbs_minus1 = 79 (width = 1280)
pic_height_in_map_units_minus1 = 44 (height = 720)
SPS 추출 (FFmpeg)
# SPS/PPS 추출
ffmpeg -i input.mp4 -c copy -bsf:v h264_mp4toannexb -f h264 - | \
xxd | head -50
# 또는 MediaInfo로 확인
mediainfo input.mp4 --Details=1
PPS (Picture Parameter Set)
PPS란?
PPS는 각 픽처(프레임)의 설정을 담고 있습니다. SPS보다 자주 변경될 수 있습니다.
포함 정보:
- Entropy 코딩 모드 (CAVLC/CABAC)
- 슬라이스 그룹 정보
- 양자화 파라미터
- 디블로킹 필터 설정
PPS 구조
PPS NAL Unit:
[Start Code] [NAL Header (0x68)] [PPS Data]
PPS Data:
- pic_parameter_set_id (ue(v))
- seq_parameter_set_id (ue(v))
- entropy_coding_mode_flag (1 bit)
→ 0: CAVLC, 1: CABAC
- pic_order_present_flag (1 bit)
- num_slice_groups_minus1 (ue(v))
- num_ref_idx_l0_active_minus1 (ue(v))
- num_ref_idx_l1_active_minus1 (ue(v))
- weighted_pred_flag (1 bit)
- weighted_bipred_idc (2 bits)
- pic_init_qp_minus26 (se(v))
- deblocking_filter_control_present_flag (1 bit)
- ...
PPS 예시
00 00 00 01 68 EE 3C 80
00 00 00 01: Start Code
68: NAL Header (Type 8, PPS)
EE 3C 80:
→ 비트 단위 파싱:
pic_parameter_set_id = 0
seq_parameter_set_id = 0
entropy_coding_mode_flag = 1 (CABAC)
pic_order_present_flag = 0
num_slice_groups_minus1 = 0
num_ref_idx_l0_active_minus1 = 3
num_ref_idx_l1_active_minus1 = 1
weighted_pred_flag = 0
weighted_bipred_idc = 0
pic_init_qp_minus26 = 0 (QP = 26)
deblocking_filter_control_present_flag = 1
Slice와 Macroblock
Slice란?
Slice는 프레임을 독립적으로 디코딩 가능한 단위로 나눈 것입니다.
Slice 타입:
| 타입 | 이름 | 설명 |
|---|---|---|
| I | Intra Slice | I-Frame (독립적) |
| P | Predicted Slice | P-Frame (이전 참조) |
| B | Bi-directional Slice | B-Frame (양방향 참조) |
| SI | Switching I | 스위칭용 I |
| SP | Switching P | 스위칭용 P |
Slice 구조
Slice NAL Unit:
[Start Code] [NAL Header] [Slice Header] [Slice Data]
Slice Header:
- first_mb_in_slice (ue(v))
- slice_type (ue(v))
- pic_parameter_set_id (ue(v))
- frame_num (u(v))
- pic_order_cnt_lsb (u(v))
- ...
Slice Data:
- Macroblock 1
- Macroblock 2
- ...
Macroblock이란?
Macroblock은 16×16 픽셀 블록으로, H.264의 기본 처리 단위입니다.
프레임 (1920×1080):
→ 120 × 68 = 8,160개의 Macroblock
각 Macroblock:
- 16×16 Luma (Y)
- 8×8 Chroma (Cb, Cr) × 2
Macroblock 파티션:
16×16 (전체)
┌────────────────┐
│ │
│ │
│ │
│ │
└────────────────┘
16×8 (상하 분할)
┌────────────────┐
│ │
├────────────────┤
│ │
└────────────────┘
8×16 (좌우 분할)
┌────────┬───────┐
│ │ │
│ │ │
│ │ │
│ │ │
└────────┴───────┘
8×8 (4분할)
┌────────┬───────┐
│ │ │
├────────┼───────┤
│ │ │
└────────┴───────┘
프로파일과 레벨
프로파일 (Profile)
프로파일은 사용 가능한 기능의 집합입니다.
주요 프로파일:
| 프로파일 | profile_idc | 특징 | 용도 |
|---|---|---|---|
| Baseline | 66 | B-Frame 없음, CAVLC | 모바일, 화상 통화 |
| Main | 77 | B-Frame, CABAC | 방송, 스트리밍 |
| High | 100 | 8×8 변환, 양자화 | Blu-ray, HDTV |
| High 10 | 110 | 10-bit | 전문가용 |
| High 4:2:2 | 122 | 4:2:2 색상 | 방송 스튜디오 |
| High 4:4:4 | 244 | 4:4:4 색상, 무손실 | 전문가용 |
프로파일 선택:
모바일/WebRTC: Baseline
YouTube/Netflix: Main 또는 High
Blu-ray: High
전문가: High 10 이상
레벨 (Level)
레벨은 해상도, 프레임 레이트, 비트레이트 제한을 정의합니다.
주요 레벨:
| 레벨 | level_idc | 최대 해상도 | 최대 fps | 최대 비트레이트 |
|---|---|---|---|---|
| 3.0 | 30 | 720×576 | 30 | 10 Mbps |
| 3.1 | 31 | 1280×720 | 30 | 14 Mbps |
| 4.0 | 40 | 1920×1080 | 30 | 20 Mbps |
| 4.1 | 41 | 1920×1080 | 30 | 50 Mbps |
| 5.0 | 50 | 2560×1920 | 30 | 135 Mbps |
| 5.1 | 51 | 3840×2160 (4K) | 30 | 240 Mbps |
| 5.2 | 52 | 4096×2160 (4K) | 60 | 240 Mbps |
레벨 계산:
MacroBlocks per Second (MBPS):
= (Width / 16) × (Height / 16) × FPS
예시: 1920×1080 @ 30fps
= (1920/16) × (1080/16) × 30
= 120 × 68 × 30
= 244,800 MBPS
Level 4.0: 최대 245,760 MBPS
→ 1920×1080 @ 30fps 가능!
비트스트림 분석
실제 H.264 비트스트림 예시
00 00 00 01 67 64 00 1F AC D9 40 50 05 BB 01 6A [email protected]
00 00 00 01 68 EE 3C 80 ....h.<.
00 00 00 01 65 88 84 00 ... ....e...
00 00 00 01 41 9A 21 ... ....A.!.
분석:
1. SPS (00 00 00 01 67 ...)
- Start Code: 00 00 00 01
- NAL Header: 67 (Type 7)
- Profile: High (100)
- Level: 3.1
- 해상도: 1280×720
2. PPS (00 00 00 01 68 ...)
- Start Code: 00 00 00 01
- NAL Header: 68 (Type 8)
- Entropy: CABAC
3. IDR Frame (00 00 00 01 65 ...)
- Start Code: 00 00 00 01
- NAL Header: 65 (Type 5, IDR)
- I-Frame 데이터
4. P Frame (00 00 00 01 41 ...)
- Start Code: 00 00 00 01
- NAL Header: 41 (Type 1, Non-IDR)
- P-Frame 데이터
비트스트림 분석 도구
1. FFmpeg:
# NAL Unit 정보 출력
ffmpeg -i input.mp4 -c copy -bsf:v trace_headers -f null - 2>&1 | grep -A 10 "nal_unit_type"
# 상세 분석
ffprobe -show_packets -show_data -print_format json input.mp4 > analysis.json
2. H264Naked (Python):
from h264_naked import H264Parser
parser = H264Parser()
with open('video.h264', 'rb') as f:
data = f.read()
for nal in parser.parse(data):
print(f"NAL Type: {nal.type}")
print(f"Size: {nal.size}")
if nal.type == 7: # SPS
print(f"Profile: {nal.profile}")
print(f"Level: {nal.level}")
print(f"Width: {nal.width}")
print(f"Height: {nal.height}")
3. MediaInfo:
mediainfo input.mp4 --Details=1 | grep -A 20 "Video"
실전 인코딩
FFmpeg로 H.264 인코딩
기본 인코딩:
ffmpeg -i input.mp4 \
-c:v libx264 \
-crf 23 \
-preset medium \
-c:a aac -b:a 128k \
output.mp4
프로파일과 레벨 지정:
ffmpeg -i input.mp4 \
-c:v libx264 \
-profile:v high \
-level 4.1 \
-crf 23 \
-preset medium \
output.mp4
고급 설정:
ffmpeg -i input.mp4 \
-c:v libx264 \
-profile:v high \
-level 4.1 \
-crf 23 \
-preset slow \
-tune film \
-x264-params "keyint=60:min-keyint=30:scenecut=40:ref=4:bframes=3:b-adapt=2:me=umh:subme=8:trellis=2:aq-mode=3" \
-c:a aac -b:a 192k \
output.mp4
# 설명:
# keyint=60: GOP 크기 (2초 @ 30fps)
# min-keyint=30: 최소 GOP 크기
# scenecut=40: 장면 전환 감지
# ref=4: 참조 프레임 수
# bframes=3: B-Frame 수
# b-adapt=2: 적응형 B-Frame
# me=umh: 움직임 추정 알고리즘
# subme=8: 서브픽셀 정밀도
# trellis=2: Trellis 양자화
# aq-mode=3: 적응형 양자화
프리셋 비교
| 프리셋 | 속도 | 품질 | 파일 크기 | 용도 |
|---|---|---|---|---|
| ultrafast | 매우 빠름 | 낮음 | 큼 | 실시간 스트리밍 |
| superfast | 빠름 | 낮음 | 큼 | 라이브 |
| veryfast | 빠름 | 중간 | 중간 | 일반 스트리밍 |
| faster | 빠름 | 중간 | 중간 | 일반 |
| fast | 보통 | 좋음 | 중간 | 일반 |
| medium | 보통 | 좋음 | 작음 | 기본값 |
| slow | 느림 | 매우 좋음 | 작음 | VOD |
| slower | 매우 느림 | 최고 | 매우 작음 | 아카이브 |
| veryslow | 극도로 느림 | 최고 | 매우 작음 | 전문가 |
CRF (Constant Rate Factor)
CRF는 품질 기반 인코딩입니다.
CRF 값:
0 = 무손실 (매우 큼)
18 = 시각적 무손실
23 = 기본값 (권장)
28 = 낮은 품질
51 = 최악의 품질
권장:
애니메이션: CRF 15-20
영화: CRF 18-24
웹 비디오: CRF 23-28
2-Pass 인코딩
더 나은 품질을 위한 2단계 인코딩:
# Pass 1: 분석
ffmpeg -i input.mp4 \
-c:v libx264 \
-b:v 5M \
-pass 1 \
-f null /dev/null
# Pass 2: 인코딩
ffmpeg -i input.mp4 \
-c:v libx264 \
-b:v 5M \
-pass 2 \
-c:a aac -b:a 192k \
output.mp4
왜 아직도 H.264인가
H.264 vs H.265 (HEVC)
| 특성 | H.264 | H.265 |
|---|---|---|
| 압축률 | 기준 | 50% 더 효율적 |
| 인코딩 속도 | 빠름 | 느림 (10배) |
| 디코딩 부하 | 낮음 | 높음 |
| 하드웨어 지원 | 모든 기기 | 최신 기기만 |
| 브라우저 지원 | 100% | 제한적 |
| 특허 | 만료 (무료) | 복잡 (유료) |
| 파일 크기 | 기준 | 50% 작음 |
H.264를 선택하는 이유
1. 범용성
H.264 지원:
- 모든 스마트폰 (iPhone 3GS 이후)
- 모든 브라우저
- 모든 스마트 TV
- 모든 게임 콘솔
- 모든 카메라
H.265 지원:
- 최신 스마트폰만
- 일부 브라우저 (Safari만)
- 최신 TV만
2. 하드웨어 가속
H.264 하드웨어 디코딩:
- CPU 사용률: 5-10%
- 배터리 소모: 낮음
- 발열: 적음
H.265 소프트웨어 디코딩:
- CPU 사용률: 80-100%
- 배터리 소모: 높음
- 발열: 많음
3. 인코딩 비용
1시간 영상 인코딩:
H.264 (preset=medium):
- 시간: 10분
- 비용: $0.10 (AWS)
H.265 (preset=medium):
- 시간: 100분
- 비용: $1.00 (AWS)
10배 차이!
4. 특허 문제
H.264:
- 2023년 특허 만료
- 무료 사용 가능
- 법적 리스크 없음
H.265:
- 특허 유효
- 로열티 필요
- 특허 풀 복잡
실제 사용 현황 (2026년)
YouTube:
- 기본: H.264
- 4K: VP9 또는 AV1
- 8K: AV1
Netflix:
- 1080p 이하: H.264
- 4K: H.265 (지원 기기만)
- 4K: H.264 (나머지 기기)
Twitch:
- 모든 스트림: H.264
Zoom:
- 모든 통화: H.264
트러블슈팅
1. “Unsupported codec” 에러
문제: 일부 기기에서 재생 안됨
원인: 높은 프로파일/레벨
해결:
# Baseline Profile로 재인코딩
ffmpeg -i input.mp4 \
-c:v libx264 \
-profile:v baseline \
-level 3.0 \
-pix_fmt yuv420p \
output.mp4
2. 파일 크기가 너무 큼
문제: 인코딩 후 파일이 원본보다 큼
원인: CRF 값이 너무 낮음
해결:
# CRF 값 증가 (23 → 28)
ffmpeg -i input.mp4 \
-c:v libx264 \
-crf 28 \
-preset medium \
output.mp4
3. 인코딩 속도가 너무 느림
문제: 인코딩에 시간이 너무 오래 걸림
해결:
# 빠른 프리셋 사용
ffmpeg -i input.mp4 \
-c:v libx264 \
-preset veryfast \
-crf 23 \
output.mp4
# 또는 하드웨어 인코더 사용
ffmpeg -i input.mp4 \
-c:v h264_nvenc \ # NVIDIA GPU
-preset fast \
-crf 23 \
output.mp4
4. SPS/PPS 추출 실패
문제: MP4에서 SPS/PPS를 찾을 수 없음
해결:
# Annex B 포맷으로 변환
ffmpeg -i input.mp4 \
-c copy \
-bsf:v h264_mp4toannexb \
output.h264
# 첫 100바이트 확인
xxd -l 100 output.h264
마무리
H.264는 2026년에도 여전히 표준입니다.
핵심 요약:
- NAL Unit: H.264 비트스트림의 기본 단위
- SPS: 전체 비디오 설정 (해상도, 프로파일, 레벨)
- PPS: 프레임별 설정 (엔트로피 코딩, 양자화)
- 프로파일: Baseline (모바일), Main (스트리밍), High (고품질)
- 레벨: 해상도와 프레임 레이트 제한
H.264를 선택하는 이유:
- ✅ 모든 기기 지원
- ✅ 하드웨어 가속
- ✅ 빠른 인코딩
- ✅ 특허 만료 (무료)
- ✅ 검증된 안정성
H.265를 선택하는 경우:
- 4K/8K 고해상도
- 저장 공간 중요
- 최신 기기만 지원
- 인코딩 시간 충분
실전 추천:
# 범용 웹 비디오 (YouTube, 블로그)
ffmpeg -i input.mp4 \
-c:v libx264 \
-profile:v high \
-level 4.1 \
-crf 23 \
-preset medium \
-c:a aac -b:a 128k \
output.mp4
# 모바일 호환 (최대 호환성)
ffmpeg -i input.mp4 \
-c:v libx264 \
-profile:v baseline \
-level 3.0 \
-crf 28 \
-preset fast \
-pix_fmt yuv420p \
-c:a aac -b:a 96k \
output.mp4
# 고품질 아카이브
ffmpeg -i input.mp4 \
-c:v libx264 \
-profile:v high \
-level 5.1 \
-crf 18 \
-preset slow \
-c:a aac -b:a 192k \
output.mp4
다음 단계:
- 영상 스트리밍 프로토콜
- WebM 컨테이너 포맷
- FFmpeg 완벽 가이드
H.264는 앞으로도 수년간 표준으로 남을 것입니다! 🎬