Jeevan Rupacha
Jeevan Rupacha

Reputation: 5856

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

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()
        }
}

Upvotes: 2

Views: 1267

Answers (4)

Riccardo Camattari
Riccardo Camattari

Reputation: 11

As fobidlim said, you could try clipToOutline to solve the problem:

In compose:

AndroidView(
    factory = { context ->
        PlayerView(context).apply {
            player = mExoPlayer
            resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
            clipToOutline = true   //line to avoid overlaps
        }
    })

If this still doesn't work, you can try this:

AndroidView(
    factory = { context ->
        PlayerView(context).apply {
            val view = LayoutInflater.from(context).inflate(R.layout.my_exo_player, null, false)
            (view as PlayerView).apply {
                player = mExoPlayer
                clipToOutline = true   //line to avoid overlaps
            }
        }
    })

with my_exo_player being:

<?xml version="1.0" encoding="utf-8"?>
<androidx.media3.ui.PlayerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:resize_mode="zoom"
    app:surface_type="texture_view" />    //line to avoid overlaps

Upvotes: 0

I find a solution by changing SufaceView to TextureView

    AndroidView(
    factory = { ctx ->
        TextureView(ctx).also {
            player.setVideoTextureView(it)
            player.videoScalingMode = VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
        }
    },
    modifier = modifier
)

Upvotes: 0

Mateo Vakili
Mateo Vakili

Reputation: 712

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

Upvotes: 1

fobidlim
fobidlim

Reputation: 77

On your PlayerView, add clipToOutline as true.

Upvotes: 0

Related Questions