라이브 스트리밍 코덱 문제 6가지와 진단 순서
화질 문의의 대부분은 코덱 자체가 아니라 코덱을 실어 나르는 방식에서 터집니다. 증상에서 거꾸로 원인을 따라가 보겠습니다.
증상 1: 재생 버튼을 눌렀는데 검은 화면만 나온다
국내 OTT 사업자의 실 운영 라이브 채널에서 있었던 일입니다. 인제스트는 정상이고 세그먼트도 정상적으로 쌓이는데, 플레이어는 한 프레임도 그리지 못했습니다. 원인은 단순했습니다. 디코더가 IDR을 만나지 못한 것입니다.
ffprobe -show_frames input.ts | grep "pict_type=I"
# IDR이 몇 프레임마다 나오는지 확인
# 첫 프레임이 I가 아니면 문제!이 명령을 실행했을 때 첫 프레임이 I가 아니거나, I 프레임 간격이 10초 단위로 벌어져 있으면 그게 답입니다.
왜 IDR이 없으면 화면이 안 나오는가
디코더는 참조 픽처 버퍼(DPB)를 채워 둔 상태에서 P, B 프레임의 차이값을 더해 화면을 만듭니다. 중간부터 재생을 시작하면 그 참조가 없습니다. 참조 없이 단독으로 복원 가능한 프레임, 즉 IDR 을 만나야 첫 그림이 나옵니다.
여기서 자주 나오는 원인은 세 가지입니다.
| 원인 | 무슨 일이 벌어지나 | 주로 발생하는 지점 |
|---|---|---|
| SPS/PPS 누락 | 해상도, 프로파일을 몰라 디코더 초기화 실패 | RTMP에서 HLS로 변환할 때 AVCC에서 Annex B로 넘어가는 구간 |
| 첫 세그먼트가 P-frame으로 시작 | 참조할 프레임이 없어 복원 불가 | 세그먼트 경계를 임의로 자른 경우 |
| keyint가 플레이어 버퍼보다 큼 | IDR을 만날 때까지 계속 대기 | keyint 10초인데 플레이어 버퍼가 5초일 때 |
특히 세 번째가 함정입니다. 인코더 입장에서는 압축률을 위해 keyint를 늘리는 게 맞지만, 플레이어 입장에서는 IDR이 나올 때까지 버퍼를 버티지 못하면 그냥 검은 화면이 됩니다.
주의: 진단 순서를 지켜야 합니다. 첫 프레임이 I인지 먼저 확인하고, 그다음에 SPS/PPS 존재를 확인합니다. 순서를 바꾸면 헛수고하게 됩니다.
해결: keyint를 2~5초 구간으로 줄이고, 첫 세그먼트는 반드시 IDR로 시작하도록 인코딩 파이프라인을 고칩니다.
증상 2: 입 모양보다 소리가 0.5초 늦다
한 VOD 플랫폼에서 올라온 제보였습니다. 영상 자체는 문제없이 나오는데 입 모양과 소리가 맞지 않았습니다. 이건 거의 항상 PTS가 어디서부터 시작하는지의 문제입니다.
ffprobe -show_entries frame=pkt_pts_time,pict_type \
-of csv=p=0 input.ts | head -20
# PTS가 0에서 시작하는지 확인원인 후보는 세 가지입니다.
- 오디오와 비디오의 PTS 출발점이 다른 경우. 인코더가 오디오는 0부터, 비디오는 100ms부터 시작하면 그 100ms가 그대로 어긋남이 됩니다. 여기에 버퍼링이 겹치면 체감은 0.5초까지 벌어집니다.
- B-frame reorder 지연. 비디오는 reorder buffer를 한 번 거치지만 오디오는 직행합니다. 구조적으로 미세한 차이가 누적됩니다.
- MPEG-TS에서 PCR과 PTS가 다른 속도로 진행하는 경우. 클럭 기준(PCR)과 타임스탬프(PTS)가 어긋나면 재생이 진행될수록 밀립니다.
참고: 오디오가 늦다고 느껴지는 경우의 절반은 사실 비디오가 빠른 경우입니다. 어느 쪽을 움직일지 정하기 전에 두 트랙의 시작 PTS를 먼저 찍어 봐야 합니다.
해결: 인코더에서 오디오와 비디오의 시작 PTS를 0으로 통일합니다. 이미 만들어진 파일이라면 -fflags +genpts로 PTS 재생성을 시도해 볼 수 있습니다.
증상 3: 720p에서 1080p로 바뀌는 순간 화면이 멈춘다
ABR 래더를 타고 올라갈 때만 0.5초 정도 화면이 굳는 현상입니다. 원인은 래더 간 IDR 위치가 어긋나 있는 것입니다.
720p: [IDR(PTS=6000)] ... [IDR(PTS=12000)]
1080p: [IDR(PTS=6033)] ... [IDR(PTS=12033)]
↑ 33ms 차이33ms면 사람이 인지하지 못할 숫자 같지만, 플레이어는 현재 화질의 GOP를 끝까지 재생한 뒤에야 다른 래더로 건너갈 수 있습니다. 즉 전환 자체는 다음 IDR까지 미뤄지고, 그 사이에 버퍼가 비면 멈춤 현상으로 보입니다.
참고: 핵심은 전환 지점의 IDR이 같은 PTS에 있어야 한다는 것입니다. 비슷한 위치가 아니라, 정확히 같은 위치여야 합니다.
해결: 모든 래더에 동일한 keyint를 적용하고, -force_key_frames로 동일한 프레임에서 IDR을 강제합니다. 씬 전환 감지로 IDR을 넣는 자동 모드를 래더별로 돌리면 이 어긋남이 거의 확실히 발생합니다.
증상 4: H.265가 특정 기기에서만 검은 화면이다
같은 HEVC 스트림인데 어떤 기기에서는 잘 나오고, iOS 10 이하나 구형 안드로이드, 일부 스마트 TV에서는 검은 화면만 나옵니다. 스트림 문제가 아니라 디코더 지원 범위의 문제입니다.
ffprobe -show_streams input.ts | grep codec
# codec_name=hevc 인지 확인점검 순서는 다음과 같습니다.
| 확인 항목 | 왜 문제가 되는가 |
|---|---|
| HEVC 자체 지원 여부 | 하드웨어 디코더가 HEVC를 아예 모르면 소프트웨어 폴백도 없는 기기가 있습니다 |
| 프로파일 (Main / Main10) | High, Main10은 지원율이 낮습니다. Main으로 낮추면 커버리지가 늘어납니다 |
| 샘플 엔트리 태그 | hvc1과 hev1 중 한쪽만 받는 플레이어가 있습니다 |
해결: ABR 매니페스트에 H.264 래더를 반드시 함께 넣어서 fallback 경로를 확보합니다. HEVC는 Main 프로파일로 인코딩하고, 태그는 호환성을 보고 hev1도 함께 고려합니다.
주의: "H.265면 무조건 이득"이라는 전제가 여기서 깨집니다. 절감된 비트레이트보다 재생 실패로 이탈한 시청자가 많다면 그건 이득이 아닙니다.
증상 5: RTMP에서는 잘 나오는데 HLS로 변환하면 첫 프레임에서 죽는다
증상 1(검은 화면)과 비슷해 보이지만 원인이 다릅니다. 플레이어가 조용히 기다리는 게 아니라 디코더가 크래시합니다.
원인은 NAL 유닛 포장 방식의 차이입니다.
- RTMP(FLV)는 AVCC 방식으로, NAL 앞에 4바이트 길이 prefix가 붙습니다.
- HLS(.ts)는 Annex B 방식으로, NAL 앞에
00 00 00 01Start Code가 붙습니다.
변환할 때 세 가지를 동시에 처리해야 합니다.
- 4바이트 길이 prefix를 Start Code로 교체합니다.
- SPS/PPS NAL도 동일하게 변환합니다. extra data에 있던 것을 스트림 안으로 넣어야 합니다.
- Emulation Prevention Bytes를 삽입합니다. Annex B에서는
00 00 00이 Start Code로 오해되므로00 00 03으로 이스케이프해야 합니다.
3번을 빼먹은 구현이 의외로 많습니다. AVCC에서는 길이 prefix로 경계를 알기 때문에 이스케이프가 필요 없는데, Annex B로 넘어가면 이야기가 달라집니다.
ffmpeg -i input.mp4 -c copy -bsf:v h264_mp4toannexb output.ts참고: 이 한 줄이면 위 세 가지가 모두 처리됩니다. 직접 변환 로직을 짜고 있다면 이 비트스트림 필터의 출력을 기준으로 비교해 보는 게 가장 빠른 검증법입니다.
증상 6: 광고가 끝나고 본방송으로 돌아올 때 0.5초간 음소거
광고 구간 전후에서만 발생하는 오디오 끊김입니다. 영상은 이어지는데 소리만 잠깐 사라집니다.
원인은 HLS 매니페스트에서 광고 구간 경계에 #EXT-X-DISCONTINUITY 태그가 없는 것입니다. 이 태그가 없으면 디코더는 이전 구간의 오디오 코덱 상태(샘플레이트, 채널 구성, 디코더 내부 상태)를 그대로 유지한 채 새로운 세그먼트를 해석하려 합니다. 광고와 본방송의 오디오 설정이 다르면 그 순간 출력이 무너집니다.
해결: 광고 구간의 시작과 끝, 양쪽에 모두 DISCONTINUITY 태그를 넣습니다. 그리고 광고 세그먼트 자체도 반드시 IDR로 시작해야 합니다.
현장용 빠른 진단 체크리스트
증상에서 시작해서 스트림 구조로 내려가는 순서만 지켜도, 대부분의 이슈는 ffprobe 두세 번 안에 원인이 좁혀집니다.
정리
- 검은 화면의 1순위 원인은 IDR 부재입니다. 첫 프레임이 I인지, keyint가 플레이어 버퍼보다 짧은지 먼저 봅니다.
- A/V 어긋남은 대개 PTS 출발점 불일치입니다. 오디오와 비디오의 시작 PTS를 0으로 통일합니다.
- ABR 전환 깜빡임은 래더 간 IDR PTS 어긋남입니다. 동일한 keyint와
force_key_frames로 강제 정렬합니다. - HEVC 미지원 기기는 스트림이 아니라 디코더 문제입니다. H.264 fallback 래더를 항상 매니페스트에 넣어 둡니다.
- AVCC에서 Annex B로 넘어갈 때는 Start Code 교체, SPS/PPS 삽입,
00 00 03이스케이프 세 가지를 함께 처리해야 디코더가 죽지 않습니다.