HLS Interstitials

The Bitmovin Player Android 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.hlsConfig / HlsConfig.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 }
  ]
}

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

Basic playback

Load the HLS source as usual. If HLS interstitials are enabled, the SDK schedules interstitial ad breaks from the manifest automatically.

val sourceConfig = SourceConfig(
    url = "https://example.com/content-with-interstitials.m3u8",
    type = SourceType.Hls,
)

player.load(sourceConfig)
player.play()

HLS interstitials are enabled by default. If your app needs to opt out of HLS interstitial handling, disable it through TweaksConfig.enableHlsInterstitials.

val playerConfig = PlayerConfig(
    tweaksConfig = TweaksConfig(enableHlsInterstitials = false),
)

Custom tracking and click-through metadata

Use HlsConfig.interstitialsConfig when interstitial metadata contains custom attributes that should be mapped to tracking URLs or click-through information.

SVTA X-AD-CREATIVE-SIGNALING

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

val playerConfig = PlayerConfig(
    hlsConfig = HlsConfig(
        interstitialsConfig = InterstitialsConfig(
            customAttributesMapping = CustomAttributesMappingPresets.adCreativeSignaling,
        ),
    ),
)

Custom mapping

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

val playerConfig = PlayerConfig(
    hlsConfig = HlsConfig(
        interstitialsConfig = InterstitialsConfig(
            customAttributesMapping = CustomAttributesMappingHandler { mappingData, mappingRegistry ->
                val trackingUrl = mappingData.customAttributes["X-IMPRESSION-URL"]
                    ?: return@CustomAttributesMappingHandler

                mappingRegistry.tracking.register(
                    trackingEvent = InterstitialTrackingEvent(urls = listOf(trackingUrl)),
                    eventTrigger = InterstitialTrackingEventTrigger.Impression,
                )
            },
        ),
    ),
)

The mapping handler can be invoked at multiple levels depending on the interstitial type:

Tracking macros

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

val playerConfig = PlayerConfig(
    hlsConfig = HlsConfig(
        interstitialsConfig = InterstitialsConfig(
            macroValueProvider = InterstitialMacroValueProvider { macro ->
                when (macro.name) {
                    "USER_ID" -> listOf("current-user-id")
                    else -> macro.values
                }
            },
        ),
    ),
)

Asynchronous $remote resolution from the SVTA ad creative signaling specification is not supported.

Viewability tracking

If viewability tracking URLs are provided through InterstitialsConfig, the SDK can fire Viewable, NotViewable, and ViewUndetermined tracking events.

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.

Ad events and schedule observation

Interstitials are exposed through the regular advertising event flow. Listen for events such as:

Use the ads API and PlayerEvent.AdScheduleChanged 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.idcontains the ID from the EXT-X-DATERANGE tag, and InterstitialAdBreak.plannedDuration exposes PLANNED-DURATION when present.

player.on<PlayerEvent.AdBreakStarted> { event ->
    val interstitialAdBreak = event.adBreak as? InterstitialAdBreak ?: return@on
    val plannedDuration = interstitialAdBreak.plannedDuration
}

Use PlayerEvent.AdScheduleChanged for reliable schedule observation; HLS interstitial ad breaks may not be available immediately after PlayerEvent.SourceLoaded.

Live workflows and 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 and X-PLAYOUT-LIMIT. Updates add missing supported values only. Existing interstitial values are not overwritten or removed.

Replay and seek behavior

HLS interstitials are replayable 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.

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.

Resume offset logic

When X-RESUME-OFFSET is present, it is used as the content replacement duration.

When X-RESUME-OFFSET is not present, the resume offset is calculated with the following priority:

  1. Playout limit
  2. Sum of the duration of all assets in the asset list
  3. EXT-X-DATERANGE tag attributes in this order:
    1. DURATION
    2. Duration derived from start and end timestamps
    3. PLANNED-DURATION

Networking and troubleshooting

Failures during asset-list loading or decoding, empty asset lists, and ad media load or playback failures are reported through PlayerEvent.AdError. Use PlayerEvent.AdManifestLoad, PlayerEvent.AdManifestLoaded, and PlayerEvent.AdError to troubleshoot interstitial loading.

For networking customization and troubleshooting, X-ASSET-LIST requests are identified as HttpRequestType.HlsInterstitialsAssetList in the SDK networking APIs and download events.

Limitations

  • Interstitials are always treated as ads.
  • Timeline-related interstitial features such as X-TIMELINE-OCCUPIES and X-TIMELINE-STYLE are not supported.
  • X-CONTENT-MAY-VARY is not supported.
  • X-SNAP is not considered.
  • The skip duration (how long the ad remains skippable) is not considered or reported.
  • Custom skip-until messages are not applied.
  • Skip control properties defined only in the asset-list JSON are not considered. Only skip control attributes on the EXT-X-DATERANGE tag are processed.
  • Asynchronous $remote resolution from the SVTA ad creative signaling specification is not supported.
  • The content type is generally inferred from the interstitial URL. If inference is not possible, the interstitial is treated as an HLS stream.


Did this page help you?