GOP-세그먼트 정렬의 산수 — Agora Media Push에서 YouTube/Twitch까지
YouTube Studio가 자꾸 키프레임 경고를 띄우거나, Agora Media Push에 GOP를 넉넉히 잡았는데 오히려 화질이 떨어진 적이 있나요? 두 증상 모두 결국 한 줄의 산수로 풀립니다. 세그먼트 길이는 인코더가 아니라 YouTube나 Twitch 같은 송출 플랫폼이 정하고, GOP는 그 길이의 정수 약수로 맞춰야 한다는 규칙이죠. 이 글은 GOP가 어긋났을 때 HLS 패키저가 무엇을 하는지, 그리고 Agora Media Push의 gop 값을 플랫폼에 맞게 어떻게 계산하는지를 정리합니다.
목차(40개 항목)
- 0. 핵심 명제 — 세그먼트는 플랫폼이 정하고, GOP는 거기 맞춘다
1. GOP가 정확히 뭔가
2. 세그먼트 길이는 누가 정하나
3. GOP가 세그먼트와 안 맞으면 — 패키저가 무엇을 하나
4. GOP-Divisor 규칙 — 산수 한 줄
5. Scene-Cut Detection — 라이브에서는 끄는 게 정답
7. 클린 정렬 모델이 깨지는 지점들
- 8. SA 체크리스트 — Media Push 설정 검토
- 관련 글
- 참고 자료
"YouTube Studio가 자꾸 'gopSizeLong' 경고를 띄워요. 키프레임은 2초마다 박았는데 왜요?", "Agora Media Push에 gop=90으로 설정했는데 화질이 떨어집니다". 둘 다 같은 산수 한 줄로 풀립니다 — GOP가 세그먼트 길이와 정수 배수로 맞아야 한다는 규칙.
이 글은 GOP의 정의부터, 세그먼트 길이를 누가 정하는지, GOP가 안 맞을 때 HLS 패키저가 무엇을 하는지, Agora Media Push의 gop 파라미터를 어떻게 설정해야 하는지까지 정리합니다.
0. 핵심 명제 — 세그먼트는 플랫폼이 정하고, GOP는 거기 맞춘다
인코더가 세그먼트 길이를 정하는 게 아니다. YouTube/Twitch/CDN 같은 송출 플랫폼이 자기 HLS target duration을 가지고 있고, 인코더(Agora Media Push 포함)는 그 값에 정수 배수로 GOP를 맞춰야 한다.
흔한 오해와 정정:
| 오해 | 정확한 이해 |
|---|---|
| 인코더 GOP를 자유롭게 골라도 됨 | 송출 플랫폼의 세그먼트 길이가 GOP 상한을 결정 |
| GOP가 작으면 무조건 좋음 | 너무 작으면 압축 효율 ↓ → bitrate 동일이면 화질 ↓ |
| Scene-cut detection은 항상 켜야 화질 좋음 | 라이브 송출에서는 OFF가 정답 (가변 GOP 방지) |
| GOP=90, Seg=2초여도 그냥 보내면 됨 | 패키저가 세그먼트를 늘리거나 재인코딩 → 지연/화질 손실 |
1. GOP가 정확히 뭔가
GOP = Group of Pictures = 하나의 I-frame부터 다음 I-frame 직전 프레임까지의 묶음.
GOP는 독립적으로 디코딩 가능한 단위입니다. GOP 안의 모든 P/B-frame은 그 GOP의 I-frame을 (직간접적으로) 참조함. GOP가 끝나면 새 I-frame으로 새 GOP 시작.
Open GOP vs Closed GOP
| 구분 | 정의 | 라이브 송출 |
|---|---|---|
| Closed GOP | 다음 GOP의 프레임이 이전 GOP를 참조하지 않음 — 경계가 깨끗 | ✅ HLS/DASH 라이브의 기본값 |
| Open GOP | B-frame이 GOP 경계를 넘어 참조 가능 | VOD 압축 효율은 좋지만 라이브 부적합 |
라이브/HLS 맥락에서 GOP는 닫힌 GOP가 기본입니다. 이 글의 모든 산수는 닫힌 GOP 전제.
두 가지 의미로 쓰임 — 헷갈리는 포인트
| 용어 | 의미 | 예시 (30fps, 키프레임 매 2초) |
|---|---|---|
| GOP size (프레임 수) | I-frame 사이의 프레임 개수 | 60 |
| GOP length (시간) | I-frame 사이의 시간 | 2초 |
| GOP interval / 키프레임 인터벌 | 위 둘 중 하나 (보통 시간) | 2초 |
계산 공식:
인코더 설정에서는 보통 프레임 단위로 입력:
| 도구 | 옵션 |
|---|---|
| FFmpeg | -g 60 |
| x264 | keyint=60 |
| Agora Media Push | gop: 60 |
GOP 구조 시각화
GOP size가 크다 = 압축 효율 ↑ (P/B-frame 비율 ↑) but 시작점 드뭄 GOP size가 작다 = 시작점 자주 = 빠른 채널 전환 but 압축 효율 ↓
이 트레이드오프 때문에 "어디에 맞춰야 하나"가 문제 → 세그먼트 길이에 맞추는 게 정답.
더 깊게: #27 프레임의 모든 것, #30 x264 실전 옵션
2. 세그먼트 길이는 누가 정하나
이게 의외로 자주 헷갈리는 부분. 답: 송출 플랫폼이 정함. 인코더/Media Push가 정하는 게 아님.
결정 주체별 정리
| 주체 | 결정하는 것 | 예시 |
|---|---|---|
| CDN/플랫폼 | 세그먼트 길이 (target duration) | YouTube ~2초, Twitch ~2초, AWS IVS ~2초 |
| 인코더 (Media Push) | GOP 길이, 비트레이트, 코덱 | gop=60, 2500kbps, H.264 |
| Apple HLS 표준 | 권장값/제약 | RFC 8216 typical 10초, 모던 라이브 2~4초 |
플랫폼별 세그먼트 길이
아래 수치는 공식 문서/엔지니어링 블로그 기준. 비공개 플랫폼은 [NEEDS VERIFICATION] 표시.
| 플랫폼 | 세그먼트 길이 | 출처 |
|---|---|---|
| YouTube Live | 권장 2초, 1~4초 허용 (최대 5초 초과 불가) | YouTube HLS Ingestion Guide |
| Twitch | ~2초 | Twitch Engineering Blog (비공식) [NEEDS VERIFICATION] |
| Facebook Live | 비공개 | [NEEDS VERIFICATION] |
| AWS IVS | IDR 2초 권장 (모든 채널 타입) | AWS IVS docs |
| Vimeo Live | 비공개 | [NEEDS VERIFICATION] |
| RFC 8216 원문 | "typical target duration is 10 seconds" | RFC 8216 §6.2.1 |
| Apple iOS 권장 | 6초 | Apple HLS Authoring Spec |
| 모던 라이브 | 2~4초 | 업계 관행 |
| LL-HLS (Apple 2019+) | part target duration 200ms~500ms | RFC 8216bis, AWS LL-HLS doc |
| LL-DASH | 0.5~2초 (CMAF chunk) | Wowza LL-CMAF |
⚠️ 수정 포인트: AWS IVS는 "Standard=2s / Low Latency=1s" 같은 채널 타입 매핑이 없습니다. IVS 제품 자체가 Low-Latency Streaming이고, 모든 채널 타입에서 IDR 2초가 권장입니다. 1초로 낮추면 시작 지연 ~3-4초로 줄어들지만 ABR 전환이 잦아지는 QoS 트레이드오프 발생.
라이브 지연 공식
⚠️ 이것은 이론적 최솟값의 단순화 공식. 실제 E2E 지연은 더 큼.
YouTube가 6초 → 2초로 줄인 이유, LL-HLS가 part 단위로 쪼개는 이유.
실제 E2E 지연은 여기에 추가:
- 인코더 버퍼링 (첫 GOP 완성 대기)
- CDN edge 전파 시간
- 네트워크 RTT
따라서 "6s × 3 = 18초 E2E"는 이론적 최솟값. 실제는 20~25초 수준.
워크플로우
플랫폼이 세그먼트 길이를 정하면, 인코더는 거기 맞추는 입장. 인코더가 GOP를 잘못 설정하면 플랫폼이 강제 재인코딩하거나 화질 저하.
3. GOP가 세그먼트와 안 맞으면 — 패키저가 무엇을 하나
핵심: HLS 패키저는 영상을 자를 때 키프레임 위치에서만 자를 수 있습니다. 키프레임이 없는 위치에서 시작하는 세그먼트는 독립 디코딩 불가능 → 패키저가 그런 세그먼트를 emit하지 않음.
시나리오 A: GOP = 세그먼트 길이 (정렬됨)
시나리오 B: GOP = 90 (3초), 세그먼트 = 2초
패키저의 선택지 — 실제로는 2개
⚠️ "P-frame부터 시작하는 세그먼트를 그대로 발행한다"는 시나리오는 현실에서 발생하지 않습니다. 컴플라이언트 HLS 패키저는 그런 세그먼트를 emit하지 않음. 옵션 1 또는 2로 폴백합니다.
옵션 1: 키프레임에 맞춰 세그먼트 길이를 늘림
원래 2초 세그먼트 → 강제로 3초 세그먼트로 변경.
- FFmpeg HLS 세그먼터의 기본 동작:
segment_time을 초과한 직후 다음 키프레임이 나올 때 자름 - 결과: 모든 시청자 지연 +3초, target_duration 변동 → ABR 안정성 ↓
- 클라이언트가 원한 동작이 아님
옵션 2: 강제 키프레임 삽입 + 재인코딩
들어온 영상 → 디코딩 → 2초마다 I-frame 강제 삽입 → 재인코딩.
- 결과: CPU 비용 ↑, 화질 손실 (재인코딩은 항상 손실), 지연 ↑
- YouTube가 보통 이 길로 가는 것으로 추정 [NEEDS VERIFICATION] — 강제 재인코딩이 일어난다는 명시적 공개 문서는 확인되지 않음
YouTube Stream Health 경고 (공식 확인)
YouTube는 키프레임 간격 문제에 대해 4종의 공식 헬스 메시지를 정의합니다.
| 코드 | 의미 |
|---|---|
gopSizeLong / gopSizeOver | "4초 이하 키프레임 주기를 사용하세요. 키프레임이 충분히 자주 전송되지 않아 버퍼링 발생 가능" |
gopSizeShort | GOP가 너무 작아 화질 저하. 권장 4초 |
gopMismatch | 키프레임 주기 불일치 |
출처: YouTube Health Status Messages
이 경고가 뜬다면 GOP 값을 다시 검토할 신호.
4. GOP-Divisor 규칙 — 산수 한 줄
규칙: segment_length / GOP_length가 양의 정수여야 함.
방향 헷갈리지 않게 다시:
- 세그먼트 길이 ÷ GOP 길이 = 양의 정수 ✓
- "GOP가 세그먼트 길이의 약수"라는 표현도 같은 뜻
케이스 매트릭스
| 설정 | 비율 | 결과 |
|---|---|---|
| GOP=2s, Seg=2s | 2/2 = 1 ✓ | 1:1 정렬, 세그먼트당 I-frame 1개 |
| GOP=1s, Seg=2s | 2/1 = 2 ✓ | 1:2 정렬, 세그먼트당 I-frame 2개 |
| GOP=2s, Seg=6s | 6/2 = 3 ✓ | 1:3 정렬, 세그먼트당 I-frame 3개 |
| GOP=4s, Seg=2s | 2/4 = 0.5 ✗ | 세그먼트가 GOP 중간을 자름 → 옵션 1 또는 2 |
| GOP=1.67s (50f@30fps), Seg=2s | 2/1.67 = 1.198 ✗ | 비정수 배수, 불규칙 정렬 |
시나리오 C: GOP = 50 (1.67초), 세그먼트 = 2초
세그먼트마다 키프레임과 어긋나는 위치가 다름 → 일부는 정렬, 일부는 어긋남 → 옵션 1/2 패키저 보정 발생.
가장 안전한 설정
1:1로 일치시키는 것. 세그먼트당 I-frame 정확히 1개.
YouTube 권장 = 2초 세그먼트 + 2초 GOP = 30fps의 경우 gop=60.
Apple HLS Authoring Spec의 ±0.5초 톨러런스
Apple은 세그먼트 duration이 EXT-X-TARGETDURATION에서 ±0.5초까지 변동을 허용합니다. 즉 약간의 GOP drift는 실무에서 허용됨. 다만 일관된 drift는 누적되어 플레이리스트 레벨 에러를 일으킬 수 있으니 정렬 자체를 포기하면 안 됨.
5. Scene-Cut Detection — 라이브에서는 끄는 게 정답
Scene-Cut Detection이 뭔가
대부분의 비디오 인코더(x264, NVENC, VideoToolbox)는 화면이 갑자기 바뀌는 순간(컷 전환, 카메라 스위치)을 감지하면 자동으로 그 자리에 I-frame을 끼워 넣습니다.
왜 이게 좋은가 (압축 효율 관점):
화면이 완전히 바뀌면 이전 프레임을 참조해봤자 차이가 너무 커서 P-frame이 거의 I-frame만큼 커짐. 차라리 그냥 새 I-frame 박는 게 효율적.
라이브 스트리밍에서 왜 문제
가변 GOP가 됨.
비활성화 방법
| 도구 | 옵션 |
|---|---|
| FFmpeg | -sc_threshold 0 |
| x264 params | scenecut=0 또는 no-scenecut |
| AWS IVS 권장 FFmpeg | -x264opts "nal-hrd=cbr:no-scenecut" |
| NVENC | -no-scenecut (일부 버전에서 -sc_threshold 무시 이슈 보고) |
| VideoToolbox / MediaCodec | 직접 끄는 공개 옵션 명확 X — 고정 GOP(-g)와 -force_key_frames로 우회 |
실무 팁: 하드웨어 인코더는 소프트웨어(x264)보다 GOP 제어 옵션이 제한적입니다. NVENC는 일부 옵션이 무시되는 이슈가 있어 결과를 ffprobe로 확인 권장.
6. Agora Media Push — 구체 설정
gop 파라미터는 공식 노출됨
Agora Media Push REST API의 transcodeOptions.videoOptions 하위에 gop 파라미터가 있습니다.
| 항목 | 값 |
|---|---|
| 기본값 | frameRate × 2 (기본 frameRate가 15fps이므로 기본 GOP = 30프레임 = 2초) |
| 단위 | 프레임 수 |
| 명시적 설정 | 클라이언트가 송출 대상에 맞춰 직접 계산해 넣어야 함 |
설정 예 — YouTube Live (30fps, 2초 세그먼트)
Rate Control — 라이브에서는 CBR이 정답
GOP만큼 자주 묻는 질문: "비트레이트는 고정해야 하나, 가변으로 해야 하나?"
| 모드 | 동작 | 용도 |
|---|---|---|
| CBR (Constant Bitrate) | 매 순간 비트레이트 고정 | 라이브 스트리밍 |
| VBR (Variable Bitrate) | 복잡한 장면엔 더, 단순한 장면엔 덜 | VOD, 파일 저장 |
| CRF (Constant Rate Factor) | 화질 일정하게 유지, 비트레이트 가변 | 고품질 VOD |
| Capped VBR | VBR이지만 최대 비트레이트 제한 | 라이브 + 화질 우선 |
라이브에서 CBR이 권장되는 이유:
- 네트워크 대역폭 예측 가능 → 시청자 디바이스의 ABR 알고리즘이 정확히 판단 가능
- 시청자 버퍼 관리 안정적 → 갑자기 비트레이트가 폭증하는 일 없음
- CDN 비용 예측 가능 → 정량적 청구
FFmpeg CBR 설정 예:
-x264-params nal-hrd=cbr이 핵심 — 이게 빠지면 x264는 평균 비트레이트만 맞추고 순간 비트레이트는 가변. AWS IVS 같은 엄격한 플랫폼에서는 이 옵션이 없으면 거부될 수 있음.
Agora Media Push 한정: Agora Media Push는 RTMP 송출 시 CBR 모드로 동작하도록 내부적으로 설계되어 있습니다 [NEEDS VERIFICATION].
bitrate파라미터에 설정한 값이 평균이 아니라 목표 CBR로 작동.
Scene-Cut Detection — Agora 내부 동작
[NEEDS VERIFICATION] "Agora Media Push가 내부적으로 scene-cut detection을 자동 비활성화한다"는 공개 문서로 확인되지 않습니다. Agora 내부 지식일 수 있으나 공개 KB에는 명시되어 있지 않음. 안전한 가정: 클라이언트가
gop값을 명시적으로 설정해 고정 GOP가 되도록 한다.
설사 scene-cut OFF가 보장된다 해도, GOP 값 자체는 클라이언트가 정확히 설정해야 세그먼트와 정렬됨. 두 조건이 모두 충족되어야 깨끗한 송출:
- ✓ Scene-cut 비활성화 (가변 GOP 방지)
- ✓ GOP 값이 세그먼트 길이의 정수 약수 (정렬)
잘못된 케이스 예
클라이언트가 gop: 90 (3초)로 설정한 채 YouTube(2초 세그먼트)로 송출:
- Scene-cut은 (가정상) 꺼져 있어서 GOP는 정확히 90프레임으로 고정됨 ✓
- 하지만 세그먼트(2초=60프레임)와 정렬 안 됨 ✗
- → YouTube의 옵션 1 또는 2 폴백 → 지연 또는 화질 저하
- Stream Health에
gopSizeLong경고 발생 가능
7. 클린 정렬 모델이 깨지는 지점들
이 글의 모든 산수는 표준 HLS 기준입니다. 다음 시나리오에서는 모델 자체가 바뀜.
LL-HLS — Part 단위로 쪼갬
LL-HLS는 EXT-X-PART 태그로 세그먼트 안의 sub-segment "part"를 표현합니다. Part target duration은 200~500ms.
이 환경에서는 GOP를 part target duration에 맞춰야 합니다 — "GOP = 세그먼트" 규칙이 "GOP = part target duration"으로 바뀜.
- 30fps + part 333ms → gop = 30 × 0.333 ≈ 10 프레임
- 너무 짧은 GOP는 압축 효율 ↓ → 같은 화질에 비트레이트 ↑
CMAF Chunked Transfer
CMAF는 moof+mdat 청크를 키프레임 대기 없이 flush할 수 있습니다. DASH/LL-DASH에서 GOP-세그먼트 결합도가 약해짐.
LL-DASH는 0.5~2초 chunk가 일반적이며, GOP는 chunk 단위가 아닌 segment 단위에 맞춤.
HLS vs DASH 패키저 차이
| 항목 | HLS | DASH |
|---|---|---|
| 세그먼트 duration 표현 | EXT-X-TARGETDURATION (엄격) | SegmentTemplate + $Time$/$Number$ |
| 가변 GOP 톨러런스 | 낮음 | 비교적 높음 (실제 duration을 명시) |
| 라이브 모드 | 표준 + LL-HLS | 표준 + LL-DASH |
DASH는 각 세그먼트의 실제 duration을 명시할 수 있어 약간의 GOP drift에 더 관대합니다. 멀티 프로토콜 환경에서는 이 차이를 고려.
8. SA 체크리스트 — Media Push 설정 검토
9. 한 줄 결론 + 진단 흐름
GOP는 인코더가 자유롭게 정하는 게 아니라 송출 플랫폼의 세그먼트 길이에 맞춰가는 것.
segment_length / GOP_length가 양의 정수여야 깨끗하다.
트러블 진단 흐름
한 장 요약
관련 글
- 프레임의 모든 것 — I·P·B-frame / GOP / PLI·FIR
- B-frame 완전정복 — 왜 라이브에서 B-frame이 위험한가
- x264 실전 옵션 — ref / tune / keyint — keyint 강제 설정 패턴
- M3U8과 TS 파일의 모든 것 — HLS 세그먼트 구조
- 라이브 스트림 점검 — ffprobe / gstreamer — GOP 검증 명령어
- RTMP B-frame PTS rollback 사례 — Agora Media Gateway 트러블
- ABR Ladder Layer B 정렬 — 키프레임 동기화 — 멀티 레이더에서 GOP 정렬이 ABR 전환에 미치는 영향
- 라이브 스트리밍 프로토콜 선택 — HLS / LL-HLS / WebRTC — 세그먼트 길이가 지연을 결정하는 큰 그림
참고 자료
- RFC 8216 — HTTP Live Streaming — EXT-X-TARGETDURATION, 세그먼트 duration 제약의 원전 (§4.3.3.1, §6.2.1)
- draft-pantos-hls-rfc8216bis — HTTP Live Streaming 2nd Edition — LL-HLS / EXT-X-PART / HOLD-BACK·PART-HOLD-BACK (3배 규칙) 정의
- Apple — HLS Authoring Specification for Apple Devices — 세그먼트 duration 톨러런스, 키프레임 정렬, IDR 권장사항
- YouTube Help — Live streaming error messages — gopSizeLong / gopSizeShort / gopMismatch 등 Stream Health 메시지 정의
- YouTube Help — Choose live encoder settings, bitrates, and resolutions — 2초 키프레임 간격 및 해상도별 비트레이트 권장값
- Agora — Media Push RESTful API — transcodeOptions.videoOptions.gop 등 송출 파라미터 명세
- FFmpeg — x264 / libx264 encoding options — keyint, sc_threshold, nal-hrd=cbr 등 GOP·Rate Control 옵션