playback: cache streamed audio to stop redundant refetches

A streamed MP3 with no Xing/Info/VBRI header gets an estimated duration
from Mp3Extractor. At end of stream the extractor rewrites it, which
refreshes the timeline; MediaPeriodQueue.updateQueuedPeriods then sees
the playing period's duration change, drops every period after it and
re-enqueues the next one, so the next track is opened from byte 0 again.
After a seek near the end that repeats a few times.

Route streamed bytes through a bounded SimpleCache (DefaultDataSource ->
CacheDataSource -> KnownLengthDataSource -> OkHttp). A re-opened next
period is served from the bytes the prefetch already committed, so the
redundant network fetches stop without changing playback length or
gapless padding. DefaultDataSource still handles file:/content:, so
downloads bypass the cache.

The cache is a process-scoped 64 MiB LRU in cacheDir/stream-cache; a
construction failure falls back to no cache so playback still starts.
This commit is contained in:
austinried
2026-10-09 13:36:55 +00:00
parent 0cef3657bc
commit e806eaef89
5 changed files with 105 additions and 4 deletions
+2
View File
@@ -55,7 +55,9 @@ androidx-paging-compose = { group = "androidx.paging", name = "paging-compose",
androidx-palette = { group = "androidx.palette", name = "palette-ktx", version.ref = "palette" }
reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" }
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
androidx-media3-datasource = { group = "androidx.media3", name = "media3-datasource", version.ref = "media3" }
androidx-media3-datasource-okhttp = { group = "androidx.media3", name = "media3-datasource-okhttp", version.ref = "media3" }
androidx-media3-database = { group = "androidx.media3", name = "media3-database", version.ref = "media3" }
androidx-media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3" }
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }