Skip to content

SCTE-35 OUT 신호와 START-DATE 사이 8초 지연의 원인 ​

SRT로 들어온 광고 신호와 매니페스트에 찍힌 광고 시작 시각이 최대 8초까지 벌어졌습니다. 범인은 한 명이 아니라 두 명이었고, 그중 한 명은 항상 같은 만큼만 훔쳐 갔습니다.

같은 광고 브레이크, 두 개의 시각 ​

국내 OTT 사업자의 실 운영 채널에서 SSAI 광고 삽입을 검증하던 중이었습니다. SRT로 올라오는 원본 스트림에는 SCTE-35 OUT 큐가 실려 있고, 운영 중인 SSAI 파이프라인은 이 큐를 받아 HLS 매니페스트의 EXT-X-DATERANGE에 옮겨 적습니다. SCTE-35는 방송 신호 안에 광고 전환 지점을 표시하는 큐 포맷이고, EXT-X-DATERANGE는 그 지점을 HLS 플레이어가 읽을 수 있는 시각 정보로 바꿔 광고 구간의 시작과 길이를 알려주는 태그입니다. 이 둘이 정확히 같은 순간을 가리켜야 광고가 원본 방송이 끝나는 지점에 딱 맞춰 붙습니다. 그런데 두 시각이 맞지 않았습니다.

인제스트 쪽에서 신호를 잡은 순간의 로그입니다.

text
[ingest] srt://origin.example.com:9000?streamid=<stream-id>

2026-03-11T04:21:07.318Z  scte35 detected  cue=OUT
                          splice_pts=8143092391
                          planned_duration=508.000
2026-03-11T04:21:07.318Z  estimated_utc=2026-03-11T04:21:04.902Z

같은 브레이크가 매니페스트에는 이렇게 나왔습니다.

m3u8
#EXT-X-PROGRAM-DATE-TIME:2026-03-11T04:21:12.900Z
#EXT-X-DATERANGE:ID="<ad-break-id>",START-DATE="2026-03-11T04:21:12.900Z",PLANNED-DURATION=508.000,SCTE35-OUT=0xFC302F00...
#EXTINF:4.000,
segment_<channel-id>_10482.ts

산수를 해보면 이렇습니다.

text
START-DATE      2026-03-11T04:21:12.900Z
estimated_utc   2026-03-11T04:21:04.902Z
--------------------------------------------
Delta                              +7.998s

8초입니다. 508초짜리 브레이크에서 8초면 비율로는 작아 보이지만, 광고 소재 하나가 15초인 환경에서 8초는 절반이 넘습니다. 브레이크 앞부분에 원본 방송 화면이 8초쯤 남아서 새어 나가는 셈입니다.

측정 기준: 무엇을 무엇과 비교했는가 ​

이런 종류의 이슈는 "무엇을 기준으로 쟀는가"를 흐리게 두면 논쟁만 길어집니다. 그래서 측정에 쓰는 값을 세 개로 못박았습니다.

값정의성격
detection_timeSRT 스트림에서 SCTE-35 패킷을 최초로 감지한 wall clock관측 시각이라 경로 지연이 섞여 있습니다
estimated_utcvideo PTS를 wall clock으로 매핑해서 추정한 splice 시점실제 splice 시점에 가장 가까운 값입니다 (오차 약 0.3초)
START-DATE패키저가 EXT-X-DATERANGE에 써넣은 광고 시작 시각플레이어가 실제로 믿게 되는 값입니다

추적한 지표는 하나입니다.

text
SRT Delta = START-DATE - estimated_utc

detection_time을 기준으로 쓰지 않은 이유가 있습니다. detection_time은 "패킷이 관측 지점에 도착한 시각"이라서 네트워크와 처리 경로가 그대로 섞여 들어갑니다. 반면 estimated_utc는 PTS 기반이라 미디어 타임라인 위에 붙어 있습니다. 지연을 재려면 미디어 타임라인 위의 진짜 splice 지점과 매니페스트에 적힌 값을 비교해야 원인 구간이 드러납니다.

참고: 지연 측정에서 "도착 시각"과 "발생 시각"을 섞으면, 나중에 어느 구간을 고쳐야 하는지 끝까지 나오지 않습니다. PTS 기반 추정값을 하나 만들어 두는 것이 디버깅 비용을 크게 줄여줍니다.

