Yum News Yum News

Technology  ·  Artificial Intelligence  ·  Developer Insights

Breaking
Gemini Task Automation Guide: India UK Japan Apps ✦   How to Set Up ChatGPT Wallet for Agent Purchases ✦   How to Set Up and Use Gemini Live on Apple CarPlay ✦   How to Set Up Gemini Live on Apple CarPlay ✦   How to Use the Gemini Decisions API for Fast Choices ✦   How to Use Gemini Task Automation on Pixel Phones   ✦   Gemini 3.8 Live launches with native audio dialogue  ✦  Android September Drop adds Find Hub memory  ✦  ML Kit now supports Gemini Nano on-device inference
Use Media3 KtorDataSource With ExoPlayer
androidtutorialsdeveloperhow-to

Use Media3 KtorDataSource With ExoPlayer

Wire Media3 1.11 KtorDataSource into ExoPlayer for Kotlin-first HTTP playback instead of Cronet or OkHttp.

Media3 1.11 shipped a Kotlin-first HTTP stack for ExoPlayer. The new media3-datasource-ktor module exposes KtorDataSource, an HttpDataSource that delegates to Ktor’s HttpClient.

If your app already uses Ktor for APIs, you can share one client for REST calls and media bytes. This guide shows how to add the module, build a factory, and attach it to ExoPlayer without inventing extra stats.

What Media3 1.11 added

On 11 August 2026, Google published Media3 1.11. The official post lists Compose player slots, PlayerPool, Cast SystemUI Output Switcher support, Ogg and WAV muxers, and the Ktor network extension.

KtorDataSource sits next to the older Cronet and OkHttp data source modules. Android Developers documents it as an HttpDataSource that sets request headers from the DataSpec, then setRequestProperty, then factory defaults.

Use it when you want coroutine-friendly HTTP and a single Kotlin client. Stay on DefaultHttpDataSource, Cronet, or OkHttp if those stacks already work for you.

Laptop and code editor used for Android media development

Add the Media3 1.11 modules

Pin one version. Official getting-started docs currently list 1.11.1 for ExoPlayer and Compose UI.

val media3 = "1.11.1"

dependencies {
    implementation("androidx.media3:media3-exoplayer:$media3")
    implementation("androidx.media3:media3-datasource-ktor:$media3")
    implementation("androidx.media3:media3-ui-compose-material3:$media3")
    implementation("io.ktor:ktor-client-okhttp:3.0.3")
    implementation("io.ktor:ktor-client-core:3.0.3")
}

KtorDataSource.Factory requires a Ktor HttpClient. On Android the usual engine is OkHttp. You can swap CIO or other engines if your project already standardized on them.

Declare internet permission in the manifest. Media playback still needs INTERNET even when Ktor wraps the socket.

Build a shared HttpClient

Create the client once. Reuse it for JSON APIs and for the player factory so connection pools stay warm.

fun createMediaHttpClient(): HttpClient =
    HttpClient(OkHttp) {
        expectSuccess = false
        engine {
            config {
                followRedirects(true)
                retryOnConnectionFailure(true)
            }
        }
    }

expectSuccess = false matters. ExoPlayer reads status codes itself. If Ktor throws on 4xx or 5xx, the player never sees the HTTP error as a media load error.

Keep timeouts on the engine, not only on ExoPlayer. Range requests for seeking still go through this client.

Create KtorDataSource.Factory

The public constructor takes an HttpClient, an optional user agent, an optional content-type predicate, and an optional TransferListener.

fun ktorDataSourceFactory(
    httpClient: HttpClient,
    transferListener: TransferListener? = null,
): KtorDataSource.Factory =
    KtorDataSource.Factory(
        httpClient,
        /* userAgent = */ "MyApp/1.0",
        /* contentTypePredicate = */ null,
        transferListener,
    ).setDefaultRequestProperties(
        mapOf("Accept" to "*/*"),
    )

Header order is documented: DataSpec headers win, then setRequestProperty, then these defaults. Auth tokens belong in factory defaults or a wrapper that refreshes them before createDataSource().

