"이 영상 1080p인데 왜 깨져 보이지?"

스트리밍 공부하기 전엔 해상도 = 화질이라고 생각했다. 1080p면 좋고 4K면 더 좋고. 근데 막상 인코딩 옵션을 만지다 보면 같은 1080p인데도 깨끗하게 보이는 영상이 있고 깍두기처럼 보이는 영상이 있다.

이번 글은 지난 글에 이어서, "화질"이 실제로 뭘로 결정되는지 — 해상도, 비트레이트, ABR, 그리고 오디오까지 정리한 노트다.


1. 해상도와 FPS — 화질을 수치로 정의하는 첫 단계

해상도

해상도는 단순히 픽셀 수다.

1920 × 1080 = 약 200만 픽셀 (1080p)
3840 × 2160 = 약 830만 픽셀 (4K)

같은 해상도라도 화면 크기에 따라 체감 화질이 다르다. PPI (Pixel Per Inch) 가 다르기 때문.

같은 1080p지만 PPI는 다르다

27인치 모니터의 1080p와 6인치 폰의 1080p — 폰이 훨씬 선명해 보인다. 같은 픽셀 수를 더 작은 면적에 넣으니 4배 가까이 촘촘하다.

이래서 치지직이 모바일 시청자에게 굳이 1080p를 줄 필요가 없다. 720p, 심지어 480p로도 충분히 선명해 보인다. ABR ladder에서 모바일 화질을 낮게 잡는 이유다.

FPS

FPS(Frames Per Second)는 1초에 보여주는 이미지 수.

24fps: 영화 (자연스러운 모션 블러)
30fps: 일반 방송, 유튜브 기본
60fps: 게임 방송, 스포츠
120fps: 고사양 게임, 스포츠 중계

치지직 스트리머들이 대부분 1080p60으로 방송한다. 게임 방송이라서. 빠른 움직임 — 총 발사, 점프, 회피 — 이 30fps에선 뭉개지고 60fps에선 선명하게 보인다.

FPS를 올리면 데이터량이 그만큼 늘 것 같지만 실제론 1.5배 정도다. 60fps의 연속 프레임은 16ms 간격이라 30fps의 33ms 간격보다 변화량이 작아서 P-frame이 더 잘 압축된다.


2. 같은 1080p도 비트레이트가 다르면 화질이 다르다

비트레이트는 1초에 전송되는 데이터량. 단위 bps (bits per second).

치지직 1080p60 권장이 6,000 Kbps = 6 Mbps. 1초에 약 750KB의 영상 데이터.

같은 1080p인데 화질이 다른 이유가 이거다.

같은 1080p, 다른 비트레이트의 화질 차이

  • 1080p @ 6 Mbps: 깨끗함
  • 1080p @ 2 Mbps: 빠른 움직임에서 깍두기(블록 노이즈) 보임
  • 1080p @ 0.5 Mbps: 거의 안 봄. 뭉개진 1080p

해상도는 픽셀의 "수"고, 비트레이트는 각 픽셀에 쓸 수 있는 "정보량"이다.

여기서 흥미로운 사실: 같은 비트레이트라면 낮은 해상도가 더 깨끗할 수 있다.

2 Mbps에서:

  • 1080p로 인코딩 → 픽셀당 정보 부족 → 깍두기
  • 720p로 인코딩 → 픽셀 수 적어 정보 충분 → 깨끗

치지직이 모바일에 720p나 480p를 주는 게 단순히 데이터 절약 때문만이 아니다. 비트레이트가 낮은 상황에서는 낮은 해상도가 실제로 더 좋아 보인다.

CBR vs VBR — 비트를 어떻게 분배할지

영상은 장면마다 압축 난이도가 다르다. 정적인 토크쇼는 비트 적게 써도 되지만 폭발 장면은 비트 많이 필요하다.

