Commit Graph

1116 Commits

Author SHA1 Message Date
daniel-c-harvey 8a6acd5f5f Merge Opus single-load guard (instrument double-load) into streaming-overhaul 2026-06-24 23:09:08 -04:00
daniel-c-harvey be9de8d77c Collapse duplicate same-track streaming loads to enforce one load per play
A second LoadTrackStreaming for the same in-flight track (UI double-fire, queue re-entry, or JS false-end auto-advance) is now dropped; a different-track load still supersedes. Targets the Opus double-load; keeps load-gen diagnostics.
2026-06-24 23:08:58 -04:00
daniel-c-harvey d686fe48ce Apply stream-quality change live by reloading at current position
Finish the Settings "Apply" behavior so changing streaming quality mid-track
switches format immediately instead of only persisting the cookie for the next
play.

- SettingsMenu reads the AudioPlayerProvider cascade and threads the player into
  StreamQualitySetting as an explicit parameter (the MudMenu panel portals to
  MudPopoverProvider, outside the cascade scope, so a [CascadingParameter] there
  lands null). StreamQualitySetting's Apply persists the cookie, then asks the
  player to reload preserving position.

- Add a "load at timestamp" path to the player rather than restart-from-0-then-
  seek (which audibly played the start and raced the just-started scheduler into
  a crash). ReloadPreservingPositionAsync loads the track in the newly-resolved
  format beginning DIRECTLY at the saved position:
    * new JS resolveStreamOffset(position) resolves the file-absolute byte offset
      with no playback/buffer state (Opus from its sidecar immediately; WAV after
      a header probe),
    * StartFromPositionAsync converges onto the existing seek/refill loop
      (RunSegmentedStreamAsync with a non-null seekPosition) so the decoder
      reinitializes for a header-less Range continuation and starts playback at
      the target,
    * ProbeHeaderAsync feeds the byte-0 segment to the decoder WITHOUT starting
      playback until the WAV header parses (bounded by 256 KB); the probe buffers
      are dropped by the continuation's clearForSeek, so nothing is audible.

- IStreamingPlayerService gains ReloadPreservingPositionAsync; the QueueService
  test fake implements it.
