External camera example
An external camera driver includes key functions like start_preview(), stop_preview(), and get_preview_frame(). The QNX Sensor Service uses dlopen() to load the external camera driver at runtime and call these functions to manage the camera.
You can find a working example in the QNX Software Center:
- Package: com.qnx.qnx800.target.sf.camera_examples.source
- File path: source_package_sf_camera/lib/sensor_drivers/external_cameras/example/external_camera_example.cpp
This example simulates a camera by writing color bars to a frame and sending them to the Sensor Service.
external_camera_defs
implementation
Navigate to line 658 of external_camera_example.cpp:
camera_external_camera_t external_camera_defs = {
open : open_external_camera,
close : close_external_camera,
init : init_camera,
deinit : deinit_camera,
start_preview : start_preview,
stop_preview : stop_preview,
get_preview_frame : get_preview_frame,
get_preview_num_buffers : get_preview_num_buffers,
get_supported_vf_frametype : get_supported_vf_frame_types,
get_supported_vf_resolution : get_supported_vf_resolutions,
get_supported_vf_framerates : get_supported_vf_frame_rates,
get_time : get_time,
set_framerate : set_framerate,
get_framerate : get_framerate,
is_feature_capable : is_feature_capable,
#if SUPPORT_EXTERNAL_BUFFER
allocate_preview_buffer : allocate_preview_buffer,
free_preview_buffer : free_preview_buffer,
#endif
set_callbacks : set_callbacks
};
The purpose of external_camera_defs
is to integrate external camera
functionality into the Sensor service.
How it works:
Functions like start_preview() are registered with the Sensor service via
external_camera_defs
.For example, when the Sensor service wants to start streaming:
It looks up and calls start_preview().
This function is dynamically loaded using dlopen().
The actual implementation (e.g., in external_camera_example.cpp) handles the camera behavior.
The key functions include:
open() – Initialize the camera device
close() – Clean up resources
start_preview() – Start streaming frames
stop_preview() – Stop the frame stream
get_preview_frame() – Retrieve a frame to pass to the Sensor service
For other helper or less critical functions, please refer to the source code in external_camera_example.cpp.
open_external_camera() implementation
static void* open_external_camera(uint32_t input)
{
externalCameraContext_t *ctx = NULL;
// Init the library if not done yet
pthread_once(&cameraLibInit, initCameraLibrary);
ctx = (externalCameraContext_t*) calloc(1, sizeof(externalCameraContext_t));
return (void*)ctx;
}
The externalCameraContext_t
is a struct which keeps track of the state of
the camera. The open_external_camera() function allocates memory for
externalCameraContext_t
and returns a pointer to it. The pointer is
given as a parameter to other functions so that they can access
externalCameraContext_t
.
close_external_camera() implementation
static void close_external_camera(void* handle)
{
if (handle != NULL) {
free(handle);
}
return;
}
The close_external_camera() simply frees the externalCameraContext_t
pointer, which
was allocated earlier in open_external_camera(). Note that pointer to
externalCameraContext_t
is passed as void* handle as a parameter.
start_preview() implementation
static int start_preview(void* handle, camera_preview_params_t* params,
camera_buffer_list_t* buflist)
{
externalCameraContext_t* ctx = (externalCameraContext_t*) handle;
if ((handle == NULL) || (params == NULL)) {
return EINVAL;
}
if (ctx->mPreviewActive == true) {
LOG_ERROR("Preview already started");
return CAMERA_EALREADY;
}
ctx->mPreviewActive = true;
ctx->frameCnt = 0;
memcpy(&ctx->mPreviewParams, params, sizeof(camera_preview_params_t));
return EOK;
}
The external camera example writes color bars into memory, so there isn't
much to do in start_preview(). In a real camera driver, it would set a
streaming register to 1
to start streaming.
stop_preview() implementation
static int stop_preview(void* handle)
{
externalCameraContext_t* ctx = (externalCameraContext_t*) handle;
if (handle == NULL) {
return EINVAL;
}
ctx->mPreviewActive = false;
return EOK;
}
In a real camera driver, it would set streaming register to 0
to stop
streaming.
get_preview_frame() implementation
static int get_preview_frame(void* handle, void* bufferIn, camera_preview_frame_flags_t *flags,
void** bufferOut, int64_t *timestamp, void* metaOut,
uint64_t *metaSize)
{
int posx, bar_width, bar, bar_cnt, height, width, stride;
int y, i;
uint8_t *buf;
uint64_t timeStart, timeEnd;
int64_t timeDiff, framePeriod;
camera_frametype_t frametype;
externalCameraContext_t* ctx = (externalCameraContext_t*) handle;
if ((handle == NULL) || (bufferIn == NULL) || (flags == NULL)){
return EINVAL;
}
// Free input buffer if an error is encountered
flags->freeInputBuffer = true;
// Writing color bars is skipped, but in a real camera driver, get_preview_frame would acquire a frame from a camera
// If bufferIn is not consumed by the camera, leave flags->captured to be false and return to indicate that bufferIn
// is queued.
flags->captured = true;
*metaSize = 0;
if (bufferOut) {
flags->freeInputBuffer = false;
*bufferOut = bufferIn;
}
// Before returning, wait based on program frame rate + how long it took to build the frame
ClockTime(CLOCK_MONOTONIC, NULL, &timeEnd);
timeDiff = (timeEnd - timeStart) / 1000;
framePeriod = 1000000 / (ctx->mPreviewParams.framerate_q16 >> 16);
if (timeDiff < framePeriod) {
timeDiff = framePeriod - timeDiff;
} else {
timeDiff = 1;
}
usleep(timeDiff);
if (timestamp) {
*timestamp = get_time(handle);
}
return EOK;
}
This function simulates a camera by writing color bars directly into a buffer (bufferIn).
Because it's just writing to memory (not waiting on hardware), a frame is always ready.
It sets:
flags→captured = true (indicates a frame is ready)
bufferOut = bufferIn (returns the buffer to the Sensor service)
The function uses usleep(timeDiff) to simulate the camera’s frame rate.
Optionally, it sets timestamp if timing data is available.
For a real camera, bufferIn is passed to the camera hardware to write actual image data.
There are two scenarios in the function:
Frame not yet captured:
Sets flags→captured = false
Returns early – frame is still being processed
Frame has been captured:
Sets flags→captured = true
Sets bufferOut to the buffer containing the captured frame
Sets timestamp if available
Running external_camera_example on QNX
Build external_camera_example and scp
it to your QNX target:
# Navigate to the external camera example code
cd source_package_sf_camera/lib/sensor_drivers/external_cameras/example
# Source SDP script
source ~/qnx800/qnxsdp-env.sh
# Build external_camera_example
make
# scp libexternal_camera_example.so over to /system/lib on QNX Raspberry Pi
scp ./nto/aarch64/so.le/libexternal_camera_example.so root@<ip-address>:/system/lib
Create the following file at /system/etc/system/config/external_camera_example.conf:
begin SENSOR_UNIT_1
type = external_camera
name = external_camera_example
address = /system/lib/libexternal_camera_example.so,1
end SENSOR_UNIT_1
Start sensor service and run camera_example3_viewfinder
to display
color bars to screen:
# Start sensor service
sensor -U 521:521,1001 -r /data/share/sensor -c /system/etc/system/config/external_camera_example.conf
# Display color bars to screen
camera_example3_viewfinder