CBR vs VBR — 비트레이트 분배 방식

  • CBR (Constant Bit Rate): 비트레이트 고정. 라이브에 유리 (대역폭 예측 가능)
  • VBR (Variable Bit Rate): 어려운 장면엔 더 많이, 쉬운 장면엔 적게. VOD에 유리 (화질 일정)

라이브 스트리밍은 보통 CBR. 네트워크 대역폭을 예측 가능하게 유지하기 위해서. OBS 설정에서 본 적 있는 그 옵션이 이거다.

x264 vs NVENC

같은 비트레이트라도 인코더가 다르면 결과가 다르다.

  • x264: CPU 인코딩. 정밀하지만 느림. CPU 50% 정도 먹음.
  • NVENC: NVIDIA GPU의 전용 인코딩 칩. 압도적으로 빠르지만 같은 비트레이트에서 화질이 약간 낮음. CPU 1%.

스트리머가 NVENC를 쓰는 이유는 게임이 CPU를 써야 하기 때문이다. 인코딩에 CPU 뺏기면 게임 프레임 떨어진다.

치지직 서버 측에서 NVENC를 쓰는 이유는 비용. 멘토 피드백에서 "NVENC GPU 트랜스코딩으로 비용 60% 절감"이 있었는데 이게 그거다.


3. ABR — 시청자 네트워크에 맞춰 자동으로 화질 바꾸기

시청자의 네트워크는 변한다. 집 와이파이로 시작 → 지하철 진입 → 4G → 다시 와이파이.

비트레이트 6 Mbps 영상을 받고 있는데 네트워크가 1 Mbps로 떨어지면 버퍼링이 난다. 그래서 화질을 자동으로 바꾸는 메커니즘이 필요하다. 이게 ABR (Adaptive Bitrate Streaming).

ABR Ladder

플랫폼이 같은 영상을 여러 화질로 미리 인코딩해둔다.

ABR Ladder — 여러 화질을 미리 인코딩

이 화질 목록이 ABR Ladder (사다리) 다. 사다리를 어떻게 설계하느냐가 플랫폼 엔지니어의 핵심 역량이다.

  • 너무 촘촘하면 인코딩 비용 폭증
  • 너무 듬성하면 시청자 경험이 떨어짐

치지직 같은 한국 플랫폼은 보통 1080p / 720p / 480p / 360p 4단계.

Master Playlist

HLS에서 ABR을 어떻게 표현하나? .m3u8 파일이 하나 더 있다.

master.m3u8 (마스터 플레이리스트)
├── playlist_1080p.m3u8  (BANDWIDTH=6500000)
├── playlist_720p.m3u8   (BANDWIDTH=4800000)
├── playlist_480p.m3u8   (BANDWIDTH=1600000)
└── playlist_360p.m3u8   (BANDWIDTH=900000)

플레이어가 처음에 마스터를 받고, 현재 대역폭에 맞는 sub playlist를 골라서 본다.

화질을 바꿀지는 플레이어가 결정한다

ABR의 결정은 서버가 아니라 플레이어가 한다. 플레이어가 측정하는 것:

  1. 다운로드 속도: 직전 세그먼트를 몇 초만에 받았는지
  2. 버퍼 상태: 현재 버퍼에 몇 초치 데이터가 있는지

대역폭만 보면 와이파이 신호가 잠깐 튀어서 30 Mbps 측정 → 1080p로 올림 → 다시 떨어짐 → 다운그레이드를 반복한다. 그래서 현대 플레이어들은 하이브리드 알고리즘 (BOLA, MPC 등) 을 쓴다.

화질 전환 시 왜 안 끊기나

플레이어가 1080p에서 480p로 바꿀 때 영상이 안 끊긴다. 비밀이 있다.

모든 화질의 세그먼트 경계가 같은 위치에 있다.

모든 화질의 GOP 경계가 정렬되어 있다

