SSAI is the reason connected TV advertising can look like broadcast instead of like a web page. This is what it actually does, what it asks of your pipeline, and the parts that catch teams out.
Server-side ad insertion assembles advertising into the video stream before it reaches the device. The player receives one continuous stream and has no idea which segments are content and which are ads.
Contrast client-side insertion, where the player requests ads itself and plays them through a separate ad module. That switch between content and ad playback is where buffering, black frames and abandoned sessions come from.
Streaming delivery is a playlist of short segments. In HLS that is an .m3u8 manifest; in DASH, an MPD. The player fetches the manifest, then fetches the segments it lists, in order.
SSAI works by rewriting that playlist. This is manifest manipulation, and it is the whole mechanism.
#EXTM3U
#EXT-X-VERSION:6
#EXTINF:6.000,
content-0001.ts
#EXTINF:6.000,
content-0002.ts
#EXT-X-CUE-OUT:30.000 <- break starts here, 30s
#EXT-X-CUE-IN
#EXTINF:6.000,
content-0003.ts
#EXTM3U
#EXT-X-VERSION:6
#EXTINF:6.000,
content-0001.ts
#EXTINF:6.000,
content-0002.ts
#EXT-X-DISCONTINUITY <- decoder boundary
#EXTINF:5.000,
ad-a-0001.ts
#EXTINF:5.000,
ad-a-0002.ts
#EXT-X-DISCONTINUITY
#EXTINF:6.000,
content-0003.ts
The ad segments sit inline with content segments. The player walks the list and plays what it is given — no ad SDK, no separate request, no switch between players.
Ad creative rarely matches content encoding exactly — different resolution ladder, different audio configuration, different codec profile. EXT-X-DISCONTINUITY tells the player to reset its decoder at that boundary. Omit it and you get the artefacts SSAI was supposed to eliminate: frozen frames, audio desync, or a hard playback failure on stricter devices.
SSAI moves work off the device and onto the packaging pipeline. Three things have to be in place.
The insertion layer has to know where a break starts and how long it runs. For live streams that normally means SCTE-35 markers carried in the transport stream and surfaced as EXT-X-CUE-OUT / CUE-IN. For on-demand it is usually a VMAP document or your own schedule.
Because the manifest is rewritten per viewer, the player must request it from the insertion layer rather than straight from your CDN origin. In practice this is a change to the manifest URL your app requests. Segment delivery normally stays on your existing CDN.
Ad creative arrives as a single MP4 and must be transcoded into segments matching your delivery profile before it can be stitched. This is done ahead of time and cached, not at request time — which is why the first appearance of a new creative can behave differently from later ones.
SSAI is the right default for connected TV. It is not free.
| What you give up | Why, and what to do about it |
|---|---|
| Client-side signals | The server does not automatically know screen state, mute state, or viewport. Anything the buyer wants that only the device knows has to be passed explicitly in the ad request. |
| Simple tracking | The player is not firing the beacons, so the server has to model playback to know when a quartile was reached. See below — this is where most SSAI problems actually live. |
| Interactive formats | Anything requiring the viewer to act on the ad needs a client-side component alongside the stitched stream. Stitching alone cannot deliver it. |
| Straightforward debugging | A per-viewer manifest means no two sessions are identical. Reproducing a fault requires session-level logging, not a URL you can open. |
| Cache efficiency | Personalised manifests are not shareable between viewers. Segments still cache normally; manifests largely do not. |
In client-side insertion the player knows exactly what it played and fires tracking events itself. In SSAI the server has to infer it from which segments were requested.
That inference is where discrepancies come from, and buyers pay against these numbers:
Any SSAI vendor, us included, should be able to tell you exactly how they distinguish a pre-fetched segment from a watched one, and how they handle mid-break abandonment. If the answer is vague, expect discrepancy disputes with your buyers later.
| Symptom | Usual cause |
|---|---|
| Frozen frame at the break | Missing discontinuity tag, or ad segments encoded outside the content's profile. |
| Audio desync after an ad | Audio configuration mismatch between creative and content, or an audio-only segment boundary landing mid-frame. |
| Slate or blank where an ad should be | Unfilled avails — no eligible demand, floors set too high, or the auction timeout firing before bids returned. |
| Break runs short or long | Creative durations not summing to the signalled break length. Pod construction has to fit the window it was given. |
| Impressions under-reported versus the buyer | Beaconing model discarding fetches the buyer counts, or a quartile threshold measured differently at each end. |
| First play of a new creative fails | Conditioning not complete. Creative needs transcoding before it can be stitched. |
Our ad server performs decisioning and insertion together, so the auction that picks the pod is the same system that stitches it. That matters for break-fitting: the decisioning stage knows the exact duration it has to fill.
The manifest endpoint format, the session parameters we accept, the beaconing model in detail, and the creative conditioning pipeline are not documented on this site yet. Rather than approximate them, we send the current specification on request — contact@gogoctv.com.
We can shadow-test a slice of your live inventory and show you latency, fill and playback behaviour side by side with your current setup.