PVCAM  3.9.x
Programmable Virtual Camera Access Method library
Common Functions

In order to reduce repetitive code, all code samples use additional structures and helper functions that are defined in separate source files. The most important types are documented in a separate chapter Common Data Types.

Please note that although PVCAM is a C99 API, the code samples in our SDK are written using C++11 features. This also helps to keep the code short and more readable.

Getting Error Messages

All code samples use PrintErrorMessage to log an error. This function prints the most recent error condition (returned by pl_error_code function) together with the related error message (received via pl_error_message function). For more information please refer to Error Handling chapter.

void PrintErrorMessage(int16 errorCode, const char* message)
{
char pvcamErrMsg[ERROR_MSG_LEN];
pl_error_message(errorCode, pvcamErrMsg);
printf("%s\n Error code: %d\n Error message: %s\n",
message, errorCode, pvcamErrMsg);
}

Initializing PVCAM and Opening the Camera

General initialization is implemented inside InitPVCAM function. This function first calls the pl_pvcam_init method, then, by calling pl_cam_get_total it retrieves the number of cameras that are recognized by PVCAM. Finally, the function creates an instance of CameraContext structure for every camera. The structure is initially filled with camera name that is obtained using pl_cam_get_name call.

Two more helper functions are used in order to further simplify the code samples: the first function opens one camera at a given index, the second function opens multiple cameras starting from index 0.

bool InitAndOpenOneCamera(std::vector<CameraContext*>& contexts, uns16 camIndex);
bool InitAndOpenMultipleCameras(std::vector<CameraContext*>& contexts, uns16& camCount);

Initialize PVCAM Library

Before calling any of the PVCAM API functions the library must be initialized. This only needs to be done once. Initialization section:

static bool s_isPvcamInitialized = false;
...
bool InitPVCAM(std::vector<CameraContext*>& contexts)
{
if (s_isPvcamInitialized)
return true;
if (PV_OK != pl_pvcam_init())
{
PrintErrorMessage(pl_error_code(), "pl_pvcam_init() error");
return false;
}
s_isPvcamInitialized = true;
...
Note
For more information about PVCAM error handling, please refer to the Getting Error Messages
section. PV_OK and PV_FAIL are synonyms for TRUE and FALSE value.

Scan for Cameras

Retrieve the number of cameras connected to the system. The order of cameras and their names follow a specific scheme, please see Camera Enumeration for more details.

...
int16 nrOfCameras = 0;
if (PV_OK != pl_cam_get_total(&nrOfCameras))
{
PrintErrorMessage(pl_error_code(), "pl_cam_get_total() error");
UninitPVCAM(contexts);
return false;
}
if (nrOfCameras <= 0)
{
printf("No cameras found in the system\n");
UninitPVCAM(contexts);
return false;
}
...

Create CameraContext for each camera

...
for (int16 i = 0; i < nrOfCameras; i++)
{
CameraContext* ctx = new (std::nothrow) CameraContext();
if (!ctx)
{
printf("Unable to allocate memory for CameraContext\n");
UninitPVCAM(contexts);
return false;
}
if (PV_OK != pl_cam_get_name(i, ctx->camName))
{
PrintErrorMessage(pl_error_code(), "pl_cam_get_name() error");
UninitPVCAM(contexts);
return false;
}
contexts.push_back(ctx);
}
return true;
}
Note
Retrieving the name of the camera is important since the name is used to open the camera.

Open the Camera

bool OpenCamera(CameraContext* ctx)
{
if (ctx->isCamOpen)
return true;
if (PV_OK != pl_cam_open(ctx->camName, &ctx->hcam, OPEN_EXCLUSIVE))
{
PrintErrorMessage(pl_error_code(), "pl_cam_open() error");
return false;
}
ctx->isCamOpen = true;
...
// Initialize the rest of the CameraContext
return true;
}

The camera has been opened successfully. From now on, the ctx->hcam handle will be used to identify the camera when calling camera-specific API functions.

The OpenCamera function fully initializes the CameraContext structure and retrieves various camera information that do not change during camera operation. Such information may include:

Closing Camera and Uninitializing PVCAM

This function closes the camera if it has been opened previously. It's unlikely that the pl_cam_close call fails, but if it does, the camera is considered closed anyway.

void CloseCamera(CameraContext* ctx)
{
if (!ctx->isCamOpen)
return;
if (PV_OK != pl_cam_close(ctx->hcam))
PrintErrorMessage(pl_error_code(), "pl_cam_close() error");
ctx->isCamOpen = false;
}

PVCAM library is uninitialized via UninitPVCAM function. It calls pl_pvcam_uninit after all CameraContext instances are released.

void UninitPVCAM(std::vector<CameraContext*>& contexts)
{
if (!s_isPvcamInitialized)
return;
for (CameraContext* ctx : contexts)
delete ctx;
contexts.clear();
PrintErrorMessage(pl_error_code(), "pl_pvcam_uninit() error");
s_isPvcamInitialized = false;
}

Following is a function that closes all cameras and releases PVCAM library in one convenient call. This function is often used inside error handlers where a critical error leads to the immediate program interruption.

void CloseAllCamerasAndUninit(std::vector<CameraContext*>& contexts);

Terminating the Application

Several code examples in the SDK show how to use external hardware trigger feature. In order to cancel an acquisition that waits for the external trigger or long exposure acquisition, the example code uses the following function to install a termination handler. The handler is usually invoked by pressing Ctrl+C inside a running console application or system terminal.

