player: keep the thumbnail until the first frame, not just STATE_READY

ExoPlayer reports STATE_READY a beat before it actually draws the first frame,
so the buffering thumbnail was removed into a black gap right before playback
("thumbnail, then black, then playing"). Hold the thumbnail overlay until
onRenderedFirstFrame fires, so it hands straight over to the picture.
This commit is contained in:
Jeena 2026-10-15 13:46:26 +00:00
parent 9ce367b601
commit 9f69cabf79

View file

@ -323,6 +323,10 @@ private fun PlayerSurface(
// 200 ms: quick flaps cancel the pending change and the overlay stays
// composed, so the animation never restarts mid-playback.
var bufferingPending by remember { mutableStateOf(true) }
// STATE_READY can fire before the first frame is actually drawn, leaving a black
// gap between the thumbnail and the picture. Keep the thumbnail until the first
// frame really renders (user report: "thumbnail, then black, then playing").
var firstFrame by remember { mutableStateOf(false) }
LaunchedEffect(bufferingPending) {
kotlinx.coroutines.delay(200)
buffering = bufferingPending
@ -436,6 +440,10 @@ private fun PlayerSurface(
override fun onPlaybackStateChanged(playbackState: Int) {
bufferingPending = playbackState != Player.STATE_READY
}
override fun onRenderedFirstFrame() {
firstFrame = true
}
})
onDispose {
viewModel.persistExit(shortUuid, (player.currentPosition / 1000).toInt())
@ -588,7 +596,7 @@ private fun PlayerSurface(
// ready to render — metadata resolution alone (the Loading state) is not
// enough (issue #8). Fullscreen loading shows the video's own thumbnail
// dimmed under the circle loader (issue #19) instead of plain black.
if (buffering) {
if (buffering || !firstFrame) {
Box(
Modifier
.fillMaxSize()