Skip to main content
Adapt and enhance existing sample applications to address specific use case requirements. This section guides you through downloading, building, and customizing source code using the Qualcomm® Intelligent Multimedia Product (QIMP) SDK on Dragonwing device running Canonical Ubuntu—providing full control over application behavior and enabling performance optimization across runtime environments. Prebuilt applications are great for quick evaluation, but customizing them allows you to:
  • Integrate your own models, media, or logic
  • Optimize performance for specific runtime targets (CPU, GPU, NPU)
  • Add new features or modify existing ones to match your use case
  • Experiment with different pipeline configurations using GStreamer
  • Build production-ready applications starting from a working baseline
To begin customize existing sample applications on the target device, follow these step-by-step instructions to install required dependencies and configure the system. Prerequisites:
  • Setup the Device.
  • Ensure that the IQ‑9075 device is running Ubuntu OS.
  • Setup Wi-Fi and access IQ-9075 EVK device via SSH.
  • If the PPA packages are not already installed, run the Install needed packages step.

Install needed packages

To install the needed pre-built packages, refer to the 🔗Install required software packages section and run the provided “Install PPA Packages script”.

Build from Source

Follow the steps below to download, configure, and compile the sample application source code. This allows you to modify application behavior and integrate your own logic as needed. Install the following packages to download source code:
sudo apt-add-repository -s ppa:ubuntu-qcom-iot/qcom-ppa
sudo apt-get install adreno-dev
sudo apt-get install gstreamer1.0-qcom-sample-apps-utils-dev

Build the dependencies

Run the following command to get the plugins needed for source code compilation:
sudo apt build-dep gst-plugins-qti-oss

Download source code

Download the sample application source code:
cd /home/ubuntu
sudo apt source gst-plugins-qti-oss

Sample application code walkthrough

Consider the gst-ai-usb-camera-app for code walkthrough. This is a GStreamer-based application developed by Qualcomm to demonstrate how to use a USB camera for different purposes:
  • Show live video on a display
  • Save video to a file
  • Stream video over RTSP
  • Run object detection using AI models
The source code of gst-ai-usb-camera-app available in:
cd gst-plugins-qti-oss/gst-sample-apps/gst-ai-usb-camera-app

Details

Header Files and Constants:
#include <glib-unix.h>
#include <stdio.h>
#include <gst/gst.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <json-glib/json-glib.h>
These provide support for:
  • GLib: Utility functions and main loop
  • GStreamer: Multimedia framework
  • Video4Linux2 (V4L2): Accessing USB camera
  • JSON-GLib: Reading configuration from JSON
Then we define default values:
#define DEFAULT_WIDTH 1280
#define DEFAULT_HEIGHT 720
#define DEFAULT_FRAMERATE 30
#define DEFAULT_OUTPUT_FILENAME "/etc/media/video.mp4"
#define DEFAULT_IP "127.0.0.1"
#define DEFAULT_PORT "8900"
These are used if the user doesn’t provide custom settings. Application Context Structures:
  • GstCameraAppCtx
    This structure holds the state of the application:
struct GstCameraAppCtx {
    GstElement *pipeline;
    GMainLoop *mloop;
    gchar *output_file;
    gchar *ip_address;
    gchar *port_num;
    gchar *enable_ml;
    gchar dev_video[16];
    enum GstSinkType sinktype;
    enum GstVideoFormat video_format;
    gint width;
    gint height;
    gint framerate;
};
  • GstAppOptions
    This structure holds user-defined options from the config file:
typedef struct {
    gchar *file_path;
    gchar *model_path;
    gchar *labels_path;
    gchar *constants;
    gchar **snpe_layers;
    GstCameraSourceType camera_type;
    GstModelType model_type;
    GstYoloModelType yolo_model_type;
    gdouble threshold;
    gint delegate_type;
    gint snpe_layer_count;
    gboolean use_cpu;
    gboolean use_gpu;
    gboolean use_dsp;
} GstAppOptions;
Reading Configuration from JSON: The function parse_json() reads the config file and sets values in appctx and options.
Example config:
{
  "width": 1280,
  "height": 720,
  "framerate": 30,
  "output": "waylandsink",
  "enable-object-detection": "TRUE",
  "yolo-model-type": "yolov8",
  "ml-framework": "tflite"
}
Code snippet:
if (json_object_has_member(root_obj, "width")) {
    appctx->width = json_object_get_int_member(root_obj, "width");
}
This sets the camera resolution width from the config file. Finding the USB Camera: Function: find_usb_camera_node()
This function loops through /dev/video0 to /dev/video63 to find a valid USB camera.
while (idx < MAX_VID_DEV_CNT) {
    snprintf(appctx->dev_video, sizeof(appctx->dev_video), "/dev/video%d", idx);
    mFd = open(appctx->dev_video, O_RDWR);
    ioctl(mFd, VIDIOC_QUERYCAP, &v2cap);
    if (strcmp((const char *)v2cap.driver, "uvcvideo") == 0) {
        break;
    }
    idx++;
}
Creating the GStreamer Pipeline: Function: create_preview_pipe()
This function builds a pipeline based on the output type (display, file, or RTSP).
Example 1: Live Preview
v4l2src → capsfilter → waylandsink
Code:
v4l2src = gst_element_factory_make("v4l2src", "v4l2src");
capsfilter = gst_element_factory_make("capsfilter", "capsfilter");
waylandsink = gst_element_factory_make("waylandsink", "waylandsink");
Example 2: Save to File
v4l2src → capsfilter → qtivtransform → v4l2h264enc → h264parse → filesink
Code:
filesink = gst_element_factory_make("filesink", "filesink");
v4l2h264enc = gst_element_factory_make("v4l2h264enc", "v4l2h264enc");
h264parse = gst_element_factory_make("h264parse", "h264parse");
Example 3: RTSP Streaming
v4l2src → capsfilter → qtivtransform → v4l2h264enc → h264parse → qtirtspbin
Object Detection Pipeline: Function: create_pipe()
This builds a more complex pipeline for AI-based object detection.
Pipeline Flow:
v4l2src → capsfilter → tee → qtivcomposer → waylandsink
Code:
qtimlvconverter = gst_element_factory_make("qtimlvconverter", "qtimlvconverter");
qtimlelement = gst_element_factory_make("qtimltflite", "qtimlelement");
qtimlvdetection = gst_element_factory_make("qtimlvdetection", "qtimlvdetection");
qtivcomposer = gst_element_factory_make("qtivcomposer", "qtivcomposer");
These plugins handle:
  • Preprocessing: qtimlvconverter
  • Inference: qtimltflite, qtimlsnpe, or qtimlqnn
  • Postprocessing: qtimlvdetection
  • Overlay: qtivcomposer
