• Support Home
  • Getting Started
    • Connecting Your Camera
    • 3rd Party Software Getting Started Guides
  • Tech Ref Manuals
    • Arena SDK Documentation
    • HTP003S – Helios2+ ToF 3D
    • HLT003S – Helios2 ToF 3D
    • HLS003S – Helios ToF 3D
    • HLF003S – Helios Flex ToF 3D
    • ATX245S – Atlas10 24.5 MP
    • ATX204S – Atlas10 20.4 MP
    • ATX162S – Atlas10 16.2 MP
    • ATX124S – Atlas10 12.3 MP
    • ATX081S – Atlas10 8.1 MP
    • ATX051S – Atlas10 5.0 MP
    • ATL314S – Atlas 31.4 MP
    • ATL196S – Atlas 19.6 MP
    • ATL168S – Atlas 16.8 MP
    • ATL120S – Atlas 12.3 MP
    • ATL089S – Atlas 8.9 MP
    • ATL071S – Atlas 7.1 MP
    • ATL050S – Atlas 5.0 MP
    • ATL028S – Atlas 2.8 MP
    • ATP200S – Atlas IP67 20 MP
    • ATP120S – Atlas IP67 12.3 MP
    • ATP089S -Atlas IP67 8.9 MP
    • ATP071S – Atlas IP67 7.1 MP
    • ATP028S – Atlas IP67 2.8 MP
    • TRI200S – Triton 20.0 MP
    • TRI120S – Triton 12.3 MP
    • TRI122S – Triton 12.2 MP
    • TRI089S – Triton 8.9 MP
    • TRI071S – Triton 7.1 MP
    • TRI064S – Triton 6.3 MP
    • TRI054S – Triton 5.4 MP
    • TRI050S-P/Q – Triton 5.0 MP Polarized
    • TRI050S – Triton 5.0 MP
    • TRI032S – Triton 3.2 MP
    • TRI028S – Triton 2.8 MP
    • TRI023S – Triton 2.3 MP
    • TRI016S – Triton 1.6 MP
    • TRI005S – Triton 0.5 MP
    • TRI004S – Triton 0.4 MP
    • TRI02KA – Triton 2K Line Scan
    • PHX200S – Phoenix 20.0 MP
    • PHX120S – Phoenix 12.3 MP
    • PHX122S – Phoenix 12.2 MP
    • PHX089S – Phoenix 8.9 MP
    • PHX064S – Phoenix 6.3 MP
    • PHX050S-P/Q – Phoenix 5.0 MP Polarized
    • PHX050S – Phoenix 5.0 MP
    • PHX032S – Phoenix 3.2 MP
    • PHX023S – Phoenix 2.3 MP
    • PHX016S – Phoenix 1.6 MP
    • PHX004S – Phoenix 0.4 MP
  • App Notes
    • Bandwidth Sharing in Multi-Camera Systems
    • Combine Helios 3D Point Cloud with RGB Color
    • I2C Support on LUCID Cameras
    • Using Helios2 with the Point Cloud Library for Dimensioning
    • Using GPIO on LUCID Cameras
    • Using PTP & Scheduled Action Commands
    • Helios2 And Triton Synchronization
  • Knowledge Base
  • PCNs
  • Contact Support
  • Log In
  • Support Home
  • Getting Started
    • Connecting Your Camera
    • 3rd Party Software Getting Started Guides
  • Tech Ref Manuals
    • Arena SDK Documentation
    • HTP003S – Helios2+ ToF 3D
    • HLT003S – Helios2 ToF 3D
    • HLS003S – Helios ToF 3D
    • HLF003S – Helios Flex ToF 3D
    • ATX245S – Atlas10 24.5 MP
    • ATX204S – Atlas10 20.4 MP
    • ATX162S – Atlas10 16.2 MP
    • ATX124S – Atlas10 12.3 MP
    • ATX081S – Atlas10 8.1 MP
    • ATX051S – Atlas10 5.0 MP
    • ATL314S – Atlas 31.4 MP
    • ATL196S – Atlas 19.6 MP
    • ATL168S – Atlas 16.8 MP
    • ATL120S – Atlas 12.3 MP
    • ATL089S – Atlas 8.9 MP
    • ATL071S – Atlas 7.1 MP
    • ATL050S – Atlas 5.0 MP
    • ATL028S – Atlas 2.8 MP
    • ATP200S – Atlas IP67 20 MP
    • ATP120S – Atlas IP67 12.3 MP
    • ATP089S -Atlas IP67 8.9 MP
    • ATP071S – Atlas IP67 7.1 MP
    • ATP028S – Atlas IP67 2.8 MP
    • TRI200S – Triton 20.0 MP
    • TRI120S – Triton 12.3 MP
    • TRI122S – Triton 12.2 MP
    • TRI089S – Triton 8.9 MP
    • TRI071S – Triton 7.1 MP
    • TRI064S – Triton 6.3 MP
    • TRI054S – Triton 5.4 MP
    • TRI050S-P/Q – Triton 5.0 MP Polarized
    • TRI050S – Triton 5.0 MP
    • TRI032S – Triton 3.2 MP
    • TRI028S – Triton 2.8 MP
    • TRI023S – Triton 2.3 MP
    • TRI016S – Triton 1.6 MP
    • TRI005S – Triton 0.5 MP
    • TRI004S – Triton 0.4 MP
    • TRI02KA – Triton 2K Line Scan
    • PHX200S – Phoenix 20.0 MP
    • PHX120S – Phoenix 12.3 MP
    • PHX122S – Phoenix 12.2 MP
    • PHX089S – Phoenix 8.9 MP
    • PHX064S – Phoenix 6.3 MP
    • PHX050S-P/Q – Phoenix 5.0 MP Polarized
    • PHX050S – Phoenix 5.0 MP
    • PHX032S – Phoenix 3.2 MP
    • PHX023S – Phoenix 2.3 MP
    • PHX016S – Phoenix 1.6 MP
    • PHX004S – Phoenix 0.4 MP
  • App Notes
    • Bandwidth Sharing in Multi-Camera Systems
    • Combine Helios 3D Point Cloud with RGB Color
    • I2C Support on LUCID Cameras
    • Using Helios2 with the Point Cloud Library for Dimensioning
    • Using GPIO on LUCID Cameras
    • Using PTP & Scheduled Action Commands
    • Helios2 And Triton Synchronization
  • Knowledge Base
  • PCNs
  • Contact Support
  • Log In
