Skip to content

Server Guided Ad Insertion

Torbjörn Einarsson edited this page Jun 15, 2026 · 2 revisions

Server-Guided Ad Insertion (SGAI)

livesim2 can signal Server-Guided Ad Insertion using the DASH 6th-edition Alternative-MPD Replace event. The main live stream stays a single, continuous-timeline presentation; an EventStream of scheme urn:mpeg:dash:event:alternativeMPD:replace:2025 marks the ad breaks and points each break at an ad-decisioning endpoint. A supporting player resolves that endpoint during the break, plays the returned (personalized) ad pod, and then resumes the live edge. A player that does not support the event simply keeps playing the underlying content, so it is always a graceful fallback.

It runs entirely inside livesim2 — no external ad server is required — and is enabled per-stream with the sgai_ URL option.

Same live stream, two viewers, different ads

The same live stream seen by two viewers. alice (interests=cooking) has no matching ad, so the "AD BREAK" slate stays; bob (interests=sailing) gets a themed pod (gotland_runt_adstockholm_sailing_ad) — both at the same instant.

How it works

  1. The live MPD carries a Replace event per ad break, with a ReplacePresentation@uri pointing at /sgai/ads and the Annex I RequestParam that copies the MPD-URL query (e.g. the session id and interests) onto the ad request.
  2. The player resolves /sgai/ads, which returns a personalized List MPD (type="list") whose Periods import the chosen ad creatives as a pod.
  3. The player plays the pod, firing impression and quartile beacons to /sgai/beacon, then resumes the live edge at the break end.
  4. Inside the break window the underlying video shows a generated "AD BREAK" countdown slate — the visible "ad to be replaced" for players that do not execute the event.

Enabling it — the sgai_ URL option

The sgai_ option schedules the breaks:

  • Fixed breakssgai_30:20 is one 20 s break 30 s after the availabilityStartTime, or a comma-separated list sgai_18:20,48:20,78:20.
  • Periodic breakssgai_p60:20 is a 20 s break at every full UTC minute, recurring forever. Periodic breaks are wall-clock anchored, so all sessions share the schedule and a late joiner lands mid-break.
  • Options are appended with ;key=val: skipafter=<s>, nojump=<0|1|2>, clip=<0|1>, once=<0|1>, resolve=<s> (earliest resolution offset) and ep=<path> (ad endpoint). For example sgai_p60:20;skipafter=5.

A full example, with personalization, against the bundled test content:

https://livesim2.dashif.org/livesim2/sgai_p60:20/testpic_2s/Manifest.mpd?sessionId=alice&interests=travel,sailing

The /urlgen/ page can build these URLs for you.

Ad creatives and personalization

The ad creatives are ordinary Single-Period-Static DASH VoD assets placed under <vodroot>/ads/ (one directory per ad). An optional <vodroot>/ads/ads.json tags each ad with a title and interest keywords:

{
  "train_ad":        { "title": "Train Journey",             "interests": ["travel"] },
  "gotland_runt_ad": { "title": "Gotland Runt Sailing Race", "interests": ["sailing", "boats"] }
}

Those two creatives are bundled in the test content, so SGAI works out of the box. A viewer is identified and steered with two query parameters that are carried to the ad request via Annex I: sessionId (personalization key) and interests (a comma-separated list).

How the ad pod is selected

/sgai/ads returns a List MPD that imports the chosen ads as a pod. The selection:

  • Ads tagged with any requested interest come first (rotated per sessionId for variety), then the remaining ads are used as filler.
  • The pod is trimmed to fit the break duration — ads are added while the running total stays within the break, always keeping at least the lead ad.
  • If the break is longer than the available inventory, the remainder of the break shows the AD BREAK slate; the pod is not padded to a gapless fill.
  • If the break is shorter than a single ad, the lead ad is still returned and the player clamps playout to the event's maxDuration.
  • With no interests, or interests that match no creative, /sgai/ads answers 404; the player skips the break and the viewer keeps the slate.

So a clean setup — interests that match some ads and a break duration that is a multiple of the ad length (e.g. sgai_…:20 with the bundled 10 s ads and interests=travel,sailing) — yields a gapless two-ad pod.

The AD BREAK slate

Inside a break window the clear (non-DRM) AVC video track serves a generated "AD BREAK / #<event id> / <countdown>" slate instead of the normal content. The slate video is generated on the fly with hi264, a pure-Go H.264 generator, so it fits perfectly into the original video: it is encoded against the representation's own SPS/PPS, mirrors the replaced segment's sample timing and per-sample sizes (so frame rate and bitrate match), and splices into the track with no init change and no parameter-set break. It also works with low-latency (chunked) delivery — the slate is chunked exactly like normal segments.

Beacons and monitoring

Each ad fires impression and quartile callback beacons to /sgai/beacon, carrying the session and break id via Annex I. The per-session ad decisions and beacons are recorded and can be inspected live at /sgai/session_status?sid=<sessionId>, or via the API at /api/sgai/sessions[/{sid}] and /api/sgai/ads (the ad catalog). This is the way to observe SGAI activity on a public deployment where the process log is not visible.

SGAI session status page

Player support

Stock players vary in their support for the spec-pure path (List MPD resolution, Annex I and callback reporting). The feature has been validated end-to-end with patched builds of shaka-player and dash.js; players without Alternative-MPD support fall back gracefully to the underlying content (the AD BREAK slate).

Clone this wiki locally