PVCAM  3.9.x
Programmable Virtual Camera Access Method library
Live Image Callbacks

This example shows how to setup and run camera in continuous circular buffer mode with callback notifications.

This is the preferred acquisition mode for obtaining the highest camera frame rate. Using large circular buffer also allows PVCAM to provide basic load balancing that is essential for reliable frame delivery when the system experiences temporary intervals of high resource usage. This is crucial for very high camera frame rates where the circular buffer should be large enough to hold at least 500ms of acquisition or more, taking into account other tasks the system might be executing during the acquisition.

Please note that both PVCAM or the camera can still decide to skip frames if the transfer interface is busy or the circular buffer starts overrunning due to application not being able to process the callbacks. In such case, the application can detect these events by tracking the FRAME_INFO FrameNr or the frame number provided by Embedded Frame Metadata feature.

This sample code collects 50 frames and stops automatically. However, the live mode is often used for image preview and is usually stopped by a UI event such as user pressing a 'Stop' button or similar.

Unlike the Single Image Callbacks, this example uses custom callback handler instead of a generic one defined in the common code part. The implementation is similar, we only added custom counters inside the SampleContext structure:

typedef struct SampleContext
{
int myData1;
int myData2;
}
SampleContext;
Callback function called automatically by PVCAM every time an EOF event arrives
(every time a frame is written to the user buffer).
void PV_DECL CustomEofHandler(FRAME_INFO* pFrameInfo, void* pContext)
{
if (!pFrameInfo || !pContext)
return;
auto ctx = static_cast<CameraContext*>(pContext);
SampleContext* eofCtx = static_cast<SampleContext*>(ctx->eofContext);
if (eofCtx)
{
printf("SampleContext::myData1 = %d\n", eofCtx->myData1);
printf("SampleContext::myData2 = %d\n", eofCtx->myData2);
}
// Store the frame information for later use on the main thread
ctx->eofFrameInfo = *pFrameInfo;
// Obtain a pointer to the last acquired frame
if (PV_OK != pl_exp_get_latest_frame(ctx->hcam, &ctx->eofFrame))
{
PrintErrorMessage(pl_error_code(), "pl_exp_get_latest_frame() error");
ctx->eofFrame = nullptr;
}
{ // Unblock the acquisition thread
std::lock_guard<std::mutex> lock(ctx->eofEvent.mutex);
ctx->eofEvent.flag = true;
}
ctx->eofEvent.cond.notify_all();
}

Because the callback handler is invoked by PVCAM from a different thread than the main thread, printing to standard output should be properly synchronized. Otherwise the text printed from multiple threads could be interleaved and output formatting could become broken. For simplicity, this code sample relies on relatively low camera frame rate and basic synchronization with the eofEvent.

The code snippet below initializes PVCAM and opens the first available camera. Please, refer to the actual code sample in the SDK installation directory for more details about the common helper functions used in this documentation.

std::vector<CameraContext*> contexts;
if (!InitAndOpenOneCamera(contexts, cSingleCamIndex))
return APP_EXIT_ERROR;
CameraContext* ctx = contexts[cSingleCamIndex];

Register a callback handler to receive a notification when EOF event arrives. Similar handler is implemented in the common Frame Callback Handler section used across the SDK examples. However, additionally to the usual CameraContext, this specific example also uses two custom counters:

SampleContext dataContext;
dataContext.myData1 = 0;
dataContext.myData2 = 50;
ctx->eofContext = &dataContext;
(void*)CustomEofHandler, ctx))
{
PrintErrorMessage(pl_error_code(), "pl_cam_register_callback() error");
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}
Note
Please, refer to the Getting Error Messages section.

Prepare the continuous acquisition with circular buffer mode. The pl_exp_setup_cont function returns the size of one frame (unlike the pl_exp_setup_seq which returns the full buffer size). See more in Acquisition Configuration section.

uns32 exposureBytes;
const uns32 exposureTime = 5; // milliseconds
const uns16 circBufferFrames = 20;
const int16 bufferMode = CIRC_OVERWRITE;
if (PV_OK != pl_exp_setup_cont(ctx->hcam, 1, &ctx->region, exposureMode,
exposureTime, &exposureBytes, bufferMode))
{
PrintErrorMessage(pl_error_code(), "pl_exp_setup_cont() error");
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}
Note
For detailed information about error handling, please refer to Getting Error Messages.
This example uses the extended trigger mode configuration and sets up the camera to use internal trigger with first-row expose out mode. However, legacy cameras do not support the extended modes and the list of supported expose out modes may differ as well. It is important to always discover the supported modes using the PARAM_EXPOSURE_MODE and PARAM_EXPOSE_OUT_MODE parameters when working with multiple camera models.

Now allocate the buffer memory. The application is in control of the circular buffer and should allocate memory of appropriate size.

const uns32 circBufferBytes = circBufferFrames * exposureBytes;
uns8* circBufferInMemory = new (std::nothrow) uns8[circBufferBytes];
if (!circBufferInMemory)
{
printf("Unable to allocate buffer for camera %d\n", ctx->hcam);
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

Start the continuous acquisition. By passing the entire size of the buffer to pl_exp_start_cont, PVCAM can calculate the capacity of the circular buffer.

if (PV_OK != pl_exp_start_cont(ctx->hcam, circBufferInMemory, circBufferBytes))
{
PrintErrorMessage(pl_error_code(), "pl_exp_start_cont() error");
CloseAllCamerasAndUninit(contexts);
delete [] circBufferInMemory;
return APP_EXIT_ERROR;
}

Loop to acquire 50 frames:

bool errorOccured = false;
uns32 framesAcquired = 0;
while (framesAcquired < 50)
{
dataContext.myData1++;
dataContext.myData2--;

Here we need to wait for a frame readout notification signaled by the eofEvent in the CameraContext which is raised in the callback handler we registered. If the frame does not arrive within 5 seconds or if user aborts the acquisition with ctrl+c keyboard shortcut, the main while loop is interrupted and the acquisition is aborted.

printf("Waiting for EOF event to occur on camera %d\n", ctx->hcam);
if (!WaitForEofEvent(ctx, 5000, errorOccurred))
break;

Print basic information from the FRAME_INFO structure and display the frame.

// Timestamp is in hundreds of microseconds
printf("Frame #%d acquired, timestamp = %lldus\n",
ctx->eofFrameInfo.FrameNr, 100 * ctx->eofFrameInfo.TimeStamp);
ShowImage(ctx, ctx->eofFrame, exposureBytes);
framesAcquired++;
} // End of while cycle

Once we have acquired the desired number of frames, the continuous acquisition should be stopped with pl_exp_abort function. For more information about closing cameras and uninitializing PVCAM, please, refer to the Closing Camera and Uninitializing PVCAM section.

if (PV_OK != pl_exp_abort(ctx->hcam, CCS_HALT))
PrintErrorMessage(pl_error_code(), "pl_exp_abort() error");
delete [] circBufferInMemory;
CloseAllCamerasAndUninit(contexts);
if (errorOccured)
return APP_EXIT_ERROR;
return 0;