home/Knowledge Base/Arena Software/How to choose an appropriate buffer count for the Arena SDK

How to choose an appropriate buffer count for the Arena SDK

12 views 0 May 22, 2026

This KB article is a companion article to the article  User-defined buffers in the Arena SDK.

Buffers help as shock absorbers between the camera’s steady frame rate and your application’s variable processing time. To understand buffers, it is helpful to see how they work within a StartStream() call. A key decision that you will then have to make is how many buffers you need.

How StartStream works

Before getting into buffer count selection, it helps to know what Arena SDK is actually doing when you start a stream.

Calling StartStream() tells the SDK to allocate a pool of image buffers in system RAM. The SDK owns this memory entirely. It allocates during StartStream(), shuffles buffers around during streaming, and frees everything on StopStream().

Data flow: Camera → Network → NIC → DMA to System RAM → Arena SDK buffer pool

The three phases of Arena SDK buffer handling during streaming

1. Buffer Allocation (during StartStream())

The SDK allocates N buffers on the heap. Each one is big enough to hold a full image (width x height x bytes-per-pixel), plus chunk data if you have that enabled. It builds a queue to manage them and registers the buffers with the streaming layer.

pDevice->StartStream();    // default: 10 buffers
pDevice->StartStream(30);  // explicit: 30 buffers

2. Streaming (camera is running)

The camera captures a frame. The resulting GigE Vision packets come in through the NIC. DMA moves packet data into OS socket buffers. Arena assembles the packets and copies the complete image into the next available buffer, which then goes into the “filled” queue.

