using System; using Xunit; using ytLive.Models; using ytLive.Services; using ytLive.Services.Compositor; namespace ytLive.Tests; /// /// "Preview looks great" only proves the preview. The recording and the stream go through /// , which is a different path: the preview pane draws each layer as its /// own WPF image, while the output blits every layer into one frame. This proves the two things that /// matter for a KEYED video layer in the output — the keyed (alpha 0) pixels let the layer beneath /// through, and the opaque part of the clip covers it. /// public class MediaCompositeOutputTests { private static VideoFrame Solid(int w, int h, byte r, byte g, byte b, byte a = 255) { var pixels = new byte[w * h * 4]; for (var i = 0; i < pixels.Length; i += 4) { pixels[i] = b; pixels[i + 1] = g; pixels[i + 2] = r; pixels[i + 3] = a; } return new VideoFrame(w, h, pixels); } [Fact] public void KeyedVideoLayer_LetsTheBackgroundThrough_AndCoversTheRest() { // Background: red. Video layer at (640,340) 640×400: transparent left half (keyed out), // opaque blue right half (the subject). var background = Solid(1920, 1080, 255, 0, 0); var keyedClip = Solid(640, 400, 0, 0, 255, a: 0); var half = 640 / 2; for (var y = 0; y < 400; y++) for (var x = half; x < 640; x++) { var i = (y * 640 + x) * 4; keyedClip.BgraPixels[i + 3] = 255; } var bg = new Source { Type = SourceType.DisplayCapture, IsBackground = true, CaptureKey = "monitor:0" }; var video = new Source { Type = SourceType.MediaSource, MediaPath = "clip.mp4", X = 640, Y = 340, Width = 640, Height = 400 }; var scene = new Scene { Name = "Live", Elements = { bg, video } }; VideoFrame? FrameFor(SceneElement element) => element switch { Source { IsBackground: true } => background, Source { IsMediaSource: true } => keyedClip, _ => null, }; var options = new CompositorOptions { SourceRectX = 0, SourceRectY = 0, SourceRectWidth = 1920, SourceRectHeight = 1080, OutputWidth = 1920, OutputHeight = 1080, }; var output = new SceneCompositor().Render(scene, FrameFor, null, options); // Keyed half: the background must show through — this is what "green screen removed" means // in the recording, not just in the pane. AssertColor(output, 700, 400, 255, 0, 0); AssertColor(output, 950, 600, 255, 0, 0); // Subject half: the clip's own pixels cover the background. AssertColor(output, 1000, 400, 0, 0, 255); AssertColor(output, 1250, 700, 0, 0, 255); // Outside the layer: untouched background. AssertColor(output, 100, 100, 255, 0, 0); } private static void AssertColor(VideoFrame frame, int x, int y, byte r, byte g, byte b) { var i = (y * frame.Width + x) * 4; Assert.Equal((b, g, r), (frame.BgraPixels[i], frame.BgraPixels[i + 1], frame.BgraPixels[i + 2])); } }