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;
}
}