3. Retrieval (your code)

GetImage() provides you with a filled buffer. RequeueBuffer() puts it back in the empty pool so it can be reused for the next incoming frame.

Arena::IImage* pImage = pDevice->GetImage(timeout);
// do something with the image...
pDevice->RequeueBuffer(pImage);  // give it back
		

Buffer pool lifecycle

  1. Camera fills empty buffers from the pool
  2. Filled buffers move to the output queue
  3. You retrieve them with GetImage()
  4. RequeueBuffer() sends them back to the empty pool

GetImage()

GetImage() pulls the next filled buffer off the output queue and gives your application ownership of it. It blocks until either a buffer shows up or your timeout (in milliseconds) runs out. If the timeout expires with nothing available, you get an exception.

Arena::IImage* pImage = pDevice->GetImage(2000);  // wait up to 2 seconds

While you’re holding a buffer, the acquisition engine can’t use it. That buffer is out of circulation. If you hold onto too many at once without requeuing, the engine runs dry and starts dropping incoming frames.

The IImage pointer gives you access to the pixel data plus metadata like width, height, pixel format, timestamp:

Arena::IImage* pImage = pDevice->GetImage(2000);
size_t width = pImage->GetWidth();
size_t height = pImage->GetHeight();
uint64_t timestampNs = pImage->GetTimestampNs();
size_t sizeFilled = pImage->GetSizeFilled();
GenICam::gcstring pixelFormat = GetPixelFormatName(
    static_cast<PfncFormat>(pImage->GetPixelFormat()));

Set your timeout comfortably above the camera’s frame period. If you’re running at 30 fps (33 ms/frame), 2000 ms is plenty. If you’re using hardware triggers at irregular intervals, set it to whatever the longest expected gap is, plus margin.

RequeueBuffer()

RequeueBuffer() hands the buffer back to the acquisition engine so it can be filled again. After you call it, the IImage pointer is stale.

pDevice->RequeueBuffer(pImage);
// pImage is invalid now

If you forget to requeue, two things happen. The acquisition engine runs out of buffers and starts dropping frames. And the memory behind those buffers never gets freed until StopStream(), so you’ve got a leak.

A good habit: copy out whatever data you need, requeue right away, then do your heavy processing on the copy. Alternatively, you could use ImageFactory::Copy(), instead of memcpy, to preserve the metadata with the image data.

Arena::IImage* pImage = pDevice->GetImage(2000);
// grab the data
memcpy(myBuffer, pImage->GetData(), pImage->GetSizeFilled());
// give the buffer back immediately
pDevice->RequeueBuffer(pImage);
// now take your time
ProcessImage(myBuffer);

This keeps the hold time short, which matters a lot at high frame rates.

StreamInputBufferCount

StreamInputBufferCount is a read-only node on the TL DataStream node map. It tells you how many buffers are sitting in the input pool right now, i.e., empty and waiting for the acquisition engine to fill them.

int64_t inputCount = Arena::GetNodeValue<int64_t>(
    pDevice->GetTLStreamNodeMap(),
    "StreamInputBufferCount");
		

Right after StartStream(N), all N buffers are in the input pool. As frames come in, buffers move to the output queue. RequeueBuffer() sends them back.

If this number hits 0, the engine has nothing to work with. Any frame that arrives while the input pool is empty gets dropped. If you’re investigating frame drop issues, this value is one of the first things to check. Seeing this value stuck at 0 during normal operation means you either need more buffers or need to requeue more quickly.

StreamOutputBufferCount

StreamOutputBufferCount is also read-only on the TL DataStream node map. It tells you how many filled buffers are sitting in the output queue, waiting for your application to pick them up with GetImage().

int64_t outputCount = Arena::GetNodeValue<int64_t>(
    pDevice->GetTLStreamNodeMap(),
    "StreamOutputBufferCount");

If this number is growing, your application is falling behind. Frames are piling up faster than you can pull them out. Once it hits the total buffer count (meaning StreamInputBufferCount is 0), the next frame gets dropped.

