Pause Ads

The Bitmovin Player Android SDK can present pause ads: non-linear ads that are shown while the viewer has the main content paused. A pause ad never interrupts playback. The viewer pauses, the ad is rendered over the paused player, and it is taken down when playback resumes.

Pause ads are scheduled through AdItem, but they are not anchored to the media timeline. Instead of a time, set AdItem.position to "pause".

For cross-platform context, see Pause Ads.

Pause ad support is experimental. The API may change in a future version.

Requirements

  • Pause ads are resolved through Bitmovin Advertising. The ad source must use AdSourceType.Bitmovin. Other ad source types are not supported
  • The VAST response must contain a NonLinearAds creative with a StaticResource that is an image/jpeg, image/jpg or image/png image. Other resource types are not supported
  • An ad ViewGroup must be set. PlayerView sets one automatically. Integrations that don't use the provided PlayerView must set one themselves for pause ads to work

Scheduling a pause ad

Schedule the ad upfront through AdvertisingConfig:

val pauseAd = AdItem(
    sources = arrayOf(AdSource(AdSourceType.Bitmovin, "https://example.com/pause-ad-tag")),
    position = "pause",
)

val player = Player(
    context,
    PlayerConfig(advertisingConfig = AdvertisingConfig(schedule = listOf(pauseAd))),
)

Or schedule it at runtime through AdvertisingApi.schedule once a source is loaded:

player.ads.schedule(pauseAd)

Pause ads are scoped to the playback session, not to a single source. A pause ad that has not been presented yet stays scheduled across a source transition, and one that has already been consumed does not become eligible again because the source changed.

Because a pause ad has no timeline position, it is not listed in AdvertisingApi.schedule and does not produce PlayerEvent.AdScheduleChanged entries.

Suppressing a pause ad

AdvertisingConfig.shouldLoadAdItem is called on every pause ad opportunity, before the ad tag is resolved. Returning false skips the pause ad for that opportunity: nothing is presented, and the next scheduled pause ad is tried instead.

By default, a pause ad rejected by shouldLoadAdItem is discarded and not tried again at later pauses. To keep it for later opportunities, set its AdItem.discardPolicy to KeepAfterAttempt or use the shouldDiscardAdItem callback.

val advertisingConfig = AdvertisingConfig(
    schedule = listOf(pauseAd),
    shouldLoadAdItem = { adItem -> adItem.position != "pause" || shouldShowPauseAds() },
)

AdvertisingConfig.shouldPlayAdBreak is not called for pause ads, as they are not part of an ad break.

Controlling pause ad reuse

📘

Experimental API

Pause ad discard control is experimental. The API may change in a future version.

By default, a pause ad is discarded once an attempt to play it ends, whether it was presented, rejected by shouldLoadAdItem, or failed to load. The ad is then removed from the schedule and not eligible for later pause opportunities in the current playback session.

You can control this behavior in two ways:

Per-ad policy with discardPolicy

Set AdItem.discardPolicy to decide the default behavior for a single pause ad:

  • AdItemDiscardPolicy.DiscardAfterAttempt (default): the ad is discarded after any attempt, so it plays at most once
  • AdItemDiscardPolicy.KeepAfterAttempt: the ad stays eligible for later pauses and its tag is resolved again before each presentation
import com.bitmovin.player.api.ExperimentalBitmovinApi
import com.bitmovin.player.api.advertising.AdItemDiscardPolicy

@OptIn(ExperimentalBitmovinApi::class)
val reusablePauseAd = AdItem(
    sources = arrayOf(AdSource(AdSourceType.Bitmovin, "https://example.com/pause-ad-tag")),
    position = "pause",
    discardPolicy = AdItemDiscardPolicy.KeepAfterAttempt,
)

Outcome-based decision with shouldDiscardAdItem

For finer control, AdvertisingConfig.shouldDiscardAdItem is called when an attempt to play a pause ad ends. The callback receives the ad item and an AdItemDiscardContext that describes:

  • outcome: how the attempt ended—Finished (presented and then closed or resumed), Error (failed to load), or RejectedByShouldLoadAdItem
  • discardPolicyDecision: what the ad's discardPolicy says to do (Discard for DiscardAfterAttempt, Keep for KeepAfterAttempt)

Return AdItemDiscardDecision.Discard to discard the ad, AdItemDiscardDecision.Keep to keep it for later, or context.discardPolicyDecision to apply the policy's default.

For example, to keep a pause ad eligible after it was presented or rejected, but discard it if it failed:

import com.bitmovin.player.api.ExperimentalBitmovinApi
import com.bitmovin.player.api.advertising.AdItemAttemptOutcome
import com.bitmovin.player.api.advertising.AdItemDiscardDecision
import com.bitmovin.player.api.advertising.ShouldDiscardAdItemCallback

