Photo by dlxmedia.hu on Unsplash
Navigating ExoPlayer Surface Types
Tips for Smooth Video Playback in Android Apps
ExoPlayer has become the go-to video player for Android development. During my recent integrations, I faced some challenges with its surface types. In this article, I'll dive into these surface types and share valuable insights to help you navigate them effectively.
If you use ExoPlayer with a dialog or bottom sheet and a scrollable view, you might encounter this issue.
The video will scroll over the top of the dialog or bottom sheet because the default surface type is SurfaceView.
Surface Types in ExoPlayer
There are different types of surfaces that can be used, each with its own characteristics.
SurfaceView
TextureView
Null
How They Render Video Differently
SurfaceView: Renders video content outside of the app's window, directly to a separate layer in the Android view hierarchy. This can lead to better performance and lower power usage.
TextureView: Renders video content within the app's window, which allows it to be transformed and animated like any other view. This flexibility comes at the cost of higher power consumption and potential performance drawbacks.
Null: No surface should be created for rendering video.
SurfaceView vs TextureView vs Null Characteristic
SurfaceView | TextureView | Null |
Significantly Lower Power Consumption | Smooth Animations and Scrolling | Avoids the overhead of creating and managing a surface for audio only playback |
More Accurate Frame Timing | Support more synchronized view animations for Android os <= 7.0 | Optimize performance for metadata Display Without Video |
Support HDR | ||
Support DRM-protected content | ||
Full Resolution Rendering on Android TV |
In summary, SurfaceView is recommended over the TextureView in most of the cases.
When should we consider change surface type?
Ask yourself... | Surface Type |
Is video audio only? | null |
Is Android os <= 7.0 required? | TextureView |
Is this video player view required to place on a dialog or bottom sheet or any overlay layout with scrolling feature? | TextureView |
If all of the above is NO | SurfaceView |
How to switch surface type?
PlayerView (ExoPlayer UI View) does not support changing the surface type at runtime. The simplest way to switch the surface type is by setting it in the PlayerView attribute in the XML layout.
<?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/player_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:surface_type="texture_view">
</androidx.media3.ui.PlayerView>
What if the project is using Compose?
ExoPlayer currently does not provide a Composable PlayerView. Whether you switch the surface type or not, you need to use AndroidView
to implement ExoPlayer.
So, what you need to do is similar to the XML layout: create an XML layout file with PlayerView
.
Then, create a Composable to hold it.
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import com.test.composableplayground.databinding.LayoutTextureExoplayerBinding
@Composable
fun ExoplayerTextureView(modifier: Modifier = Modifier, mediaItem: MediaItem) {
val context = LocalContext.current
val exoPlayer by remember {
mutableStateOf(
ExoPlayer.Builder(context).build()
)
}
Box(modifier = modifier) {
LifecycleEventEffect(event = Lifecycle.Event.ON_PAUSE) {
exoPlayer.pause()
}
DisposableEffect(key1 = Unit) {
onDispose {
exoPlayer.release()
}
}
DisposableEffect(key1 = mediaItem) {
onDispose {
exoPlayer.pause()
exoPlayer.stop()
}
}
LaunchedEffect(key1 = mediaItem) {
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
}
AndroidViewBinding(
factory = LayoutTextureExoplayerBinding::inflate,
modifier = Modifier.fillMaxSize()
){
playerView.player = exoPlayer
}
}
}
Result
To fix the issue where the PlayerView
goes over the top of the bottom sheet, switch to using TextureView
.
Reference
https://developer.android.com/media/media3/ui/playerview#surfacetype