mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-16 02:44:51 +00:00
feat(state): add state for videos/audios/sub/rend
This commit is contained in:
@@ -26,9 +26,22 @@ import com.margelo.nitro.omni.PlayerStatus
|
||||
import com.margelo.nitro.omni.Rendition
|
||||
import com.margelo.nitro.omni.Source
|
||||
import com.margelo.nitro.omni.Track
|
||||
import com.margelo.nitro.omni.TrackProperty
|
||||
|
||||
/**
|
||||
* Source of the current track/rendition lists. Implemented by [OmniPlayer] so
|
||||
* state emissions reuse the same computation (incl. cast metadata recovery) as
|
||||
* its readonly getters.
|
||||
*/
|
||||
interface TrackProvider {
|
||||
val videos: Array<Track>
|
||||
val audios: Array<Track>
|
||||
val subtitles: Array<Track>
|
||||
val renditions: Array<Rendition>
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
class EventMap(private val tracks: TrackProvider) : HybridOmniEventMapSpec(), Player.Listener {
|
||||
private val onPrevListeners = mutableSetOf<() -> Unit>()
|
||||
private val onNextListeners = mutableSetOf<() -> Unit>()
|
||||
private val onEndListeners = mutableSetOf<() -> Unit>()
|
||||
@@ -38,6 +51,8 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
private val onAudioTrackChangeListeners = mutableSetOf<(track: Track) -> Unit>()
|
||||
private val onSubtitleChangeListeners = mutableSetOf<(track: Track?) -> Unit>()
|
||||
private val onRenditionChangeListeners = mutableSetOf<(rendition: Rendition) -> Unit>()
|
||||
private val tracksListeners = mutableMapOf<TrackProperty, MutableSet<(Array<Track>) -> Unit>>()
|
||||
private val renditionsListeners = mutableSetOf<(Array<Rendition>) -> Unit>()
|
||||
private val stateListeners = mutableMapOf<NumberProperty, MutableSet<(Double) -> Unit>>()
|
||||
private val stateBoolListeners = mutableMapOf<BoolProperty, MutableSet<(Boolean) -> Unit>>()
|
||||
private val playerStatusListeners = mutableSetOf<(PlayerStatus) -> Unit>()
|
||||
@@ -46,6 +61,7 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
|
||||
private var lastMediaItemIndex = 0
|
||||
private var lastRendition: Rendition? = null
|
||||
private var lastRenditions: Array<Rendition>? = null
|
||||
private var lastIsAutoQuality: Boolean? = null
|
||||
|
||||
// swapped on cast start/end
|
||||
@@ -59,6 +75,7 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
value.addListener(this)
|
||||
lastMediaItemIndex = value.currentMediaItemIndex
|
||||
lastRendition = null
|
||||
lastRenditions = null
|
||||
lastIsAutoQuality = null
|
||||
|
||||
// immediatly send all the state
|
||||
@@ -158,6 +175,21 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
stateBoolListeners[BoolProperty.ISAUTOQUALITY]?.forEach { it(isAuto) }
|
||||
}
|
||||
|
||||
private fun emitTracks() {
|
||||
tracksListeners[TrackProperty.VIDEOS]?.let { cbs ->
|
||||
val videos = tracks.videos
|
||||
cbs.forEach { it(videos) }
|
||||
}
|
||||
tracksListeners[TrackProperty.AUDIOS]?.let { cbs ->
|
||||
val audios = tracks.audios
|
||||
cbs.forEach { it(audios) }
|
||||
}
|
||||
tracksListeners[TrackProperty.SUBTITLES]?.let { cbs ->
|
||||
val subtitles = tracks.subtitles
|
||||
cbs.forEach { it(subtitles) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitRenditionChange() {
|
||||
val rendition = getCurrentRendition() ?: return
|
||||
if (rendition == lastRendition) return
|
||||
@@ -165,6 +197,12 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
onRenditionChangeListeners.forEach { it(rendition) }
|
||||
}
|
||||
|
||||
private fun emitRenditions() {
|
||||
if (renditionsListeners.isEmpty()) return
|
||||
val renditions = tracks.renditions
|
||||
renditionsListeners.forEach { it(renditions) }
|
||||
}
|
||||
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
val state = when (player.playbackState) {
|
||||
STATE_IDLE -> PlayerStatus.IDLE
|
||||
@@ -227,20 +265,19 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
selectedTrack(TRACK_TYPE_AUDIO)?.let { track ->
|
||||
onAudioTrackChangeListeners.forEach { it(track) }
|
||||
}
|
||||
onSubtitleChangeListeners.forEach {
|
||||
it(
|
||||
selectedTrack(
|
||||
TRACK_TYPE_TEXT
|
||||
)
|
||||
)
|
||||
selectedTrack(TRACK_TYPE_TEXT)?.let { track ->
|
||||
onSubtitleChangeListeners.forEach { it(track) }
|
||||
}
|
||||
emitTracks()
|
||||
emitIsAutoQualityChange()
|
||||
emitRenditionChange()
|
||||
emitRenditions()
|
||||
}
|
||||
|
||||
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
||||
emitIsAutoQualityChange()
|
||||
emitRenditionChange()
|
||||
emitRenditions()
|
||||
}
|
||||
|
||||
override fun onPlayerError(error: PlaybackException) {
|
||||
@@ -397,6 +434,22 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener {
|
||||
onRenditionChangeListeners.remove(cb)
|
||||
}
|
||||
|
||||
override fun addTracksListener(key: TrackProperty, cb: (value: Array<Track>) -> Unit) {
|
||||
tracksListeners.getOrPut(key) { mutableSetOf() }.add(cb)
|
||||
}
|
||||
|
||||
override fun removeTracksListener(key: TrackProperty, cb: (value: Array<Track>) -> Unit) {
|
||||
tracksListeners[key]?.remove(cb)
|
||||
}
|
||||
|
||||
override fun addRenditionsListener(cb: (value: Array<Rendition>) -> Unit) {
|
||||
renditionsListeners.add(cb)
|
||||
}
|
||||
|
||||
override fun removeRenditionsListener(cb: (value: Array<Rendition>) -> Unit) {
|
||||
renditionsListeners.remove(cb)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
player.removeListener(this)
|
||||
super.dispose()
|
||||
|
||||
@@ -51,7 +51,7 @@ import org.json.JSONObject
|
||||
class OmniPlayer(
|
||||
private val backend: AndroidBackend = AndroidBackend.VLC,
|
||||
castOptions: CastOptions? = null,
|
||||
) : HybridOmniPlayerSpec() {
|
||||
) : HybridOmniPlayerSpec(), TrackProvider {
|
||||
private val ctx = NitroModules.applicationContext ?: throw Error("No Context available!")
|
||||
|
||||
// exoplayer only, used to specify request headers.
|
||||
@@ -72,7 +72,7 @@ class OmniPlayer(
|
||||
AndroidBackend.VLC -> VlcPlayer(ctx)
|
||||
}
|
||||
}
|
||||
override val eventMap = EventMap()
|
||||
override val eventMap = EventMap(this)
|
||||
|
||||
private var castContext: CastContext? = null
|
||||
private val castStateListener =
|
||||
@@ -295,7 +295,39 @@ class OmniPlayer(
|
||||
override val videos by mainThreadProperty { tracksByType(C.TRACK_TYPE_VIDEO) }
|
||||
override val audios by mainThreadProperty { tracksByType(C.TRACK_TYPE_AUDIO) }
|
||||
override val subtitles by mainThreadProperty { tracksByType(C.TRACK_TYPE_TEXT) }
|
||||
override val rendition by mainThreadProperty { getRenditions() }
|
||||
override val renditions by mainThreadProperty {
|
||||
val group =
|
||||
player.currentTracks.groups.firstOrNull { it.isSelected && it.type == C.TRACK_TYPE_VIDEO }
|
||||
?: return emptyArray()
|
||||
|
||||
val currentIndex = when {
|
||||
isAutoQuality -> {
|
||||
if (player.videoSize.width > 0 && player.videoSize.height > 0) {
|
||||
(0 until group.length).firstOrNull { i ->
|
||||
val format = group.getTrackFormat(i)
|
||||
format.width == player.videoSize.width && format.height == player.videoSize.height
|
||||
}
|
||||
} else null
|
||||
}
|
||||
|
||||
else -> (0 until group.length).firstOrNull { group.isTrackSelected(it) }
|
||||
}
|
||||
|
||||
val result = ArrayList<Rendition>()
|
||||
for (i in 0 until group.length) {
|
||||
val format = group.getTrackFormat(i)
|
||||
result.add(
|
||||
Rendition(
|
||||
id = i.toString(),
|
||||
width = format.width.toDouble().coerceAtLeast(0.0),
|
||||
height = format.height.toDouble().coerceAtLeast(0.0),
|
||||
bitrate = format.bitrate.toDouble().coerceAtLeast(0.0),
|
||||
selected = i == currentIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
return result.toTypedArray()
|
||||
}
|
||||
|
||||
override var isAutoQuality by mainThreadProperty {
|
||||
player.trackSelectionParameters.overrides.none {
|
||||
@@ -441,37 +473,6 @@ class OmniPlayer(
|
||||
}
|
||||
|
||||
private fun getRenditions(): Array<Rendition> {
|
||||
val group =
|
||||
player.currentTracks.groups.firstOrNull { it.isSelected && it.type == C.TRACK_TYPE_VIDEO }
|
||||
?: return emptyArray()
|
||||
|
||||
val currentIndex = when {
|
||||
isAutoQuality -> {
|
||||
if (player.videoSize.width > 0 && player.videoSize.height > 0) {
|
||||
(0 until group.length).firstOrNull { i ->
|
||||
val format = group.getTrackFormat(i)
|
||||
format.width == player.videoSize.width && format.height == player.videoSize.height
|
||||
}
|
||||
} else null
|
||||
}
|
||||
|
||||
else -> (0 until group.length).firstOrNull { group.isTrackSelected(it) }
|
||||
}
|
||||
|
||||
val result = ArrayList<Rendition>()
|
||||
for (i in 0 until group.length) {
|
||||
val format = group.getTrackFormat(i)
|
||||
result.add(
|
||||
Rendition(
|
||||
id = i.toString(),
|
||||
width = format.width.toDouble().coerceAtLeast(0.0),
|
||||
height = format.height.toDouble().coerceAtLeast(0.0),
|
||||
bitrate = format.bitrate.toDouble().coerceAtLeast(0.0),
|
||||
selected = i == currentIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
return result.toTypedArray()
|
||||
}
|
||||
|
||||
override fun selectRendition(rendition: Rendition?) {
|
||||
|
||||
Reference in New Issue
Block a user