Fix media seeking flicker (#6434)

This commit is contained in:
bxdxnn 2026-03-23 18:03:50 +03:00 committed by GitHub
parent 9074692189
commit a261156c7c
5 changed files with 40 additions and 7 deletions

View file

@ -121,6 +121,7 @@ private fun ExoPlayerMediaAudioView(
durationInMillis = 0, durationInMillis = 0,
canMute = false, canMute = false,
isMuted = false, isMuted = false,
seekingToMillis = null,
) )
) )
} }
@ -171,15 +172,21 @@ private fun ExoPlayerMediaAudioView(
LaunchedEffect(exoPlayer.isPlaying) { LaunchedEffect(exoPlayer.isPlaying) {
if (exoPlayer.isPlaying) { if (exoPlayer.isPlaying) {
while (true) { while (true) {
val position = exoPlayer.currentPosition
val seekingTo = mediaPlayerControllerState.seekingToMillis
mediaPlayerControllerState = mediaPlayerControllerState.copy( mediaPlayerControllerState = mediaPlayerControllerState.copy(
progressInMillis = exoPlayer.currentPosition, progressInMillis = position,
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
) )
delay(200) delay(200)
} }
} else { } else {
// Ensure we render the final state // Ensure we render the final state
val position = exoPlayer.currentPosition
val seekingTo = mediaPlayerControllerState.seekingToMillis
mediaPlayerControllerState = mediaPlayerControllerState.copy( mediaPlayerControllerState = mediaPlayerControllerState.copy(
progressInMillis = exoPlayer.currentPosition, progressInMillis = position,
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
) )
} }
} }
@ -294,6 +301,9 @@ private fun ExoPlayerMediaAudioView(
exoPlayer.togglePlay() exoPlayer.togglePlay()
}, },
onSeekChange = { onSeekChange = {
mediaPlayerControllerState = mediaPlayerControllerState.copy(
seekingToMillis = it.toLong(),
)
exoPlayer.seekToEnsurePlaying(it.toLong()) exoPlayer.seekToEnsurePlaying(it.toLong())
}, },
onToggleMute = { onToggleMute = {

View file

@ -18,7 +18,16 @@ data class MediaPlayerControllerState(
val durationInMillis: Long, val durationInMillis: Long,
val canMute: Boolean, val canMute: Boolean,
val isMuted: Boolean, val isMuted: Boolean,
val seekingToMillis: Long?,
) { ) {
/**
* The progress in milliseconds to display. When [seekingToMillis] is non-null (during a seek operation),
* this returns the target seek position. Once the player catches up to the seek position,
* [seekingToMillis] is cleared (set to null) and this returns [progressInMillis] again.
*/
val displayProgressInMillis: Long
get() = seekingToMillis ?: progressInMillis
@FloatRange(from = 0.0, to = 1.0) @FloatRange(from = 0.0, to = 1.0)
val progressAsFloat = (progressInMillis.toFloat() / durationInMillis.toFloat()).coerceIn(0f, 1f) val progressAsFloat = (displayProgressInMillis.toFloat() / durationInMillis.toFloat()).coerceIn(0f, 1f)
} }

View file

@ -34,6 +34,7 @@ private fun aMediaPlayerControllerState(
durationInMillis: Long = 83_000, durationInMillis: Long = 83_000,
canMute: Boolean = true, canMute: Boolean = true,
isMuted: Boolean = false, isMuted: Boolean = false,
seekingToMillis: Long? = null,
) = MediaPlayerControllerState( ) = MediaPlayerControllerState(
isVisible = isVisible, isVisible = isVisible,
isPlaying = isPlaying, isPlaying = isPlaying,
@ -42,4 +43,5 @@ private fun aMediaPlayerControllerState(
durationInMillis = durationInMillis, durationInMillis = durationInMillis,
canMute = canMute, canMute = canMute,
isMuted = isMuted, isMuted = isMuted,
seekingToMillis = seekingToMillis,
) )

View file

@ -126,7 +126,7 @@ fun MediaPlayerControllerView(
modifier = Modifier modifier = Modifier
.widthIn(min = 48.dp) .widthIn(min = 48.dp)
.padding(horizontal = 8.dp), .padding(horizontal = 8.dp),
text = state.progressInMillis.toHumanReadableDuration(), text = state.displayProgressInMillis.toHumanReadableDuration(),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
color = ElementTheme.colors.textPrimary, color = ElementTheme.colors.textPrimary,
style = ElementTheme.typography.fontBodyXsMedium, style = ElementTheme.typography.fontBodyXsMedium,
@ -135,7 +135,9 @@ fun MediaPlayerControllerView(
Slider( Slider(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
valueRange = 0f..state.durationInMillis.toFloat(), valueRange = 0f..state.durationInMillis.toFloat(),
value = lastSelectedValue.takeIf { it >= 0 } ?: state.progressInMillis.toFloat(), value = lastSelectedValue.takeIf { it >= 0 }
?: state.seekingToMillis?.toFloat()
?: state.progressInMillis.toFloat(),
onValueChange = { onValueChange = {
lastSelectedValue = it lastSelectedValue = it
}, },

View file

@ -108,6 +108,7 @@ private fun ExoPlayerMediaVideoView(
durationInMillis = 0, durationInMillis = 0,
canMute = true, canMute = true,
isMuted = false, isMuted = false,
seekingToMillis = null,
) )
) )
} }
@ -225,6 +226,9 @@ private fun ExoPlayerMediaVideoView(
}, },
onSeekChange = { onSeekChange = {
autoHideController++ autoHideController++
mediaPlayerControllerState = mediaPlayerControllerState.copy(
seekingToMillis = it.toLong(),
)
exoPlayer.seekToEnsurePlaying(it.toLong()) exoPlayer.seekToEnsurePlaying(it.toLong())
}, },
onToggleMute = { onToggleMute = {
@ -242,15 +246,21 @@ private fun ExoPlayerMediaVideoView(
LaunchedEffect(exoPlayer.isPlaying) { LaunchedEffect(exoPlayer.isPlaying) {
if (exoPlayer.isPlaying) { if (exoPlayer.isPlaying) {
while (true) { while (true) {
val position = exoPlayer.currentPosition
val seekingTo = mediaPlayerControllerState.seekingToMillis
mediaPlayerControllerState = mediaPlayerControllerState.copy( mediaPlayerControllerState = mediaPlayerControllerState.copy(
progressInMillis = exoPlayer.currentPosition, progressInMillis = position,
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
) )
delay(200) delay(200)
} }
} else { } else {
// Ensure we render the final state // Ensure we render the final state
val position = exoPlayer.currentPosition
val seekingTo = mediaPlayerControllerState.seekingToMillis
mediaPlayerControllerState = mediaPlayerControllerState.copy( mediaPlayerControllerState = mediaPlayerControllerState.copy(
progressInMillis = exoPlayer.currentPosition, progressInMillis = position,
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
) )
} }
} }