6초 시점에 화질을 바꾸면, 1080p seg001을 다 본 다음 480p seg002를 받아서 이어 붙인다. 시점은 같으니 끊김 없이 자연스럽게 이어진다.

이걸 보장하려면 모든 화질의 GOP 경계가 정렬되어 있어야 한다. I-frame이 같은 시점에 찍혀야 한다. FFmpeg로 ABR ladder 만들 때 -g(GOP 크기)와 -force_key_frames를 정확히 맞추는 이유가 이거.


4. 오디오도 비트레이트가 있고, 멀티 트랙도 있다

지금까지 영상만 봤는데, 오디오도 똑같이 비트레이트가 있다.

영상 1080p60: 6,000 Kbps (6 Mbps)
오디오 스테레오: 128 Kbps
오디오 모노: 64 Kbps

오디오는 영상보다 데이터량이 훨씬 작다. 전체 스트림에서 2~5% 정도.

코덱

영상이 H.264로 압축되듯, 오디오도 코덱으로 압축된다.

코덱 특징 어디 쓰나
AAC 가장 범용, HLS 표준 일반 라이브 스트리밍
Opus 음질 우수, 저지연 WebRTC, 화상 통화
MP3 옛 표준, 호환성 옛 음원

라이브 스트리밍 인제스트는 보통 AAC. 호환성 때문.

멀티 오디오 트랙 — 같은 기술, 다른 목적

HLS는 한 영상에 여러 오디오 트랙을 붙일 수 있다. 근데 사용 목적이 두 가지다.

멀티 오디오 트랙: HLS 다국어 vs PokeClip 분석

Netflix식: 시청자에게 "선택지"를 준다. 한국어 / 영어 / 일본어 중 하나만 골라서 듣는다. 영상은 한 번만 받고 오디오만 선택.

PokeClip식: 분석을 위해 "동시에 모두" 받는다. 게임 소리, 마이크, 디스코드, 후원 알림음을 각각 따로 분석.

목적이 다르니 컨테이너 선택도 다르다. HLS 시청자용 멀티 오디오는 .m3u8에서 별도 플레이리스트로 관리하면 되지만, PokeClip은 한 스트림 안에 동시에 담아야 했다. 그래서 MPEG-TS의 PID 구조가 필요했던 거다.

영상-오디오 동기화 — PTS

영상과 오디오가 어긋나면 입 모양과 소리가 안 맞는다. 이걸 막는 게 PTS (Presentation Timestamp) 다.

각 프레임과 오디오 샘플에 시간 정보가 박혀있다.

영상 프레임 1: PTS = 0.000초
영상 프레임 2: PTS = 0.033초 (30fps)
오디오 샘플 1: PTS = 0.000초
오디오 샘플 2: PTS = 0.021초 (48kHz)

플레이어는 PTS를 기준으로 영상과 오디오를 맞춘다.

OBS에서 마이크 입력에 "오디오 오프셋"을 -50ms 같이 음수로 설정하는 옵션이 있는데, 마이크가 게임 화면보다 늦게 들어오니 앞당겨서 보정하는 거다.


정리하면

화질을 결정하는 건 해상도 하나가 아니다.

  1. 해상도 — 픽셀 수. PPI에 따라 체감 다름 (모바일은 720p로 충분)
  2. 비트레이트 — 각 픽셀에 쓸 수 있는 정보량. 같은 1080p도 비트레이트가 낮으면 깍두기
  3. FPS — 모션 부드러움. 게임 방송이 60fps인 이유
  4. 인코더 — 같은 비트레이트라도 x264(고화질, 느림) vs NVENC(빠름, CPU 안 씀) 선택
  5. ABR — 시청자 네트워크 변화에 맞춰 자동 전환
  6. 오디오 — 영상 대비 비중 작지만(2~5%), 멀티 트랙은 목적이 다양

다음 글에선 버퍼링이 정확히 왜 생기는지 — TCP의 HoL Blocking부터 QUIC까지 정리할 예정이다.


참고