Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video is cut on the edge when using Compose and uses RESIZE_MODE_ZOOM - Only Android 14 #1123

Closed
1 task done
jdelga opened this issue Feb 22, 2024 · 12 comments
Closed
1 task done
Assignees

Comments

@jdelga
Copy link

jdelga commented Feb 22, 2024

Version

Media3 1.2.1

More version details

Reproduced also in latest version of Exoplayer, and Media3 1.3.0-beta01

Devices that reproduce the issue

Any device or emulator running Android 14

Devices that do not reproduce the issue

Any device or emulator running Android 13 or lower

Reproducible in the demo app?

No

Reproduction steps

Reproduced in Demo Project: Demo project

Just open the app and check the video dimensions

The bug happens in the following conditions:

  • PlayerView is inside an AndroidView in Compose
  • The Composable function is inside a component with fillMaxSize() modifier
  • The video is using UnstableApi RESIZE_MODE_ZOOM

(It also happens in RESIZE_MODE_FIXED_HEIGHT, RESIZE_MODE_FIXED_WIDTH and RESIZE_MODE_FIT, but does not happen with RESIZE_MODE_FILL)

Expected result

The video is not cut on the edge.

Actual result

The video is cut:

Captura de pantalla 2024-02-22 a las 13 33 49

You can check closer to the status bar to identify the issue

Captura de pantalla 2024-02-22 a las 13 13 05

Just change the resizeMode in the demo app to check the issue with other resize modes: https://github.com/jdelga/Media3ComposeDemo/blob/main/app/src/main/java/com/javierdelgado/media3composedemo/VideoScreen.kt#L21

Media

Not applicable, but a media item is already added to the demo project

Bug Report

Comments

It would be very useful to add full support to resizeMode (currently as UnstableApi) and add a proper Compose player.

@jdelga jdelga changed the title Video is cut on the edge when it is inside a Compose NavHost and uses RESIZE_MODE_ZOOM Video is cut on the edge when using Compose and uses RESIZE_MODE_ZOOM Feb 22, 2024
@jdelga
Copy link
Author

jdelga commented Feb 22, 2024

In the bugreport there is an AspectRatioFrameLayout that doesn't fit with its parent PlayerView:

            androidx.compose.ui.platform.ComposeView{56f1745 V.E...... ........ 0,0-1080,2270}
              androidx.compose.ui.platform.AndroidComposeView{6ba67ab VFED..... ........ 0,0-1080,2270}
                androidx.compose.ui.platform.AndroidViewsHandler{745e9fd V.E...... ......ID 0,0-1080,2270}
                  androidx.compose.ui.viewinterop.ViewFactoryHolder{5747a8 V.E...... ......I. 0,0-1080,2270}
                    androidx.media3.ui.PlayerView{84a4c6d V.E...... ........ 0,0-1080,2270}
                      androidx.media3.ui.AspectRatioFrameLayout{439f0a2 V.E...... ........ -58,0-1139,2270 #7f07003d app:id/exo_content_frame}

@jdelga jdelga changed the title Video is cut on the edge when using Compose and uses RESIZE_MODE_ZOOM Video is cut on the edge when using Compose and uses RESIZE_MODE_ZOOM - Only Android 14 Feb 22, 2024
@oceanjules
Copy link
Contributor

According to:

int width = getMeasuredWidth();
int height = getMeasuredHeight();
float viewAspectRatio = (float) width / height;
float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) {
// We're within the allowed tolerance.
aspectRatioUpdateDispatcher.scheduleUpdate(videoAspectRatio, viewAspectRatio, false);
return;
}
switch (resizeMode) {
case RESIZE_MODE_FIXED_WIDTH:
height = (int) (width / videoAspectRatio);
break;
case RESIZE_MODE_FIXED_HEIGHT:
width = (int) (height * videoAspectRatio);
break;
case RESIZE_MODE_ZOOM:
if (aspectDeformation > 0) {
width = (int) (height * videoAspectRatio);
} else {
height = (int) (width / videoAspectRatio);
}
break;
case RESIZE_MODE_FIT:
if (aspectDeformation > 0) {
height = (int) (width / videoAspectRatio);
} else {
width = (int) (height * videoAspectRatio);
}
break;
case RESIZE_MODE_FILL:
default:
// Ignore target aspect ratio
break;
}

RESIZE_MODE_FILL is actually not doing any resizing ("Ignore target aspect ratio" branch)

RESIZE_MODE_ZOOM in the case of your media will increase the width (since AspectDeformation>0)

And you are right, AspectRatioFrameLayout will be bigger than PlayerView, but that's ok, because the parent will end up cropping it.

Pixel5 API34 Pixel5 API30
Android 14 11
View
width 1080 1080
height 2138 2072
View aspect ratio 0.505145 0.5212355
Video aspect ratio 0.52734375 0.52734375
AspectDeformation 0.043945312 0.01171875
New view width 1127 1092
AspectRatioFrameLayout
x -8dp -2dp
y 49dp 49p
width 1127/2.75=409 1092/2.75=397
heigth 2138/2.75=777 2072/2.75=753

Given that the screen width is only 392dp, Android tries to position the ARFL in the centre by:

  • leaving 8dp and 9dp on each side (8+392+9 = 409)
  • leaving 2dp and 3dp on each side (2+392+3 = 397)