여러 브레이크를 모아보니 범위는 3초에서 8초 ​

한 건만 보면 8초인데, 브레이크를 여러 개 모으면 값이 흔들렸습니다. 대표적인 관측 샘플입니다.

브레이크estimated_utcSTART-DATESRT Delta
A04:21:04.902Z04:21:12.900Z+7.998s
B05:02:33.140Z05:02:36.280Z+3.140s
C06:14:51.005Z06:14:56.500Z+5.495s

흥미로운 건 하한선이 존재한다는 점이었습니다. 아무리 좋은 조건에서도 3초 아래로는 내려가지 않았습니다. 위로는 8초 근처에서 멈췄습니다. "0초에서 8초 사이에서 무작위로 흔들린다"가 아니라 "3초는 무조건 붙고, 거기에 최대 5초가 더 얹힌다"는 모양입니다.

이 모양이 힌트였습니다. 항상 붙는 고정값과 조건에 따라 붙는 가변값은 원인이 다를 수밖에 없습니다.

SRT Delta가 고정 3초와 가변 0초에서 5초의 합으로 구성된 구조를 보여주는 그래프

원인 1: 신호 경로가 두 개라서 생기는 고정 3초 ​

첫 번째는 지연이라기보다 측정 구조의 문제였습니다.

파이프라인 안에서 SCTE-35 태그는 두 경로를 지납니다. 하나는 ingest 계층이 원본 태그를 파싱해 패키저로 넘기고, 패키저가 이 값을 그대로 START-DATE로 써넣는 실제 경로입니다. 다른 하나는 검증을 위해 SRT 모니터링 스트림에서 따로 태그를 감지하는 경로입니다.

SCTE-35 태그가 ingest 계층을 거쳐 패키저로 가는 경로와, 검증용 SRT 모니터링 스트림에서 따로 감지하는 경로로 나뉘는 구조

estimated_utc를 뽑은 곳은 아래쪽, 즉 검증용 SRT 모니터링 스트림입니다. 정작 START-DATE를 결정하는 것은 위쪽 ingest 계층입니다. 두 경로는 버퍼링과 처리 단계가 다르고, 그 차이가 약 3초였습니다.

이 3초는 파이프라인이 광고를 늦게 넣어서 생긴 값이 아니라, 관측 지점이 서로 달라서 생긴 상수 오프셋입니다. 그래서 실제 지연을 논할 때는 이 값을 먼저 차감해야 합니다.

text
실제 삽입 지연 = SRT Delta - 3.0s (경로 오프셋)

브레이크 A: 7.998 - 3.0 = 약 5.0s
브레이크 B: 3.140 - 3.0 = 약 0.14s
브레이크 C: 5.495 - 3.0 = 약 2.5s

주의: 이 오프셋을 모른 채로 "지연 8초"라고 보고하면, 실제로는 5초짜리 문제를 8초짜리로 부풀려서 엉뚱한 곳을 파게 됩니다. 측정계의 고정 오차를 먼저 분리하는 것이 순서입니다.

원인 2: CTS가 크면 광고 시작점을 segment 경계까지 밀어버린다 ​

3초를 빼고 남은 0초에서 5초, 이게 진짜 문제입니다. 이 값이 흔들리는 이유는 원본 스트림의 CTS 였습니다.

CTS는 디코딩 순서와 표시 순서의 차이를 메우는 값입니다. B-frame이 들어간 스트림에서는 먼저 디코딩되지만 나중에 표시되는 프레임이 생기고, 그 간격이 CTS로 표현됩니다.

B-frame이 포함된 스트림에서 디코딩 순서(DTS)와 표시 순서(PTS)가 어긋나며 CTS 오프셋이 발생하는 구조

패키저는 SCTE-35의 splice 지점을 받으면 이 지점이 어느 segment 경계에 해당하는지 판단해야 합니다. 광고는 segment 단위로만 갈아끼울 수 있으니, splice 지점이 segment 중간에 걸리면 어느 한쪽 경계로 붙여야 합니다.

