- IMediaFrameSource gains bool Looping. - MediaVideoSource ctor takes Func<IDecodeProcess> processFactory instead of a single IDecodeProcess: a System.Diagnostics.Process can't be re-Start()ed, so each loop pass creates a fresh decoder. Decode wrapped in do-while(Looping): restart on natural EOF instead of raising Completed. - Production wiring (MainViewModel media factory): passes the process factory AND FfmpegFrameRateProbe -- closes the slice-2b gap where production had no probe and therefore no pacing. - Tests: loop test (single frame re-emits across passes, Completed only when loop cleared); fakes updated for the new interface member. Media tests 12/12, build 0 warnings. Wiring Source.MediaIsLooping into the flag needs a manager-level per-path loop provider -> lands with the UI-picker (acquisition) slice. Derivative reference: looping media by restarting decode on EOF, standard in playback/overlay tooling (OBS media source repeat).
6.0 KiB
HANDOFF — Session State
Branch / Commit State
main, working through TASK 21 (media source). Local commits ahead of
origin/main (not yet pushed — user pushes at agreed checkpoints):
a1d9751— docs: ffmpeg decode-contract verification recipe + WSL CLR boundary8f49010— TASK 21 Increment B: FFmpeg rawvideo video decoder -> VideoFramef89b9f9— docs: default-device audio assumption (README + startup log)e8ff4df— TASK 22: global audio sync offset (positive delay at the mixer out)
Prior milestones pushed through origin/main: TASK 31 (3bf053a..670fe3a), plus
the full refactor series (Commit F 3107f92, Commit G 85893ea).
What's In Flight
TASK 21 (media source) — slice 1 step 3 shipped (the session-manager + interface); next open work is the VM wire-in (slice 1 step 4).
Increment A (the Source model: MediaSourceType, SourceType.MediaSource,
MediaPath/MediaIsLooping/MediaVolume/MediaPlaybackState + LayoutStore
persistence) and Increment B (the decoder MediaVideoSource) shipped earlier.
This session:
- Refactored
Services/MediaVideoSource.csonto the newServices/IMediaFrameSource.cs(Key,FrameAvailable,Completed,StartAsync/StopAsync;Start→StartAsync,FrameReady→FrameAvailable) — behavior preserved,MediaVideoSourceTestsupdated and passing. - Added
Services/MediaVideoSourceManager.cs— app-wide decode-session ownership refcounted byMediaPath,Func<string, IMediaFrameSource?>factory seam,AcquireAsync/ReleaseAsync/ReleaseAllAsync/GetLatestFrame, frames coalesced onto the UI dispatcher onto one sharedWriteableBitmapper file,MediaFailed+PreviewBitmapChanged. Structural mirror ofScreenCaptureManager. - Added
ytLive.Tests/MediaVideoSourceManagerTests.cs(5 unit + 1 integration: single shared bitmap, coalesce-to-latest). Verified: all 10 media tests pass (5 manager + 3 reader + 1 decoder integration + 1), build 0 warnings.
Slice 1 step 4 (wire-in) shipped: _mediaManager wired in the core ctor
(path => new MediaVideoSource(path, 1920, 1080, new FfmpegLocator(), new FfmpegDecodeProcess()),
dispatched preview), OnMediaPreviewBitmapChanged sets VideoImageSource on every
IsMediaSource with a matching MediaPath, a Source { IsMediaSource, MediaPath }
case in ResolveOutputFrame feeds _mediaManager.GetLatestFrame, Source.DisplaySource
routes VideoImageSource for MediaSource, and the manager is disposed on shutdown.
Lives in the new ViewModels/MainViewModel.Media.cs partial. Verified: 14/14
media + DisplaySource tests pass, build 0 warnings.
Next (slice 1 step 4's remaining open end): no session acquisition yet — nothing
AcquireAsyncs a media path, so no frames flow until the UI picker (a later slice)
starts a session on add and releases on remove.
Slice 2a shipped: the native-FPS probe seam (not yet wired into pacing).
Services/FfmpegFrameRateParser.cs (pure — prefers avg_frame_rate= then
r_frame_rate=, rational N/N/M, unknown→null), Services/FrameRateProbe.cs
(IFrameRateProbe + FfmpegFrameRateProbe — derives sibling ffprobe.exe from
the located ffmpeg dir, reuses the IDecodeProcess seam for the ffprobe
subprocess text, null if ffprobe absent), and FfmpegLocator.ProbeFileName
(ffprobe.exe) + conditional extraction alongside ffmpeg.exe. Tests:
ytLive.Tests/FfmpegFrameRateParserTests.cs (6 pure units + 1 probe integration
via fake locator/process); FfmpegLocatorTests still green — 15/15 pass, build
0 warnings. Slice 2b shipped too: MediaVideoSource now takes optional
IFrameRateProbe? + Func<TimeSpan,CancellationToken,Task>? delay seams,
probes FPS once in RunAsync, and paces each emitted frame by 1/fps (unknown/
absent probe → no pacing). New test MediaVideoSource_PacesFramesByProbedFps
(fake probe + recording delay, one delay per frame ≈1ms). Media tests 5/5,
build 0 warnings.
Next (slice 3): loop control — SHIPPED. MediaVideoSource takes a
Func<IDecodeProcess> process factory (a single Process can't be re-Start()ed,
so each loop pass creates a fresh decoder) and wraps the decode in a
do…while (Looping) restart, keyed on the new IMediaFrameSource.Looping flag;
Production wiring in MainViewModel (media factory) now passes the factory AND
FfmpegFrameRateProbe (closing the slice-2b gap where production had no probe →
no pacing). Loop test MediaVideoSource_LoopsUntilLoopDisabled (single frame
re-emits, Completed only when loop cleared) — media tests 12/12 (6 source + 6
manager), build 0 warnings. Open: wiring Source.MediaIsLooping into
IMediaFrameSource.Looping needs a manager-level per-path loop provider — lands
with the UI picker (acquisition) slice.
Landmine: a stale testhost can lock ytLive.Tests's ytLive.dll and break
dotnet build of the test project — kill it first (cmd.exe /c "taskkill /PID N /F")
on MSB3027. Full-suite vstest hangs headless (RealAppHost/WASAPI) — only filtered
pure tests run in WSL; user verifies the GUI suites on native Windows PowerShell
(which may also hang if WASAPI startup blocks — pre-existing, not this change).
Next
- Commit slice 3 (this unit): loop mechanism + process factory + probe wiring + tests + docs.
- UI picker slice: acquire/release media sessions on add/remove AND wire
Source.MediaIsLooping→IMediaFrameSource.Looping(manager-level per-path loop provider). - Slice 3: loop control (restart decode on
CompletedwhenMediaIsLooping). - UI picker slice: acquire/release media sessions on add/remove (the remaining slice-1-step-4 open end).
- Await user push checkpoint for the pending TASK 22/21 commits.
The directive (2026-08-31, user)
Rewrite the project into functional components to aid AI retrieval —
Services/ChatOverlayLayer.cs / SceneGraph.cs style (owner-state extraction),
not line-count chasing. Audio assumption is now an explicit contract (README
"Audio Assumption"): the app uses system defaults, never fights Windows device
locking, and does not debug user audio issues — OS's problem, not ours.