Why architecture matters here
Architecture matters here because ad insertion sits on the intersection of two things that both fail loudly: the viewer's experience and the advertiser's money. A misplaced splice point cuts off the last second of a scene or drops the first frame of an ad; a slow ad decision leaves a black screen where a commercial should be; a mis-counted impression means an advertiser is billed wrong. Streaming ad revenue is measured in fractions of a cent per impression across billions of impressions, so systematic errors in boundary alignment or beaconing translate directly into lost revenue or refund liability. The architecture exists to make the splice frame-accurate and the measurement trustworthy.
The reason SCTE-35 is the linchpin is that it decouples when a break happens from who decides what fills it. The content pipeline — encoder and packager — only needs to know the timing of avails, expressed as splice cues at precise presentation timestamps. The business logic — which creatives to show this particular viewer, at what price, with what frequency cap — lives entirely in an ad decision server contacted at playback time. This separation lets the same content stream be monetized differently for every viewer and every session without re-encoding the program, which is the entire economic premise of addressable, personalized streaming advertising.
Server-side insertion matters specifically because it puts the splice where it can be done correctly and unblockably. When the manifest a player receives already contains the ad segments interleaved with content segments — same encoding profile, same encryption, same origin — there is nothing for the player to switch and nothing for an ad blocker to filter. The tradeoff is that the server now owns a hard real-time responsibility: for live streams it has only the duration of the avail's lead time to decide, fetch, transcode, and stitch the ads, and if it misses that window the viewer sees a gap. Understanding this timing budget is central to the whole design.
The architecture also has to reconcile two audiences with opposite tolerances. Viewers demand that ads feel like part of the stream — no buffering at the break, no resolution drop, no jarring loudness jump — because any glitch at the ad boundary is read as a product defect, not an ad-serving hiccup. Advertisers demand exact, verifiable delivery — the right creative, to the right viewer, counted the right way — because they pay per confirmed impression. SSAI is the architecture that tries to satisfy both simultaneously from the server side: seamless for the viewer, measurable for the advertiser. Nearly every downstream design decision — IDR alignment, encoding parity, server-side beaconing, slate fallback — exists to hold one or both of those guarantees under real-world load.
The architecture: every piece explained
Start at the source. The encoder embeds SCTE-35 splice_info messages into the transport stream at ad boundaries — a splice_insert or the richer time_signal with segmentation descriptors — each carrying a precise presentation timestamp (PTS) for the splice point, a duration for the avail, and identifiers (a break ID, segmentation type like 'Provider Advertisement Start/End'). These cues are the ground truth for where a break opens (cue-out) and closes (cue-in). For live, upstream systems inject them; for VOD, they are authored into the asset.
The packager consumes the stream and its SCTE-35 cues and does two things: it ensures segment boundaries align with the splice points (creating a segment break exactly at the avail, ideally on an IDR frame so the ad can be inserted cleanly), and it translates the binary cues into manifest-level signalling. In HLS this becomes #EXT-X-DATERANGE tags (or the older #EXT-X-CUE-OUT/CUE-IN) carrying the SCTE-35 payload and duration; in DASH it becomes period boundaries and EventStream/emsg signalling. The manifest now declares 'an avail of duration D starts here.'
The SSAI stitcher is the personalization engine. It sits between the packager's origin and the player, and for each viewer session it generates a per-session manifest. When it reaches an avail, it calls an ad decision server (ADS) using VAST/VMAP — passing viewer and context signals — and receives a set of creatives with their durations and tracking (beacon) URLs. It ensures those creatives exist as segments encoded to match the content's profile, resolution ladder, GOP structure, and encryption (transcoding them on demand or pulling from a pre-conditioned cache), then rewrites the manifest so the ad segments appear inline where the avail was, followed by the content resuming at cue-in. The player, fetching this manifest, sees one uninterrupted list of segments and plays through the ads as if they were content.
End-to-end flow
Follow a live avail end to end. The encoder emits a time_signal at PTS T marking a 90-second Provider Ad Start, and an in-band cue that the break closes at T+90s. The packager, seeing the cue, forces an IDR-aligned segment boundary at T, writes an #EXT-X-DATERANGE into the HLS media playlist announcing a 90-second avail, and continues packaging content. Because the cue arrives with lead time (a pre-roll signal ahead of the actual splice), downstream systems have a few seconds of warning before the break must be filled.
The SSAI stitcher, generating manifests for thousands of concurrent sessions, sees the avail and fires a VAST request to the ADS for each session, including that viewer's identifiers and context. The ADS applies targeting, frequency capping, and pacing and returns, say, three 30-second creatives per session — different across viewers. The stitcher confirms each creative is available as ABR segments matching the content ladder (transcoding any that are not, or serving from a warm cache), and rewrites each session's manifest so that at the avail the content segments are replaced by that session's ad segments, spanning exactly 90 seconds, with tracking metadata attached.
The player downloads its personalized manifest and plays continuously: content up to T, then its three ads, then content resuming at the cue-in. As each ad plays, the player (or the stitcher, in server-side beaconing) fires impression and quartile beacons (start, first-quartile, midpoint, third-quartile, complete) to the tracking URLs, which feed measurement and billing. At cue-in the program resumes seamlessly. If the ADS had been slow or returned too little to fill 90 seconds, the stitcher would have inserted a slate (house filler or a default ad) to cover the gap rather than showing black — a fallback that is exercised far more often than teams expect.
For video-on-demand the same flow runs with the timing pressure removed, which changes the tradeoffs. Because the asset and its avails are known ahead of playback, the stitcher can pre-compute and cache conditioned ad segments, resolve ad decisions with more generous latency, and even personalize the manifest at session start rather than racing a live cue. This makes VOD ad insertion far more forgiving of ADS latency, but it introduces its own concern: a single popular asset watched by millions means the same avails fire enormous, synchronized bursts of ad requests, so the ADS and stitcher must scale to concentrated demand rather than the steadier arrival pattern of a live stream. The mechanics are identical; the load shape and the slack in the timing budget are what differ.