Basic vlc implementation

This commit is contained in:
2026-04-10 18:52:41 +02:00
parent 014c75d575
commit 7a5f495b39
13 changed files with 123 additions and 212 deletions
@@ -1,76 +1,121 @@
package dev.zoriya.omni
import android.net.Uri
import com.margelo.nitro.NitroModules
import com.margelo.nitro.omni.HybridOmniPlayerPropsSpec
import com.margelo.nitro.omni.HybridOmniPlayerSpec
import com.margelo.nitro.omni.PlayerStatus
import com.margelo.nitro.omni.Rendition
import com.margelo.nitro.omni.Track
import com.margelo.nitro.omni.Variant_NullType_Rendition
import dev.zoriya.omni.utils.deferredObservable
import org.videolan.libvlc.LibVLC
import org.videolan.libvlc.Media
import org.videolan.libvlc.MediaPlayer
import org.videolan.libvlc.interfaces.IMedia
class OmniPlayer(source: HybridOmniPlayerPropsSpec) : HybridOmniPlayerSpec() {
override val hasPrev: Boolean
get() = TODO("Not yet implemented")
override val hasNext: Boolean
get() = TODO("Not yet implemented")
override val status: PlayerStatus
get() = TODO("Not yet implemented")
override val isPlaying: Boolean
get() = TODO("Not yet implemented")
class OmniPlayer() : HybridOmniPlayerSpec() {
val vlc = LibVLC(NitroModules.applicationContext ?: throw Error("No Context available!"))
val player = MediaPlayer(vlc)
var source: HybridOmniPlayerPropsSpec by deferredObservable { _, _, new ->
val src = new.src.firstOrNull()?.uri ?: return@deferredObservable
player.media = Media(vlc, Uri.parse(src))
new.startTime?.let { start ->
player.time = (start * 1000.0).toLong().coerceAtLeast(0L)
}
}
override val hasPrev get() = source.metadata?.hasPrev ?: false
override val hasNext get() = source.metadata?.hasNext ?: false
override val status
get() = when (player.playerState) {
IMedia.State.NothingSpecial -> PlayerStatus.IDLE
IMedia.State.Opening -> PlayerStatus.LOADING
IMedia.State.Playing -> PlayerStatus.READYTOPLAY
IMedia.State.Paused -> PlayerStatus.READYTOPLAY
IMedia.State.Stopped -> PlayerStatus.IDLE
IMedia.State.Ended -> PlayerStatus.IDLE
IMedia.State.Error -> PlayerStatus.ERROR
else -> PlayerStatus.ERROR
}
override val isPlaying: Boolean get() = player.isPlaying
override var currentTime: Double
get() = TODO("Not yet implemented")
set(value) {}
get() = player.time.coerceAtLeast(0L) / 1000.0
set(value) {
player.time = (value * 1000.0).toLong().coerceAtLeast(0L)
}
override val buffered: Double
get() = TODO("Not yet implemented")
override val duration: Double
get() = TODO("Not yet implemented")
get() {
val total = duration
if (total <= 0.0) return 0.0
return (total * player.position.toDouble()).coerceIn(0.0, total)
}
override val duration: Double get() = player.length.coerceAtLeast(0L) / 1000.0
override var playbackRate: Double
get() = TODO("Not yet implemented")
set(value) {}
get() = player.rate.toDouble()
set(value) {
if (value > 0.0) player.rate = value.toFloat()
}
override var volume: Double
get() = TODO("Not yet implemented")
set(value) {}
override val videos: Array<Track>
get() = TODO("Not yet implemented")
override val audios: Array<Track>
get() = TODO("Not yet implemented")
override val subtitles: Array<Track>
get() = TODO("Not yet implemented")
override val rendition: Array<Rendition>
get() = TODO("Not yet implemented")
get() = (player.volume.coerceIn(0, 100) / 100.0)
set(value) {
player.volume = (value.coerceIn(0.0, 1.0) * 100.0).toInt()
}
override val videos get() = getTracks(IMedia.Track.Type.Video)
override val audios get() = getTracks(IMedia.Track.Type.Audio)
override val subtitles get() = getTracks(IMedia.Track.Type.Text)
fun getTracks(type: Int): Array<Track> {
val tracks = player.getTracks(type)
val selected = player.getSelectedTrack(type)
return tracks.map {
Track(
id = it.id,
label = it.name,
language = it.language,
selected = it.id == selected.id
)
}.toTypedArray()
}
override val rendition: Array<Rendition> get() = emptyArray()
override fun play() {
TODO("Not yet implemented")
player.play()
}
override fun pause() {
TODO("Not yet implemented")
player.pause()
}
override fun seekBy(offset: Double) {
TODO("Not yet implemented")
currentTime += offset
}
override fun playPrev() {
TODO("Not yet implemented")
// App-level action; no default implementation in VLC player.
}
override fun playNext() {
TODO("Not yet implemented")
// App-level action; no default implementation in VLC player.
}
override fun selectVideo(video: Track) {
TODO("Not yet implemented")
player.selectTrack(video.id)
}
override fun selectAudio(audio: Track) {
TODO("Not yet implemented")
player.selectTrack(audio.id)
}
override fun selectSubtitle(subtitle: Track) {
TODO("Not yet implemented")
override fun selectSubtitle(subtitle: Track?) {
when (subtitle) {
null -> player.unselectTrackType(IMedia.Track.Type.Text)
else -> player.selectTrack(subtitle.id)
}
}
override fun selectRendition(rendition: Variant_NullType_Rendition?) {
TODO("Not yet implemented")
override fun selectRendition(rendition: Rendition?) {
// Not supported by libVLC MediaPlayer track APIs.
}
}
}
@@ -7,6 +7,8 @@ import com.margelo.nitro.omni.HybridOmniPlayerSpec
class OmniPlayerFactory(val context: ThemedReactContext): HybridOmniPlayerFactorySpec() {
override fun createPlayer(props: HybridOmniPlayerPropsSpec): HybridOmniPlayerSpec {
return OmniPlayer(props)
return OmniPlayer().apply {
source = props
}
}
}
@@ -0,0 +1,21 @@
package dev.zoriya.omni.utils
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
inline fun <T : Any> deferredObservable(
crossinline onChange: (thisRef: Any?, oldValue: T?, newValue: T) -> Unit
): ReadWriteProperty<Any?, T> =
object : ReadWriteProperty<Any?, T> {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val old = this.value
this.value = value
onChange(old, value)
}
}