> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Showing your first ad

> Create the video's ads, get an ad, load it early, and show it at the right moment: overlay, pause or linear.

This is the 10-minute integration. Before you start, complete [Installation](https://docs.kontext.so/video/sdk/kotlin/installation.md) so `KontextVideo.initialize` runs at launch.

Every format follows the same four steps:

1. `VideoAds(content)` for the video the viewer is watching.
2. `ads.format()` asks the server and returns a `VideoAd`.
3. `ad.load()` renders the creative off screen, so showing it later is instant. Or `ad.reject()` if you won't use it.
4. `ad.show()` at the moment you choose. One `onAdEnded` callback when it is over.

`format()` and `load()` are `suspend` functions; the snippets call them from `lifecycleScope`. From Java, use their callback variants; see [From Java](https://docs.kontext.so/video/sdk/kotlin/guides.md#from-java).

## 1. Create the video's ads

One `VideoAds` per video (episode). Creating it sends nothing. Keep it as long as you use its ads, for example as a property of your player Activity or Fragment, and call `destroy()` when the screen goes: dropping the reference does not end its ads.

```kotlin theme={null}
import so.kontext.video.Content
import so.kontext.video.VideoAds

val content = Content("episode-1234", "en") {
    title = "Episode 1"
    durationMillis = 93_000
    genre = "romance"
    series = Content.Series("series-42") { title = "My Marionette Lover"; episodeCount = 6 }
}
val ads = VideoAds(content)
```

Only `id` and `language` are required; everything else improves targeting, so send what you have.

Give the SDK your player, so events carry the content playhead and an overlay follows playback:

```kotlin theme={null}
import so.kontext.video.media3.track

ads.track(player)   // your ExoPlayer, once this episode plays in it
```

`track(Player)` comes with `so.kontext:video-media3`. For a player that is not a Media3 `Player`, implement [`VideoAdsPlayer`](https://docs.kontext.so/video/sdk/kotlin/api#videoadsplayer) and pass it the same way.

## 2. Get an ad

```kotlin theme={null}
lifecycleScope.launch {
    overlayAd = ads.format(AdFormat.OVERLAY)   // pauseAd with PAUSE, linearAd with LINEAR
}
```

`format()` asks the server for an ad of the format you pass. Ask early, when the episode opens, not at the moment you want to show the ad: the request and the load take a moment, and the ad should appear instantly.

It throws `VideoAdException.NoFill` when the server has nothing. That is normal, not a failure; ask again later.

## 3. Load it, or reject it

```kotlin theme={null}
overlayAd?.load()
overlayAd?.listener = this
```

`load()` renders the creative off screen and returns once it is ready, so `show()` is instant. If you decide not to use the ad, call `overlayAd?.reject()` and the advertiser is told.

## 4. Show it

Each format has its own moment and its own `show()`.

> [tab] Overlay
>
> A strip over the player while the episode plays, at a moment you pick. It goes away by itself after a few seconds, or as soon as the viewer pauses. Show it while the content plays: over paused content `show(container)` throws `ContentPaused`.
>
> ```kotlin theme={null}
> fun showOverlay() {
>     try {
>         overlayAd?.show(playerContainer)   // the ViewGroup your video plays in
>     } catch (e: VideoAdException) {
>         // not shown; nothing else follows
>     }
> }
> ```
>
> To take it away earlier, call `dismiss()` on it. If your own controls sit over the player, keep the ad clear of them with `insets`; see [Keeping the ad clear of your controls](https://docs.kontext.so/video/sdk/kotlin/guides.md#keeping-the-ad-clear-of-your-controls).

> [tab] Pause
>
> A card, or the full player, when the viewer pauses. Take it away when they resume.
>
> ```kotlin theme={null}
> fun viewerPaused() {
>     player.pause()
>     try { pauseAd?.show(playerContainer) } catch (e: VideoAdException) { }   // the ViewGroup your video plays in
> }
>
> fun viewerResumed() {
>     pauseAd?.dismiss()
>     player.play()
> }
> ```
>
> The viewer can also tap Resume on a full-screen pause ad; you resume playback in `onAdEnded`.

> [tab] Linear
>
> A full-screen video between, before or after episodes. Pause your content first; the SDK shows the ad in its own Activity.
>
> ```kotlin theme={null}
> fun showLinear() {
>     val ad = linearAd ?: return
>     player.pause()
>     try {
>         ad.show(this)                    // this: your Activity
>     } catch (e: VideoAdException) {
>         player.play()                    // it never appeared, so nothing else follows
>     }
> }
> ```
>
> You resume your content in `onAdEnded`. The system Back button does nothing on the ad's screen: the creative's own controls end it.

`show()` either throws, and nothing happened, or the ad is on screen. From then on the SDK runs it until it ends.

## 5. React to the end

Implement `VideoAdListener`. Every method has an empty default; the one you need is `onAdEnded`. It arrives exactly once per shown ad, whoever ended it, so put your resume logic there and nowhere else, and ask for the next ad from there, so it is ready early.

> [tab] Overlay
>
> Your content kept playing, so there is nothing to resume.
>
> ```kotlin theme={null}
> override fun onAdEnded(ad: VideoAd, reason: VideoAd.EndReason) {
>     overlayAd = null
>     // prepare the next ad early: format(), then load()
> }
> ```

> [tab] Pause
>
> When the viewer tapped Resume on a full-screen pause ad, resume playback here. When they resumed through your own control, `viewerResumed()` already did.
>
> ```kotlin theme={null}
> override fun onAdEnded(ad: VideoAd, reason: VideoAd.EndReason) {
>     pauseAd = null
>     if (reason == VideoAd.EndReason.RESUMED) player.play()   // the viewer tapped Resume on the ad
>     // prepare the next ad early: format(), then load()
> }
> ```

> [tab] Linear
>
> Your content paused for the ad; resume it, whatever the reason, unless the viewer left. Check `ads.isDestroyed` rather than the reason: it is `true` whenever the viewer left, whichever reason the ad ended with.
>
> ```kotlin theme={null}
> override fun onAdEnded(ad: VideoAd, reason: VideoAd.EndReason) {
>     linearAd = null
>     if (ads.isDestroyed) return          // the viewer left: nothing to resume
>     player.play()
>     // prepare the next ad early: format(), then load()
> }
> ```

## 6. Tear down

Call `destroy()` when the viewer leaves the video: in `onDestroy()` of an Activity, `onDestroyView()` of a Fragment, or the `onDispose` of a `DisposableEffect` in Compose. It drops every held ad, tells the server which ones were not used, and ends the one on screen.

```kotlin theme={null}
override fun onDestroy() {
    super.onDestroy()
    ads.destroy()
}
```

## The whole thing

One Activity per episode, playing in an `ExoPlayer`. Pick the tab for your format:

> [tab] Overlay
>
> ```kotlin theme={null}
> class PlayerActivity : ComponentActivity(), VideoAdListener {
>     private lateinit var player: ExoPlayer
>     private lateinit var playerContainer: ViewGroup
>     private lateinit var ads: VideoAds
>     private var overlayAd: VideoAd? = null
>
>     override fun onCreate(savedInstanceState: Bundle?) {
>         super.onCreate(savedInstanceState)
>         setContentView(R.layout.activity_player)          // a FrameLayout holding your PlayerView
>         playerContainer = findViewById(R.id.player_container)
>         player = ExoPlayer.Builder(this).build()
>         findViewById<PlayerView>(R.id.player_view).player = player
>         ads = VideoAds(Content("episode-1234", "en"))
>         ads.track(player)
>         lifecycleScope.launch { prepare() }
>     }
>
>     private suspend fun prepare() {
>         try {
>             val ad = ads.format(AdFormat.OVERLAY)
>             ad.load()
>             ad.listener = this
>             overlayAd = ad
>         } catch (e: VideoAdException) {
>             // no fill this time; ask again at the next moment
>         }
>     }
>
>     fun showOverlay() {                                     // a moment you pick
>         try { overlayAd?.show(playerContainer) } catch (e: VideoAdException) { }
>     }
>
>     override fun onAdEnded(ad: VideoAd, reason: VideoAd.EndReason) {
>         overlayAd = null
>         if (!ads.isDestroyed) lifecycleScope.launch { prepare() }
>     }
>
>     override fun onDestroy() {
>         super.onDestroy()
>         ads.destroy()
>         player.release()
>     }
> }
> ```

> [tab] Pause
>
> ```kotlin theme={null}
> class PlayerActivity : ComponentActivity(), VideoAdListener {
>     private lateinit var player: ExoPlayer
>     private lateinit var playerContainer: ViewGroup
>     private lateinit var ads: VideoAds
>     private var pauseAd: VideoAd? = null
>
>     override fun onCreate(savedInstanceState: Bundle?) {
>         super.onCreate(savedInstanceState)
>         setContentView(R.layout.activity_player)          // a FrameLayout holding your PlayerView
>         playerContainer = findViewById(R.id.player_container)
>         player = ExoPlayer.Builder(this).build()
>         findViewById<PlayerView>(R.id.player_view).player = player
>         ads = VideoAds(Content("episode-1234", "en"))
>         ads.track(player)
>         lifecycleScope.launch { prepare() }
>     }
>
>     private suspend fun prepare() {
>         try {
>             val ad = ads.format(AdFormat.PAUSE)
>             ad.load()
>             ad.listener = this
>             pauseAd = ad
>         } catch (e: VideoAdException) {
>             // no fill this time; ask again at the next moment
>         }
>     }
>
>     fun viewerPaused() {
>         player.pause()
>         try { pauseAd?.show(playerContainer) } catch (e: VideoAdException) { }
>     }
>
>     fun viewerResumed() {
>         pauseAd?.dismiss()
>         player.play()
>     }
>
>     override fun onAdEnded(ad: VideoAd, reason: VideoAd.EndReason) {
>         pauseAd = null
>         if (ads.isDestroyed) return
>         if (reason == VideoAd.EndReason.RESUMED) player.play()   // the viewer tapped Resume on the ad
>         lifecycleScope.launch { prepare() }
>     }
>
>     override fun onDestroy() {
>         super.onDestroy()
>         ads.destroy()
>         player.release()
>     }
> }
> ```

> [tab] Linear
>
> ```kotlin theme={null}
> class PlayerActivity : ComponentActivity(), VideoAdListener {
>     private lateinit var player: ExoPlayer
>     private lateinit var ads: VideoAds
>     private var linearAd: VideoAd? = null
>
>     override fun onCreate(savedInstanceState: Bundle?) {
>         super.onCreate(savedInstanceState)
>         setContentView(R.layout.activity_player)          // a FrameLayout holding your PlayerView
>         player = ExoPlayer.Builder(this).build()
>         findViewById<PlayerView>(R.id.player_view).player = player
>         ads = VideoAds(Content("episode-1234", "en"))
>         ads.track(player)
>         lifecycleScope.launch { prepare() }
>     }
>
>     private suspend fun prepare() {
>         try {
>             val ad = ads.format(AdFormat.LINEAR)
>             ad.load()
>             ad.listener = this
>             linearAd = ad
>         } catch (e: VideoAdException) {
>             // no fill this time; ask again at the next moment
>         }
>     }
>
>     fun showLinear() {                                      // a moment you pick
>         val ad = linearAd ?: return
>         player.pause()
>         try { ad.show(this) } catch (e: VideoAdException) { player.play() }
>     }
>
>     override fun onAdEnded(ad: VideoAd, reason: VideoAd.EndReason) {
>         linearAd = null
>         if (ads.isDestroyed) return                         // after onDestroy(), whatever the reason
>         player.play()
>         lifecycleScope.launch { prepare() }
>     }
>
>     override fun onDestroy() {
>         super.onDestroy()
>         ads.destroy()
>         player.release()
>     }
> }
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.