> ## 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.

# AVPlayer

> Hand an AVPlayer to the SDK: ads.track(player).

The SDK follows your content player: where the viewer is in the episode, and whether the content plays. An overlay shows over playing content only and ends when the viewer pauses; a pause ad ends when the content plays again. An `AVPlayer` needs no adapter: `track(_:)` takes it directly.

## Track the player

```swift theme={null}
player.replaceCurrentItem(with: AVPlayerItem(url: episode.url))
ads.track(player)          // once this episode is in the player
player.play()
```

* **Any `AVPlayer`**, an `AVQueuePlayer` included, wherever it renders: an `AVPlayerLayer`, an `AVPlayerViewController`, a SwiftUI `VideoPlayer`.
* **Held weakly:** the SDK never keeps your player alive. It lets go at `destroy()` or the next `track(_:)`.
* **Call it when the episode starts playing**, not when you create the `VideoAds`: until then the player's time belongs to the previous episode. Before that the SDK sends 0, which is right for a prefetch.

## One player across episodes

Most apps reuse one player and swap its item. Track it from each episode's `VideoAds` once that episode's item is in the player:

```swift theme={null}
func open(_ episode: Episode) {
    ads?.destroy()                                     // after a post-roll's didEnd, if one is on screen
    ads = VideoAds(content: episode.content)
    player.replaceCurrentItem(with: AVPlayerItem(url: episode.url))
    ads?.track(player)
}
```

Tracked before the item changed, the new episode's overlay and pause ads throw `invalidated(.contentChanged)` at `show`: the SDK took the previous item for this episode's. Track again once the new item is in and they show.

## What the SDK reads

| The SDK needs | Read from `AVPlayer` |
| - | - |
| Where the content is | `currentTime()` |
| Whether it plays | `rate` is not 0, and the item has not played to its end. Paused by the viewer or your app is not playing; buffering keeps the rate, so it counts as playing. |
| The speed | `rate`, which also tells fast playback from a seek |
| A seek | a time jump of the current item |
| Another episode | another `currentItem`, or the item played to its end (whatever `actionAtItemEnd` does) |

The SDK observes the player, so an overlay ends the moment the viewer pauses, wherever the pause came from: your controls, the lock screen, Control Center, a headset, picture-in-picture.

## In the background

iOS pauses an `AVPlayer` when the app goes to the background. An overlay on screen freezes instead of ending: it goes on if the content plays again within a second of the app's return, and ends as `.interrupted` otherwise.

## A looped episode

An `AVPlayerLooper` plays copies of the item, so each loop is another item: an overlay or pause ad requested in one loop is refused in the next (`invalidated(.contentChanged)`). Request the next ad from `didEnd`, as always, and it belongs to the loop that plays.


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