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:

  1. Frame not yet captured:

    • Sets flags→captured = false

    • Returns early – frame is still being processed

  2. 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
Page updated: