mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
feat(playback): stream buffering UI, M4A moov-at-end streaming, hot-cache spill (#737)
* feat(playback): stream buffering UI, ranged M4A tail prefetch, demuxer fix Defer seekbar/progress until HTTP stream is armed for both legacy and RangedHttpSource; show buffering overlay on cover art. Add MP4 tail prefetch and Symphonia isomp4 bounded-mdat/moov-at-EOF probing so moov-at-end M4A can start without reading the full mdat. * feat(hot-cache): spill large ranged streams to disk for promote When a ranged HTTP download completes above the 64 MiB RAM promote cap, write the existing buffer once to app-data stream-spill/ and register it for hot-cache promote (rename) and replay via fetch_data. Analysis seeds from the spill file up to the local-file cap (512 MiB). * fix(ui): stream buffering — grayscale cover and static clock icon Desaturate player and queue cover art while isPlaybackBuffering; keep a non-animated clock overlay for visibility without the spinning animation. * fix(playback): review follow-up — tests, i18n, spill cleanup, changelog Clippy and test layout fixes; stream spill orphan cleanup on startup; buffering flag guard in progress handler; bufferingStream in all player locales; CHANGELOG and contributor credits for stream/M4A work. * docs: attribute stream buffering and M4A streaming to PR #737 * test(audio): avoid create_engine in stream spill unit test CI runners have no audio output device; test spill take/consume via the Mutex slot only, matching install_stream_completed_spill tests.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
|
||||
mod icy;
|
||||
mod local_file;
|
||||
mod mp4;
|
||||
mod radio;
|
||||
mod ranged_http;
|
||||
mod reader;
|
||||
@@ -32,7 +33,8 @@ pub(crate) const RADIO_BUF_CAPACITY: usize = 256 * 1024;
|
||||
pub(crate) const TRACK_STREAM_MIN_BUF_CAPACITY: usize = 1024 * 1024;
|
||||
/// Cap ring buffer growth when content-length is known.
|
||||
pub(crate) const TRACK_STREAM_MAX_BUF_CAPACITY: usize = 32 * 1024 * 1024;
|
||||
/// Max bytes kept in memory to promote a completed streamed track for fast replay/seek recovery.
|
||||
/// Max bytes kept in RAM (`stream_completed_cache`) for fast replay; larger completed
|
||||
/// ranged streams are spilled under app-data `stream-spill/` for hot-cache promote.
|
||||
pub(crate) const TRACK_STREAM_PROMOTE_MAX_BYTES: usize = 64 * 1024 * 1024;
|
||||
/// Hot/offline `psysonic-local://` files are read from disk for waveform/LUFS seeding — not the
|
||||
/// same heap pressure as retaining a full HTTP capture. FLAC/DSD tracks often exceed 64 MiB;
|
||||
@@ -42,7 +44,26 @@ pub(crate) const LOCAL_FILE_PLAYBACK_SEED_MAX_BYTES: usize = 512 * 1024 * 1024;
|
||||
pub(crate) const TRACK_STREAM_MAX_RECONNECTS: u32 = 3;
|
||||
/// Seconds at stall threshold while paused before hard-disconnect.
|
||||
pub(crate) const RADIO_HARD_PAUSE_SECS: u64 = 5;
|
||||
/// AudioStreamReader timeout: if no audio bytes arrive for this long → EOF.
|
||||
/// Live radio: if no audio bytes arrive for this long → EOF.
|
||||
pub(crate) const RADIO_READ_TIMEOUT_SECS: u64 = 15;
|
||||
/// On-demand tracks (`track-stream`, `RangedHttpSource`): allow long gaps while a
|
||||
/// large file is still downloading (format probe may read/seek ahead of the filler).
|
||||
pub(crate) const TRACK_READ_TIMEOUT_SECS: u64 = 120;
|
||||
/// HTTP track paths (`AudioStreamReader`, `RangedHttpSource`): minimum linear
|
||||
/// download before audible playback and seekbar progress (demux probe may read
|
||||
/// far ahead of the play cursor).
|
||||
pub(crate) const TRACK_STREAM_PLAY_START_BYTES: u64 = 384 * 1024;
|
||||
|
||||
/// Arm deferred playback / progress once enough of the file is buffered.
|
||||
pub(crate) fn maybe_arm_stream_playback(downloaded: u64, playback_armed: &std::sync::atomic::AtomicBool) {
|
||||
use std::sync::atomic::Ordering;
|
||||
if !playback_armed.load(Ordering::Relaxed) && downloaded >= TRACK_STREAM_PLAY_START_BYTES {
|
||||
playback_armed.store(true, Ordering::SeqCst);
|
||||
crate::app_deprintln!(
|
||||
"[stream] playback armed after {} KiB buffered",
|
||||
downloaded / 1024
|
||||
);
|
||||
}
|
||||
}
|
||||
/// Sleep interval when ring buffer is empty (prevents CPU spin).
|
||||
pub(crate) const RADIO_YIELD_MS: u64 = 2;
|
||||
|
||||
Reference in New Issue
Block a user