> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://fastpix.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://fastpix.com/docs/_mcp/server.

# Preload and cache for faster startup

> Reduce video startup time in the FastPix Android Player with fast-start buffering, preloading, disk caching, and pre-rendering for playlists and short-form feeds.

Short-form feeds, such as reels and episodes, live or die on how fast the next item starts. Arriving at a cold item costs a TLS handshake, a multivariant playlist fetch, a media playlist fetch, and then the first segment: four sequential round trips before a frame can be decoded, and then a decoder still has to start before the first frame appears. The FastPix Android Player SDK gives you four independent levers to cut that delay. Each one is a single setting: you say how much, and the SDK works out the rest. Use as many as fit your feed.

* **Fast start** tunes buffering so the first frame appears sooner (on by default).
* **Preloading** prepares the network round trips for upcoming playlist entries before you reach them.
* **Disk caching** stores what was downloaded so re-watches, scroll-backs, and preloaded entries play from local storage.
* **Pre-caching** warms items into the cache before a player even exists, for example the rows of a list before one is opened.
* **Pre-rendering** prepares upcoming entries up to their first frame so moving to them never shows a black screen.

| Feature       | Setting                          | What it removes                                                         |
| ------------- | -------------------------------- | ----------------------------------------------------------------------- |
| Fast start    | `BufferConfig` (on by default)   | Waiting for a deep buffer before starting                               |
| Preloading    | `PreloadConfig(count, behind)`   | The network round trips for upcoming entries                            |
| Disk cache    | `CacheConfig.enabled()`          | Re-downloading anything watched or preloaded before                     |
| Pre-caching   | `FastPixPreCacher`               | The network round trips for items opened later, for example from a list |
| Pre-rendering | `PrerenderConfig(count, behind)` | The black frame while the next video's decoder starts                   |

\


## Before you begin

Make sure you have the following:

* The FastPix Android Player SDK version 2.2.2 or later, installed and set up. See [Install FastPix Android player](/docs/android-player/install-fastpix-android-player) and [Set up the player](/docs/android-player/set-up-the-player).
* A [playlist](/docs/android-player/manage-playlists) in place if you want preloading or pre-rendering to warm the entries around the current one.

\


## Fast start (on by default)

The player installs a tuned `LoadControl`, so playback begins after 500 ms of buffered media rather than Media3's 1000 ms. There's nothing to configure, but you can tune it or opt out with `BufferConfig`.

```kotlin
FastPixPlayer.Builder(context)
    .setBufferConfig(BufferConfig.FEED)            // reel-tuned: start at 250 ms, shallow buffer
    // .setBufferConfig(BufferConfig.MEDIA3_DEFAULT) // restore Media3's stock thresholds
    .build()
```

| Preset                        | `bufferForPlaybackMs` | Ahead buffer | Use for                                  |
| ----------------------------- | --------------------- | ------------ | ---------------------------------------- |
| `BufferConfig.DEFAULT`        | 500 ms                | 50 s         | General playback (applied automatically) |
| `BufferConfig.FEED`           | 250 ms                | 20 s         | Reel and short-form feeds                |
| `BufferConfig.MEDIA3_DEFAULT` | 1000 ms               | 50 s         | Restoring Media3's stock behavior        |

A shallower ahead-buffer is deliberate for feeds: a user who swipes after three seconds never watches the 50 seconds you downloaded, and that is the user's mobile data.

\


## Preload upcoming entries

`PreloadConfig(count, behind)` decides how many [playlist](/docs/android-player/manage-playlists) entries around the current one to prepare ahead of time. Set `count` for the entries after the current one, and `behind` for the entries before it.

```kotlin
FastPixPlayer.Builder(context)
    .setPreloadConfig(PreloadConfig(count = 3))              // the next 3 entries
    // .setPreloadConfig(PreloadConfig(count = 3, behind = 1)) // and the previous one
    .build()

player.setPlaylist(items)
```

