fix: MPRIS Position is now exact at READ time, not as fresh as the last push
The owner asked whether the remaining ~1 s was the sweet spot. It was not — it was an artefact of fixing this with a push instead of a getter. MPRIS `Position` is read on demand, so what matters is the answer when the client ASKS, not when the app last spoke. The backend stored a snapshot, so the answer was only ever as fresh as the last push, and the only lever left was polling faster — which would have meant a second timer the perf rules refuse. Storing the instant alongside the value removes the tradeoff: the getter returns `position + elapsed` while Playing, exact to the microsecond however rarely the app pushes. The 1 Hz push stops being the clock and becomes what it should have been all along — a re-anchor against drift. Every write re-stamps, including a status change that carries no position: without that the base would keep running across a pause and the clock would count while the music did not. cc
This commit is contained in:
1 file changed
+40
-3
@@ -43,7 +43,16 @@ struct State {
|
||||
metadata: Metadata,
|
||||
status: MprisStatus,
|
||||
volume: Volume,
|
||||
/// The last position we were TOLD, and when we were told it.
|
||||
///
|
||||
/// MPRIS `Position` is read on demand, so what matters is the answer at
|
||||
/// READ time, not at push time. Storing the instant alongside lets the
|
||||
/// getter extrapolate — `position + (now - stamped)` while Playing — which
|
||||
/// is exact to the microsecond no matter how often the app pushes. Without
|
||||
/// it the answer is only as fresh as the last push, and the app would have
|
||||
/// to choose between a stale clock and a faster poll it does not want.
|
||||
position: Time,
|
||||
stamped: std::time::Instant,
|
||||
}
|
||||
|
||||
/// Update commands sent from the app to the server thread.
|
||||
@@ -255,7 +264,15 @@ impl PlayerInterface for QbzMpris {
|
||||
Ok(())
|
||||
}
|
||||
async fn position(&self) -> fdo::Result<Time> {
|
||||
Ok(self.state.lock().unwrap().position)
|
||||
let st = self.state.lock().unwrap();
|
||||
// Extrapolate while playing; a paused or stopped player has not moved
|
||||
// since it was stamped.
|
||||
if st.status == MprisStatus::Playing {
|
||||
let elapsed = st.stamped.elapsed().as_micros() as i64;
|
||||
Ok(Time::from_micros(st.position.as_micros() + elapsed))
|
||||
} else {
|
||||
Ok(st.position)
|
||||
}
|
||||
}
|
||||
async fn minimum_rate(&self) -> fdo::Result<PlaybackRate> {
|
||||
Ok(1.0)
|
||||
@@ -295,6 +312,19 @@ async fn apply(server: &Server<QbzMpris>, state: &Arc<Mutex<State>>, update: Upd
|
||||
st.status = status;
|
||||
if let Some(p) = position {
|
||||
st.position = p;
|
||||
st.stamped = std::time::Instant::now();
|
||||
} else {
|
||||
// A status change with no position still re-anchors: the
|
||||
// extrapolation base must not keep running across a pause.
|
||||
st.position = Time::from_micros(
|
||||
st.position.as_micros()
|
||||
+ if st.status == MprisStatus::Playing {
|
||||
st.stamped.elapsed().as_micros() as i64
|
||||
} else {
|
||||
0
|
||||
},
|
||||
);
|
||||
st.stamped = std::time::Instant::now();
|
||||
}
|
||||
}
|
||||
let _ = server
|
||||
@@ -306,10 +336,16 @@ async fn apply(server: &Server<QbzMpris>, state: &Arc<Mutex<State>>, update: Upd
|
||||
let _ = server.properties_changed([Property::Volume(v)]).await;
|
||||
}
|
||||
Update::Position(p) => {
|
||||
state.lock().unwrap().position = p;
|
||||
let mut st = state.lock().unwrap();
|
||||
st.position = p;
|
||||
st.stamped = std::time::Instant::now();
|
||||
}
|
||||
Update::Seeked(p) => {
|
||||
state.lock().unwrap().position = p;
|
||||
{
|
||||
let mut st = state.lock().unwrap();
|
||||
st.position = p;
|
||||
st.stamped = std::time::Instant::now();
|
||||
}
|
||||
let _ = server.emit(Signal::Seeked { position: p }).await;
|
||||
}
|
||||
}
|
||||
@@ -340,6 +376,7 @@ pub fn spawn(on_event: EventCb) -> Option<LinuxHandle> {
|
||||
status: MprisStatus::Stopped,
|
||||
volume: 1.0,
|
||||
position: Time::ZERO,
|
||||
stamped: std::time::Instant::now(),
|
||||
}));
|
||||
let imp = QbzMpris {
|
||||
on_event,
|
||||
|
||||
Reference in new issue
Block a user