Server-Guided Ad Insertion with Web Player
Server-Guided Ad Insertion (SGAI) lets the Bitmovin Web Player play ads that your server signals directly in the HLS manifest. This guide shows you how to enable and control it on Web. For a product-agnostic overview — what SGAI is, HLS Interstitials, and how manifests signal ads — see the SGAI overview.
On Web, HLS Interstitials power SGAI: the player reads interstitial tags from the manifest, loads the ad assets they point to, plays each ad at the position its tag marks, and fires the standard advertising events. You do not schedule ad breaks from the client (unlike client-side advertising).
Prerequisites
SGAI / HLS Interstitials handling is provided by the SGAI module, which depends on the Advertising Core module. Viewability tracking additionally needs the Style module. How you make these available depends on which player build you use.
Bundled player
The bundled (all-in-one) bitmovinplayer.js build already includes every module. You don't need to load or register anything extra — just enable the enable_sgai_handling tweak (see Basic setup).
<script src="//cdn.bitmovin.com/player/web/8/bitmovinplayer.js"></script>Modular player
With the modular player you must load and register each required module before creating the player:
<script src="//cdn.bitmovin.com/player/web/8/bitmovinplayer.js"></script>
<!-- Advertising Core — required (SGAI depends on it) -->
<script src="//cdn.bitmovin.com/player/web/8/modules/bitmovinplayer-advertising-core.js"></script>
<!-- SGAI / HLS Interstitials — required -->
<script src="//cdn.bitmovin.com/player/web/8/modules/bitmovinplayer-sgai.js"></script>
<!-- Style — only required for viewability tracking -->
<script src="//cdn.bitmovin.com/player/web/8/modules/bitmovinplayer-style.js"></script>bitmovin.player.Player.addModule(bitmovin.player['advertising-core'].default);
bitmovin.player.Player.addModule(bitmovin.player['sgai'].default);
// Only when using viewability tracking:
bitmovin.player.Player.addModule(bitmovin.player['style'].default);Basic setup
Interstitials handling is off by default. Enable it with the tweaks.enable_sgai_handling tweak (requires the Advertising Core module, available since v8.238.0). Once enabled and the module is loaded, the player picks up interstitials from the manifest automatically.
const playerConfig = {
key: '<PLAYER_LICENSE_KEY>',
tweaks: {
// Enables HLS Interstitials / SGAI handling. Off by default.
enable_sgai_handling: true,
},
};
const player = new bitmovin.player.Player(
document.getElementById('player-container'),
playerConfig,
);
const source = {
hls: 'https://your-cdn.example.com/stream/master.m3u8',
};
player.load(source);
SafariSafari plays HLS with its native player. To enable interstitials handling there, also set the
tweaks.native_hls_parsingtweak so the player fetches and parses the HLS playlist itself.
tweaks: {
enable_sgai_handling: true,
native_hls_parsing: true,
}Optional behavior is configured under hls.interstitials in the player config. See the InterstitialsConfig API reference for the full list of options.
const playerConfig = {
key: '<PLAYER_LICENSE_KEY>',
hls: {
interstitials: {
// callbacks, tracking mapping, and viewability options go here
},
},
};Controlling which interstitials load and play
SGAI exposes callbacks so you can filter interstitials on the client. Each is optional; when omitted, all signaled interstitials load and play.
Skip loading an interstitial's asset list
const playerConfig = {
key: '<PLAYER_LICENSE_KEY>',
hls: {
interstitials: {
shouldLoadInterstitial: interstitial => {
// Only load the pre-roll; skip everything else before any network request
return interstitial.id === 'pre-roll-1';
},
},
},
};Skip playback of an interstitial that was already loaded
Return false to skip playback of an interstitial; true (or omitting the callback) plays it.
hls: {
interstitials: {
// Skip the mid-roll; play every other interstitial
shouldPlayInterstitial: interstitial => interstitial.id !== 'mid-roll-1',
},
}Choose which jumped-over interstitials still play
When a user seeks across several interstitials, decide which ones must still be played:
hls: {
interstitials: {
shouldPlayJumpedOverInterstitials: (interstitials, fromTime, toTime) => {
// Only play interstitials longer than 15 seconds
return interstitials.filter(interstitial => interstitial.duration > 15);
},
},
}Playback restrictions
When an interstitial signals an X-RESTRICT attribute (see the SGAI overview for the manifest format), the Web Player enforces it and exposes the mapped values through these enums:
X-RESTRICT value | Effect | Enum |
|---|---|---|
SKIP | Seeking / higher playback rates disabled during the ad | SeekingRestriction.NO_SEEKING |
JUMP | Ad break can't be jumped over — at least one ad must play | JumpPolicy.RESTRICT |
The SGAI API
Access SGAI-specific controls through player.sgai. It is null when the SGAI module isn't loaded, so guard access:
const sgai = player.sgai;
if (sgai) {
// All scheduled SGAI ads (does NOT include the currently active ad)
const scheduled = sgai.list();
// The currently playing SGAI ad, if any
const active = sgai.getActiveAd();
// Skip the currently playing ad
await sgai.skip();
}| Method | Returns | Description |
|---|---|---|
list() | SgaiAd[] | HlsInterstitial[] | All scheduled SGAI ads, excluding the active one. |
getActiveAd() | LinearAd | undefined | The currently active SGAI ad, if one is playing. |
skip() | Promise<void> | Skips the currently playing ad. |
Events
SGAI reuses the standard advertising events, so existing ad handling works without change. The player fires:
AdBreakStarted/AdBreakFinished— around each interstitial ad breakAdStarted/AdFinished— around each individual adAdSkipped— when an ad is skipped (e.g. viaplayer.sgai.skip())AdError— when an interstitial fails (see Error handling)
player.on(bitmovin.player.PlayerEvent.AdBreakStarted, event => {
console.log('SGAI ad break started', event.adBreak);
});
player.on(bitmovin.player.PlayerEvent.AdStarted, event => {
console.log('SGAI ad started', event.ad);
});
player.on(bitmovin.player.PlayerEvent.AdFinished, () => {
console.log('SGAI ad finished');
});For SGAI, the adBreak on these events is an SgaiAdBreak (extends AdBreak) and carries extra fields such as totalPlannedDuration, restrictions, and adCreativeSignaling. Individual ads are SgaiLinearAd (extends LinearAd) with adCreativeSignaling and customAdTrackingInfo.
Ad tracking and creative signaling
SGAI supports server-driven tracking via custom attributes on the interstitial tags (e.g. SVTA X-AD-CREATIVE-SIGNALING). You map those attributes into tracking events the player fires at the right moments.
Use a built-in preset
The simplest path is the standard SVTA mapping preset:
hls: {
interstitials: {
// 'AD-CREATIVE-SIGNALING' is the value of CustomAttributesMappingPresets.AD_CREATIVE_SIGNALING
customAttributesMappingPreset: 'AD-CREATIVE-SIGNALING',
},
}Custom mapping callback
For full control, provide customAttributesMapping. It receives the parsed tracking data and a registry to register tracking events and the click-through URL:
hls: {
interstitials: {
customAttributesMapping(mappingData, mappingRegistry) {
const adCreativeSignaling = mappingData.customAttributes['X-AD-CREATIVE-SIGNALING'];
const payload = adCreativeSignaling && adCreativeSignaling.payload;
if (!payload) {
return;
}
const first = payload[0];
if (first.clickThrough) {
mappingRegistry.clickThroughUrl = first.clickThrough;
}
for (const trackingEvent of first.tracking || []) {
const { type, urls, offset } = trackingEvent;
if (!type || !urls) {
continue;
}
// 'impression' is the value of AdTrackingEventTrigger.IMPRESSION
if (type === 'impression') {
mappingRegistry.tracking.register('impression', { urls, offset });
}
}
},
},
}The player fires registered tracking URLs for the full ad lifecycle, including impression, start, firstQuartile, midpoint, thirdQuartile, complete, pause, resume, mute, unmute, clickTracking, skip, and progress triggers (see AdTrackingEventTrigger).
Viewability tracking
The player can emit viewable / notViewable tracking events based on how long the ad stays visible. Viewability tracking requires two things:
- Player visibility tracking enabled via
style.visibility.enableTracking(off by default). With the modular player, the Style module (bitmovinplayer-style.js) must also be loaded. - The
hls.interstitials.viewabilityoptions to tune the decision.
const playerConfig = {
key: '<PLAYER_LICENSE_KEY>',
style: {
visibility: {
// Required — enables player viewport visibility tracking
enableTracking: true,
// Minimum visible percentage (0–100) to count the player as visible. Default: 50
visibilityThreshold: 50,
},
},
hls: {
interstitials: {
viewability: {
// Consecutive seconds visible before emitting `viewable` (default: 2)
viewableDuration: 2,
// Max window to make the viewability decision (default: ad duration)
decisionTimeout: 10,
},
},
},
};If no viewable decision is reached within decisionTimeout, the tracker emits notViewable. When viewability can't be determined for technical reasons, viewUndetermined is emitted.
Error handling
SGAI failures surface as an AdError event whose top-level ErrorCode is MODULE_SGAI_ERROR (3101). The AdvertisingError.code on that event carries a more specific SgaiErrorCode:
SgaiErrorCode | Value | Meaning |
|---|---|---|
INTERSTITIAL_ASSET_LIST_INVALID | 111 | Asset list downloaded but malformed / missing a valid ASSETS array. (An empty no-fill response is a warning, not an error.) |
INTERSTITIAL_ASSET_LIST_LOAD_FAILED | 406 | Asset list couldn't be downloaded (network error or non-2xx response). |
INTERSTITIAL_ASSET_PLAYBACK_FAILED | 407 | An ad asset failed to play (network/decode error). Underlying cause is in AdvertisingError.deficiencyData. |
player.on(bitmovin.player.PlayerEvent.AdError, event => {
if (event.code === bitmovin.player.ErrorCode.MODULE_SGAI_ERROR) {
console.error('SGAI error', event.data && event.data.code, event.name);
// event.data.deficiencyData holds the underlying playback error, when present
}
});What's next
- SGAI overview — product-agnostic SGAI concepts and manifest signaling
- Setting up Ads with the Web Player — client-side VAST/VMAP advertising
- Working with Event Handlers — how to subscribe to player events
- API reference: InterstitialsConfig, PlayerSgaiAPI, SgaiErrorCode
- Apple HLS Interstitials specification