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

# TTSDK player

> Hand a TTSDK engine (TTVideoEngine) to the SDK with the KontextVideoTTSDK product.

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. With a TTSDK player (`TTVideoEngine`, the BytePlus / Volcengine VOD player SDK), one call gives the SDK all of that.

## Add the product

`KontextVideoTTSDK` is a second product of the same package. In Xcode, add it next to `KontextVideoSDK` (File → Add Package Dependencies, then tick both), or in `Package.swift`:

```swift theme={null}
.target(name: "YourApp", dependencies: [
    .product(name: "KontextVideoSDK", package: "sdk-swift-video"),
    .product(name: "KontextVideoTTSDK", package: "sdk-swift-video"),
]),
```

**No TTSDK dependency.** TTSDK ships as a CocoaPod, which a Swift package cannot depend on. The product reads the engine by its Objective-C properties (`currentPlaybackTime`, `playbackState`, `playbackSpeed`), so it builds without TTSDK and works with your app's own TTSDK and license.

## Track the engine

```swift theme={null}
import KontextVideoTTSDK

ads.track(ttVideoEngine: engine) { episode.id }
```

* Call it once the engine has the episode's source, when the episode starts.
* **The trailing closure** names the episode the engine plays (its vid, its URL). An overlay or pause ad of this `VideoAds` throws `invalidated(.contentChanged)` at `show` once it returns another value. Without it, only the engine moving to another `VideoAds` is checked.
* **Your delegate stays yours.** TTSDK has one `delegate`, and the SDK never sets it: it reads the engine four times a second while an overlay or pause ad is on screen, and at every `show`.
* **Held weakly:** the SDK never keeps the engine alive. It lets go at `destroy()` or the next `track`.

## In a feed

Short-drama feeds usually give each page its own engine. Give each page its own `VideoAds`, and track the page's engine when the page becomes current:

```swift theme={null}
func pageDidBecomeCurrent(_ page: EpisodePage) {
    page.ads.track(ttVideoEngine: page.engine) { page.episode.id }
}
```

Show an overlay or pause ad on the current page only. A page swiped away with its ad on screen ends that ad within about a second (`.interrupted`), so the next page's ad can show.

## What the SDK reads

| The SDK needs | Read from TTSDK |
| - | - |
| Where the content is | `currentPlaybackTime`, in seconds |
| Whether it plays | `playbackState` is playing. Paused or stopped is not playing; buffering is. |
| The speed | `playbackSpeed` |
| Another episode | the value of your closure |
| A seek | inferred from the position |

An object that is not a `TTVideoEngine` (it lacks those properties) is followed as never playing, with an error on the console.


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