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. /// public sealed class RawVideoFrameReader { private readonly int _frameBytes; private readonly int _width; private readonly int _height; private byte[]? _pending; public RawVideoFrameReader(int width, int height) { _width = width; _height = height; _frameBytes = width * height * 4; } /// 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 pending = _pending; byte[] combined; if (pending != null && pending.Length > 0) { combined = new byte[pending.Length + bytes.Length]; Buffer.BlockCopy(pending, 0, combined, 0, pending.Length); Buffer.BlockCopy(bytes, 0, combined, pending.Length, bytes.Length); _pending = null; } else { combined = bytes; } var frames = new List(); var offset = 0; while (combined.Length - offset >= _frameBytes) { var frame = new byte[_frameBytes]; Buffer.BlockCopy(combined, offset, frame, 0, _frameBytes); frames.Add(new VideoFrame(_width, _height, frame)); offset += _frameBytes; } if (offset < combined.Length) _pending = combined[offset..]; return frames; } }