SmartToolFactory / Compose-BeforeAfter

🚀🌆🏙 Display differences or animate progress between 2 images or Composables with overlay and customization options, zoom, pan gestures, and progress to observe properties for animating before-after progress
Apache License 2.0
172 stars 10 forks source link

Is Exoplayer 2, still an issue? #27

Open nisrulz opened 1 month ago

nisrulz commented 1 month ago

Readme mentions an issue but the link doesn't work

Screenshot 2024-09-15 at 19 06 06

Maybe this issue can be used to discuss what the actual issue was. This way the knowledge is not lost and someone can issue a fix.

SmartToolFactory commented 1 month ago

my question on SO was deleted. In sample with video you don't see before video which is black and white color.

https://stackoverflow.com/questions/78973837/video-overflows-from-bottom-sheet-when-scrolling/78990380#78990380

It can be related with this issue. Exoplayer doesn't get clipped even though it should be.

SmartToolFactory commented 1 month ago

https://github.com/SmartToolFactory/Compose-BeforeAfter/blob/48085d4623251d81511258de56f9dd246afcc614/app/src/main/java/com/smarttoolfactory/composebeforeafter/demo/BeforeAfterLayoutDemo.kt#L217 It's here the issue is.

SmartToolFactory commented 1 month ago

I build a before/after layout that runs two Images, Composables or videos at the same time. It works except with Exoplayer2 showing 2 vidoes. When second video is loaded first video changes to second one.

I made a reproducible sample below. It happens if i set shape and clip with Modifier.graphicsLayer

Modifier
      .graphicsLayer {
            clip = true
            shape = shapeBefore
      }

enter image description here

How i create ExoPlayer2

@Composable
fun MyPlayer(modifier: Modifier, uri: String) {
    val context = LocalContext.current
    val player = SimpleExoPlayer.Builder(context).build()
    val playerView = remember {
        PlayerView(context)
    }

    println("🚀 MyPlayer URI $uri, player: $player, playerView: $playerView")

    LaunchedEffect(player, uri) {
        playerView.useController = false
        playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH
        val mediaItem = MediaItem.fromUri(uri)

        player.setMediaItem(mediaItem)
        playerView.player = player
        player.repeatMode = Player.REPEAT_MODE_ONE
        player.prepare()
        player.playWhenReady = true
    }

    AndroidView(
        modifier = modifier,
        factory = {
            playerView
        }
    )
}

How they are displayed

@Composable
private fun Test() {
    Column(modifier = Modifier.fillMaxSize()) {

        val handlePosition = 500f
        val shapeBefore by remember(handlePosition) {
            mutableStateOf(
                GenericShape { size: Size, layoutDirection: LayoutDirection ->
                    moveTo(0f, 0f)
                    lineTo(handlePosition - 50f, 0f)
                    lineTo(handlePosition - 50f, size.height)
                    lineTo(0f, size.height)
                    close()
                }
            )
        }

        val shapeAfter by remember(handlePosition) {
            mutableStateOf(
                GenericShape { size: Size, layoutDirection: LayoutDirection ->
                    moveTo(handlePosition + 50f, 0f)
                    lineTo(size.width, 0f)
                    lineTo(size.width, size.height)
                    lineTo(handlePosition + 50f, size.height)
                    close()
                }
            )
        }

        Box(
            modifier = Modifier
                .fillMaxWidth()
                .aspectRatio(4 / 3f)
        ) {
            Box(modifier = Modifier
                .graphicsLayer {
                    clip = true
                    shape = shapeBefore
                }
                .border(3.dp, Color.Red)
            ) {
                MyPlayer(
                    modifier = Modifier.fillMaxSize(),
                    uri = "asset:///floodplain_dirty.mp4"
//                    uri = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/floodplain-dirty.mp4"
                )
            }
            Box(modifier = Modifier
                .graphicsLayer {
                    clip = true
                    shape = shapeAfter
                }
                .border(3.dp, Color.Yellow)
            ) {
                MyPlayer(
                    modifier = Modifier.fillMaxSize(),
//                    uri = "asset:///floodplain_clean.mp4"
                    uri = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/floodplain-clean.mp4"
                )
            }
        }
    }
}

Edit

Created a custom layout to check if both videos play and it looks like they do but second video is clipped on intersection of first and second video

@Composable
private fun MyLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables: List<Measurable>, constraints: Constraints ->

        val placeables = measurables.map { measurable ->
            measurable.measure(constraints)
        }

        val maxWidth = placeables.maxOf { it.width }
        val maxHeight = placeables.maxOf { it.height }

        var verticalOffset = 0

        layout(maxWidth, maxHeight) {
            placeables.forEach { placeable: Placeable ->
                placeable.placeRelative(0, verticalOffset)
                verticalOffset += 140
            }
        }
    }
}

And put both before and after inside this layout with

MyLayout() {
    Box(modifier = Modifier
        .fillMaxWidth()
        .aspectRatio(4 / 3f)
        .graphicsLayer {
            clip = true
            shape = shapeBefore
        }
        .border(3.dp, Color.Red)
    ) {

        MyPlayer(
            modifier = Modifier.fillMaxSize(),
            uri = "asset:///floodplain_dirty.mp4"
//                    uri = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/floodplain-dirty.mp4"
        )
    }
    Box(modifier = Modifier
        .fillMaxWidth()
        .aspectRatio(4 / 3f)
        .graphicsLayer {
            clip = true
            shape = shapeAfter
        }
        .border(3.dp, Color.Yellow)
    ) {
        MyPlayer(
            modifier = Modifier.fillMaxSize(),
//                uri = "asset:///floodplain_clean.mp4"
                uri = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/floodplain-clean.mp4"
        )
    }
}

enter image description here

This was the question. I don't know if issue still exists in this minimal reproducible sample

nisrulz commented 1 month ago

Thank you for all the details, I'll take a look and see what can be done 👍🏼