8000 Support `Player$Listener.onVideoSizeChanged()` to be called before `Player.setVideoSurface()` or `Player.setsetVideoTextureView()` · Issue #2066 · androidx/media · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Support Player$Listener.onVideoSizeChanged() to be called before Player.setVideoSurface() or Player.setsetVideoTextureView() #2066

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

Closed
jiyewushen opened this issue Jan 22, 2025 · 3 comments

Comments

@jiyewushen
Copy link

I can't customize the PlayerSurface (from media3-ui-compose) size by getting the video size in the Player$Listener.onVideoSizeChanged() method.

@OptIn(UnstableApi::class)
@Composable
fun Demo(uri: Uri) {
    val TAG="VideoSizeDemo"
    var curVideoSize by remember {
        mutableStateOf(VideoSize.UNKNOWN)
    }
    val context = LocalContext.current
    val exoPlayer = remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(uri))
            addListener(object : Player.Listener{
                override fun onPlaybackStateChanged(playbackState: Int) {
                    Log.i(TAG, "onPlaybackStateChanged:${playbackState== Player.STATE_READY} ")
                }
                override fun onVideoSizeChanged(videoSize: VideoSize) {
                    Log.i(TAG, "onVideoSizeChanged: width:${videoSize.width} height:${videoSize.height}")
                    curVideoSize=videoSize
                }
            })
            prepare()
            playWhenReady = true
            repeatMode = Player.REPEAT_MODE_ONE
        }
    }
    if (curVideoSize.width != 0 && curVideoSize.height != 0) {
        val ratio=curVideoSize.width*1f/curVideoSize.height
        PlayerSurface(
            player = exoPlayer,
            surfaceType = SURFACE_TYPE_SURFACE_VIEW,
            modifier = Modifier
                .fillMaxWidth()
                .aspectRatio(ratio),
        )
    }
}
@microkatz
Copy link
Contributor
microkatz commented Jan 30, 2025

@jiyewushen

The decoder requires a set surface in order to provide accurate dimensions for the Player to provide the onVideoSizeChanged events. That is why its not support to invoke onVideoSizeChanged prior to setting the surface.

Is there a reason you cannot set the surface or texture unto the player and then update it after receiving the onVideoSizeChanged event? The demo applications showcase utilizing a "shutter" in order to hide the views/composable until the events have returned with info to configure the ui.

@oceanjules
Copy link
Contributor
oceanjules commented Jan 30, 2025

To add to what @microkatz had to say:

  1. Please refer to a fully Compose demo on the main branch: https://github.com/androidx/media/blob/main/demos/compose/src/main/java/androidx/media3/demo/compose/MainActivity.kt. You will see that as part of the 1.6.0-alpha02, we are adding PresentationState that will help you determine what to do with the Surface

  2. I appreciate your struggle with it, simply because the original design of the PlayerSurface intuitevely felt like the following should be allowed:

if (presentationState.showSurface) { 
    PlayerSurface(player, modifier)
}

However, we run into catch-22 where PlayerSurface calls Android(Embedded)ExternalSurface(modifier = modifier, >. That onInit is important because that is what runs player.setVideoSurface(surface). You want to base your decision for having PlayerSurface being part of the Composition tree on the Surface being a correct size. This assumes that onVideoSizeChanged and onFirstFrameRendered callbacks will come to us first. But they can't - exactly for the reason above - PlayerSurface is not part of the Composition, onInit has not been called, no rendering of any frames in happening.

The size of the video seemed like something independent of rendering and I looked into ways of extracting it earlier in the pipeline. Perhaps from some Metadata callbacks, from Format of selected Tracks etc. None of these places are reliable to give the video size at all, let alone the correct one. It is unfortunate that we get the sizing information so late, but that is the limitation of the setup where only the act of rendering the video to the surface can guarantee the sizing callback.

For that reason, the only UX friendly thing you can do (as described in MainActivity) is always keep the PlayerSurface() composable as part of the tree. Don't guard it with any if-statements. If the Surface is not ready, nothing will be rendered anyway, so you don't have to worry about anything. The only slight glitch can be the initial frame occupying the incoming constraints and stretching unnaturally. For that, just like in PlayerView, we have a "shutter" - an opaque (not see-throught) overlay that hides the unprepared surface and disappears when needed:

PlayerSurface(player, modifier)
if (presentationState.coverSurface) { 
    Box(Modifier.background(Color.Black)
}

I urge you to browse PresentationState and you will see how it's used in MainActivity to get the correct aspect ratio like

Modifier.resizeWithContentScale(contentScale, presentationState.videoSizeDp)

@jiyewushen
Copy link
Author

To add to what @microkatz had to say:

  1. Please refer to a fully Compose demo on the main branch: https://github.com/androidx/media/blob/main/demos/compose/src/main/java/androidx/media3/demo/compose/MainActivity.kt. You will see that as part of the 1.6.0-alpha02, we are adding PresentationState that will help you determine what to do with the Surface
  2. I appreciate your struggle with it, simply because the original design of the PlayerSurface intuitevely felt like the following should be allowed:
if (presentationState.showSurface) { 
    PlayerSurface(player, modifier)
}

However, we run into catch-22 where PlayerSurface calls Android(Embedded)ExternalSurface(modifier = modifier, >. That onInit is important because that is what runs player.setVideoSurface(surface). You want to base your decision for having PlayerSurface being part of the Composition tree on the Surface being a correct size. This assumes that onVideoSizeChanged and onFirstFrameRendered callbacks will come to us first. But they can't - exactly for the reason above - PlayerSurface is not part of the Composition, onInit has not been called, no rendering of any frames in happening.

The size of the video seemed like something independent of rendering and I looked into ways of extracting it earlier in the pipeline. Perhaps from some Metadata callbacks, from Format of selected Tracks etc. None of these places are reliable to give the video size at all, let alone the correct one. It is unfortunate that we get the sizing information so late, but that is the limitation of the setup where only the act of rendering the video to the surface can guarantee the sizing callback.

For that reason, the only UX friendly thing you can do (as described in MainActivity) is always keep the PlayerSurface() composable as part of the tree. Don't guard it with any if-statements. If the Surface is not ready, nothing will be rendered anyway, so you don't have to worry about anything. The only slight glitch can be the initial frame occupying the incoming constraints and stretching unnaturally. For that, just like in PlayerView, we have a "shutter" - an opaque (not see-throught) overlay that hides the unprepared surface and disappears when needed:

PlayerSurface(player, modifier)
if (presentationState.coverSurface) { 
    Box(Modifier.background(Color.Black)
}

I urge you to browse PresentationState and you will see how it's used in MainActivity to get the correct aspect ratio like

Modifier.resizeWithContentScale(contentScale, presentationState.videoSizeDp)

Many thanks to you and your colleagues for your comprehensive and professional answers, which have completely deepened my understanding of the subject!

@androidx androidx locked and limited conversation to collaborators Apr 2, 2025
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

3 participants
0