문제는 CTS 값이 클 때 이 판단이 흐려진다는 점입니다. splice 지점을 정확히 짚지 못하면 안전한 쪽을 택해 다음 segment 경계까지 미룹니다. segment 길이가 4초 전후인 환경에서 경계를 한 번 미루면 4초, 판단이 한 번 더 어긋나면 그 이상이 붙습니다. 가변 지연의 상한이 5초 근처에서 형성된 이유입니다.

splice 지점이 이상적인 위치 대신 다음 segment 경계로 밀리는 구조

이 구조를 이해하면 지연이 왜 항상 같은 값이 아닌지도 설명됩니다. splice 지점이 segment 경계에 우연히 가까우면 밀리는 양이 작고(브레이크 B의 0.14초), 경계 직후에 걸리면 거의 한 segment를 통째로 기다립니다(브레이크 A의 5초).

참고: 광고 삽입 지연이 segment 길이의 배수에 가깝게 튀어나오면, 원인은 거의 항상 경계 정렬입니다. 네트워크 지연이라면 이렇게 계단식으로 나오지 않습니다.

시계는 범인이 아니었다 ​

이런 종류의 시간차를 보면 제일 먼저 의심받는 것이 서버 시계입니다. 그래서 먼저 걷어냈습니다.

  • 모니터링 서버와 패키저는 같은 리전에 배치되어 있습니다
  • 양쪽 모두 NTP 동기화 상태입니다
  • 실측 시계 차이는 100ms 미만입니다

100ms는 지금 다루는 3초에서 8초 스케일에 비하면 무의미한 값입니다. 시계 동기화가 측정값에 영향을 주지 않는다는 것을 먼저 확정해 두면, 이후 논의에서 "혹시 시계가 틀린 것 아니냐"는 되돌이표를 없앨 수 있습니다.

이 지연이 뒤에 낳는 문제들 ​

여기서 확인한 OUT 태그 지연은 그 자체로도 문제지만, 이후 다룰 현상들의 뿌리이기도 합니다.

이 지연이 만드는 결과비고
IN 마커가 END-DATE보다 먼저 나타납니다다른 글에서 다룹니다
광고 구간의 PDT 간격과 EXTINF 합이 맞지 않습니다다른 글에서 다룹니다
이미 발행된 매니페스트가 나중에 바뀝니다다른 글에서 다룹니다

OUT의 위치가 밀리면 광고 구간 전체의 좌표계가 함께 밀립니다. 뒤쪽 현상들을 각각 독립된 버그로 취급하면 근본 원인에 닿지 못합니다.

지금까지 정리한 것과 남은 것 ​

지연을 두 조각으로 쪼개고 나면 대응이 갈립니다.

조각크기성격대응
신호 경로 오프셋약 3초 고정관측 구조에서 오는 상수측정할 때 차감하면 끝입니다
segment 경계 밀림0초에서 5초 가변CTS 기반 splice 판단 정확도 문제실제로 고쳐야 하는 부분입니다

고쳐야 할 것은 두 번째입니다. splice 지점을 CTS가 큰 스트림에서도 정확히 짚어내면, 다음 경계로 통째로 미루는 회피 동작이 사라지고 가변 지연이 줄어듭니다.

정리 ​

  • SRT로 들어온 SCTE-35 OUT의 splice 시점과 매니페스트의 START-DATE 사이에 3초에서 8초의 차이가 관측됐습니다.
  • 이 차이는 두 성분의 합입니다. 관측 경로가 달라서 생기는 고정 3초와, splice 지점을 segment 경계로 밀면서 생기는 0초에서 5초의 가변분입니다.
  • 지연을 잴 때는 detection_time(도착 시각)이 아니라 PTS 기반 estimated_utc(발생 시각)를 기준으로 삼아야 원인 구간이 드러납니다.
  • 가변분의 원인은 CTS가 큰 스트림에서 splice 지점 판단이 흐려져 다음 segment 경계까지 미루는 동작입니다. 지연이 segment 길이의 배수처럼 튀는 것이 그 증거입니다.
  • 시계 동기화는 원인이 아닙니다(차이 100ms 미만). 이를 먼저 배제해 두면 조사 범위가 크게 줄어듭니다.

基于 VitePress 构建 · 部署于腾讯云 EdgeOne Pages