광고 구간에서 PDT 간격과 EXTINF 합이 어긋나는 이유
508초 마커에 15초짜리 광고를 채우면 495초에서 멈춥니다. 남은 13초가 PDT와 EXTINF를 갈라놓습니다. 이건 버그이기도 하고, 산술적 한계이기도 합니다.
먼저 안 맞는 걸 직접 봅니다
광고 구간에서 두 값을 각각 더해봤습니다. 하나는 EXT-X-PROGRAM-DATE-TIME의 간격이고, 하나는 EXTINF duration의 합입니다. HLS에서 이 둘은 이론적으로 같은 시간의 흐름을 가리켜야 합니다.
#EXT-X-PROGRAM-DATE-TIME:2026-03-11T04:21:12.900Z
#EXT-X-DISCONTINUITY
#EXTINF:15.000,
ad_<creative-id>_001.ts
#EXTINF:15.000,
ad_<creative-id>_002.ts
...
#EXTINF:15.000,
ad_<creative-id>_033.ts
#EXT-X-PROGRAM-DATE-TIME:2026-03-11T04:29:27.900Z
#EXT-X-DISCONTINUITY
#EXTINF:4.000,
segment_<channel-id>_10609.ts계산해 보면 이렇습니다.
PDT 간격 04:29:27.900 - 04:21:12.900 = 495.000s
EXTINF 합 15.000 x 33 = 495.000s
차이 0.000s (여기는 맞습니다)
그런데 마커가 선언한 길이는
PLANNED-DURATION = 508.000s
---------
남는 갭 13.000s광고 자체는 495초를 정확히 채웠습니다. PDT와 EXTINF도 일치합니다. 문제는 마커가 508초를 예약했는데 실제로 들어간 게 495초라는 것입니다. 그리고 이 13초가 원본 스트림으로 복귀하는 순간 문제를 일으킵니다.
13초는 어디서 왔나: 나눗셈이 안 떨어진다
광고 소재의 단위 길이는 15초입니다. 마커가 요구한 길이는 508초입니다.
508 / 15 = 33.8666...
33개 넣으면 33 x 15 = 495s -> 13초 부족
34개 넣으면 34 x 15 = 510s -> 2초 초과어느 쪽도 508이 아닙니다. 광고는 segment 단위로만 채워 넣을 수 있으니 중간값이 없습니다. "하나 더 넣으면 초과, 하나 덜 넣으면 부족" 상황이고, 파이프라인은 초과보다 부족을 택합니다. 원본 방송을 덮어버리는 게 광고를 조금 덜 트는 것보다 나쁘기 때문입니다.
| 선택 | 결과 | 부작용 |
|---|---|---|
| 33개 (495s) | 13초 부족 | 원본 복귀 지점이 마커 경계와 안 맞습니다 |
| 34개 (510s) | 2초 초과 | 원본 방송의 앞 2초를 덮어씁니다 |
참고: 이건 로직 버그가 아니라 나눗셈입니다. 마커 duration이 광고 단위 길이의 정수배가 아니면 갭은 원리적으로 생깁니다. 없앨 수 있는 건 갭 자체가 아니라 갭이 만드는 2차 증상입니다.
그 13초가 PDT를 어긋나게 하는 경로
여기서 파이프라인이 광고 구간의 PDT를 어떻게 계산하는지 알아야 합니다. 공식은 이렇습니다.
광고 segment PDT = (avail 시작점 원본 segment의 PDT) + (누적 광고 duration)말로 풀면, avail이 열린 원본 segment의 PDT를 기준점으로 박아두고, 거기에 지금까지 삽입한 광고 길이를 더해서 현재 광고 segment의 PDT를 만듭니다. 이 방식은 광고 구간 내부에서는 아주 잘 맞습니다. 실제로 위 매니페스트에서 PDT 간격과 EXTINF 합이 0.000초로 일치한 이유가 이것입니다. 두 값이 같은 숫자(누적 광고 duration)에서 파생됐으니 어긋날 수가 없습니다.
어긋나는 건 광고가 끝나고 원본으로 돌아오는 경계입니다.
원본 스트림은 그동안 508초어치 시간을 흘려보냈습니다. 그런데 광고 구간의 PDT는 495초만 흐른 것으로 계산됐습니다. 복귀하는 첫 원본 segment는 이 두 좌표계 사이 어딘가에 걸리고, 그 segment의 경계는 어느 쪽에도 정확히 맞지 않습니다. 그 결과 광고 구간을 포함해서 누적을 다시 재보면 PDT 기반 값과 EXTINF 누적값이 갈립니다.
딱 맞을 때는 문제가 없다
반대 경우를 보면 구조가 더 선명해집니다. 마커의 PLANNED-DURATION과 실제 삽입 duration이 정확히 같으면 어떨까요.
마커 = 495s, 광고 33개 x 15s = 495s
PDT 기반 누적 : 기준점 + 495s
EXTINF 누적 : 495s
마커 경계 : 495s
-> 세 값이 모두 만난다세 개가 한 점에서 만나니 어긋날 곳이 없습니다. 이 조건에서 위 PDT 공식은 가장 정확한 결과를 냅니다.
| 조건 | PDT vs EXTINF | 원본 복귀 경계 |
|---|---|---|
| 마커 duration = 광고 단위의 정수배 | 일치 | 깔끔하게 맞습니다 |
| 정수배가 아님 (508 vs 495) | 갭 발생 | 마지막 원본 segment가 경계에 안 걸립니다 |
참고: SSAI 검증 환경을 만들 때 마커 duration을 광고 단위 길이의 정수배로 잡으면 이 계열 문제가 아예 안 나옵니다. 반대로 재현하고 싶으면 508처럼 안 떨어지는 값을 쓰면 됩니다.
광고가 없는 구간에도 원래 편차는 있다
여기서 짚어둘 게 있습니다. SSAI가 개입하지 않은 순수 원본 라이브 구간에서도 PDT와 EXTINF는 완벽히 일치하지 않습니다. 작은 편차가 늘 있습니다. 그리고 그게 정상입니다.
두 값의 정체가 다르기 때문입니다.
| 태그 | 무엇을 나타내는가 | 어디서 오는가 |
|---|---|---|
EXT-X-PROGRAM-DATE-TIME | 이 segment가 대응하는 실제 시각 | wall clock 계열. 인제스트 시점의 시스템 시각 |
EXTINF | 이 segment 안에서 영상이 흐르는 시간 | media timeline. 프레임 수와 frame rate |
인코더의 클럭과 서버의 wall clock은 완전히 같은 속도로 흐르지 않습니다. frame rate가 29.97처럼 정수가 아니면 segment 길이도 딱 떨어지지 않습니다. 그래서 긴 시간 누적하면 두 값은 조금씩 벌어집니다. PDT는 "지금 몇 시인가"를 나타내는 wall clock이고, EXTINF는 "영상이 얼마나 흘렀나"를 나타내는 media timeline입니다. 두 시계는 원래 같은 속도가 아닙니다.
주의: 이 구분을 안 하면 "PDT와 EXTINF가 0.04초 다르다"는 관측을 버그로 오해합니다. 광고 구간의 13초급 갭과, 원본 구간의 밀리초급 편차는 원인이 완전히 다른 이야기입니다.
다른 구현은 어떻게 하나: MediaTailor의 방식
같은 문제를 AWS MediaTailor는 다르게 접근합니다. 광고 구간의 PDT를 계산하지 않고, 원본 스트림의 해당 위치 PTS를 그대로 가져다 씁니다.
방식 A (누적 duration 기반)
광고 PDT = avail 시작 원본 segment PDT + 누적 광고 duration
-> 광고 구간 내부는 EXTINF와 정확히 일치
-> 원본 복귀 경계에서 갭
방식 B (원본 PTS 직접 사용, MediaTailor)
광고 PDT = 그 위치의 원본 스트림 PTS
-> 원본 타임라인과의 연결은 자연스러움
-> 광고 segment 길이와 원본 segment 길이가 다르므로 구간 내부에서 갭방식 B는 광고 구간 안에서 PDT가 원본의 시간 진행을 따라가는데, 광고 segment의 EXTINF는 광고 소재의 길이입니다. 두 값이 애초에 다른 원천에서 나오니 이론적으로는 갭이 더 크게 벌어집니다. 누적 duration 기반 방식이 광고 구간 내부의 정합성 측면에서 유리합니다. 어느 쪽이 절대적으로 옳다고 말할 문제는 아닙니다. 트레이드오프가 다릅니다.
| 관점 | 누적 duration 기반 | 원본 PTS 기반 |
|---|---|---|
| 광고 구간 내부 PDT vs EXTINF | 일치합니다 | 갭이 생깁니다 |
| 원본 복귀 경계 | 갭이 남습니다 | 원본 좌표와 연속적입니다 |
| 플레이어의 광고 구간 seek 정확도 | 유리합니다 | 불리합니다 |
중간 segment의 PDT 누락도 같은 맥락이다
SCTE-35 OUT 지연을 다룬 글에서 언급한 PDT 누락도 여기서 이어집니다. 광고 구간에서는 소재의 시작과 끝에만 PDT를 넣고, 중간 segment에는 넣지 않습니다.
#EXT-X-PROGRAM-DATE-TIME:2026-03-11T04:21:12.900Z <- 시작: 있음
#EXTINF:15.000,
ad_<creative-id>_001.ts
#EXTINF:15.000,
ad_<creative-id>_002.ts <- 중간: 없음
#EXTINF:15.000,
ad_<creative-id>_003.ts <- 중간: 없음
...
#EXT-X-PROGRAM-DATE-TIME:2026-03-11T04:29:27.900Z <- 끝: 있음MediaTailor도 시작과 끝에만 PDT를 넣는 같은 방식입니다. 즉 이건 특이한 선택이 아니라 SSAI 구현의 일반적인 관행입니다. 플레이어는 PDT 하나를 기준으로 EXTINF를 누적해서 나머지 위치를 계산할 수 있으니 재생에는 문제가 없습니다.
다만 검증 스크립트를 짤 때는 이걸 알고 있어야 합니다. 모든 segment에 PDT가 있다고 가정하면 광고 구간에서 파싱이 깨집니다. 필요하면 모든 광고 segment에 PDT를 붙이도록 켤 수 있고, 측정 정확도를 위해서는 그 옵션을 쓰는 편이 낫습니다.
어디까지가 고칠 수 있는 문제인가
정리하면 세 층이 겹쳐 있습니다.
| 층 | 원인 | 고칠 수 있나 |
|---|---|---|
| 마커 duration이 광고 단위로 안 떨어짐 | 산술적 한계 | 못 고칩니다. 갭은 남습니다 |
| 원본 복귀 경계에서 PDT vs EXTINF 갭 | 위 갭의 파생 | 완화 가능합니다. 마지막 소재 길이 조정 등 |
| 원본 구간의 밀리초급 편차 | wall clock vs media timeline | 고칠 대상이 아닙니다. 정상입니다 |
| 광고 구간의 좌표계 자체가 밀림 | OUT 태그 지연 | 고쳐야 합니다 |
맨 아래 항목이 중요합니다. OUT 태그 지연이 남아 있으면 광고 구간의 기준점(avail 시작 원본 segment PDT)부터 밀린 상태로 계산이 시작됩니다. 그러면 위 갭 계산이 아무리 정확해도 결과는 어긋납니다. 그래서 순서는 OUT 지연을 먼저 잡는 것입니다. 그리고 이렇게 어긋난 좌표계는 매니페스트가 발행된 뒤에도 문제를 만듭니다. 이미 내보낸 플레이리스트의 같은 위치가 나중에 다른 내용으로 바뀌는 현상으로 이어집니다.
정리
- 광고 구간의 PDT는 "avail 시작 원본 segment PDT + 누적 광고 duration"으로 계산됩니다. 이 방식은 광고 구간 내부에서는
EXTINF합과 정확히 일치합니다. - 어긋나는 지점은 원본 복귀 경계입니다. 마커가 508초를 예약했는데 15초 단위 소재로는 495초까지만 채워지고, 남은 13초가 두 좌표계를 갈라놓습니다.
- 이 갭은 로직 버그가 아니라 나눗셈의 결과입니다. 마커 duration이 광고 단위 길이의 정수배면 PDT, EXTINF, 마커 경계가 한 점에서 만납니다.
- SSAI가 없는 원본 구간의 밀리초급 편차는 정상입니다. PDT는 wall clock, EXTINF는 media timeline이라 원래 속도가 다릅니다.
- MediaTailor는 원본 PTS를 직접 쓰는 방식이라 광고 구간 내부 갭이 이론적으로 더 큽니다. 광고 구간 정합성과 원본 연속성 사이의 트레이드오프입니다.