androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
https://developer.android.com/media/media3
Apache License 2.0
1.73k stars 413 forks source link

Exo player covered by something when it inside Surface #1682

Open NuclleaR opened 2 months ago

NuclleaR commented 2 months ago

I am new to android, I want to write an android tv app.

I ran into a problem, I started a new empty project, added Media3 and created a simple composable in this case I can't see the video, only the sound. After a few hours of debugging, I realized what the cause of the problem was when Exo Player is inside the Surface, it seems that the video is covered by something, but I can see the controls.

  TestExoTheme {
      Surface(
          modifier = Modifier.fillMaxSize(),
          shape = RectangleShape
      ) {
          Greeting("Android")
      }
  }

image

When I remove surface everything works as expected.

So can someone pls explain me why it happens

here is the full code

class MainActivity : ComponentActivity() {
    @UnstableApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            TestExoTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    shape = RectangleShape
                ) {
                    Greeting("Android")
                }
            }
        }
    }
}

@UnstableApi
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    val EXAMPLE_VIDEO_URI = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"

    // Get the current context
    val context = LocalContext.current

    // Initialize ExoPlayer
    val exoPlayer = remember {
        ExoPlayer.Builder(context).build().apply {
            videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
        }
    }

    // Create a MediaSource
    val mediaSource = remember(EXAMPLE_VIDEO_URI) {
        MediaItem.fromUri(EXAMPLE_VIDEO_URI)
    }

    // Set MediaSource to ExoPlayer
    LaunchedEffect(mediaSource) {
        exoPlayer.setMediaItem(mediaSource)
        exoPlayer.prepare()
        exoPlayer.playWhenReady = true
    }

    // Manage lifecycle events
    DisposableEffect(Unit) {
        onDispose {
            exoPlayer.release()
        }
    }

    // Use AndroidView to embed an Android View (PlayerView) into Compose
    AndroidView(
        factory = {
            PlayerView(context).apply {
                player = exoPlayer
                useController = true
            }
        },
        modifier = Modifier
            .fillMaxSize()
            .zIndex(1f)
            .height(500.dp) // Set your desired height
    )
}
marcbaechinger commented 2 months ago

I'm unsure why you want or are required to touch a Surface for your use case. However, I'd start without this when using a PlayerView. The PlayerView laready is inflating a Surface internally and a user doesn't require to do something regaring the UI for a basic setup.

I suggest to remove all code that creates or sets a Surface as a first step. Until that is working, I'd also remove all code that manually manipulates the size of the PlayerView in case there is such code.

oceanjules commented 2 months ago

To add to Marc's comment:

Please take a look at Surface composable documentation. Quoting:

The Surface is responsible for:

* Clipping
* Elevation
* Borders
* Background
* Content color
* Blocking touch propagation behind the surface (<--!!!)

You can even see Surface's implementation - it's essentially a Box with special properties.

I just opened Android Studio and set up an Empty Activity. This is what MainActivity.kt would have in it:

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContent {
      MyApplicationTheme {
        Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
          Greeting(
            name = "Android",
            modifier = Modifier.padding(innerPadding)
          )
        }
      }
    }
  }
}

Some time ago Surface was replaced with Scaffold to help you build out your layout first. You might never end up needing a Surface composable.

Also, Surface is a bit of an overloaded term. You will see that

NuclleaR commented 2 months ago

The thing that I understood Surface controls all colors inside. In my case Surface was added by android studio itself when I started a new project. Don't know if Scaffold works for TV. If I remove Surface all my colors stops inherits from theme, it means I need to specify color to each text node, If my screen wrapped with Surface all texts inherits colors from it. I think I can't do much here, I removed Surface from top level and wrap each screen individually as a workaround.

JolandaVerhoef commented 2 months ago

Your Material Theme (in this case TestExoTheme) is the one that sets the colors. Surface is just a handy composable that you can use to set a background and such.

However, when running your code, it works fine on my device, the video is shown as expected. Just to check, I'm using the following imports:

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import com.example.myapplication.ui.theme.MyApplicationTheme
oceanjules commented 2 months ago

Attaching a screenshare that shows Surface composable working

https://github.com/user-attachments/assets/af4d857e-8dc1-4bf8-ba50-481271f51946

https://github.com/user-attachments/assets/cbac7426-f50d-4e5d-9e8d-675f7853db8f