mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-16 02:44:51 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2ca76b4a9 | ||
|
|
77a8b2337d | ||
|
|
cebe97f168 |
@@ -279,7 +279,7 @@ class OmniPlayer(
|
||||
|
||||
override var playbackRate by mainThreadProperty(
|
||||
get = { player.playbackParameters.speed.toDouble() },
|
||||
set = { value -> player.setPlaybackSpeed(value.toFloat().coerceAtLeast(0f)) }
|
||||
set = { value -> player.setPlaybackSpeed(value.toFloat().coerceAtLeast(0.01f)) }
|
||||
)
|
||||
|
||||
override var muted by mainThreadProperty(
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.PictureInPictureParams
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.util.Rational
|
||||
import android.view.Gravity
|
||||
import android.view.SurfaceHolder
|
||||
import android.view.SurfaceView
|
||||
import android.view.View
|
||||
@@ -11,6 +12,10 @@ import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.VideoSize
|
||||
import androidx.media3.common.text.CueGroup
|
||||
import androidx.media3.ui.AspectRatioFrameLayout
|
||||
import androidx.media3.ui.SubtitleView
|
||||
import com.facebook.react.bridge.LifecycleEventListener
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.margelo.nitro.omni.HybridOmniPlayerSpec
|
||||
@@ -68,6 +73,16 @@ class OmniView(val context: ThemedReactContext) :
|
||||
}
|
||||
}
|
||||
|
||||
private val contentFrame = AspectRatioFrameLayout(context).apply {
|
||||
setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT)
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
Gravity.CENTER
|
||||
)
|
||||
view.addView(this)
|
||||
}
|
||||
|
||||
private val surfaceView = SurfaceView(context).apply {
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
@@ -75,8 +90,19 @@ class OmniView(val context: ThemedReactContext) :
|
||||
)
|
||||
holder.addCallback(this@OmniView)
|
||||
addOnLayoutChangeListener(this@OmniView)
|
||||
contentFrame.addView(this)
|
||||
}
|
||||
|
||||
private val subtitleView = SubtitleView(context).apply {
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
setUserDefaultStyle()
|
||||
setUserDefaultTextSize()
|
||||
view.addView(this)
|
||||
}
|
||||
|
||||
private var surfaceReady = false
|
||||
private var boundPlayer: OmniPlayer? = null
|
||||
private var rootContent: ViewGroup? = null
|
||||
@@ -94,6 +120,20 @@ class OmniView(val context: ThemedReactContext) :
|
||||
context.addLifecycleEventListener(this)
|
||||
}
|
||||
|
||||
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
||||
contentFrame.setAspectRatio(
|
||||
if (videoSize.width > 0 && videoSize.height > 0) {
|
||||
videoSize.width * videoSize.pixelWidthHeightRatio / videoSize.height
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCues(cueGroup: CueGroup) {
|
||||
subtitleView.setCues(cueGroup.cues)
|
||||
}
|
||||
|
||||
override fun onLayoutChange(
|
||||
view: View?,
|
||||
left: Int,
|
||||
@@ -219,6 +259,11 @@ class OmniView(val context: ThemedReactContext) :
|
||||
return
|
||||
}
|
||||
|
||||
if (boundPlayer?.isCasting == true) {
|
||||
clearPictureInPictureParams()
|
||||
return
|
||||
}
|
||||
|
||||
val autoEnterEnabled =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
|
||||
autoPip == true &&
|
||||
@@ -242,9 +287,18 @@ class OmniView(val context: ThemedReactContext) :
|
||||
)
|
||||
}
|
||||
|
||||
private fun clampedAspectRatio(width: Int, height: Int): Rational {
|
||||
val minRatio = 0.42
|
||||
val maxRatio = 2.38
|
||||
val ratio = width.toDouble() / height.toDouble()
|
||||
if (ratio in minRatio..maxRatio) return Rational(width, height)
|
||||
val clamped = ratio.coerceIn(minRatio, maxRatio)
|
||||
return Rational((clamped * 1000).toInt(), 1000)
|
||||
}
|
||||
|
||||
private fun buildPipParams(autoEnterEnabled: Boolean): PictureInPictureParams {
|
||||
val aspectRatio = if (surfaceView.width > 0 && surfaceView.height > 0) {
|
||||
Rational(surfaceView.width, surfaceView.height)
|
||||
clampedAspectRatio(surfaceView.width, surfaceView.height)
|
||||
} else {
|
||||
Rational(16, 9)
|
||||
}
|
||||
@@ -293,7 +347,7 @@ class OmniView(val context: ThemedReactContext) :
|
||||
child.visibility = View.GONE
|
||||
}
|
||||
|
||||
view.removeView(surfaceView)
|
||||
contentFrame.removeView(surfaceView)
|
||||
root.addView(
|
||||
surfaceView,
|
||||
FrameLayout.LayoutParams(
|
||||
@@ -317,7 +371,7 @@ class OmniView(val context: ThemedReactContext) :
|
||||
}
|
||||
rootContentViews = emptyList()
|
||||
|
||||
view.addView(surfaceView, FrameLayout.LayoutParams(
|
||||
contentFrame.addView(surfaceView, FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
))
|
||||
|
||||
@@ -97,9 +97,11 @@ class VlcPlayer(ctx: Context) :
|
||||
private var mediaItems: List<MediaItem> = emptyList()
|
||||
private var currentMediaItemIndex: Int = INDEX_UNSET
|
||||
private var currentTrackSelectionParameters = TrackSelectionParameters.Builder().build()
|
||||
@Volatile
|
||||
private var playerError: PlaybackException? = null
|
||||
private var playlistMetadata: MediaMetadata = MediaMetadata.EMPTY
|
||||
private var userInitiatedTransition: Boolean = false
|
||||
@Volatile
|
||||
private var cachedBufferedPosition: Long = 0L
|
||||
private var boundSurfaceView: SurfaceView? = null
|
||||
|
||||
@@ -467,7 +469,11 @@ class VlcPlayer(ctx: Context) :
|
||||
|
||||
override fun getShuffleModeEnabled(): Boolean = false
|
||||
|
||||
override fun isLoading() = player.playerState == IMedia.State.Opening
|
||||
override fun isLoading(): Boolean {
|
||||
val state = playbackState
|
||||
if (state == STATE_IDLE || state == STATE_ENDED) return false
|
||||
return player.playerState == IMedia.State.Opening
|
||||
}
|
||||
|
||||
override fun seekTo(
|
||||
mediaItemIndex: Int,
|
||||
@@ -493,11 +499,11 @@ class VlcPlayer(ctx: Context) :
|
||||
override fun getSeekForwardIncrement(): Long = 15_000L
|
||||
|
||||
override fun setPlaybackParameters(playbackParameters: PlaybackParameters) {
|
||||
player.rate = playbackParameters.speed.coerceAtLeast(0f)
|
||||
player.rate = playbackParameters.speed.coerceAtLeast(0.01f)
|
||||
}
|
||||
|
||||
override fun getPlaybackParameters(): PlaybackParameters =
|
||||
PlaybackParameters(player.rate.coerceAtLeast(0f))
|
||||
PlaybackParameters(player.rate.takeIf { it > 0f } ?: 1f)
|
||||
|
||||
override fun stop() {
|
||||
player.stop()
|
||||
@@ -505,6 +511,7 @@ class VlcPlayer(ctx: Context) :
|
||||
|
||||
override fun release() {
|
||||
player.setEventListener(null)
|
||||
listeners.release()
|
||||
abandonAudioFocus()
|
||||
player.stop()
|
||||
clearVideoSurface()
|
||||
@@ -518,36 +525,43 @@ class VlcPlayer(ctx: Context) :
|
||||
val selectedAudio = player.getSelectedTrack(IMedia.Track.Type.Audio)
|
||||
val selectedSubtitle = player.getSelectedTrack(IMedia.Track.Type.Text)
|
||||
|
||||
val videoTracks = player.getTracks(IMedia.Track.Type.Video)
|
||||
if (!videoTracks.isNullOrEmpty()) {
|
||||
val videoFormats = videoTracks.map { track ->
|
||||
Format.Builder()
|
||||
.setId(track.id)
|
||||
.setLabel(track.name)
|
||||
.setLanguage(track.language)
|
||||
.setSampleMimeType("video/x-unknown")
|
||||
.build()
|
||||
player.getTracks(IMedia.Track.Type.Video).orEmpty()
|
||||
.groupBy { listOf(it.language, it.name, it.description) }
|
||||
.values.forEach { videoTracks ->
|
||||
val videoFormats = videoTracks.map { track ->
|
||||
Format.Builder()
|
||||
.setId(track.id)
|
||||
.setLabel(track.name)
|
||||
.setLanguage(track.language)
|
||||
.setSampleMimeType("video/x-unknown")
|
||||
.build()
|
||||
}
|
||||
val group = TrackGroup("vlc-video-${videoTracks.minOf { it.id }}", *videoFormats.toTypedArray())
|
||||
val selected = BooleanArray(videoFormats.size) { idx ->
|
||||
selectedVideo != null && selectedVideo.id == videoTracks[idx].id
|
||||
}
|
||||
val support = IntArray(videoFormats.size) { FORMAT_HANDLED }
|
||||
result.add(Group(group, videoFormats.size > 1, support, selected))
|
||||
}
|
||||
val group = TrackGroup("vlc-video", *videoFormats.toTypedArray())
|
||||
val selected = BooleanArray(videoFormats.size) { idx ->
|
||||
selectedVideo != null && selectedVideo.id == videoTracks[idx].id
|
||||
}
|
||||
val support = IntArray(videoFormats.size) { FORMAT_HANDLED }
|
||||
result.add(Group(group, videoFormats.size > 1, support, selected))
|
||||
}
|
||||
|
||||
player.getTracks(IMedia.Track.Type.Audio)?.forEach { track ->
|
||||
val format = Format.Builder()
|
||||
.setId(track.id)
|
||||
.setLabel(track.name)
|
||||
.setLanguage(track.language)
|
||||
.setSampleMimeType("audio/x-unknown")
|
||||
.build()
|
||||
val group = TrackGroup("vlc-audio-${track.id}", format)
|
||||
val selected = booleanArrayOf(selectedAudio != null && selectedAudio.id == track.id)
|
||||
val support = intArrayOf(FORMAT_HANDLED)
|
||||
result.add(Group(group, false, support, selected))
|
||||
}
|
||||
player.getTracks(IMedia.Track.Type.Audio).orEmpty()
|
||||
.groupBy { listOf(it.language, it.name, it.description) }
|
||||
.values.forEach { audioTracks ->
|
||||
val audioFormats = audioTracks.map { track ->
|
||||
Format.Builder()
|
||||
.setId(track.id)
|
||||
.setLabel(track.name)
|
||||
.setLanguage(track.language)
|
||||
.setSampleMimeType("audio/x-unknown")
|
||||
.build()
|
||||
}
|
||||
val group = TrackGroup("vlc-audio-${audioTracks.minOf { it.id }}", *audioFormats.toTypedArray())
|
||||
val selected = BooleanArray(audioFormats.size) { idx ->
|
||||
selectedAudio != null && selectedAudio.id == audioTracks[idx].id
|
||||
}
|
||||
val support = IntArray(audioFormats.size) { FORMAT_HANDLED }
|
||||
result.add(Group(group, audioFormats.size > 1, support, selected))
|
||||
}
|
||||
|
||||
player.getTracks(IMedia.Track.Type.Text)?.forEach { track ->
|
||||
val format = Format.Builder()
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="nosensor"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:resizeableActivity="true"
|
||||
|
||||
Reference in New Issue
Block a user