namespace ytLive.Services;
///
/// A normalized CPU frame (32bpp BGRA, tightly packed). The capture path hands
/// these to the UI thread, which copies them into the shared WriteableBitmap.
/// Deliberately the only pixel type the rest of the app knows about — every
/// future capture source (screen, background-removed webcam) feeds the same seam.
///
public sealed class VideoFrame
{
public int Width { get; }
public int Height { get; }
public byte[] BgraPixels { get; }
public int Stride => Width * 4;
/// Producer frame epoch — a monotonically increasing number stamped by
/// sources that RECYCLE their pixel buffers (the screen-capture ring, take-11 spike
/// fix). Consumers that key caches on buffer identity (the compositor's paste cache)
/// MUST include it, or a recycled array false-hits with stale content. Producers
/// that hand out fresh arrays per frame leave it 0 — identity alone is then enough.
public long Epoch { get; init; }
/// Producer contract: every alpha byte in is 255
/// (DWM capture surfaces and MediaCapture video carry no alpha — the OS fills 255).
/// Lets the compositor take a straight-copy fast path for a full-canvas opaque
/// layer instead of 2M per-pixel blends. NEVER set it for paths whose pixels can
/// be transparent (static art, web overlays, chat, the social-bar strip).
public bool IsOpaque { get; init; }
/// WebSource crop metadata: the alpha bounding box of the widget content
/// within the full canvas (X/Y/W/H in pixels). When set, the compositor uses these
/// bounds for Fill-style scaling (stretch to cover, no aspect preservation) instead of
/// UniformToFill. The transparent margins of the full canvas are preserved so they
/// reveal layers beneath — the crop defines the actual content region.
public (int X, int Y, int W, int H)? CropBounds { get; init; }
/// Where a SMALLER-than-canvas overlay composites, in master pixels
/// (creator ruling 2026-09-26: the alert ticker draws INSIDE the Stream Alerts video
/// box, not as a full-width strip pinned to the top edge). null — the default
/// — means the canvas origin, which is what every full-canvas overlay wants.
/// This is deliberately NOT a full-canvas frame: at 30–60fps a 1920×1080
/// overlay is 8.3MB of large-object-heap garbage per tick, and a 10s alert would
/// churn ~2.5GB. A box-sized strip is ~370KB, so the position travels on the frame
/// instead of in a 1920-wide transparent margin.
public (int X, int Y)? Placement { get; init; }
/// Composite origin X: if set, else 0.
public int OriginX => Placement?.X ?? 0;
/// Composite origin Y: if set, else 0.
public int OriginY => Placement?.Y ?? 0;
public VideoFrame(int width, int height, byte[] bgraPixels)
{
Width = width;
Height = height;
BgraPixels = bgraPixels;
}
}