Reputation: 5856
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
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
Reputation: 1
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
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