If it’s 0, you’ve consumed everything. The next GetImage() call will block until a new frame shows up or the timeout fires.

Between these two counters you can account for every buffer at any given moment:

Total buffers = StreamInputBufferCount + StreamOutputBufferCount + Buffers you’ve retrieved with GetImage() but haven’t requeued yet

Example

Suppose you called StartStream(10) and see StreamInputBufferCount = 5 and StreamOutputBufferCount= 3.

Your application is currently sitting on 2 buffers (grabbed via GetImage() but not yet requeued).

How Many Buffers Do You Need?

The number of buffers you need depends on how much RAM you can afford to spend, and how much slack you need between the camera and your processing.

Memory

The number of buffers you need depends on how much RAM you can afford to spend, and how much slack you need between the camera and your processing.

The per-buffer cost is:

bytes_per_buffer = width x height x bytes_per_pixel + chunk_data_size

Example

5 MP mono camera: 2448 x 2048 x 1 byte = 5 MB per buffer.

12 MP color camera with RGB8: 4096 x 3000 x 3 bytes = 36 MB per buffer.

Buffer Count5 MP Mono (5 MB/buf)12 MP Color (12 MB/buf)
10 (default)50 MB360 MB
30150 MB1080 MB
100500 MB3.6 GB
10005 GB36 GB

For multi-camera systems, buffers can take up a substantial amount of system memory. For example: Twenty cameras at 10 buffers each, 5 MB per buffer: 1 GB of RAM just for image buffers before your application does anything.

Absorbing Processing Spikes

Buffers help as shock absorbers between the camera’s steady frame rate and your application’s variable processing time.

Example

You have a camera at 100 fps (10 ms/frame) and your processing averages 8 ms.

Most of the time you keep up fine. But every so often processing spikes to 15-20 ms on a tough frame.

  • With 3 buffers: frames 1-3 fill the pool. You hit a 20 ms spike on frame 1. Frames 4 and 5 arrive with nowhere to go. They are dropped.
  • With 10 buffers: frames accumulate in the pool during the spike. You catch up over the next few normal frames at 8 ms each. No frames are dropped.

Parallel / GPU Processing

If you’re distributing frames across multiple CUDA streams or worker threads, you need enough buffers to cover all the frames in flight at once.

Example

You have four CUDA streams, each taking 25 ms per frame. Your camera is at 100 fps.

  • Frame 1 → Stream 1 (processing, 25 ms)
  • Frame 2 → Stream 2 (processing, started at t=10 ms)
  • Frame 3 → Stream 3 (processing, started at t=20 ms)
  • Frame 4 → Stream 4 (processing, started at t=30 ms)

Each stream is holding a buffer. With 10 total, you’ve got room for 4 in-flight plus 6 queued. Throughput stays at 100 fps even though each individual frame takes 25 ms.

When Fewer Buffers Are Better

More buffers aren’t always the right call. In latency-sensitive applications, a big buffer pool actually works against you.

Example

Suppose you have a live display at 60 fps with 60 buffers.

If processing slips to 59 fps effective, you’re pulling from a permanently full queue. The frames you’re showing are from over a second ago. The video looks smooth, but it’s stale. With 2-3 buffers you might drop a frame here and there, but what you display is always live.

Why not just use a ridiculous number of buffers?

Example

Suppose you have use 1000 buffers.

  • Memory: 1000 buffers at 12 MB each = 12 GB of RAM. On a multi-camera system that’s probably not realistic, and your application still needs memory for its own work.
  • Hiding real problems: A huge buffer pool masks performance issues. Everything looks fine for a while, then the queue fills up all at once and you get a burst of drops with no warning.
  • Stale data: If you fall behind, you’re processing frames from seconds ago. Depending on the application, an old frame is a useless frame.

Buffer handling mode

