mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-27 06:52:18 +00:00
Merge ec63003fab into 19f216bedf
This commit is contained in:
commit
82ebdf31b5
1 changed files with 83 additions and 5 deletions
|
|
@ -7,6 +7,7 @@ import android.text.SpannableString
|
|||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
|
|
@ -39,6 +40,7 @@ import androidx.media3.common.MimeTypes
|
|||
import androidx.media3.common.PlaybackException
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.TrackSelectionOverride
|
||||
import androidx.media3.common.VideoSize
|
||||
import androidx.media3.common.text.Cue
|
||||
import androidx.media3.common.text.CueGroup
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
|
@ -254,6 +256,9 @@ private fun ExoPlayerSurface(
|
|||
var selectedExternalSubtitleMimeType by remember(playerSourceKey) { mutableStateOf<String?>(null) }
|
||||
val latestSubtitleDelayMs = rememberUpdatedState(subtitleDelayMs)
|
||||
val latestExternalSubtitleMimeType = rememberUpdatedState(selectedExternalSubtitleMimeType)
|
||||
var playerViewRef by remember { mutableStateOf<PlayerView?>(null) }
|
||||
var videoAspectRatio by remember(playerSourceKey) { mutableStateOf(0f) }
|
||||
val latestVideoAspectRatio = rememberUpdatedState(videoAspectRatio)
|
||||
var decoderPriorityOverride by remember(playerSourceKey) { mutableStateOf<Int?>(null) }
|
||||
var fallbackStartPositionMs by remember(playerSourceKey) { mutableStateOf<Long?>(null) }
|
||||
val effectiveDecoderPriority = decoderPriorityOverride ?: playerSettings.decoderPriority
|
||||
|
|
@ -342,6 +347,9 @@ private fun ExoPlayerSurface(
|
|||
shouldNormalizeCuePositionProvider = {
|
||||
latestExternalSubtitleMimeType.value == MimeTypes.TEXT_VTT
|
||||
},
|
||||
videoBoundsFractionProvider = {
|
||||
playerViewRef?.videoBoundsFraction(latestVideoAspectRatio.value)
|
||||
},
|
||||
)
|
||||
.setExtensionRendererMode(effectiveDecoderPriority)
|
||||
.setEnableDecoderFallback(true)
|
||||
|
|
@ -449,7 +457,6 @@ private fun ExoPlayerSurface(
|
|||
|
||||
val pendingSubtitleTrackIndex = remember { mutableListOf<Int>() }
|
||||
val pendingAudioTrackSelection = remember { mutableListOf<TrackSelectionSnapshot>() }
|
||||
var playerViewRef by remember { mutableStateOf<PlayerView?>(null) }
|
||||
var currentSubtitleStyle by remember { mutableStateOf(SubtitleStyleState.DEFAULT) }
|
||||
var subtitleSelectionJob by remember { mutableStateOf<Job?>(null) }
|
||||
val isInPip = rememberIsInPictureInPicture()
|
||||
|
|
@ -580,10 +587,6 @@ private fun ExoPlayerSurface(
|
|||
dispatchExoPlayerSnapshot()
|
||||
}
|
||||
|
||||
override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
|
||||
latestOnSnapshot.value(exoPlayer.snapshot())
|
||||
}
|
||||
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
Log.i(
|
||||
PLAYER_DIAGNOSTIC_TAG,
|
||||
|
|
@ -608,6 +611,13 @@ private fun ExoPlayerSurface(
|
|||
dispatchExoPlayerSnapshot()
|
||||
}
|
||||
|
||||
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
||||
latestOnSnapshot.value(exoPlayer.snapshot())
|
||||
if (videoSize.width > 0 && videoSize.height > 0) {
|
||||
videoAspectRatio = videoSize.width.toFloat() / videoSize.height.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTracksChanged(tracks: androidx.media3.common.Tracks) {
|
||||
Log.d(TAG, "onTracksChanged: ${tracks.groups.size} groups total")
|
||||
exoPlayer.logCurrentTracks("onTracksChanged")
|
||||
|
|
@ -1794,11 +1804,50 @@ private fun ExoPlayer.logCurrentTracks(context: String) {
|
|||
Log.d(TAG, "--- end logCurrentTracks ---")
|
||||
}
|
||||
|
||||
@androidx.annotation.OptIn(UnstableApi::class)
|
||||
private fun PlayerView.videoBoundsFraction(aspectRatio: Float): RectF? {
|
||||
val subtitleView = this.subtitleView ?: return null
|
||||
val viewWidth = subtitleView.width.toFloat()
|
||||
val viewHeight = subtitleView.height.toFloat()
|
||||
if (viewWidth <= 0f || viewHeight <= 0f) return null
|
||||
|
||||
if (aspectRatio > 0f) {
|
||||
val parentRatio = viewWidth / viewHeight
|
||||
return if (parentRatio > aspectRatio) {
|
||||
val fitW = viewHeight * aspectRatio
|
||||
val leftPx = (viewWidth - fitW) / 2f
|
||||
RectF(leftPx / viewWidth, 0f, (leftPx + fitW) / viewWidth, 1f)
|
||||
} else {
|
||||
val fitH = viewWidth / aspectRatio
|
||||
val topPx = (viewHeight - fitH) / 2f
|
||||
RectF(0f, topPx / viewHeight, 1f, (topPx + fitH) / viewHeight)
|
||||
}
|
||||
}
|
||||
|
||||
val contentFrame = getTag(androidx.media3.ui.R.id.exo_content_frame) as? AspectRatioFrameLayout
|
||||
?: findViewById<AspectRatioFrameLayout>(androidx.media3.ui.R.id.exo_content_frame)
|
||||
?.also { setTag(androidx.media3.ui.R.id.exo_content_frame, it) }
|
||||
?: return null
|
||||
val frameWidth = contentFrame.width.toFloat()
|
||||
val frameHeight = contentFrame.height.toFloat()
|
||||
if (frameWidth <= 0f || frameHeight <= 0f) return null
|
||||
if (frameWidth > viewWidth || frameHeight > viewHeight) return null
|
||||
val left = contentFrame.x / viewWidth
|
||||
val top = contentFrame.y / viewHeight
|
||||
return RectF(
|
||||
left,
|
||||
top,
|
||||
left + frameWidth / viewWidth,
|
||||
top + frameHeight / viewHeight,
|
||||
)
|
||||
}
|
||||
|
||||
@androidx.annotation.OptIn(UnstableApi::class)
|
||||
private class SubtitleOffsetRenderersFactory(
|
||||
context: Context,
|
||||
private val subtitleDelayUsProvider: () -> Long,
|
||||
private val shouldNormalizeCuePositionProvider: () -> Boolean,
|
||||
private val videoBoundsFractionProvider: () -> RectF?,
|
||||
) : DefaultRenderersFactory(context) {
|
||||
override fun buildTextRenderers(
|
||||
context: Context,
|
||||
|
|
@ -1810,6 +1859,7 @@ private class SubtitleOffsetRenderersFactory(
|
|||
val normalizingOutput = CueNormalizingTextOutput(
|
||||
delegate = output,
|
||||
shouldNormalizeCuePositionProvider = shouldNormalizeCuePositionProvider,
|
||||
videoBoundsFractionProvider = videoBoundsFractionProvider,
|
||||
)
|
||||
val startIndex = out.size
|
||||
super.buildTextRenderers(context, normalizingOutput, outputLooper, extensionRendererMode, out)
|
||||
|
|
@ -1825,6 +1875,7 @@ private class SubtitleOffsetRenderersFactory(
|
|||
private class CueNormalizingTextOutput(
|
||||
private val delegate: TextOutput,
|
||||
private val shouldNormalizeCuePositionProvider: () -> Boolean,
|
||||
private val videoBoundsFractionProvider: () -> RectF?,
|
||||
) : TextOutput {
|
||||
override fun onCues(cueGroup: CueGroup) {
|
||||
val processed = cueGroup.cues.map(::processCue)
|
||||
|
|
@ -1841,9 +1892,36 @@ private class CueNormalizingTextOutput(
|
|||
if (shouldNormalizeCuePositionProvider()) {
|
||||
processed = normalizeCuePosition(processed)
|
||||
}
|
||||
if (processed.bitmap != null) {
|
||||
val bounds = videoBoundsFractionProvider()
|
||||
if (bounds != null && bounds.width() > 0f && bounds.height() > 0f) {
|
||||
val isIdentity = bounds.left == 0f && bounds.top == 0f
|
||||
&& bounds.width() == 1f && bounds.height() == 1f
|
||||
if (!isIdentity) {
|
||||
processed = remapBitmapCueToVideoBounds(processed, bounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
return processed
|
||||
}
|
||||
|
||||
private fun remapBitmapCueToVideoBounds(cue: Cue, bounds: RectF): Cue {
|
||||
val builder = cue.buildUpon()
|
||||
if (cue.position != Cue.DIMEN_UNSET) {
|
||||
builder.setPosition(bounds.left + cue.position * bounds.width())
|
||||
}
|
||||
if (cue.size != Cue.DIMEN_UNSET) {
|
||||
builder.setSize(cue.size * bounds.width())
|
||||
}
|
||||
if (cue.lineType == Cue.LINE_TYPE_FRACTION && cue.line != Cue.DIMEN_UNSET) {
|
||||
builder.setLine(bounds.top + cue.line * bounds.height(), Cue.LINE_TYPE_FRACTION)
|
||||
}
|
||||
if (cue.bitmapHeight != Cue.DIMEN_UNSET) {
|
||||
builder.setBitmapHeight(cue.bitmapHeight * bounds.height())
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun normalizeCuePosition(cue: Cue): Cue {
|
||||
if (cue.bitmap != null || cue.verticalType != Cue.TYPE_UNSET || cue.line == Cue.DIMEN_UNSET) {
|
||||
return cue
|
||||
|
|
|
|||
Loading…
Reference in a new issue