fix(android)/aspect-ratio (#4594)
This commit is contained in:
committed by
GitHub
parent
86ea0e3101
commit
481aa7ca65
@@ -4,8 +4,6 @@ import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.SurfaceView
|
||||
import android.view.TextureView
|
||||
import android.view.View
|
||||
import android.view.View.MeasureSpec
|
||||
import android.widget.FrameLayout
|
||||
@@ -19,14 +17,13 @@ import androidx.media3.ui.DefaultTimeBar
|
||||
import androidx.media3.ui.PlayerView
|
||||
import com.brentvatne.common.api.ResizeMode
|
||||
import com.brentvatne.common.api.SubtitleStyle
|
||||
import com.brentvatne.common.api.ViewType
|
||||
|
||||
@UnstableApi
|
||||
class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
|
||||
FrameLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var localStyle = SubtitleStyle()
|
||||
private var surfaceView: View? = null
|
||||
private var pendingResizeMode: Int? = null
|
||||
private val liveBadge: TextView = TextView(context).apply {
|
||||
text = "LIVE"
|
||||
setTextColor(Color.WHITE)
|
||||
@@ -51,15 +48,19 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
// Enable proper surface view handling to prevent rendering issues
|
||||
setUseArtwork(false)
|
||||
setDefaultArtwork(null)
|
||||
// Ensure proper video scaling
|
||||
// Ensure proper video scaling - start with FIT mode
|
||||
resizeMode = androidx.media3.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||
}
|
||||
|
||||
init {
|
||||
addView(playerView)
|
||||
val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
||||
lp.setMargins(16, 16, 16, 16)
|
||||
addView(liveBadge, lp)
|
||||
// Add PlayerView with explicit layout parameters
|
||||
val playerViewLayoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
addView(playerView, playerViewLayoutParams)
|
||||
|
||||
// Add live badge with its own layout parameters
|
||||
val liveBadgeLayoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
||||
liveBadgeLayoutParams.setMargins(16, 16, 16, 16)
|
||||
addView(liveBadge, liveBadgeLayoutParams)
|
||||
}
|
||||
|
||||
fun setPlayer(player: ExoPlayer?) {
|
||||
@@ -67,21 +68,16 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
|
||||
if (currentPlayer != null) {
|
||||
currentPlayer.removeListener(playerListener)
|
||||
// Clear any existing surface from the player
|
||||
when (surfaceView) {
|
||||
is SurfaceView -> currentPlayer.clearVideoSurfaceView(surfaceView as SurfaceView)
|
||||
is TextureView -> currentPlayer.clearVideoTextureView(surfaceView as TextureView)
|
||||
}
|
||||
}
|
||||
|
||||
playerView.player = player
|
||||
|
||||
if (player != null) {
|
||||
player.addListener(playerListener)
|
||||
// Set the surface view for the new player
|
||||
when (surfaceView) {
|
||||
is SurfaceView -> player.setVideoSurfaceView(surfaceView as SurfaceView)
|
||||
is TextureView -> player.setVideoTextureView(surfaceView as TextureView)
|
||||
|
||||
// Apply pending resize mode if we have one
|
||||
pendingResizeMode?.let { resizeMode ->
|
||||
playerView.resizeMode = resizeMode
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +85,7 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
fun getPlayerView(): PlayerView = playerView
|
||||
|
||||
fun setResizeMode(@ResizeMode.Mode resizeMode: Int) {
|
||||
playerView.resizeMode = when (resizeMode) {
|
||||
val targetResizeMode = when (resizeMode) {
|
||||
ResizeMode.RESIZE_MODE_FILL -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
||||
ResizeMode.RESIZE_MODE_CENTER_CROP -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||
ResizeMode.RESIZE_MODE_FIT -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||
@@ -97,6 +93,18 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
ResizeMode.RESIZE_MODE_FIXED_HEIGHT -> AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT
|
||||
else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||
}
|
||||
|
||||
// Apply the resize mode to PlayerView immediately
|
||||
playerView.resizeMode = targetResizeMode
|
||||
|
||||
// Store it for reapplication if needed
|
||||
pendingResizeMode = targetResizeMode
|
||||
|
||||
// Force PlayerView to recalculate its layout
|
||||
playerView.requestLayout()
|
||||
|
||||
// Also request layout on the parent to ensure proper sizing
|
||||
requestLayout()
|
||||
}
|
||||
|
||||
fun setSubtitleStyle(style: SubtitleStyle) {
|
||||
@@ -132,49 +140,20 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
}
|
||||
|
||||
fun updateSurfaceView(viewType: Int) {
|
||||
val currentSurfaceView = surfaceView
|
||||
val newSurfaceView: View = when (viewType) {
|
||||
ViewType.VIEW_TYPE_TEXTURE -> TextureView(context)
|
||||
|
||||
ViewType.VIEW_TYPE_SURFACE_SECURE -> SurfaceView(context).apply {
|
||||
// Requires API 17+
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
(this as SurfaceView).setSecure(true)
|
||||
}
|
||||
}
|
||||
|
||||
else -> SurfaceView(context)
|
||||
}
|
||||
|
||||
if (currentSurfaceView === newSurfaceView) {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove the old surface view
|
||||
if (currentSurfaceView != null) {
|
||||
removeView(currentSurfaceView)
|
||||
}
|
||||
|
||||
// Add the new surface view
|
||||
surfaceView = newSurfaceView
|
||||
val layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
addView(newSurfaceView, 0, layoutParams)
|
||||
|
||||
// Attach player to the new surface
|
||||
playerView.player?.let {
|
||||
when (newSurfaceView) {
|
||||
is SurfaceView -> it.setVideoSurfaceView(newSurfaceView)
|
||||
is TextureView -> it.setVideoTextureView(newSurfaceView)
|
||||
}
|
||||
}
|
||||
// TODO: Implement proper surface type switching if needed
|
||||
}
|
||||
|
||||
val isPlaying: Boolean
|
||||
get() = playerView.player?.isPlaying ?: false
|
||||
|
||||
fun invalidateAspectRatio() {
|
||||
// PlayerView handles aspect ratio automatically
|
||||
requestLayout()
|
||||
// PlayerView handles aspect ratio automatically through its internal AspectRatioFrameLayout
|
||||
playerView.requestLayout()
|
||||
|
||||
// Reapply the current resize mode to ensure it's properly set
|
||||
pendingResizeMode?.let { resizeMode ->
|
||||
playerView.resizeMode = resizeMode
|
||||
}
|
||||
}
|
||||
|
||||
fun setUseController(useController: Boolean) {
|
||||
@@ -245,7 +224,13 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
|
||||
private val playerListener = object : Player.Listener {
|
||||
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
||||
playerView.post { playerView.requestLayout() }
|
||||
playerView.post {
|
||||
playerView.requestLayout()
|
||||
// Reapply resize mode to ensure it's properly set after timeline changes
|
||||
pendingResizeMode?.let { resizeMode ->
|
||||
playerView.resizeMode = resizeMode
|
||||
}
|
||||
}
|
||||
updateLiveUi()
|
||||
}
|
||||
|
||||
@@ -255,6 +240,15 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
) {
|
||||
updateLiveUi()
|
||||
}
|
||||
|
||||
// Handle video size changes which affect aspect ratio
|
||||
if (events.contains(Player.EVENT_VIDEO_SIZE_CHANGED)) {
|
||||
pendingResizeMode?.let { resizeMode ->
|
||||
playerView.resizeMode = resizeMode
|
||||
}
|
||||
playerView.requestLayout()
|
||||
requestLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,4 +276,14 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
||||
// Post a second layout pass so the ExoPlayer internal views get correct bounds.
|
||||
post(layoutRunnable)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
|
||||
if (changed) {
|
||||
pendingResizeMode?.let { resizeMode ->
|
||||
playerView.resizeMode = resizeMode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user