SCTE-35 and VAST: Ad Signaling from Splice to Beacon
How SCTE-35 splice_insert, time_signal, and segmentation descriptors surface in HLS DATERANGE/CUE-OUT and DASH, plus VAST/VMAP ad tags, wrappers, and tracking.
Ad insertion has two distinct halves that engineers often conflate. SCTE-35 answers "where and when is an ad opportunity?" — it marks the break in the stream. VAST/VMAP answers "what ad plays, and how do I get paid?" — it delivers the creative and the tracking. This post walks the full chain from the splice marker to the impression beacon. For how these plug into client- vs server-side architectures, see SSAI vs CSAI; inspect live signals with the SSAI tool and ad responses with the VAST viewer.
SCTE-35: marking the opportunity
SCTE-35 is the broadcast standard for signaling ad insertion points. It
originated in MPEG transport streams, where it rides as a splice_info_section
on its own PID. The two command types you'll meet most:
splice_insert
The legacy, explicit command: "splice out now, for this duration." It carries:
splice_event_id— identifies the break.out_of_network_indicator—1= leaving the network feed for an ad (CUE-OUT),0= returning (CUE-IN).splice_time(PTS) — when the splice happens.break_duration— how long the avail is.
time_signal + segmentation_descriptor
The modern, richer approach. A time_signal provides a PTS, and one or more
segmentation_descriptors add semantics via a segmentation_type_id:
0x34— Provider Advertisement Start,0x35— Provider Ad End0x36— Distributor Advertisement Start,0x37— Distributor Ad End0x30/0x31— Provider Placement Opportunity Start/End0x10/0x11— Program Start/End
The descriptor also carries a segmentation_upid (e.g. an Ad-ID or a UPID type
identifying the asset) and a segmentation_duration. This is what lets an ad
decision system know what kind of break it is, not just that one exists.
A decoded SCTE-35 message conceptually looks like:
splice_info_section
splice_command_type = time_signal
pts_time = 1809000000
segmentation_descriptor
segmentation_event_id = 0x4800008e
segmentation_type_id = 0x34 (Provider Ad Start)
segmentation_duration = 30s
segmentation_upid_type = 0x09 (ADI)
You can paste a base64/hex SCTE-35 payload into the SSAI tool to decode the command and descriptors.
How SCTE-35 surfaces in HLS
When that transport stream is packaged to HLS, the SCTE-35 is translated into playlist tags.
CUE-OUT / CUE-IN
The simple, widely supported span markers:
#EXT-X-CUE-OUT:30.000
#EXTINF:6.000,
ad_0.ts
#EXTINF:6.000,
ad_1.ts
...
#EXT-X-CUE-IN
CUE-OUT opens the avail with a duration; CUE-IN closes it. Some packagers
also emit #EXT-X-CUE-OUT-CONT lines mid-break so a player joining late knows
it's inside an avail.
EXT-X-DATERANGE (carrying raw SCTE-35)
The modern, richer carrier — it preserves the binary SCTE-35 so downstream systems get full fidelity:
#EXT-X-DATERANGE:ID="ad-8e",START-DATE="2026-05-16T12:00:00Z",\
DURATION=30.0,SCTE35-OUT=0xFC302F000000000000FFF...
SCTE35-OUT / SCTE35-IN hold the splice payload; SCTE35-CMD carries a
time_signal. DATERANGE is preferred for new deployments because it doesn't
lose the segmentation semantics that CUE-OUT flattens.
How SCTE-35 surfaces in DASH
DASH represents avails two ways:
- Multi-period — each ad break becomes a distinct
<Period>in the MPD, the cleanest model for SSAI. - In-band/MPD events — an
<EventStream>(in the MPD) oremsg(in-segment) carrying the SCTE-35 signal:
<EventStream schemeIdUri="urn:scte:scte35:2013:xml" timescale="90000">
<Event presentationTime="1809000000" duration="2700000" id="142">
<scte35:Signal>
<scte35:Binary>/DAvAAAAAAAA///wFAVI...</scte35:Binary>
</scte35:Signal>
</Event>
</EventStream>
The player or SSAI service decodes the Binary to recover the same
splice_info_section it would have in HLS.
VAST: the ad and its tracking
Once you know a break exists, you ask an ad decision server for creatives. It
responds with VAST (Video Ad Serving Template) — an IAB XML format. The
essentials of a VAST InLine ad:
MediaFiles— the actual creative renditions (URLs, codecs, bitrates).Duration— creative length.TrackingEvents— the URLs to fire as playback progresses.VideoClicks— clickthrough and click tracking.Impression— the headline "ad was served" beacon.
<VAST version="4.2">
<Ad id="1234">
<InLine>
<Impression><![CDATA[https://adsrv.example/imp?id=1234]]></Impression>
<Creatives>
<Creative>
<Linear>
<Duration>00:00:15</Duration>
<TrackingEvents>
<Tracking event="start">https://adsrv.example/e?ev=start</Tracking>
<Tracking event="firstQuartile">https://adsrv.example/e?ev=q1</Tracking>
<Tracking event="midpoint">https://adsrv.example/e?ev=mid</Tracking>
<Tracking event="thirdQuartile">https://adsrv.example/e?ev=q3</Tracking>
<Tracking event="complete">https://adsrv.example/e?ev=done</Tracking>
</TrackingEvents>
<MediaFiles>
<MediaFile type="video/mp4" bitrate="2500" width="1280" height="720">
<![CDATA[https://cdn.example/creative_720.mp4]]>
</MediaFile>
</MediaFiles>
</Linear>
</Creative>
</Creatives>
</InLine>
</Ad>
</VAST>
Wrappers and the redirect chain
VAST <Wrapper> ads don't contain a creative — they carry a VASTAdTagURI
pointing at another VAST response (often a different ad server / SSP). Players
follow the chain until they reach an InLine. Each wrapper adds its own tracking
URLs (they all fire) and its own latency, so deep wrapper chains are a common
cause of slow or failed ad loads. The spec recommends a redirect limit (commonly
~5) to avoid runaway chains. Unwrap and flatten a chain with
the VAST viewer.
VMAP: scheduling multiple breaks
VMAP (Video Multiple Ad Playlist) sits above VAST and schedules breaks along
the timeline — pre-roll, mid-rolls at offsets, post-roll — each <AdBreak>
referencing a VAST <AdSource>:
<vmap:AdBreak timeOffset="00:10:00.000" breakType="linear" breakId="mid1">
<vmap:AdSource><vmap:AdTagURI templateType="vast4">
<![CDATA[https://adsrv.example/vast?break=mid1]]>
</vmap:AdTagURI></vmap:AdSource>
</vmap:AdBreak>
This is how a single response describes the whole ad schedule for an asset.
The chain end to end
- SCTE-35 marks the avail (splice_insert or time_signal + descriptor).
- Packaging surfaces it as HLS CUE-OUT/DATERANGE or DASH period/event.
- The system requests ads via VMAP/VAST.
- Wrappers redirect until an InLine creative is found.
- The creative plays; tracking events fire at impression and each quartile.
Takeaways
SCTE-35 is the signal and VAST/VMAP is the fill plus measurement — keep them mentally separate. The standard parts (decoding SCTE-35, surfacing CUE-OUT, parsing VAST) are well-trodden; the real engineering pain is wrapper-chain latency and quartile beacon accuracy. Decode splices in the SSAI tool and flatten ad tags in the VAST viewer.
Try it yourself
Reading about streaming is one thing — pull apart a real manifest, play a stream, and measure its QoE with the toolkit.
Browse all tools