Screenshot 2024-03-19 at 22 10 56

My guess is that since the stretch in Android14 is bigger, there is some delay in stretching the view?

I managed to reproduce your issue, but it disappears so quickly that I cannot catch the setup in the state where the width:

Resizing.mov

@jdelga
Copy link
Author

jdelga commented Mar 20, 2024

Thank you very much for testing it. Have you tried the uploaded Demo Project? I am using useController = false and the video is not adjusted to its correct location. The controller may be interacting somehow and after it appears, the video is adjusted.

useController = false

use_controller_false.mp4

useController = true

use_controller_true.mp4

@MaxMaesLW
Copy link

I'm +1 - ing this issue. I ran into this earlier this week where my video would be rendered stretched and it seems to be related. Setting 'controllerAutoShow' to true triggers a re-layout of the PlayerView, thus correcting its size.

Below is a sample project which is essentially this box in an activity;

            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Black),
                contentAlignment = Alignment.Center
            ) {
                AndroidView(
                    factory = { ctx ->
                        PlayerView(ctx).apply {
                            useController = true
                            // Setting controllerAutoShow to true fill show the controller automatically
                            // Having it auto-show triggers a re-layout of the PlayerView
                            // which causes the layout to be corrected
                            controllerAutoShow = false
                            resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
                        }
                    },
                    update = {
                        it.player = exoPlayer
                        exoPlayer.setMediaItem(
                            MediaItem.fromUri("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
                        )
                        exoPlayer.playWhenReady = true
                        exoPlayer.prepare()
                    },
                )
            }

media3sample.zip

@phcannesson
Copy link
Contributor

I'm also seeing this issue on my app.
it seems 100% linked to having a parent with fillMaxSize().
In my case, I have a single activity with a NavHost() that handles the whole navigation. Therefore, this issue is a big problem as the only solution I've found is to create a second activity to host the PlayerView

Also, please note that it happens a lot on Android 14 devices, but ALSO on Android 13 and lower, when playing HLS and when a track change occurs (linked to available bandwidth).

@f4bsn
Copy link

f4bsn commented Apr 11, 2024

I'm also facing this issue. resizeMode is set to AspectRatioFrameLayout.RESIZE_MODE_ZOOM. parent view is set to fillMaxSize(). Is there a workaround for now to make the PlayerView fill the entire screen on all devices without changing the aspect ratio?

@promanowicz
Copy link

I'm also facing this issue. resizeMode is set to AspectRatioFrameLayout.RESIZE_MODE_ZOOM. parent view is set to fillMaxSize(). Is there a workaround for now to make the PlayerView fill the entire screen on all devices without changing the aspect ratio?

This one might work
#1237 (comment)

@oceanjules
Copy link
Contributor

@jdelga Could you try setting app:surface_type="texture_view" to see if we can isolate this issue as a SurfaceView in Compose issue instead?

@jdelga
Copy link
Author

jdelga commented May 8, 2024

As you can see in the Demo project, the issue happens when using a full Compose implementation. The only AndroidView is added to work with PlayerView inside compose. Where should I place the attribute surfaceType for the PlayerView in code ?

@fresnohernandez99
Copy link

My current solution is use fillXXX in the specific case. For example. if i want RESIZE_MODE_FIXED_WIDTH i use the container with fillMaxWith() and not fillMaxSize(). that works to solve my use cases.

@emenjivar
Copy link

emenjivar commented Jun 13, 2024

I'm also facing this issue. resizeMode is set to AspectRatioFrameLayout.RESIZE_MODE_ZOOM. parent view is set to fillMaxSize(). Is there a workaround for now to make the PlayerView fill the entire screen on all devices without changing the aspect ratio?

This one might work #1237 (comment)

I used this solution in my compose project, it works perfectly.

First, i created a new layout called video_player.xml in the layout directory of my project:

<?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:id="@+id/custom_video_player"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:surface_type="texture_view"
    app:use_controller="false" />

As mentioned by @promanowicz, the property app:surface_type="texture_view" is the key to solve the issue.

Then i inflated that file in compose:

@Composable
fun VideoComponent() {
    val context = LocalContext.current

    val exoplayer = remember {
        ExoPlayer.Builder(context)
            .setTrackSelector(trackSelector)
            .build()
            .apply {
                setMediaItem(MediaItem.fromUri("your_video_url"l))
                prepare()
                play()
                playWhenReady = true
            }
    }

    var exoPlayerView = remember {
        val view = View.inflate(context, R.layout.video_player, null)
        val playerView = view.findViewById<PlayerView>(R.id.custom_video_player)
        playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
        playerView.player = exoplayer
        playerView
    }

    AndroidView(
        modifier = Modifier.fillMaxSize()
        factory = { exoPlayerView }
    )
}

@icbaker
Copy link
Collaborator

icbaker commented Jun 20, 2024

This is almost certainly a duplicate of #1237, which is fixed in Android 15 and a workaround for Android 14 is in 968f72f which will be released in media3 1.4.0-rc01.

@icbaker icbaker closed this as not planned Won't fix, can't repro, duplicate, stale Jun 20, 2024
@androidx androidx locked and limited conversation to collaborators Aug 20, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests