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
+11 -10
View File
@@ -1036,20 +1036,21 @@ The tasks below are ordered by dependency and risk. Each task builds on the prev
**Goal:** play video files (MP4, MOV, AVI) into scenes — starting soon videos, BRB loops, intro/outro clips.
### Status: 🔶 In progress — Increment A shipped (model + persistence); Increment B (decoder) shipped; slices 6–12 open
### Status: 🔶 In progress — Increment A (model + persistence) + Increment B (decoder) shipped; slice 1 step 3 (manager + `IMediaFrameSource`) shipped; resolver/preview wire-in + pacing + loop open
1. ✅ `MediaSourceType` enum: `Video`, `Audio` (audio-only files via media source)
2. ✅ `SourceType.MediaSource` addition to the enum
3. ✅ `MediaSourceModel`: `MediaPath`, `MediaIsLooping`, `MediaVolume` (0-1), `MediaPlaybackState` — persisted in LayoutStore (schema migration + SELECT/INSERT + round-trip test)
4. ✅ `MediaVideoSource`: FFmpeg raw video decoder → `VideoFrame` pipeline — spawns ffmpeg `-f rawvideo -pix_fmt bgra`, drains the pipe via pure `RawVideoFrameReader` (`Services/RawVideoFrameReader.cs`), raises `FrameReady` per frame; decode process behind `IDecodeProcess`/`FfmpegDecodeProcess` seam (binary-stdout mirror of `IEncoderProcess`). Shipped 2026-08-31.
5. 🔶 Frame capture from video file, output BGRA8 frames — decode + BGRA8 emit done; **native-FPS pacing still open** (ffprobe probe, wired by the caller; frames currently emit as fast as the pipe produces).
6. ☐ Wire into `FramePump` resolver — `Source { Type: MediaSource }` → latest video frame
7. ☐ Wire into `SceneCompositor` — render media source as an image element at its position/size
8. ☐ Loop control — `IsLooping` property, restart on end
9. ☐ Volume control — per-source volume slider for audio playback
10. ☐ UI: file picker (filtered to video formats), loop toggle, volume slider
11. ☐ Schema migration for media source settings (file path, loop, volume)
12. ☐ Tests: video frame extraction, loop behavior, volume scaling, file validation
4. ✅ `MediaVideoSource` implements `IMediaFrameSource`: FFmpeg raw video decoder → `VideoFrame` pipeline — spawns ffmpeg `-f rawvideo -pix_fmt bgra`, drains the pipe via pure `RawVideoFrameReader` (`Services/RawVideoFrameReader.cs`), raises `FrameAvailable`/`Completed`; lifecycle is `StartAsync`/`StopAsync`; decode process behind `IDecodeProcess`/`FfmpegDecodeProcess` seam (binary-stdout mirror of `IEncoderProcess`). Shipped 2026-08-31; refactored onto `IMediaFrameSource` with this slice.
5. ✅ `IMediaFrameSource` + `MediaVideoSourceManager` (`Services/IMediaFrameSource.cs`, `Services/MediaVideoSourceManager.cs`): app-wide decode-session ownership refcounted by `MediaPath` with a `Func<string, IMediaFrameSource?>` factory seam; `AcquireAsync`/`ReleaseAsync`/`ReleaseAllAsync`/`GetLatestFrame`; coalesces frames onto the UI dispatcher onto a single shared `WriteableBitmap` per file (mirror of `ScreenCaptureManager`); `MediaFailed` + `PreviewBitmapChanged`. Unit tests + one integration test (bitmap share/coalesce) — `MediaVideoSourceManagerTests`, 5/5 pass.
6. 🔶 Native-FPS pacing — frames currently emit as fast as the pipe produces them; ffprobe FPS probe + per-frame pacing seam still open (slice 2).
7. ☐ Wire into `FramePump` resolver — `Source { Type: MediaSource }` → latest video frame
8. ☐ Wire into `SceneCompositor` — render media source as an image element at its position/size
9. ☐ Loop control — `IsLooping` property, restart on end
10. ☐ Volume control — per-source volume slider for audio playback
11. ☐ UI: file picker (filtered to video formats), loop toggle, volume slider
12. ☐ Schema migration for media source settings (file path, loop, volume)
13. ☐ Tests: video frame extraction, loop behavior, volume scaling, file validation
### Design decisions