fix(qbz-player): seek bar and cache overlay for CMAF streaming

The CMAF path passed duration_secs: 0 into play_streaming_dynamic, and
the streaming position timer clamps current_position to the duration
it was given — so elapsed, remaining, progress and auto-advance all
froze at zero. Duration is now derived from the CMAF segment table's
per-segment sample counts.

PlaybackEvent also gained buffer_progress (the streaming buffer fill
the audio thread already tracked but never surfaced); the poll loop
feeds it to the seek-bar cache overlay.

cc
This commit is contained in:
vicrodh committed 2026-05-19 01:10:03 -06:00
1 parent 74ab627c40
commit 655a90f400
2 files changed
+25 -1

No files matched your search

+22 -1
View File
@@ -886,6 +886,10 @@ pub struct PlaybackEvent {
/// (pipewire/pulse/cpal) where bit-perfect is not guaranteed.
#[serde(default)]
pub bit_perfect_mode: Option<BitPerfectMode>,
/// Streaming buffer progress (0.0..1.0). `None` when not streaming or
/// the track is fully buffered — drives the seek-bar cache overlay.
#[serde(default)]
pub buffer_progress: Option<f32>,
}
/// Shared state between main thread and audio thread
@@ -3311,6 +3315,22 @@ impl Player {
.map(|s| s.byte_len as u64)
.sum::<u64>();
// Track duration from the CMAF segment table. The streaming
// path's position timer clamps `current_position` to the
// duration it was given, so a zero here freezes the seek bar
// at 0:00 and blocks auto-advance — derive the real value
// from the per-segment sample counts.
let total_samples: u64 = cmaf_info
.segment_table
.iter()
.map(|s| s.sample_count as u64)
.sum();
let duration_secs = if sample_rate > 0 {
total_samples / sample_rate as u64
} else {
0
};
// Estimate speed from the init segment fetch (conservative:
// assume ~10 MB/s if init was too fast to measure reliably).
let speed_mbps = if cmaf_info.init_fetch_ms > 0 {
@@ -3338,7 +3358,7 @@ impl Player {
bit_depth,
total_flac_size,
speed_mbps,
0, // duration determined by decoder
duration_secs,
0, // no resume offset
)?;
@@ -3830,6 +3850,7 @@ impl Player {
gapless_ready: self.state.is_gapless_ready(),
gapless_next_track_id: self.state.get_gapless_next_track_id(),
bit_perfect_mode: self.state.get_bit_perfect_mode(),
buffer_progress: self.state.get_buffer_progress(),
}
}
}