dev → main: Android UPnP/Sonos transport parity + server stream URL extension #79
@@ -206,6 +206,13 @@ class MinstrelForwardingPlayer(
|
|||||||
pollJob?.cancel()
|
pollJob?.cancel()
|
||||||
if (active != null) {
|
if (active != null) {
|
||||||
Timber.w("UPnP active: %s -- pollLoop starting", active.routeName)
|
Timber.w("UPnP active: %s -- pollLoop starting", active.routeName)
|
||||||
|
// Pause the wrapped ExoPlayer so we are not playing local audio
|
||||||
|
// simultaneously with the remote renderer. handler.post targets the
|
||||||
|
// application looper, so this runs on the same thread that processes
|
||||||
|
// our override calls -- no race with the pause() override branching
|
||||||
|
// to SOAP (holder.active is already non-null by the time this post
|
||||||
|
// fires, but delegate.pause() bypasses the override entirely).
|
||||||
|
handler.post { delegate.pause() }
|
||||||
pollJob = scope.launch { pollLoop(active) }
|
pollJob = scope.launch { pollLoop(active) }
|
||||||
// Do NOT pre-queue here: selectUpnp's SetAV+Play SOAP may not have
|
// Do NOT pre-queue here: selectUpnp's SetAV+Play SOAP may not have
|
||||||
// landed yet. Initial pre-queue fires from pollLoop once the first
|
// landed yet. Initial pre-queue fires from pollLoop once the first
|
||||||
@@ -225,6 +232,14 @@ class MinstrelForwardingPlayer(
|
|||||||
// SetAV+Play from selectUpnp (trackUri lands in the first
|
// SetAV+Play from selectUpnp (trackUri lands in the first
|
||||||
// successful poll). Firing from onActiveChanged would race
|
// successful poll). Firing from onActiveChanged would race
|
||||||
// with selectUpnp's SOAP calls.
|
// with selectUpnp's SOAP calls.
|
||||||
|
if (!initialPreQueueDone) {
|
||||||
|
Timber.w(
|
||||||
|
"UPnP poll OK: route=%s trackUri=%s posMs=%d",
|
||||||
|
active.routeName,
|
||||||
|
remoteState.currentTrackUri,
|
||||||
|
remoteState.positionMs,
|
||||||
|
)
|
||||||
|
}
|
||||||
if (!initialPreQueueDone && remoteState.currentTrackUri.isNotBlank()) {
|
if (!initialPreQueueDone && remoteState.currentTrackUri.isNotBlank()) {
|
||||||
Timber.w("UPnP initial pre-queue for %s", active.routeName)
|
Timber.w("UPnP initial pre-queue for %s", active.routeName)
|
||||||
preQueueNext(active)
|
preQueueNext(active)
|
||||||
|
|||||||
+7
-6
@@ -258,12 +258,13 @@ class OutputPickerController @Inject constructor(
|
|||||||
return@withLock
|
return@withLock
|
||||||
}
|
}
|
||||||
val rendering = renderingClientFor(effectiveRoute.id)
|
val rendering = renderingClientFor(effectiveRoute.id)
|
||||||
// Pause local FIRST while holder.active is still null so the pause()
|
// Local pause is handled by MinstrelForwardingPlayer.onActiveChanged
|
||||||
// call routes to ExoPlayer, not SOAP. After holder.set() below,
|
// via handler.post { delegate.pause() } -- called immediately after
|
||||||
// ForwardingPlayer.pause() would translate this into AVTransport.Pause()
|
// holder.set() fires. That path bypasses the SOAP-routing override, so
|
||||||
// -- the opposite of what we want, since we are about to tell Sonos
|
// there is no race between pausing ExoPlayer and starting the remote
|
||||||
// to start playing.
|
// renderer. Do NOT call playerController.pause() here: it dispatches
|
||||||
playerController.pause()
|
// through Handler.post on the application looper, meaning it runs after
|
||||||
|
// holder.active is non-null and the override would route it to SOAP.
|
||||||
selectedUpnpRouteIdInternal.value = effectiveRoute.id
|
selectedUpnpRouteIdInternal.value = effectiveRoute.id
|
||||||
activeUpnpHolder.set(
|
activeUpnpHolder.set(
|
||||||
ActiveUpnp(
|
ActiveUpnp(
|
||||||
|
|||||||
Reference in New Issue
Block a user