* **Adjacent entries** have their first seconds buffered in memory, so they start almost at once.
* **Entries further out** have their playlists fetched and their tracks chosen, which takes little memory and leaves no round trips before their first segment.
* **With the disk cache on**, entries further out also have their first segments written to disk, in the rendition this player will pick. The SDK follows the player's own bitrate cap and bandwidth estimate, so nothing is warmed that playback then refuses.

\


## Cache segments on disk

Disk caching persists what was downloaded, so re-watching, going back, or reaching a preloaded entry reads from disk. Enable it with `CacheConfig`.

```kotlin
FastPixPlayer.Builder(context)
    .setCacheConfig(CacheConfig.enabled())          // 256 MB, LRU-evicted
    .build()
```

It is safe for any content, live included. FastPix streams are cached **by asset**: their segment URLs are re-signed on every playlist fetch, so the SDK keys segments by playback ID and rendition rather than by URL. That way a re-watch hits the cache across sessions and token refreshes. Query parameters that only authorize a request (`token`, `signature`, `expires`, `cdn`) are ignored, while content-selecting parameters stay part of the key.

The cache is **process-wide**: every player in the app shares one store, and the first config to open it fixes the location and ceiling. Inspect or reset it with `MediaCacheProvider.cachedBytes()` and `MediaCacheProvider.clear()`.

Opening the cache reads its index from disk. To keep that work off the main thread, open it once at startup from a background thread. Every later call returns the already-open cache.

```kotlin
// Application.onCreate()
Executors.newSingleThreadExecutor().execute {
    MediaCacheProvider.getOrCreate(this, CacheConfig.enabled())
}
```

Since 2.2.1, the cache also keeps **HLS playlists** while they are safe to reuse, so a cached item can start with no network round trip at all. Only multivariant playlists and finished VOD media playlists are stored, never a live media playlist. Each is served until the earliest signed `expires=` in it, less 10 minutes, and for at most `maxPlaylistAgeMs` (24 hours by default). A stream's playlist is keyed by playback ID, so a copy stored under one host also serves the same stream on `stream.fastpix.com` or a custom domain. To fetch playlists on every start instead, set `maxPlaylistAgeMs` to `0`.

```kotlin
CacheConfig(enabled = true, maxPlaylistAgeMs = 0)   // never reuse a stored playlist
```

\


## Pre-cache before a player exists

Preloading and pre-rendering warm the entries around a player that already exists. To warm items **before any player is built**, such as the rows of a list screen or a feed's first items at app start, use `FastPixPreCacher`. The player you build later, with the same `CacheConfig`, then starts from disk. See [Pre-cache videos before a player exists](/docs/android-player/pre-cache-videos) for the full setup.

\


## Pre-render the next frame

Pre-rendering prepares an upcoming entry up to its **first frame**, so moving to it shows that frame immediately: the first frame is the loader, and there is no black screen while the video starts. Enable it with `PrerenderConfig(count, behind)`, where `count` covers the entries after the current one and `behind` covers the entries before it.

**One `PlayerView` moving through a playlist** (a player with Next and Previous, or the next episode):

```kotlin
val player = FastPixPlayer.Builder(context)
    .setPreloadConfig(PreloadConfig(count = 3))
    .setCacheConfig(CacheConfig.enabled())
    .setPrerenderConfig(PrerenderConfig(count = 1, behind = 1))   // next and previous entries
    .build()

playerView.player = player
player.setPlaylist(items)
player.next()     // shows the next entry's first frame at once, then its video
```

While the current entry plays, the SDK decodes each entry in range in the background and keeps its first frame. On `next()`, `previous()`, or `skipTo()`, the view shows that frame at once and swaps to the video the moment the video's own first frame is ready. Turn the cache on too, so the background decode and playback share one download.

