-
Notifications
You must be signed in to change notification settings - Fork 14
Server Guided Ad Insertion
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.

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_ad → stockholm_sailing_ad) — both at the same instant.
- The live MPD carries a Replace event per ad break, with a
ReplacePresentation@uripointing at/sgai/adsand the Annex IRequestParamthat copies the MPD-URL query (e.g. the session id and interests) onto the ad request. - The player resolves
/sgai/ads, which returns a personalized List MPD (type="list") whose Periods import the chosen ad creatives as a pod. - The player plays the pod, firing impression and quartile beacons to
/sgai/beacon, then resumes the live edge at the break end. - 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.
The sgai_ option schedules the breaks:
-
Fixed breaks —
sgai_30:20is one 20 s break 30 s after the availabilityStartTime, or a comma-separated listsgai_18:20,48:20,78:20. -
Periodic breaks —
sgai_p60:20is 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) andep=<path>(ad endpoint). For examplesgai_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.
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).
/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
sessionIdfor 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/adsanswers404; 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.
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.
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.

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).