mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
clip transcoded tracks to their real duration
The declared upper-bound length stopped the stall where Media3 saw no seek map duration, but it also made the track look longer than the audio: the player reached the last sample still short of the declared end and waited for data that never came, so it never advanced. Clip each transcoded item to the library duration, which Media3 applies through ClippingMediaSource, so the timeline ends where the audio ends and the next track advances. The declared length still makes the stream on-demand (seekable) and never bounds reads below the real size.
This commit is contained in:
@@ -38,7 +38,9 @@ data class ListQuery(
|
||||
data class StreamQuality(
|
||||
val maxBitrate: Int = 0,
|
||||
val format: String? = null,
|
||||
)
|
||||
) {
|
||||
val transcodes: Boolean get() = maxBitrate > 0 || !format.isNullOrEmpty()
|
||||
}
|
||||
|
||||
private val Context.preferences: DataStore<Preferences> by preferencesDataStore("user_prefs")
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ fun declaredStreamLength(
|
||||
quality: StreamQuality,
|
||||
): Long? {
|
||||
if (durationMs == null || durationMs <= 0) return null
|
||||
if (quality.maxBitrate <= 0 && quality.format.isNullOrEmpty()) return null
|
||||
if (!quality.transcodes) return null
|
||||
val bps = if (quality.maxBitrate > 0) maxOf(quality.maxBitrate * 1000L, MIN_TRANSCODE_BPS) else UNCAPPED_BPS
|
||||
return durationMs / 1000 * bps / 8 * 5 / 4 + OVERHEAD_BYTES
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaItem.ClippingConfiguration
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.PlaybackException
|
||||
import androidx.media3.common.Player
|
||||
@@ -142,12 +143,20 @@ class Media3PlayerHandle(
|
||||
item.durationMs?.let { putLong(EXTRA_DURATION_MS, it) }
|
||||
},
|
||||
).build()
|
||||
return MediaItem
|
||||
.Builder()
|
||||
.setMediaId(item.id)
|
||||
.setUri(sourceRepository.streamUri(item.id, item.durationMs))
|
||||
.setMediaMetadata(metadata)
|
||||
.build()
|
||||
val builder =
|
||||
MediaItem
|
||||
.Builder()
|
||||
.setMediaId(item.id)
|
||||
.setUri(sourceRepository.streamUri(item.id, item.durationMs))
|
||||
.setMediaMetadata(metadata)
|
||||
if (sourceRepository.quality.value.transcodes) {
|
||||
item.durationMs?.takeIf { it > 0 }?.let { duration ->
|
||||
builder.setClippingConfiguration(
|
||||
ClippingConfiguration.Builder().setEndPositionMs(duration).build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun toQueueItem(mediaItem: MediaItem): QueueItem =
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.ForwardingPlayer
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.ExperimentalApi
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
@@ -25,7 +26,7 @@ class PlaybackService : MediaSessionService() {
|
||||
private var session: MediaSession? = null
|
||||
private var artworkLoader: CoverArtBitmapLoader? = null
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
@OptIn(UnstableApi::class, ExperimentalApi::class)
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
val exoPlayer =
|
||||
@@ -40,9 +41,11 @@ class PlaybackService : MediaSessionService() {
|
||||
true,
|
||||
).setHandleAudioBecomingNoisy(true)
|
||||
.setMediaSourceFactory(
|
||||
DefaultMediaSourceFactory(this).setDataSourceFactory(
|
||||
KnownLengthDataSourceFactory(OkHttpDataSource.Factory(streamingClient())),
|
||||
),
|
||||
DefaultMediaSourceFactory(this)
|
||||
.setEnableClippingInMediaPeriod(true)
|
||||
.setDataSourceFactory(
|
||||
KnownLengthDataSourceFactory(OkHttpDataSource.Factory(streamingClient())),
|
||||
),
|
||||
).build()
|
||||
val player = MetadataDurationPlayer(exoPlayer)
|
||||
val loader =
|
||||
|
||||
Reference in New Issue
Block a user