Skip to content

Video playing cannot recover from state after removing from composition #203

@0sten

Description

@0sten

I'm trying to make this simple code snippet work. Seems metadata's width and height gets cleared when video is removed from composition and not set again when it is required again.

Screen_recording_20260414_185003.mp4
private val videoSamples = listOf(
    "https://file-examples.com/storage/fed9bb8ccb69de4e7a05784/2017/04/file_example_MP4_480_1_5MG.mp4",
    "https://filesamples.com/samples/video/mp4/sample_960x540.mp4"
)

private val cacheConfig = CacheConfig(
    enabled = true, maxCacheSizeBytes = 200L * 1024L * 1024L
)

@Composable
fun VideoInFeedTestScreen() {
    val lazyListState = rememberLazyListState()
    val firstPlayerState = rememberVideoPlayerState(cacheConfig = cacheConfig)
    val secondPlayerState = rememberVideoPlayerState(cacheConfig = cacheConfig)
    val playerStates = remember(firstPlayerState, secondPlayerState) {
        listOf(firstPlayerState, secondPlayerState)
    }

    val distanceVideo1 = remember { mutableFloatStateOf(Float.MAX_VALUE) }
    val distanceVideo2 = remember { mutableFloatStateOf(Float.MAX_VALUE) }
    val focusedVideoStateIndex by remember {
        derivedStateOf {
            val d1 = distanceVideo1.floatValue
            val d2 = distanceVideo2.floatValue
            when {
                d1 == Float.MAX_VALUE && d2 == Float.MAX_VALUE -> null
                d1 <= d2 -> 0
                else -> 1
            }
        }
    }

    LaunchedEffect(Unit) {
        firstPlayerState.openUri(videoSamples[0], InitialPlayerState.PAUSE)
        secondPlayerState.openUri(videoSamples[1], InitialPlayerState.PAUSE)
        playerStates.forEach(VideoPlayerState::pause)
    }

    LaunchedEffect(focusedVideoStateIndex) {
        playerStates.forEachIndexed { index, playerState ->
            if (index == focusedVideoStateIndex) {
                playerState.play()
            } else {
                playerState.pause()
            }
        }
    }

    Scaffold { innerPaddings ->
        LazyColumn(
            state = lazyListState,
            modifier = Modifier.fillMaxSize().padding(innerPaddings),
            contentPadding = PaddingValues(bottom = 24.dp)
        ) {
            item(key = "intro") {
                Text(
                    text = "Scroll to video cards.",
                    style = MaterialTheme.typography.bodyMedium,
                    modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
                )
                Spacer(Modifier.height(1500.dp))
            }
            item(key = "video_1") {
                VideoInFeedTestItem(
                    sample = videoSamples[0],
                    playerState = playerStates[0],
                    onVideoMeasured = { distance ->
                        distanceVideo1.floatValue = distance
                    },
                )
            }
            item(key = "video_spacing") {
                Spacer(Modifier.height(120.dp))
            }
            item(key = "video_2") {
                VideoInFeedTestItem(
                    sample = videoSamples[1],
                    playerState = playerStates[1],
                    onVideoMeasured = { distance ->
                        distanceVideo2.floatValue = distance
                    },
                )
            }
            item(key = "bottom_spacing") {
                Spacer(Modifier.height(800.dp))
            }
        }
    }
}

@Composable
private fun VideoInFeedTestItem(
    sample: String,
    playerState: VideoPlayerState,
    onVideoMeasured: (Float) -> Unit,
) {
    val windowInfo = LocalWindowInfo.current

    Text(
        text = sample,
        modifier = Modifier.fillMaxWidth().padding(16.dp)
    )
    Box(
        modifier = Modifier.fillMaxWidth()
            .padding(horizontal = 16.dp)
            .aspectRatio(16f / 9f)
            .onGloballyPositioned { layoutCoordinates ->
                val position = layoutCoordinates.positionInWindow()
                val itemCenterY = position.y + layoutCoordinates.size.height / 2f
                val viewportCenterY = windowInfo.containerSize.height / 2f
                val distance = abs(itemCenterY - viewportCenterY)
                onVideoMeasured(distance)
            }, contentAlignment = Alignment.Center
    ) {
        VideoPlayerSurface(
            playerState = playerState, modifier = Modifier.fillMaxSize()
        )
    }
}

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions