Agora SEI 메타데이터 — H.264·H.265 Payload 해석
H.264·H.265 SEI NAL unit의 payload type과 payload size를 읽는 방법을 설명합니다. Agora Media Push가 제공하는 metadata 형식은 공식 계약에 맞춰 처리하고, Annex B와 AVCC 입력을 구분해 emulation-prevention byte 제거, payload type 100·송출 경로·JSON schema를 확인합니다.
목차(40개 항목)
- 0. 핵심 명제 — SEI는 비디오 bitstream에 결합된 메타데이터
- 1. SEI란 무엇인가 — H.264/H.265 표준의 일부
- 2. 왜 시그널링 대신 SEI를 쓰나
3. Agora SEI의 JSON 구조
4. SEI 바이너리 구조 — H.264 표준 + Agora 커스텀
6. NAL 스트림 포맷 — Annex B vs AVCC
7. 시청자 측 파싱 흐름
- 9. SEI vs 다른 동기화 메커니즘 — 의사결정
- 11. SA 체크리스트
12. 한 줄 결론
- 관련 글
- 참고 자료
"Agora Media Push로 합성 송출 받는 시청자 앱에서, 화면에 누가 어디 있는지 어떻게 알아내요?", "RTM으로 보내려니 비디오 프레임과 타이밍이 어긋나는데요". 답은 비디오 스트림 안에 메타데이터를 박아 보내는 SEI (Supplemental Enhancement Information).
이 글은 H.264/H.265 표준의 SEI가 무엇인지, Agora Media Push가 어떤 JSON을 어떤 바이트 포맷으로 박아 주는지, 시청자 측 파서가 어떻게 풀어내야 하는지를 정리합니다.
0. 핵심 명제 — SEI는 비디오 bitstream에 결합된 메타데이터
SEI는 H.264/H.265 bitstream 안에 부가 정보를 싣는다. 별도 시그널링보다 비디오 timeline에 결합하기 쉽지만, 중간 트랜스코더가 SEI를 보존하거나 클라이언트가 처리한다고 보장되지는 않는다.
흔한 오해와 정정:
| 오해 | 정확한 이해 |
|---|---|
| SEI는 Agora 전용 기술 | H.264/H.265(및 MPEG-2 video)의 표준 기능. 단 모든 비디오 코덱이 지원하는 건 아님 — VP9/AV1 등에는 SEI가 없고 각각 WebM BlockAddID(ITU-T T.35) / Metadata OBU(metadata_itut_t35) 같은 별도 메타데이터 메커니즘을 씀 |
| SEI를 쓰면 시그널링 불필요 | SEI는 레이아웃 정보, 시그널링은 업링크 데이터 — 둘은 다른 채널 |
| RTMP 위에 SEI 못 박음 | RTMP/FLV는 AVCC 포맷이지만 SEI NAL은 동일하게 실림 |
| 모든 디코더가 SEI를 앱에 전달 | SEI 처리는 선택적이며 플레이어·디코더 API마다 노출 범위가 다름 |
1. SEI란 무엇인가 — H.264/H.265 표준의 일부
SEI = Supplemental Enhancement Information. ITU-T H.264 / ISO/IEC 14496-10의 Annex D에 정의된 부가 정보 영역.
핵심 특성:
| 특성 | 설명 |
|---|---|
| 표준성 | H.264/H.265 표준에 정의 — 모든 컴플라이언트 인코더/디코더가 알고 있는 형식 |
| 비강제성 | 디코더는 SEI를 처리할 의무가 없음 — 무시해도 디코딩 정상 |
| 비디오 결합 | access unit 또는 bitstream 안에서 영상 timing과 함께 운반 가능 |
| 페이로드 자유도 | payload_type별로 표준 의미가 정해져 있고, type 5 = user_data_unregistered가 자유 데이터용 |
Annex D의 의미: SEI 메시지 문법과 semantics를 정의하지만, 모든 메시지를 애플리케이션에 노출해야 한다는 뜻은 아닙니다. 필요한 SEI가 전체 ingest→transcode→package→player 경로에서 보존되는지 시험합니다.
2. 왜 시그널링 대신 SEI를 쓰나
라이브 송출에서 자주 부딪히는 문제:
별도 시그널링과 비디오는 서로 다른 네트워크 경로를 거치므로 타이밍이 맞지 않음. "이 프레임에 호스트 A가 좌상단에 있다"는 정보가 비디오보다 0.4초 늦게 도착하면, 시청자 UI가 잘못된 위치를 클릭 가능 영역으로 인식.
SEI 해결책: 메타데이터를 NAL 스트림 안에 박아서 같은 프레임과 같이 도착시킴.
뉘앙스: SEI의 적용 시점은 SEI message semantics와 access unit 배치에 따라 해석해야 합니다. 네트워크·컨테이너·트랜스코딩 단계에서 누락될 수 있으므로
ts와 비디오 timestamp를 함께 기록하고 폴백을 둡니다.
3. Agora SEI의 JSON 구조
Agora Media Push는 트랜스코딩된 H.264/H.265 스트림에 자동으로 SEI를 추가합니다 — 클라이언트 별도 설정 불필요.
최상위 필드
| 필드 | 설명 |
|---|---|
canvas | 전체 합성 화면(캔버스) 정보 |
regions | 캔버스 위에 배치된 각 호스트의 레이아웃 (배열) |
ver | SEI 프로토콜 버전 (현재 문서 값 "20190611") |
ts | 인코딩 시점의 타임스탬프 (ms) |
app_data | 사용자 정의 추가 정보 (transcodingExtraInfo에 대응) |
canvas 필드
| 키 | 의미 | LiveTranscoding 대응 |
|---|---|---|
w | 캔버스 너비 (px) | width |
h | 캔버스 높이 (px) | height |
bgnd | 배경색 (RGB hex) | backgroundColor |
regions 배열의 각 요소 (호스트별)
| 키 | 의미 | TranscodingUser 대응 |
|---|---|---|
uid | 호스트 UID | uid |
suid | 문자열 계정 (선택) | — |
alpha | 투명도 [0.0 ~ 1.0] | alpha |
zorder | 레이어 순서 [0 ~ 100] | zOrder |
volume | 음량 dB [0 ~ 100] | — |
x, y | 좌상단 기준 위치 | x, y |
w, h | 비디오 프레임 크기 (px) | width, height |
실제 페이로드 예시
이 JSON 전체가 SEI 페이로드로 들어갑니다.
4. SEI 바이너리 구조 — H.264 표준 + Agora 커스텀
SEI의 첫 몇 바이트가 핵심입니다. 예시:
바이트별 의미
| 바이트 | 의미 |
|---|---|
06 | NAL 헤더 — forbidden_zero_bit(1) + nal_ref_idc(2) + nal_unit_type(5) = 0 00 00110 → SEI |
64 | payload_type = 100 (Agora 커스텀 선택) |
bd | payload_size = 189 바이트 (이 예시 한정 — 단일 바이트로 표현 가능한 길이) |
NAL 헤더 0x06 분해
NAL unit type 6 = SEI, 표준 정의. RFC 6184도 SEI NAL은 nal_ref_idc=0이어야 한다고 명시.
payload_type 100의 정확한 위치
Agora 공식 문서는 이 형식에서 payload type을 user-defined 값 100으로 정의합니다. 일반적인 vendor-neutral 사용자 데이터에는 type 5 user_data_unregistered와 UUID를 쓰는 방식도 있습니다.
따라서 payload type 100만 보고 모든 H.264 스트림을 Agora 형식으로 해석하지 말고, 송출 경로와 JSON schema를 함께 검증합니다.
| payload_type | 표준 의미 |
|---|---|
| 0 | buffering_period |
| 1 | pic_timing |
| 5 | user_data_unregistered (UUID 네임스페이싱 — 표준 커스텀 데이터) |
| 22 | post_filter_hint |
| 100 | 표준 미정의값 (Agora가 점유, 공식 문서상 user-defined) |
| 136 | time_code |
NAL 스트림에서의 연관 관계
이 배열은 설명용 예시입니다. 실제 bitstream에는 여러 SEI message가 있을 수 있고, H.264/H.265의 prefix·suffix 및 access unit 규칙에 따라 위치가 달라집니다.
5. 0xFF 누적 길이 인코딩
payload_type과 payload_size 모두 H.264 표준의 동일한 가변 길이 인코딩을 씁니다.
인코딩 규칙
→ 0xFF가 나오면 255씩 누적, 0xFF가 아닌 마지막 바이트의 값을 더해 종료.
예시
| 길이 | 인코딩 | 분해 |
|---|---|---|
| 234 | EA | 234 < 255 → 단일 바이트 |
| 572 | FF FF 3E | 2 × 255 + 62 = 572 |
| 922 | FF FF FF 9D | 3 × 255 + 157 = 922 |
| 1500 | FF FF FF FF FF E1 | 5 × 255 + 225 = 1500 |
파서 의사 코드
실제 파서는 NAL header 차이, RBSP trailing bits, 여러 SEI message, buffer bounds, emulation-prevention byte 제거를 추가로 처리해야 합니다.
6. NAL 스트림 포맷 — Annex B vs AVCC
같은 SEI NAL이라도 어떤 컨테이너에 담기느냐에 따라 framing이 다릅니다.
Annex B 포맷 (raw byte stream·일부 MPEG-TS 처리 경로)
- 4바이트 start code(
00 00 00 01) 또는 3바이트(00 00 01)로 NAL 경계 표시 - byte stream을 스캔해서 start code 찾으면 NAL 시작
length-prefixed 포맷 (MP4·FLV 처리 경로)
- start code 없음 — avcC의
lengthSizeMinusOne에 따른 길이 prefix로 NAL 크기 명시(흔히 4바이트) - length만큼 읽고 다음 NAL로 점프
자주 틀리는 포인트: transport 이름만 보고 Annex B/length-prefix를 단정하면 안 됩니다. RTMP의 AVC video packet을 FLV demux한 뒤 codec configuration에서 NAL length size를 읽어 경계를 찾습니다.
| 컨테이너 | 포맷 | NAL 경계 표시 |
|---|---|---|
| MPEG-TS의 H.264 byte stream | Annex B | start code (00 00 00 01) |
| MP4, MOV | AVCC | 4바이트 length prefix |
| RTMP, FLV | AVCC | 4바이트 length prefix |
| RTP/H.264 | RFC 6184 packetization | depacketize 후 NAL unit 복원; start code는 RTP 규격 일부가 아님 |
7. 시청자 측 파싱 흐름
전체 파서 흐름을 한 장에 정리.
Swift / iOS 파서 예시
아래 코드는 흐름을 보여 주는 축약 예시입니다. production parser는 먼저 framing을 제거하고 EBSP→RBSP 변환과 bounds check를 끝낸 nalUnit만 받아야 합니다.
주의사항 — emulation_prevention_byte
H.264 NAL 페이로드에서 00 00 00이 우연히 등장하면 인코더가 00 00 03 00으로 escape합니다 (start code 충돌 방지). 파싱 시 0x03을 제거해야 원본 페이로드가 됩니다.
escape byte의 존재 여부는 실제 payload 바이트 패턴에 달려 있습니다. NAL의 EBSP를 RBSP로 되돌린 뒤 SEI payload를 해석합니다.
8. 활용 패턴
8-1. 클릭 가능 영역 매핑 (Agora의 표준 사용 케이스)
8-2. app_data 필드 활용 — 자유 메타데이터
app_data는 고객이 자유롭게 쓸 수 있는 필드. 예:
| 활용 | app_data 예시 |
|---|---|
| 자막 큐 | {"caption":"안녕하세요","start":1683195420123} |
| 광고 트리거 | {"ad":"midroll","duration":30} |
| 채팅 동기화 | {"chat_seq":4521} |
| 챕터 마커 | {"chapter":"intro"} |
bitstream과 함께 전달할 가치가 있는 작은 메타데이터의 후보입니다. 자막·광고처럼 별도 규격과 신뢰성 요구가 있는 기능은 전용 timed-metadata 방식과 비교합니다.
8-3. 정확도 한계
- Agora 문서는 Media Push의 트랜스코딩된 H.264/H.265 출력에 기본 encoding information SEI를 추가한다고 설명함
- 시청자 디바이스의 SEI 파싱이 실패해도 비디오 재생은 정상 — 폴백 안전
- 공개 문서는 삽입 주기를 명시하지 않으므로 매 프레임·키프레임 주기를 가정하지 말고 출력에서 측정
9. SEI vs 다른 동기화 메커니즘 — 의사결정
| 메커니즘 | 시간 결합 방식 | 페이로드 | 전달 특성 | 용도 |
|---|---|---|---|---|
| SEI | bitstream timeline에 결합 | 작게 유지 | transcoding·packaging 전후의 SEI를 추출해 보존 여부 측정 | 레이아웃, encoding 정보 |
| 별도 RTM/WebSocket | 앱 timestamp·sequence로 결합 | 자유 | 미디어와 독립된 재시도·순서 정책 | 채팅, 업링크 데이터 |
| RTP timestamp 기반 외부 채널 | 공통 clock mapping 필요 | 자유 | 두 경로의 손실·지연을 함께 처리 | A/V 외부 큐 |
| 픽셀 워터마크 | 영상에 직접 합성 | 매우 제한적 | 트랜스코딩 후에도 보일 수 있음 | 출처 표시, forensic marker |
결론:
- 비디오 bitstream과 함께 운반해야 하는 작은 메타데이터 → SEI 보존성 검증 후 사용
- 자유 형식, 양방향, 빈번한 메시지 → RTM/WebSocket
- 둘은 보완 관계, 대체재 아님
10. FAQ
Q1. SEI를 쓰면 시그널링 인프라 필요 없나?
아닙니다. 이 SEI에 들어가는 건 다운링크 encoding·레이아웃 정보입니다. 업링크 채팅이나 호스트 명령에는 별도 시그널링이 필요합니다. app_data만 일부 용도가 겹칠 수 있습니다.
Q2. SEI가 디코딩에 영향 주나?
SEI 처리는 선택적이므로 알 수 없는 메시지를 무시할 수 있습니다. 다만 잘못된 길이·RBSP를 안전하게 처리하지 못하는 자체 파서는 오류나 크래시를 낼 수 있으므로 bounds check와 fuzz test가 필요합니다.
Q3. SEI가 매 프레임마다 박히나?
Agora 공개 문서는 삽입 cadence를 명시하지 않습니다. 실제 출력에서 빈도와 변경 시점을 측정하고, 미수신·지연·중복 시의 TTL과 기본 레이아웃을 정의합니다.
Q4. RTC 원본 스트림에도 SEI가 있나?
이 글의 JSON schema는 Agora가 문서화한 Media Push 트랜스코딩 출력 형식입니다. 다른 RTC 전송 경로에 같은 형식이 있다고 일반화하지 않습니다.
Q5. payload_type 100을 쓰면 다른 디코더와 충돌하나?
이론상 가능. 다른 인코더가 같은 type 100을 다른 의미로 사용 중이면 충돌. 표준 권장은 type 5 (user_data_unregistered) + UUID 네임스페이싱이지만, Agora는 type 100 직접 점유 — 실무에서 큰 이슈는 없으나 다른 SEI 파서가 type 100을 잘못 해석할 가능성은 있음.
Q6. H.265(HEVC)에서도 같은 형식인가?
H.265 SEI도 같은 Annex D 모델 + 0xFF 누적 길이 인코딩 사용. NAL header 형식은 다름(2바이트). payload_type/payload_size 인코딩은 동일.
11. SA 체크리스트
12. 한 줄 결론
SEI는 H.264/H.265 bitstream의 부가 정보 메커니즘이다. Agora Media Push 문서는 트랜스코딩 출력의 payload type 100에 encoding 정보 JSON을 넣는 형식을 정의한다. 파서는 framing·RBSP·길이·schema를 방어적으로 처리하고 전체 경로의 보존성을 검증해야 한다.
한 장 요약
관련 글
- GOP-세그먼트 정렬의 산수 — Layer A 시간축 정렬
- ABR Ladder의 Layer B 정렬 — 화질 전환 시 키프레임 동기화
- H.264 Profile·인코더 옵션·비트레이트의 현실 — H.264 NAL 구조 기초
- 프레임의 모든 것 — I/P/B-frame과 NAL의 관계
- FFmpeg, 미디어의 스위스 아미 나이프 — NAL 디먹스
- 녹화 모드 해부 — Individual / Mix / Web — 트랜스코딩 옵션과 SEI 생성 시점
참고 자료
- ITU-T H.264 — Advanced video coding for generic audiovisual services — SEI 및 Annex D를 정의하는 1차 표준 문서
- ITU-T H.265 (HEVC) — High efficiency video coding — H.265 SEI 모델(2바이트 NAL 헤더, 동일한 0xFF 누적 길이 인코딩)
- RFC 6184 — RTP Payload Format for H.264 Video — NAL 단위/NRI(nal_ref_idc) 의미 및 RTP 패킷화 규정
- MS-H264PF: Microsoft H.264 Profile — Stream Layout SEI Message — 벤더가 커스텀 SEI 메시지 타입을 점유해 레이아웃 메타데이터를 싣는 실제 사례
- Agora Docs — Agora SEI information — JSON schema, payload type 100, 0xFF 누적 길이 형식의 공식 레퍼런스
- FFmpeg Documentation — NAL 디먹스/SEI 추출 및 Annex B ↔ AVCC 변환 실무 도구