2026-06-24 22:55:03 -04:00
daniel-c-harvey 7adc35dd5d docs: record streaming raw-queue and per-chunk memory bounds
Document the three-layer memory bound (raw queue, decoded queue, network) in the streaming seam after the HW-accel-off OOM fix landed on streaming-overhaul.
2026-06-24 22:09:01 -04:00
daniel-c-harvey 8206c0bdaf Merge streaming OOM cross-format memory bound into streaming-overhaul 2026-06-24 19:54:12 -04:00
daniel-c-harvey aeec582957 Bound decoded forward fill per chunk in streaming read loop
The inter-segment back-pressure gate matched WAV byte density but let a 4MB Opus segment (~100s at 320kbps) decode eagerly into main-process RAM, OOMing the tab with HW accel off. Drain per chunk past high-water, gated on playback start. Adds load-generation diagnostics for the double-load hypothesis.
2026-06-24 19:50:33 -04:00
daniel-c-harvey 036ee1f78e docs: record Phase 21 (windowed streaming) as landed; note Direction A to B pivot
Move Phase 21 from PLAN to COMPLETED with the as-built record, and annotate
the spec that Direction B shipped after WASM fetch buffering defeated A.
2026-06-24 16:05:30 -04:00
daniel-c-harvey c1e6930c70 Merge Phase 21 Direction B (Range-segmented forward fetch) into streaming-overhaul 2026-06-24 15:54:45 -04:00
daniel-c-harvey cc9d20184d Restore IsStreamingMode on recovery; guard superseded-load else-branch
RecoverFromFailedRefill now sets IsStreamingMode=true so the in-place
seek-retry route isn't wedged. The generic-catch unload path is gated on
the loadCts identity, so a superseded load no longer clobbers a newer
operation's state.
2026-06-24 15:37:38 -04:00
daniel-c-harvey e7762e35e8 Fix truncated-segment and mid-stream failure paths in segmented loop
cursor>=totalLength is the sole forward-EOF test; a short non-final body is
a truncation error, not EOF. Mid-stream forward-load failures now invoke
RecoverFromFailedRefill so the scheduler halts instead of a silent false end.
Two regression tests pin both paths.
2026-06-24 15:16:46 -04:00
daniel-c-harvey 11faf8888f Phase 21 Direction B: bound network memory via Range-segmented forward fetch
Replace the open-ended forward GET with sequential bounded bytes=start-end
segments, the next fetched only when the scheduler drains below low-water,
so the browser holds ~one segment regardless of file size. Seek converges
on the same loop. Strip BP-DIAG.
2026-06-24 13:20:37 -04:00
daniel-c-harvey def297e7d9 Merge Phase 21.4 streaming fix + back-pressure diagnostics into streaming-overhaul 2026-06-24 09:02:34 -04:00
daniel-c-harvey 369cb86437 Add [BP-DIAG] back-pressure instrumentation for Phase 21.4 browser run
Temporary, grep-tagged diagnostics at the read-loop pause, the scheduler
latch, and the chunk-result path to show whether ProductionPaused latches,
reaches C#, and parks the loop. Strip once the cause is confirmed.
2026-06-24 09:00:38 -04:00
daniel-c-harvey c7629c15a4 Enable WASM response streaming on audio media fetch
Without SetBrowserResponseStreamingEnabled the browser buffers the whole
body before yielding, so the Phase 21.2 read-loop pause backpressured an
already-downloaded payload. Set it on both the initial and seek/refill
requests; safe no-op on the SSR path.
2026-06-24 08:45:33 -04:00
daniel-c-harvey 9c95a5f23e Merge Phase 21.3 (seek-back-past-window refill + AC6 recovery) into streaming-overhaul 2026-06-23 23:58:51 -04:00
daniel-c-harvey b93881cd66 21.3 review fixes: guard superseded-seek failures; restore post-recovery retry
C6/AC8: IsStillActiveSeek() predicate guards all three SeekBeyondBuffer
failure exits, so a superseded seek never recovers over a newer seek's
state. AC6: empty scheduler routes to seekBeyondBuffer so a same-target
retry (seek or play) refetches instead of no-oping.
2026-06-23 23:55:28 -04:00
daniel-c-harvey af4cb186f3 Phase 21.3: seek-back-past-window refill + clean refill-failure recovery
Seek-back past the retained tail reuses the existing seek-beyond-buffer
Range path (per-path resolver). A failed refill now halts the scheduler
into a paused-but-loaded state (AC6) instead of a silent false end.
2026-06-23 23:43:17 -04:00
daniel-c-harvey 121983b19d Merge Phase 21.2 (streaming back-pressure) into streaming-overhaul 2026-06-23 23:29:35 -04:00
daniel-c-harvey 29e8747c69 21.2 review remediation: pause-spin, OQ7 comment, rename, C2 cross-check
Skip the back-pressure interop poll while paused (UC5). Document complete()
draining the stash in full by design. Rename scheduler isProductionPaused to
evaluateProductionPause (latch-advancing); window exposure name unchanged.
2026-06-23 23:28:42 -04:00
daniel-c-harvey 518479e7ae Phase 21.2: back-pressure to bound the unplayed decoded region
Shared scheduler fill signal (forward water-marks + hard byte cap) pauses
the C# read loop above high-water and, for Opus, stops the demux/decode
feed so WebCodecs queues stay near-empty. Routes through the existing
cancellation discipline; releases the latch on clear/seek.
2026-06-23 23:16:08 -04:00
daniel-c-harvey a2becf45d6 Merge Phase 21.1 (PlaybackScheduler partial eviction) into streaming-overhaul 2026-06-23 22:56:48 -04:00
daniel-c-harvey 07f29a8216 Reconcile eviction comment wording; add handleSourceEnded cascade test (Phase 21.1)
The inclusive <= bound is correct; comments now say 'at or behind'. New
test drives eviction through the real onended trigger with a live mid-array
source pinning the frontier.
2026-06-23 22:49:12 -04:00
daniel-c-harvey ed606d94c7 Add partial eviction to PlaybackScheduler (Phase 21.1)
Drop already-played buffers from the front while advancing the time
anchor so position/index bookkeeping stays exact. Shared by both decode
paths, no format branch. Back-retain is a config seam for 21.2.
2026-06-23 22:39:05 -04:00
daniel-c-harvey ccf7d3dbe3 docs: reconcile Phase 21 spec with as-built Phase 18 (two decode paths)
Window both the WAV StreamDecoder and Opus WebCodecs paths feeding one PlaybackScheduler — shared eviction, per-path back-pressure; reuse the now-live index-driven Opus seek for refill. Drops stale approximate-seek language; adds OQ6/OQ7.
2026-06-23 22:01:49 -04:00
daniel-c-harvey bbcf8be677 docs: record Phase 18 (Opus low-data streaming) in COMPLETED; stage PLAN for Phase 21 2026-06-23 21:48:39 -04:00
daniel-c-harvey 8902ce4d63 Merge Opus duration/seek/visualizer fix into streaming-overhaul 2026-06-23 21:25:26 -04:00
daniel-c-harvey eb58ae4a72 Fix Opus duration reporting so seekbar and visualizer work
Surface the sidecar duration on the first Opus chunk instead of gating it on the first decoded buffers; C# locks UI Duration on chunk 1, and async WebCodecs decode left it at 0 — killing seek and the duration-gated visualizer.
2026-06-23 21:23:43 -04:00
daniel-c-harvey d80b777e9f Merge WebCodecs streaming Opus decoder (+ AC9 seek re-sync) into streaming-overhaul 2026-06-23 21:03:02 -04:00
daniel-c-harvey 5a75da1769 fix: AC9 seek fine re-sync + deterministic decoder drain (WebCodecs Opus)
Seek now trims the lead-in so playback lands at the requested time, not the page start; decoder drain polls decodeQueueSize (bounded) instead of a single timeout. Minor cleanups.
2026-06-23 20:57:05 -04:00
daniel-c-harvey 7f3fb74126 Replace broken per-segment Opus decode with WebCodecs AudioDecoder streaming pipeline 2026-06-23 17:42:06 -04:00
daniel-c-harvey d0118997b6 Merge Opus capability-probe fix into streaming-overhaul 2026-06-23 17:03:07 -04:00
daniel-c-harvey 5b78efaad4 fix: replace hand-assembled Opus probe blob with real ffmpeg/libopus output
Previous probe sample had invalid Ogg page CRC32s, so Chrome/Firefox rejected it and the capability check always returned false. New 176-byte libopus sample has verified-correct CRCs. Adds structural-validity tests.
2026-06-23 16:57:49 -04:00
daniel-c-harvey 81d4b42b72 Merge Phase 18.6 Track A (public Settings menu + streaming-quality toggle) into streaming-overhaul 2026-06-23 14:52:28 -04:00
daniel-c-harvey 77c6c42c94 remediate: replace eval cookie writes with safe JS helper + add tests (18.6 Track A)
Both SettingsCookieService and DarkModeCookieService now call window.DeepDrftSettings.setCookie (new Interop/settings/settings.ts) instead of eval. New tests cover SettingsServiceBase parse/format round-trip and the PreferenceAwareStreamingPlayerService invariant (Lossless skips probe; LowData inherits base).
2026-06-23 14:17:34 -04:00
daniel-c-harvey ab75bbf6c1 Merge Phase 18.6 Track B (CMS Opus status: backfill badge + Post-Processing) into streaming-overhaul 2026-06-23 14:09:48 -04:00
daniel-c-harvey 59f48bb8cb feature: CMS Opus status surfaces — backfill missing-N badge + upload Post-Processing phase (18.6) 2026-06-23 14:06:21 -04:00
daniel-c-harvey c63c7ca033 feature: Phase 18.6 Track A — public Settings menu + streaming-quality toggle 2026-06-23 14:06:19 -04:00
daniel-c-harvey e5366bc4ec Merge Phase 18.5 (Opus end-to-end integration + Backfill-Opus) into streaming-overhaul 2026-06-23 12:52:07 -04:00
daniel-c-harvey 2bde4908d7 Wire Opus end-to-end playback + Backfill-Opus action (Phase 18.5)
Player picks Opus when the browser can decode it and a sidecar exists (else lossless), injecting the sidecar before stream init; seek reuses the same format. Adds the Backfill-Opus bulk API endpoint + CMS action.
2026-06-23 12:39:13 -04:00
daniel-c-harvey dce5530890 Merge Phase 18.4 (OpusFormatDecoder + index-based seek) into streaming-overhaul 2026-06-23 08:48:06 -04:00
daniel-c-harvey 8afcd3784f Merge Phase 18.3 (Opus delivery transport) into streaming-overhaul 2026-06-23 08:44:53 -04:00
daniel-c-harvey 261289c1b8 feature: OpusFormatDecoder — Ogg-page-aligned segmenting, sidecar parser, accurate index-based seek (Phase 18.4) 2026-06-23 08:34:39 -04:00
daniel-c-harvey 740d01a67f feature: Phase 18.3 — Opus delivery transport (?format= stream + seek sidecar endpoint) 2026-06-23 08:34:37 -04:00
daniel-c-harvey e807ddb91b Merge Phase 18.2 (Opus format resolution + sidecar lookup contract) into streaming-overhaul 2026-06-23 07:49:28 -04:00
daniel-c-harvey 19793ba1c3 feature: Opus format resolution + sidecar lookup contract (Phase 18.2) 2026-06-23 07:45:06 -04:00
daniel-c-harvey e845dc3496 Merge ffmpeg host provisioning into streaming-overhaul 2026-06-23 07:30:21 -04:00
daniel-c-harvey ba064cc136 provision ffmpeg on DeepDrftAPI host for Opus transcode
Phase 18.1 needs ffmpeg (libopus). Add it to bootstrap.sh apt prereqs and a
preflight guard in install.sh; resolves via the systemd user unit's default
PATH (/usr/bin), no config change.
2026-06-23 07:30:13 -04:00
daniel-c-harvey b3dadbb572 Merge Phase 18.1 (Opus ingest transcode + seek-index sidecar) into streaming-overhaul 2026-06-23 07:13:46 -04:00
daniel-c-harvey 6add30a4ff fix: Wave 18.1 review — pre-skip subtraction, t=0 anchor, PreSkip in sidecar, stderr on cancel 2026-06-23 06:55:31 -04:00
daniel-c-harvey 33d6f34d8a feature: Phase 18.1 — derive Opus 320 + seek-index sidecar at ingest
Background-job transcode (ffmpeg/libopus) after source store; pure C# Ogg
walker builds the 0.5s-bucketed granule→byte seek index + captures the
OpusHead/OpusTags setup header into a per-track sidecar in a new track-opus
vault. Best-effort, additive, regenerated on replace-audio.
2026-06-23 06:30:10 -04:00