TASK 21 slice 1 step 3: IMediaFrameSource + MediaVideoSourceManager

Refactor MediaVideoSource onto IMediaFrameSource (Key/FrameAvailable/Completed/
StartAsync/StopAsync, renaming Start->StartAsync, FrameReady->FrameAvailable),
behavior preserved. Add MediaVideoSourceManager: app-wide decode-session owner
refcounted by MediaPath with Func<string,IMediaFrameSource?> factory seam,
Acquire/Release/ReleaseAll/GetLatestFrame, coalescing each file's frames onto
the UI dispatcher onto one shared WriteableBitmap; MediaFailed + PreviewBitmapChanged.

Mirrors ScreenCaptureManager (screen-capture session ownership + dispatcher
coalescing), per the codebase precedent and derivative-work rule.

Tests: MediaVideoSourceManagerTests (5 unit + 1 integration: single shared
bitmap, coalesce-to-latest); MediaVideoSourceTests updated for renames.
10/10 media tests pass, build 0 warnings. Docs (TASKS/HANDOFF/ai.md) updated.
This commit is contained in:
2026-08-31 19:22:02 -07:00
parent a1d9751a6f
commit 480ead0fc7
8 changed files with 557 additions and 56 deletions
+2 -2
View File
@@ -66,10 +66,10 @@ public class MediaVideoSourceTests
var seen = new List<byte[]>();
var completed = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
source.FrameReady += f => seen.Add(((byte[])f.BgraPixels.Clone()));
source.FrameAvailable += f => seen.Add(((byte[])f.BgraPixels.Clone()));
source.Completed += () => completed.TrySetResult(true);
source.Start();
await source.StartAsync();
await completed.Task.WaitAsync(TimeSpan.FromSeconds(5));
Assert.Equal(2, seen.Count);