/** * Preallocated, refcounted frame buffers. Raw 1080p frames are 3 MB; allocating * one per frame is ~93 MB/s of external memory per stream and drives GC, so the * gate and the source bus recycle buffers once every in-flight write is done. */ export class BufferPool { private readonly free: Buffer[] = []; private readonly refs = new Map(); /** Buffers allocated after construction; stays 0 in steady state. */ allocs = 0; constructor(readonly size: number, prealloc: number) { for (let i = 0; i < prealloc; i += 1) this.free.push(Buffer.alloc(size)); } /** The caller owns one reference; `release()` it when done. */ get(): Buffer { let buf = this.free.pop(); if (!buf) { this.allocs += 1; buf = Buffer.alloc(this.size); } this.refs.set(buf, 1); return buf; } retain(buf: Buffer): void { this.refs.set(buf, (this.refs.get(buf) ?? 0) + 1); } release(buf: Buffer): void { const n = (this.refs.get(buf) ?? 0) - 1; if (n > 0) { this.refs.set(buf, n); return; } if (n < 0) return; // not pool-owned or already free this.refs.delete(buf); this.free.push(buf); } }