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