Buffer count controls how many buffers you have. Buffer handling mode controls what happens when they’re all full and a new frame shows up. The two modes you’ll use most are OldestFirst and NewestOnly.

  • OldestFirst (default): FIFO order. GetImage() gives you the oldest filled buffer first. If all buffers are full and a new frame arrives, the new frame gets dropped. You get every frame in sequence, nothing overwritten. Use this when you need a complete, ordered record.
  • NewestOnly: Only the latest frame survives. Each new frame overwrites the previous one. GetImage() always gives you whatever just came in. Use this when you only care about what’s happening right now (live preview, visual servoing, etc.).

Set it before starting the stream:

Arena::SetNodeValue<GenICam::gcstring>(
    pDevice->GetTLStreamNodeMap(),
    "StreamBufferHandlingMode",
    "NewestOnly"   // or "OldestFirst"
);

Real-World Example: Bidirectional Tire Inspection

Here’s a scenario that shows how buffer count and buffer handling mode can work together.

Setup

A truck tire inspection system measures tread depth by projecting a laser line onto the tire and imaging it as the tire rolls over the sensor. Multiple cameras run on the same host.

In the main direction, an external trigger fires when the tire approaches, so the system knows exactly when to start grabbing frames. In the reverse direction, there’s no advance trigger. The system only finds out a tire passed after it’s already gone.

One direction is predictable; the other is not.

Switching Buffer Modes by Direction

Main direction (triggered)

Use NewestOnly and stream continuously. When the trigger fires, just grab the latest frame. There is no reason to buffer up a backlog when you know exactly when to look.

// Main direction: just grab the latest frame when triggered
Arena::SetNodeValue<GenICam::gcstring>(
    pDevice->GetTLStreamNodeMap(),
    "StreamBufferHandlingMode",
    "NewestOnly"
);
pDevice->StartStream();
// ... wait for trigger, then GetImage() for the latest frame

Reverse direction (no trigger)

Switch to OldestFirst and allocate enough buffers to cover the tire’s full pass over the sensor (a value of 50 was chosen below). The camera streams continuously into the buffer pool. When the after-the-fact notification comes in that a tire went by, stop the stream and read out the 50 buffered frames in order. You get a complete sequential record even though you didn’t know the tire was there until it was gone.

// Reverse direction: retroactive readout
pDevice->StopStream();
Arena::SetNodeValue(
    pDevice->GetTLStreamNodeMap(),
    "StreamBufferHandlingMode",
    "OldestFirst"
);
pDevice->StartStream(50);  // 50 buffers for the full pass
// Camera acquires continuously...
// ... notification arrives that a tire passed ...
// Read out all buffered frames in order
for (int i = 0; i < 50; i++) {
    IImage* pImage = pDevice->GetImage(timeout);
    // Process frame i (laser line / tread depth measurement)
    pDevice->RequeueBuffer(pImage);
}
pDevice->StopStream()

Why this works

The 50-buffer count is sized to cover the worst-case number of frames during a tire pass. OldestFirst keeps them in order, which matters because tread depth measurement needs a sequential scan across the tire surface. On the main direction side, NewestOnly keeps latency low and doesn’t waste memory buffering frames nobody will look at.

Switching buffer handling mode at runtime like this is a useful pattern any time your system has different acquisition needs at different points in its operation.

Was this helpful?

Yes  No
Related Articles
  • User-defined buffers in the Arena SDK
  • Opening Raw Images in ArenaView MP
  • Solving driver-related RDMA streaming issues with Ubuntu 22.04
  • Using Multiple Helios Cameras Simultaneously
  • Troubleshooting Network Timeouts (Linux)
  • How to create an Ubuntu docker image and container with Arena SDK

Didn't find your answer? Contact Us

  User-defined buffers in the Arena SDK

© 2026 LUCID Vision Labs Inc.
Looking to purchase our cameras?
Visit the LUCID Webstore at thinklucid.com
Manage Consent

We use cookies to process e-commerce purchases securely and to understand how our site is used. Your privacy matters — click ‘Accept’ to continue.

Functional Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes. The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
View preferences
  • {title}
  • {title}
  • {title}
Manage Consent
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
Functional Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes. The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
View preferences
  • {title}
  • {title}
  • {title}