Server-Guided Ad Insertion (SGAI)

HLS Interstitials on iOS, tvOS, and visionOS

The Bitmovin Player iOS SDK supports Server-Guided Ad Insertion (SGAI) through HLS interstitials out of the box. When an HLS source contains valid interstitial EXT-X-DATERANGE tags, the SDK parses them and schedules the resulting ad breaks automatically.

Interstitial playback does not require a separate ad schedule in the player configuration. Configure PlayerConfig.interstitialsConfig only when you need custom tracking, click-through mapping, or tracking macro replacement.

For cross-platform context, see Server-Guided Ad Insertion (SGAI)​.

Manifest Requirements

Each interstitial must be declared with CLASS="com.apple.hls.interstitial" and exactly one asset reference.

Use X-ASSET-URI for a single interstitial asset:

#EXT-X-DATERANGE:ID="midroll-1",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:00:15Z",DURATION=10.0,X-ASSET-URI="https://example.com/ads/ad-1.m3u8",CUE="ONCE"

Use X-ASSET-LIST when the ad opportunity contains multiple assets or asset-level metadata:

#EXT-X-DATERANGE:ID="midroll-2",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:01:00Z",DURATION=20.0,X-ASSET-LIST="https://example.com/ads/midroll-2.json",CUE="ONCE"

The asset list URL should return a JSON document that describes the interstitial assets:

{
  "ASSETS": [
    { "URI": "https://example.com/ads/ad-2-a.m3u8", "DURATION": 10 },
    { "URI": "https://example.com/ads/ad-2-b.m3u8", "DURATION": 10 }
  ]
}

Basic Setup

let sourceUrl = URL(string: "https://example.com/content-with-interstitials.m3u8")!
let sourceConfig = SourceConfig(url: sourceUrl, type: .hls)

player.load(sourceConfig: sourceConfig)
player.play()

Tracking

To enable SVTA X-AD-CREATIVE-SIGNALING tracking, set the built-in mapping preset before creating the player:

let playerConfig = PlayerConfig()
playerConfig.interstitialsConfig.customAttributesMapping = InterstitialsConfig
    .CustomAttributesMappingPresets
    .adCreativeSignaling

For custom tracking metadata, provide your own mapping handler and register tracking URLs for the events you need:

playerConfig.interstitialsConfig.customAttributesMapping = { mappingData, mappingRegistry in
    guard let trackingUrl = mappingData.customAttributes["X-IMPRESSION-URL"] as? String else {
        return
    }

    mappingRegistry.tracking.register(
        trackingEvent: InterstitialTrackingEvent(uris: [trackingUrl]),
        for: .impression
    )
}

Use macroValueProvider when tracking URLs contain macros that must be resolved by the app:

playerConfig.interstitialsConfig.macroValueProvider = { macro in
    switch macro.name {
    case "USER_ID":
        return ["current-user-id"]
    default:
        return macro.values
    }
}

Viewability Tracking

If viewability tracking URLs are provided through InterstitialsConfig, the SDK can fire .viewable, .notViewable, and .viewUndetermined tracking.

An interstitial is considered viewable after accumulating at least 2 seconds of playback while at least 50% of the ad view is visible. If that requirement is not met before the ad ends, .notViewable is emitted; if visibility cannot be measured, .viewUndetermined is emitted.

Observing Playback

Interstitials are exposed through the regular advertising event flow. Listen for events such as AdBreakStartedEvent, AdStartedEvent, AdQuartileEvent, AdFinishedEvent, AdSkippedEvent, AdBreakFinishedEvent, and AdErrorEvent.

Use the ads API and AdScheduleChangedEvent to inspect upcoming ad breaks and react to schedule changes. HLS interstitials from the manifest are scheduled by the SDK.

For HLS-specific ad-break metadata, cast the event ad break to InterstitialAdBreak. AdBreak.identifier contains the ID from the EXT-X-DATERANGE tag, and InterstitialAdBreak.plannedDuration exposes PLANNED-DURATION when present.

Dynamic Updates

For live workflows, an interstitial can be announced first and completed later through an update for the same ID. One common use case is adding X-PLAYOUT-LIMIT late to cut off the remaining ad-pod playback on demand and resume the main content.

#EXT-X-DATERANGE:ID="live-midroll-1",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:02:00Z",DURATION=30.0,X-ASSET-LIST="https://example.com/ads/live-midroll-1.json"


# Later playlist update with the same ID, adding X-PLAYOUT-LIMIT

#EXT-X-DATERANGE:ID="live-midroll-1",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:02:00Z",DURATION=30.0,X-ASSET-LIST="https://example.com/ads/live-midroll-1.json",X-PLAYOUT-LIMIT=15

Dynamic updates are supported for X-RESUME-OFFSET, X-RESTRICT, and X-PLAYOUT-LIMIT. Updates add missing values only. Existing interstitial values are not overwritten or removed.

Repeated Playback and Reloading

HLS interstitials are re-playable by default. When the same interstitial opportunity is reached again, the SDK loads it again from the original URLs. For X-ASSET-LIST, this includes fetching the asset-list JSON again before loading the referenced media.

This allows ad decisioning services to return refreshed assets for repeated playback opportunities, for example when a viewer seeks back over an interstitial.

Use CUE="ONCE" when an interstitial should only play once.

Seeking and Ad Filtering

When playback seeks over interstitial positions, the SDK applies AdvertisingConfig.shouldPlaySeekedOverAdItems.

By default, seeked-over HLS interstitials without X-RESTRICT="JUMP" are not played after the seek. If one or more seeked-over interstitials are jump-restricted, the SDK plays the last jump-restricted interstitial.

Override shouldPlaySeekedOverAdItems only when your app needs different seek-over behavior.

Error Handling

Invalid interstitial manifest entries, such as missing or conflicting asset references, invalid URLs, or unsupported interstitial shape, are ignored and logged.

Failures during asset-list loading or decoding, empty asset lists, and ad media load or playback failures are reported through AdErrorEvent. Use AdManifestLoadEvent, AdManifestLoadedEvent, and AdErrorEvent to troubleshoot interstitial loading.

For networking customization and troubleshooting, X-ASSET-LIST requests are identified as HttpRequestType.hlsInterstitialsAssetList in the SDK networking APIs and DownloadFinishedEvent. See Network API for more context.

Limitations

  • Use AdScheduleChangedEvent for reliable schedule observation; HLS interstitial ad breaks may not be available immediately after SourceLoadedEvent
  • Dynamic updates add missing supported values only; they do not overwrite or remove existing values
  • Asynchronous $remote resolution from the SVTA ad creative signaling specification is not supported
  • X-SNAP, X-TIMELINE-OCCUPIES, X-TIMELINE-STYLE, and X-CONTENT-MAY-VARY are not supported