The counts are a **device-limited maximum**. Each pre-rendered entry holds a video decoder, and devices run only a few at once, so the SDK pre-renders only as many entries as fit and never refuses the item playing. A pre-render that cannot get a decoder is dropped silently, and the page then loads normally when reached. DRM-protected entries are never pre-rendered.

**A page and a `PlayerView` per entry** (reel feeds, carousels) use `FastPixPlayerPool`, which supplies each page's player and pre-renders neighboring pages up to their first frame. That shape has its own recipes for `ViewPager2`, Compose pagers, and `RecyclerView`. See [Build video feeds](/docs/android-player/build-video-feeds) rather than duplicating the setup here.

\


## Migrating from 2.1.0

The 2.1.0 APIs keep working and are marked deprecated, with IDE quick-fixes to the replacements.

| 2.1.0                                                  | 2.2.0                                                             |
| ------------------------------------------------------ | ----------------------------------------------------------------- |
| `setMediaItems(items)`                                 | `setPlaylist(items.map { PlaylistItem.fromMediaItem(it) })`       |
| `PreloadConfig(enabled = true)` / `PreloadConfig.FEED` | `PreloadConfig(count = 1)`                                        |
| `CacheConfig.forOnDemandFeed()`                        | `CacheConfig.enabled()`, which stores playlists safely on its own |
| Per-page players you build yourself                    | `FastPixPlayerPool` (which also pre-renders)                      |
| Black frame between items on one player                | `setPrerenderConfig(PrerenderConfig(count = 1))`                  |

\


## Measure the impact

The sample app ships two working feeds for this purpose. **Reel Feed: ViewPager + Compose player** is a vertical `ViewPager2` whose pages call one shared player composable, with players from `FastPixPlayerPool`; its heads-up display reports whether each page's first frame was already on screen when it arrived. **Episode Feed** is one player over a playlist with Prev and Next and a Preload toggle; its display shows the milliseconds from moving to an entry to that entry's first frame.

\


#### Frequently asked questions

Start with fast-start buffering, which is on by default (playback begins at 500 ms of buffered media). For feeds, add `BufferConfig.FEED`, preload upcoming entries with `PreloadConfig(count = 3)`, enable disk caching with `CacheConfig.enabled()`, and pre-render the next entries with `PrerenderConfig(count = 1)`. Together, these remove most of the swipe-to-play delay.

Preloading with `PreloadConfig(count, behind)` prepares the network work for upcoming playlist entries: playlists fetched, tracks chosen, and, with the cache on, first segments written to disk. Pre-rendering with `PrerenderConfig(count, behind)` goes one step further and decodes each entry up to its first frame, so moving to it shows that frame with no black screen. They complement each other and are usually enabled together.

They are. FastPix streams are cached by playback ID and rendition rather than by URL, so even though FastPix re-signs segment URLs on every playlist fetch, a re-watch still hits the cache across sessions and token refreshes. Just enable `CacheConfig.enabled()`; the older `CacheConfig.forOnDemandFeed()` is no longer needed.

A shallower ahead-buffer (as in `BufferConfig.FEED`) is a deliberate trade-off for feeds: it saves mobile data a user won't watch if they swipe away. For general-purpose playback, keep `BufferConfig.DEFAULT`, or restore Media3's thresholds with `BufferConfig.MEDIA3_DEFAULT`.

Pre-render counts are a device-limited maximum: each pre-rendered entry holds a video decoder, and budget phones run only two or three at once, so the SDK pre-renders only as many as fit. DRM-protected entries are never pre-rendered, and an entry whose own video opens on black still opens on black.

\


## What's next

* [Manage playlists](/docs/android-player/manage-playlists) to queue entries that preloading and pre-rendering can warm.
* [Build video feeds](/docs/android-player/build-video-feeds) with `FastPixPlayerPool` for reels and carousels.
* [Set the video resize mode](/docs/android-player/set-the-video-resize-mode) so feed items fill the screen.
* [Listen to playback events](/docs/android-player/listen-to-playback-events) for time, buffering, and error callbacks.