[ui-compose] Fix PlayerSurface's use of Player

When `PlayerSurface` composable gets a new `Player` object, it should initialise a new Android(Embedded)External Surface with it. However, the lambdas used for that are remembered with the reference to the old Player, even if it has been null'd and released properly.

Android(Embedded)ExternalSurface is an interop - `AndroidView` wrapping `SurfaceView` and `TextureView` under the hood. It uses the `onInit` lambda in its factory to create the View and reuses it on later recompositions, making it a longer-lived object than our Player.

`RememberUpdatedState` acts makes a mutable State out of the Player and always gets its latest value. One can think of it as creating a reference to the Player object and telling the lambdas to always resolve that reference in the moment, rather than hold onto the Player than was passed-by-value.

This change is a precursor to a better lifecycle management of Player and Surface in demo-compose.

PiperOrigin-RevId: 705529626
This commit is contained in:
jbibik 2024-12-12 09:20:35 -08:00 committed by Copybara-Service
parent 684273e4e1
commit beda44520a

View File

@ -24,6 +24,8 @@ import androidx.compose.foundation.AndroidEmbeddedExternalSurface
import androidx.compose.foundation.AndroidExternalSurface import androidx.compose.foundation.AndroidExternalSurface
import androidx.compose.foundation.AndroidExternalSurfaceScope import androidx.compose.foundation.AndroidExternalSurfaceScope
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
@ -44,11 +46,16 @@ import androidx.media3.common.util.UnstableApi
@UnstableApi @UnstableApi
@Composable @Composable
fun PlayerSurface(player: Player, surfaceType: @SurfaceType Int, modifier: Modifier = Modifier) { fun PlayerSurface(player: Player, surfaceType: @SurfaceType Int, modifier: Modifier = Modifier) {
// Player might change between compositions,
// we need long-lived surface-related lambdas to always use the latest value
val currentPlayer by rememberUpdatedState(player)
val onSurfaceCreated: (Surface) -> Unit = { surface -> val onSurfaceCreated: (Surface) -> Unit = { surface ->
if (player.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) player.setVideoSurface(surface) if (currentPlayer.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE))
player.setVideoSurface(surface)
} }
val onSurfaceDestroyed: () -> Unit = { val onSurfaceDestroyed: () -> Unit = {
if (player.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) player.clearVideoSurface() if (currentPlayer.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE))
player.clearVideoSurface()
} }
val onSurfaceInitialized: AndroidExternalSurfaceScope.() -> Unit = { val onSurfaceInitialized: AndroidExternalSurfaceScope.() -> Unit = {
onSurface { surface, _, _ -> onSurface { surface, _, _ ->