My goal is to create a video comparison. I want to display two videos next to each other and be able to freely specify scale and translation of each. The two videos should be clipped to their according half of the Row.
I created a solution that works for displaying Images, however when I tried to use two ExoPlayers instead of images, I encounted the following problem, where the clipping does partly work, however the overlap with the other Player is still rendered.
ExoPlayersClipOverlapDemonstrationGif
Here a schematic drawing of what happens:
ExoPlayersClipOverlapDemonstration
Here is the code of my Composable:
@Composable
fun VideoComparison(
) {
Row(modifier = Modifier.fillMaxSize()) {
//First Video
Box(
modifier = Modifier.background(Color.Blue).weight(1f).clipToBounds()
) {
//Scaled smaller
Box(modifier = Modifier.graphicsLayer { scaleX = 0.6f; scaleY = 0.6f}) {
MyPlayer(
Uri.parse("https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
)
}
}
//Second Video
Box(
modifier = Modifier.weight(1f).clipToBounds()
) {
//Scaled larger
Box(modifier = Modifier.graphicsLayer { scaleX = 1.8f; scaleY = 1.8f}) {
MyPlayer(
Uri.parse("https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
)
}
}
}
}
an here is the code of my Player Composable:
@Composable
fun MyPlayer(uri: Uri) {
val context = LocalContext.current
val exoPlayer = remember {
ExoPlayer.Builder(context)
.build()
.also { exoPlayer ->
val mediaItem = MediaItem.Builder()
.setUri(uri)
.build()
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
}
}
exoPlayer.playWhenReady = true
DisposableEffect(
AndroidView(factory = {
StyledPlayerView(context).apply {
hideController()
clipToOutline = true
clipChildren = true
useController = false
player = exoPlayer
}
})
) {
onDispose { exoPlayer.release() }
}
}
In the code posted above I use the modifier .clipToBounds
, which works for example for Images but not for two ExoPlayers. I also tried using .clip()
or .graphicLayer( clip = true)
which all behave similar. I also tried various combinations of putting the clipping modifiers on different elements, for example directly on the AndroidView or on the parent Box.
I found a similar question here SimilarQuestion
When changing the surface type of the StyledPlayerView
to a TextureView
, the clipping works. This can be done by overwriting the standard attributes with a custom XML file:
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.exoplayer2.ui.StyledPlayerView xmlns:app="http://schemas.android.com/apk/res-auto"
app:surface_type="texture_view" />
Loading it via:
val parser = context.resources.getXml(R.xml.styled_player_view_custom)
// Seek to the first tag.
var type = 0
while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
type = parser.next()
}
val attrs = Xml.asAttributeSet(parser)
and adding it to the StyledPlayerView like this:
StyledPlayerView(context, attrs)
However, due to the benefits of SurfaceView
for ExoPlayer, like lower power consumption, more accurate frame timing etc. (as described here), it would still be interesting how to fix it using a SurfaceView
.