Video Player View is overlapping to each other in ExoPlayer media3 Jetpack Compose?

264 Views Asked by At

I have a HorizontalPager which contains two pages and Two VideoPlayerView in each page.The problem is when I play one video and scroll to another page the another page video player shows the same video from other page instead showing it's own video. The video needs to be zoomed and croped fit the Height or full screen . I think video width is so large and it overflows since I am zooming to maintain ratio. Can we bound the video within one pager width??

Code for VideoPlayerView is :


@OptIn(UnstableApi::class) @Composable
fun VideoPlayerView(
    uri: String,
    modifier: Modifier = Modifier
) {
    val context = LocalContext.current
    val density = LocalDensity.current
    var isReady by remember{ mutableStateOf(false) }
    val screenWidth = LocalConfiguration.current.screenWidthDp
    val screenHeight = LocalConfiguration.current.screenHeightDp

    val model = ImageRequest.Builder(context)
        .data(uri)
        .videoFrameMillis(10000)
        .decoderFactory { result, options, _ ->
            VideoFrameDecoder(
                result.source,
                options
            )
        }
        .build()

    val exoPlayer = remember{ createExoPlayer(context, uri) }

    //exoPlayer.playWhenReady = true
    exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
    exoPlayer.repeatMode = Player.REPEAT_MODE_ONE

    exoPlayer.addListener(object : Player.Listener{
        override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
            isReady = playWhenReady
            super.onPlayWhenReadyChanged(playWhenReady, reason)
        }
    })

    var lifecycle by remember { mutableStateOf(Lifecycle.Event.ON_CREATE) }
    val lifecycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            lifecycle = event
        }
        lifecycleOwner.lifecycle.addObserver(observer)

        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
            exoPlayer.release()
        }
    }

    Box(
        modifier = Modifier
            //.widthIn(max = with(density) { screenWidth.toDp() })
          //  .width(200.dp)
//            .fillMaxHeight()
            //.height(200.dp)
            .onGloballyPositioned { coordinates ->
                val parentRect = coordinates.boundsInParent()
                val isFullyVisible = parentRect.contains(coordinates.positionInWindow())

                if (isFullyVisible) {
                    exoPlayer.play()
                } else {
                    exoPlayer.pause()
                }
            },
        contentAlignment = Alignment.Center
    ){
        //if(!isReady){
        if(exoPlayer.isLoading){
            AsyncImage(
                modifier = Modifier.fillMaxSize(),
                model = model,
                contentDescription = "video thumbnail",
                contentScale = ContentScale.Crop
            )
        }else{
            AndroidView(
                factory = { context ->
                    val playerView = PlayerView(context).apply {
                        hideController()
                        useController = false
                        player = exoPlayer
                        resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
                        layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
                        setKeepContentOnPlayerReset(true)
                    }
                    playerView
                },
                update = {
                    when (lifecycle) {
                        Lifecycle.Event.ON_PAUSE -> {
                            it.onPause()
                            it.player?.pause()
                        }
                        Lifecycle.Event.ON_RESUME -> {
                            it.onResume()
                        }
                        else -> Unit
                    }
                },
                modifier = modifier
                    .fillMaxSize()

            )
        }
    }
}

@OptIn(UnstableApi::class)
private fun createExoPlayer(context: Context, uri: String): ExoPlayer{
    return ExoPlayer.Builder(context)
        .build()
        .apply {
            val defaultDataSourceFactory = DefaultDataSource.Factory(context)
            val dataSourceFactory: DataSource.Factory = DefaultDataSource.Factory(
                context,
                defaultDataSourceFactory
            )
            val source = ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(MediaItem.fromUri(uri))

            setMediaSource(source)
            prepare()
        }
}
2

There are 2 best solutions below

2
On

On your PlayerView, add clipToOutline as true.

0
On

I had this same Issue for me I used a surface view and used the setVideoScalingMode on the exoplayer itself. here is an example:

       AndroidView(
            modifier = modifier
                .fillMaxSize()
                .onSizeChanged { size = it },
            factory = {
                SurfaceView(context).also {
                    exoPlayer.setVideoSurfaceView(it)
                    exoPlayer.setVideoScalingMode(VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
                }
            }
        )

This gave me the same result as RESIZE_MODE_ZOOM but without the videos overlapping each other. Bare in mind for now I did have to use OptIn(UnstableApi::class). Also this seems to only works on SurfaceView and not TextureView. Here you can find more information about it on to why not: https://developer.android.com/reference/android/media/MediaCodec#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING