Compare commits

...
4 Commits
4 changed files with 24 additions and 19 deletions
@@ -15,6 +15,8 @@ import androidx.media3.common.Player.STATE_READY
import androidx.media3.common.Timeline import androidx.media3.common.Timeline
import androidx.media3.common.Tracks import androidx.media3.common.Tracks
import androidx.media3.common.VideoSize import androidx.media3.common.VideoSize
import com.google.android.gms.cast.MediaStatus
import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.margelo.nitro.omni.BoolProperty import com.margelo.nitro.omni.BoolProperty
import com.margelo.nitro.omni.CastStatus import com.margelo.nitro.omni.CastStatus
import com.margelo.nitro.omni.HybridOmniEventMapSpec import com.margelo.nitro.omni.HybridOmniEventMapSpec
@@ -103,6 +105,8 @@ class EventMap(private val tracks: TrackProvider) : HybridOmniEventMapSpec(), Pl
onTracksChanged(value.currentTracks) onTracksChanged(value.currentTracks)
} }
var remote: RemoteMediaClient? = null
fun emitCastStatus(status: CastStatus) { fun emitCastStatus(status: CastStatus) {
castStatusListeners.forEach { it(status) } castStatusListeners.forEach { it(status) }
} }
@@ -200,7 +204,13 @@ class EventMap(private val tracks: TrackProvider) : HybridOmniEventMapSpec(), Pl
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
val state = when (player.playbackState) { val state = when (player.playbackState) {
STATE_IDLE -> PlayerStatus.IDLE STATE_IDLE -> {
// cast doesn't have STATE_ENDED, so handle this manually
if (remote?.mediaStatus?.idleReason == MediaStatus.IDLE_REASON_FINISHED)
onEndListeners.forEach { it() }
PlayerStatus.IDLE
}
STATE_BUFFERING -> PlayerStatus.LOADING STATE_BUFFERING -> PlayerStatus.LOADING
STATE_READY -> PlayerStatus.READYTOPLAY STATE_READY -> PlayerStatus.READYTOPLAY
STATE_ENDED -> { STATE_ENDED -> {
@@ -47,6 +47,7 @@ import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService import androidx.media3.session.MediaSessionService
import androidx.mediarouter.app.MediaRouteChooserDialog import androidx.mediarouter.app.MediaRouteChooserDialog
import androidx.mediarouter.app.SystemOutputSwitcherDialogController import androidx.mediarouter.app.SystemOutputSwitcherDialogController
import com.google.android.gms.cast.MediaStatus
import com.google.android.gms.cast.framework.CastContext import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.cast.framework.CastStateListener import com.google.android.gms.cast.framework.CastStateListener
@@ -110,7 +111,9 @@ class OmniPlayer(
// their own), so they ask us to do it when something else - google home, the // their own), so they ask us to do it when something else - google home, the
// assistant, ... - requests a prev/next. same path as our own buttons. // assistant, ... - requests a prev/next. same path as our own buttons.
private fun listenToReceiver() { private fun listenToReceiver() {
val session = castContext?.sessionManager?.currentCastSession ?: return val session = castContext?.sessionManager?.currentCastSession
eventMap.remote = session?.remoteMediaClient
if (session == null) return
try { try {
session.setMessageReceivedCallbacks(OMNI_NAMESPACE) { _, _, message -> session.setMessageReceivedCallbacks(OMNI_NAMESPACE) { _, _, message ->
when (JSONObject(message).optString("action")) { when (JSONObject(message).optString("action")) {
@@ -302,6 +305,8 @@ class OmniPlayer(
private fun sourceOf(item: MediaItem): Source { private fun sourceOf(item: MediaItem): Source {
val meta = item.mediaMetadata val meta = item.mediaMetadata
val status = castContext?.sessionManager?.currentCastSession
?.remoteMediaClient?.mediaStatus
return Source( return Source(
src = VideoSrc( src = VideoSrc(
uri = item.localConfiguration?.uri?.toString() ?: item.mediaId, uri = item.localConfiguration?.uri?.toString() ?: item.mediaId,
@@ -316,9 +321,8 @@ class OmniPlayer(
album = meta.albumTitle?.toString(), album = meta.albumTitle?.toString(),
artist = meta.artist?.toString(), artist = meta.artist?.toString(),
imageLink = meta.artworkUri?.toString(), imageLink = meta.artworkUri?.toString(),
// we did not load it, we don't know what comes around it. hasPrev = status?.isMediaCommandSupported(MediaStatus.COMMAND_QUEUE_PREVIOUS),
hasPrev = null, hasNext = status?.isMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT),
hasNext = null,
), ),
mixAudio = null, mixAudio = null,
castId = item.mediaId, castId = item.mediaId,
@@ -616,7 +616,7 @@ class VlcPlayer(ctx: Context) :
.setWidth(videoTrack?.width?.takeIf { it > 0 } ?: Format.NO_VALUE) .setWidth(videoTrack?.width?.takeIf { it > 0 } ?: Format.NO_VALUE)
.setHeight(videoTrack?.height?.takeIf { it > 0 } ?: Format.NO_VALUE) .setHeight(videoTrack?.height?.takeIf { it > 0 } ?: Format.NO_VALUE)
.setFrameRate( .setFrameRate(
videoTrack?.takeIf { it.frameRateDen > 0 } videoTrack?.takeIf { it.frameRateDen > 0 && it.frameRateNum > 0 }
?.let { it.frameRateNum.toFloat() / it.frameRateDen } ?.let { it.frameRateNum.toFloat() / it.frameRateDen }
?: Format.NO_VALUE.toFloat() ?: Format.NO_VALUE.toFloat()
) )
+4 -13
View File
@@ -119,9 +119,11 @@ export const OmniView = ({
const headersRef = useRef(source?.src?.headers); const headersRef = useRef(source?.src?.headers);
headersRef.current = source?.src?.headers; headersRef.current = source?.src?.headers;
const startTime = source?.startTime;
const config = useMemo<HlsMediaConfig>( const config = useMemo<HlsMediaConfig>(
() => ({ () => ({
hlsJs: { hlsJs: {
...(startTime && { startPosition: startTime }),
xhrSetup: (xhr: XMLHttpRequest) => { xhrSetup: (xhr: XMLHttpRequest) => {
const headers = headersRef.current; const headers = headersRef.current;
if (!headers) return; if (!headers) return;
@@ -131,20 +133,9 @@ export const OmniView = ({
}, },
}, },
}), }),
[], [startTime],
); );
const player = usePlayer();
const uri = source?.src.uri;
const startTime = source?.startTime;
const prevUri = useRef<string | undefined>(undefined);
useEffect(() => {
if (!uri) return;
if (startTime) player.currentTime = startTime;
if (autoplay && prevUri.current && prevUri.current !== uri) player.play();
prevUri.current = uri;
}, [player, uri, startTime, autoplay]);
// While casting, the receiver renders subtitles (the player forwards the // While casting, the receiver renders subtitles (the player forwards the
// selection); skip rendering them locally on the idle <video>. // selection); skip rendering them locally on the idle <video>.
const castStatus = usePlayerState("castStatus"); const castStatus = usePlayerState("castStatus");
@@ -156,7 +147,7 @@ export const OmniView = ({
> >
<Tech <Tech
ref={ref} ref={ref}
src={source?.src.uri} src={source && startTime ? `${source.src.uri}#t=${startTime}` : source?.src.uri}
config={config} config={config}
autoPlay={autoplay} autoPlay={autoplay}
playsInline playsInline