fix(android): UPnP survives screen-off + cursor catches up to Sonos
android / Build + lint + test (push) Has been cancelled
android / Build + lint + test (push) Has been cancelled
This commit is contained in:
+30
-10
@@ -264,16 +264,19 @@ class MinstrelForwardingPlayer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One poll tick: read position + transport state from Sonos and apply to
|
* One poll tick: read position + transport state from Sonos, apply to
|
||||||
* [remoteState]. The local cursor is NOT synced to Sonos's Track index --
|
* [remoteState], and forward-sync the local cursor to Sonos's Track
|
||||||
* during the 17-second queue load Sonos reports Track=1 (the first added
|
* index when not in queue load.
|
||||||
* track) which would silently walk the local cursor to 0, then SetAV+Seek
|
*
|
||||||
* lands and Sonos jumps to the user's actual track, racing with the
|
* Cursor sync is gated on `holder.target == null` (= not loading)
|
||||||
* sync. Local + Sonos cursors stay in sync because every override
|
* because during load Sonos reports Track=1 while we're still
|
||||||
* (seekToNext/Prev/seekTo) advances both sides by the same delta.
|
* appending, and syncing would race the SetAV+Seek that lands
|
||||||
* Hardware-button or Sonos-app driven advances ARE missed until the
|
* after. Outside load, forward sync catches Sonos auto-advances
|
||||||
* user takes a transport action -- accepted tradeoff pending GENA
|
* (queue end-of-track), Sonos-app driven Next presses, and any
|
||||||
* event subscriptions.
|
* drift after a brief poll-failure burst that didn't trip the
|
||||||
|
* drop threshold. Forward-only because a Next override we just
|
||||||
|
* issued can race with a poll still reporting the prior Track --
|
||||||
|
* the next poll catches up safely.
|
||||||
*/
|
*/
|
||||||
private suspend fun pollOnce(active: ActiveUpnp) {
|
private suspend fun pollOnce(active: ActiveUpnp) {
|
||||||
val info = active.avTransport.getPositionInfo()
|
val info = active.avTransport.getPositionInfo()
|
||||||
@@ -283,6 +286,7 @@ class MinstrelForwardingPlayer(
|
|||||||
trackUri = info.trackUri,
|
trackUri = info.trackUri,
|
||||||
trackNumber = info.track,
|
trackNumber = info.track,
|
||||||
)
|
)
|
||||||
|
maybeSyncLocalCursor(info.track)
|
||||||
val transport = active.avTransport.getTransportInfo()
|
val transport = active.avTransport.getTransportInfo()
|
||||||
when (transport.state) {
|
when (transport.state) {
|
||||||
TransportState.PLAYING -> {
|
TransportState.PLAYING -> {
|
||||||
@@ -305,6 +309,22 @@ class MinstrelForwardingPlayer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun maybeSyncLocalCursor(sonosTrack: Int) {
|
||||||
|
if (holder.target.value != null) return
|
||||||
|
if (sonosTrack <= 0) return
|
||||||
|
val sonosIdx = sonosTrack - 1
|
||||||
|
handler.post {
|
||||||
|
val localIdx = delegate.currentMediaItemIndex
|
||||||
|
if (sonosIdx > localIdx && sonosIdx < delegate.mediaItemCount) {
|
||||||
|
Timber.w(
|
||||||
|
"UPnP cursor catch-up: local=%d -> sonos=%d",
|
||||||
|
localIdx, sonosIdx,
|
||||||
|
)
|
||||||
|
delegate.seekTo(sonosIdx, 0L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val POLL_INTERVAL_MS = 1_000L
|
const val POLL_INTERVAL_MS = 1_000L
|
||||||
const val NON_PLAYING_CONFIRM = 2
|
const val NON_PLAYING_CONFIRM = 2
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ class RemotePlayerState @Inject constructor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val DROP_THRESHOLD = 3
|
// ~30 seconds of consecutive poll failures before declaring the route
|
||||||
|
// dropped. Bumped from 3 because screen-off WiFi sleep / brief Doze
|
||||||
|
// can stall socket I/O for several seconds without the renderer
|
||||||
|
// actually being unreachable -- a 3-failure drop kicked us back to
|
||||||
|
// local audio every time the phone went into a pocket.
|
||||||
|
const val DROP_THRESHOLD = 30
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user