@OptIn(ExperimentalBitmovinApi::class)
val advertisingConfig = AdvertisingConfig(
    schedule = listOf(pauseAd),
    shouldDiscardAdItem = ShouldDiscardAdItemCallback { _, context ->
        when (context.outcome) {
            is AdItemAttemptOutcome.Error -> AdItemDiscardDecision.Discard
            else -> AdItemDiscardDecision.Keep
        }
    },
)

Kept ads are resolved again before each presentation, so every opportunity fetches fresh creative and tracking.

Presentation

Once the content has stayed paused for the activation delay (one second, without seeking or time shifting), the player resolves the ad tag, selects a creative, and renders it.

The creative is drawn across the full ad ViewGroup, above the video and below the player UI, with its aspect ratio preserved. When you use PlayerView, the default Bitmovin Player UI hides its center play button while a pause ad is presented.

At most one pause ad is presented per pause. Closing the ad, resuming playback, or failing to load the ad consumes that opportunity, and no further pause ad is presented until playback resumes and is paused again. Nothing is preloaded or cached: to enable dynamic ad inventory, the tag is resolved again on every opportunity.

Ad view group

The ad ViewGroup is the container the creative is rendered into, and its bounds are what the best-fitting creative is selected against. A pause ad is only presented while one is set.

PlayerView sets the ad view group automatically. If you do not use PlayerView, or you want a customized experience, set your own through AdvertisingApi.setViewGroup:

player.ads.setViewGroup(customAdContainer)

Closing a pause ad

AdvertisingApi.skip() closes a presented pause ad. The main content stays paused, and the current pause ad opportunity is consumed.

player.ads.skip()

This has no effect while the creative is still loading, i.e., before PlayerEvent.NonLinearAdStarted is emitted.

The Bitmovin Player UI provides resume and close controls. If you build your own UI, make sure a resume control is always reachable while a pause ad is shown, and wire your close control to skip() so that the VAST close tracking is fired.

Events

Every NonLinearAdStarted is followed by exactly one NonLinearAdSkipped or NonLinearAdFinished.

A pause ad is not an ad break, so it is not surrounded by PlayerEvent.AdBreakStarted and PlayerEvent.AdBreakFinished, and it does not emit the linear AdStarted / AdFinished events.

PlayerEvent.AdManifestLoad, PlayerEvent.AdManifestLoaded and PlayerEvent.AdError are also emitted for pause ads. For a non-linear ad, the ad config they report is a NonLinearAdConfig.

Linear-only APIs

Existing linear ad APIs deliberately exclude pause ads, so integrations built on them keep working unchanged:

APIBehavior with pause ads
Player.isAdStays false
AdvertisingApi.activeAdBreakDoes not return the pause ad
AdvertisingApi.activeLinearAdDoes not return the pause ad
AdvertisingApi.scheduleThe schedule list does not include pause ads
Ad break eventsNot emitted

Tracking

The following VAST tracking events are fired for pause ads:

SituationTracking
Creative becomes visibleimpression, creativeView
Viewer explicitly closes the adclose
Presentation endsoverlayViewDuration
Loaded creative is discarded without ever being renderednotUsed

Resuming the content ends the presentation but does not fire close. Only an explicit close through skip() does.

Interaction with linear ads

Linear and pause ads are mutually exclusive. A pause ad is never loaded or rendered while a linear ad is active, even if the viewer pauses the linear ad.

Troubleshooting

If the selected creative cannot be fetched, the ad is not presented, and PlayerWarningCode.NonLinearAdCreativeFetchingFailed (1307) is emitted through PlayerEvent.Warning. Resolution failures are reported through PlayerEvent.AdError.

If no pause ad appears at all, check that an ad view group is set, that the ad source uses AdSourceType.Bitmovin, and that the VAST response contains a supported static image resource.

Creatives are fetched through the player's regular network stack, so any configured network hooks apply.

Limitations

  • Ad pods are not supported. Only the first ad of the VAST response with a usable NonLinear creative is presented
  • Only static image/jpeg, image/jpg and image/png resources are presented. SIMID, HTML and VPAID experiences are not supported
  • A loading failure does not fall back to another resource or variation from the same VAST response. Ad source waterfalls keep their normal fallback behavior
  • The activation delay is fixed at one second and cannot be configured
  • Preloading is not supported, and AdItem.preloadOffset is ignored for pause ads
  • Rendering is owned by the SDK. You can relocate the presentation with a custom ad view group, but you cannot render the creative yourself

Did this page help you?