Pass a TransferListener if you need bandwidth estimates for analytics. ExoPlayer already tracks transfer events for adaptive track selection when you attach the factory to DefaultDataSource.

Attach the factory to ExoPlayer

Wrap Ktor with DefaultDataSource.Factory so local files, assets, and content URIs still work. HTTP and HTTPS go to Ktor.

fun buildPlayer(context: Context, httpClient: HttpClient): ExoPlayer {
    val httpFactory = ktorDataSourceFactory(httpClient)
    val dataSourceFactory = DefaultDataSource.Factory(context, httpFactory)
    val mediaSourceFactory = DefaultMediaSourceFactory(context)
        .setDataSourceFactory(dataSourceFactory)

    return ExoPlayer.Builder(context)
        .setMediaSourceFactory(mediaSourceFactory)
        .build()
        .also { player ->
            player.setMediaItem(
                MediaItem.fromUri("https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-2030.mp4"),
            )
            player.prepare()
        }
}

Release the player in onDispose or when the ViewModel clears. Do not close the shared HttpClient until the process no longer needs network media.

Bind a Compose player

Media3 1.11 documents the Material 3 Player composable with slots. You can keep Default controls while the bytes come from Ktor.

@Composable
fun KtorBackedPlayer(player: Player) {
    Player(
        player = player,
        topControls = { PlayerDefaults.TopControls(player) },
        centerControls = { PlayerDefaults.CenterControls(player) },
        bottomControls = { PlayerDefaults.BottomControls(player) },
    )
}

If you also ship a short-form feed, pair this data source with the pool APIs covered in How to use Media3 PlayerPool in Compose. Recycled players still share the same DataSource.Factory.

Close-up of headphones and a phone playing video

Compare Ktor, OkHttp, and Cronet

Pick one HTTP module per app unless you have a measured reason to mix them.

ModuleWhen it fits
media3-datasource-ktorShared Ktor client, Kotlin-first code
media3-datasource-okhttpExisting OkHttp interceptors and cache
media3-datasource-cronetPlay services Cronet / HTTP/3 on device
DefaultHttpDataSourceNo extra HTTP library

Ktor does not replace adaptive streaming logic. HLS and DASH still run inside ExoPlayer. The data source only fetches segments and manifests.

Do not enable Ktor content negotiation plugins that consume the body as JSON. Media responses must stay raw bytes.

Handle auth, ranges, and errors

Signed URLs expire. Refresh the MediaItem URI or the default Authorization header before the next prepare().

Seeking sends HTTP range requests. Leave range support on in the engine. If a CDN rejects ranges, ExoPlayer cannot scrub.

Use rememberErrorState from media3-ui-compose if you show an overlay. Media3 1.11 added that holder plus ErrorText and a default Material 3 error overlay.

Test redirects. Ktor and ExoPlayer both have redirect settings. Pick one owner so a 302 does not bounce twice.

Watch a Media3 playback walkthrough

Google’s I/O session covers ExoPlayer, preloading, Cast, and HDR playback updates that sit next to the 1.11 network module.

Tips that keep playback stable

Share one HttpClient per process. Creating a client per player wastes sockets.

Call httpClient.close() only in application shutdown tests, not in every composable dispose.

Keep Media3 and Ktor versions in a catalog. The datasource artifact is androidx.media3:media3-datasource-ktor.

Log DataSpec.uri in a TransferListener during debug. Confirm the player hits your CDN, not a fallback URL.

For compact controls while audio continues, see How to Add a Media3 MiniController in Jetpack Compose.

Conclusion

KtorDataSource is a small, official Media3 1.11 addition. You inject a Ktor HttpClient, wrap it with DefaultDataSource.Factory, and hand that factory to ExoPlayer.Builder.

The player UI, pooling, and session APIs stay the same. Only the HTTP path changes. Start with a single progressive MP4, then point the same factory at your HLS or DASH manifests.

Sources

Share this story