if (!InstallGenericCliTerminationHandler(contexts))
{
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

Working With Parameters

In the SDK code samples, PVCAM parameters are usually read by directly calling pl_get_param or pl_set_param functions without using additional wrappers. However, there are two exceptions made to improve the code readability: a check whether a particular parameter is available (function IsParamAvaliable), and an extraction of all values from an enumerable parameter (function ReadEnumeration).

The functions IsParamAvailable and ReadEnumeration accept paramName argument only to output the parameter name to console in case an error occurs.

bool IsParamAvailable(int16 hcam, uns32 paramID, const char* paramName);
bool ReadEnumeration(int16 hcam, NVPC* pNvpc, uns32 paramID, const char* paramName);

The details of these functions are documented in Parameter Access Functions chapter and Enumeration Parameters section with error handling implementation.

Frame Callback Handler

Most of the SDK code samples use the recommended 'callback' acquisition technique (see chapter Polling versus Callbacks). For callback acquisition the application must register a 'callback' function via the pl_cam_register_callback_ex3 API call. The function below is used for the EOF (end-of-frame) callback notification and it is called by PVCAM automatically every time a frame is written into the frame buffer. For the purpose of SDK code samples, the function only unblocks another waiting thread. In any case, it is not recommended to do lengthy processing and hold this callback routine longer than necessary. An std::conditional_variable from standard library is used for that synchronization.

void PV_DECL GenericEofHandler(FRAME_INFO* pFrameInfo, void* pContext)
{
if (!pContext)
return;
auto ctx = static_cast<CameraContext*>(pContext);
// Store frame information for later use
ctx->eofFrameInfo = *pFrameInfo;
// Obtain a pointer to the 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 acquisition thread
std::lock_guard<std::mutex> lock(ctx->eofEvent.mutex);
ctx->eofEvent.flag = true;
}
ctx->eofEvent.cond.notify_all();
}

The callback is usually registered upon opening the camera and before starting any acquisition.

(void*)GenericEofHandler, ctx))
{
PrintErrorMessage(pl_error_code(), "pl_cam_register_callback() error");
CloseAllCamerasAndUninit(contexts);
return APP_EXIT_ERROR;
}

This is an implementation of a helper function that waits for the EOF event set in GenericEofHandler which effectively reduces the boiler-plate code that would be copied to most of the code samples using callbacks.

bool WaitForEofEvent(CameraContext* ctx, uns32 timeoutMs, bool& errorOccurred)
{
std::unique_lock<std::mutex> lock(ctx->eofEvent.mutex);
errorOccurred = false;
ctx->eofEvent.cond.wait_for(lock, std::chrono::milliseconds(timeoutMs),
[ctx]() { return ctx->eofEvent.flag || ctx->threadAbortFlag; });
if (ctx->threadAbortFlag)
{
printf("Processing aborted on camera %d\n", ctx->hcam);
return false;
}
if (!ctx->eofEvent.flag)
{
printf("Camera %d timed out waiting for a frame\n", ctx->hcam);
errorOccurred = true;
return false;
}
ctx->eofEvent.flag = false; // Reset flag
if (!ctx->eofFrame)
{
errorOccurred = true;
return false;
}
return true;
}

Interaction with User

Each code sample has different requirements on how to interact with the user. The following functions provide means of interaction with the user via console application or system terminal.

GetCh and GetChE functions are used for single key press detection.

int GetCh();
int GetChE();

Next two functions can be used to retrieve a string or numerical input.

bool WaitForInputString(std::string& str);
template<typename T>
bool WaitForInputNumber(T min, T max, T def, T& number)

GetMenuSelection can be used to print a formatted menu with title and items from NVPC structure. The user should enter one of the values from the NVP items for valid input.

bool GetMenuSelection(const std::string& title, const NVPC& menu, int32& selection);

Data Presentation

ShowImage function prints out the first few pixel values. The function is generic and its implementations shows how to process the pixel data for various image formats and bit-depths via C++ templates.

void UpdateCtxImageFormat(CameraContext* ctx);
void ShowImage(const CameraContext* ctx, const void* pBuffer,
uns32 bufferBytes, const char* title = nullptr);

The following set of functions prints out content of all metadata structures. See Embedded Frame Metadata chapter for details.

void PrintMetaFrame(const CameraContext* ctx, const md_frame* pFrameDesc,
bool printAllRois);
void PrintMetaRoi(const CameraContext* ctx, const md_frame_roi* pRoiDesc);
void PrintMetaExtMd(void* pMetaData, uns32 metaDataSize);

SaveImage writes raw image pixels to a file.

bool SaveImage(const void* pBuffer, uns32 bufferBytes, const char* path);

Post-Processing Helpers

Please, read the Post-Processing chapter first to understand what post-processing is and how it is presented to application.

These helper functions simplify the access to post-processing functionality.

std::vector<PvcamPpFeature> DiscoverCameraPostProcessing(CameraContext* ctx);
bool FindPpFeatureIndex(const std::vector<PvcamPpFeature>& ppFeatureList,
uns32 ppFeatureId, int16& featIdx);
bool FindPpParamIndex(const std::vector<PvcamPpParameter>& ppParameterList,
uns32 ppParamId, int16& paramIdx);
bool FindPpParamIndexes(const std::vector<PvcamPpFeature>& ppFeatureList,
uns32 ppParamId, int16& featIdx, int16& paramIdx);
bool GetPpParamValue(CameraContext* ctx, int16 featIdx, int16 paramIdx, uns32& value);
bool SetPpParamValue(CameraContext* ctx, int16 featIdx, int16 paramIdx, uns32 value);

Other Helpers

A helper function simplifying use of SMART Streaming feature

bool UploadSmartStreamingExposures(const CameraContext* ctx,
const uns32* pExposures, uns16 exposuresCount);