Main Function: This is where everything starts:
int main(int argc, char *argv[]) {
    appctx = gst_app_context_new();
    parse_json(config_file, &options, appctx);
    find_usb_camera_node(appctx);
    create_pipe(appctx, &options);
    gst_element_set_state(pipeline, GST_STATE_PAUSED);
    g_main_loop_run(appctx->mloop);
}
This code will:
  • Initializes the app context
  • Reads the config file
  • Finds the USB camera
  • Builds the pipeline
  • Runs the main loop

Compile sample app

cd gst-plugins-qti-oss/gst-sample-apps/gst-sample-apps-utils
mkdir build; cd build
cmake \
   -DGST_VERSION_REQUIRED=1.20.1 \
   -DSYSROOT_INCDIR=/usr/include \
   -DSYSROOT_LIBDIR=/usr/lib \
   -DGST_PLUGINS_QTI_OSS_INSTALL_BINDIR=/usr/bin \
   -DGST_PLUGINS_QTI_OSS_INSTALL_CONFIG=/etc/configs \
   -DENABLE_CAMERA=TRUE \
   -DENABLE_VIDEO_ENCODE=TRUE \
   -DENABLE_VIDEO_DECODE=TRUE \
   -DENABLE_DISPLAY=TRUE \
   -DENABLE_ML=TRUE \
   -DENABLE_AUDIO=TRUE \
   -DCAMERA_SERVICE=LECAM \
   -DGST_PLUGINS_QTI_OSS_INSTALL_INCDIR=/usr/include \
   ..
make
make install

Compile

This example shows how to build the object detection app.
cd gst-plugins-qti-oss/gst-sample-apps/gst-ai-object-detection
mkdir build; cd build
cmake \
   -DGST_VERSION_REQUIRED=1.20.1 \
   -DSYSROOT_INCDIR=/usr/include \
   -DSYSROOT_LIBDIR=/usr/lib \
   -DGST_PLUGINS_QTI_OSS_INSTALL_BINDIR=/usr/bin \
   -DGST_PLUGINS_QTI_OSS_INSTALL_CONFIG=/etc/configs \
   -DENABLE_CAMERA=TRUE \
   -DENABLE_VIDEO_ENCODE=TRUE \
   -DENABLE_VIDEO_DECODE=TRUE \
   -DENABLE_DISPLAY=TRUE \
   -DENABLE_ML=TRUE \
   -DENABLE_AUDIO=TRUE \
   -DCAMERA_SERVICE=LECAM \
   -DGST_PLUGINS_QTI_OSS_INSTALL_INCDIR=/usr/include \
   ..
make
make install
Note: Every sample app needs to be compiled individually.

Run the compiled sample application

  1. Set the required environment variables to connect a Wayland application to the active user session.
export XDG_RUNTIME_DIR=/run/user/$(id -u ubuntu)
export WAYLAND_DISPLAY=wayland-1
  1. For Ubuntu Desktop, run the following command to creates a missing symbolic link (for TensorFlow Lite C library).
sudo ln -s /lib/aarch64-linux-gnu/libtensorflowlite_c.so.2 /lib/aarch64-linux-gnu/libtensorflowlite_c.so  
  1. Run gst-ai-usb-camera-app application by using following command:
gst-ai-usb-camera-app
  1. To display the available help options, run the following command in the SSH shell:
gst-ai-usb-camera-app -h
For Debug Messages:
Set GST_DEBUG environment variable used by GStreamer to control the verbosity and category of debug messages.
export GST_DEBUG=3
  1. To stop the use case, press CTRL + C

Reference Documentation:

AI developer workflow - Ubuntu on Qualcomm® IoT Platforms Documentation