namespace ytLive.Services; /// /// TASK 21: parses a stream of tightly-packed raw BGRA frames into /// s. The consumer of an ffmpeg -f rawvideo -pix_fmt /// bgra stdout pipe: every frame is exactly width*height*4 bytes, so a /// frame is emitted whenever that many bytes have accumulated. Pure and fully /// deterministic, so it is tested with a synthetic byte stream — no ffmpeg, no /// process, no audio device needed. /// Fill a preallocated frame, do not re-concatenate a pending buffer (2026-11-05). /// The previous version copied the whole accumulated partial frame on every Feed, so a /// 1080p frame arriving in 64 KB reads cost ~64 copies of a growing 8 MB buffer — about 0.5 GB of /// copying per frame, which capped playback at ~4 fps no matter what the pacing said (reported as /// "playing at a hideously slow rate"; measured 6 frames in 3 s on a 30 fps clip). Now the cost is /// one copy per frame plus one copy of the tail that spans reads. /// public sealed class RawVideoFrameReader { private readonly int _frameBytes; private readonly int _width; private readonly int _height; // Staging buffer for the frame currently being assembled. It BECOMES the emitted frame's // pixel array, then a fresh one is allocated — so a frame is copied exactly once. private byte[] _frame; private int _count; public RawVideoFrameReader(int width, int height) { _width = width; _height = height; _frameBytes = width * height * 4; _frame = new byte[_frameBytes]; } /// Feeds read from the pipe and returns any /// whole frames reconstructed so far. Partial/pending bytes are kept for the /// next call — frames never split or merge across reads. public List Feed(byte[] bytes) { var frames = new List(); if (bytes.Length == 0) return frames; var offset = 0; while (offset < bytes.Length) { var take = Math.Min(_frameBytes - _count, bytes.Length - offset); Buffer.BlockCopy(bytes, offset, _frame, _count, take); _count += take; offset += take; if (_count < _frameBytes) continue; frames.Add(new VideoFrame(_width, _height, _frame)); _frame = new byte[_frameBytes]; _count = 0; } return frames; } }