Skip to main content

Overview

This guide walks through the complete implementation of a GStreamer-based object detection application using Qualcomm hardware-accelerated plugins. The application:
  • Reads an H.264-encoded MP4 file from disk
  • Performs YOLOv8 object detection using a TFLite inference pipeline
  • Overlays detection bounding boxes and labels onto video frames
  • Renders the annotated output to a Wayland display
The complete application source code is available in the repository. For foundational concepts — including pipeline construction, state management, caps negotiation, bus handling, and dynamic pads — refer to Building a Native GStreamer Application.

Pipeline Diagram

Pipeline Diagram

Implementation Steps

1

Step 1 — Define the Application Context

The application consolidates all runtime state into a single context structure:
This pattern avoids global variables, keeps application state self-contained, and simplifies callback implementations that require access to pipeline objects.
2

Step 2 — Create the Pipeline Object

A GstPipeline object is created using gst_pipeline_new(), establishing the top-level container that owns and manages all elements added to it.As a GstBin subclass, it is responsible for:
  • Element lifecycle management
  • State propagation across all contained elements
  • Synchronization of clocks and timing
All subsequent pipeline operations — element management, state transitions, and bus access — are performed through this object.
3

Step 3 — Instantiate Pipeline Elements

Each element is instantiated using gst_element_factory_make(). The first argument identifies the element by its factory name; the second assigns a unique instance name (retrievable later via gst_bin_get_by_name()).
4

Step 4 — Configure Elements

Before the pipeline starts, element properties are set to define runtime behavior.Input source — configure filesrc with the path to the input file:
AI inference — configure qtimlvideotflitebin with delegate, post-processing module, model, and labels:
Display sink — configure waylandsink for synchronized, full-screen rendering:
5

Step 5 — Add Elements to the Pipeline

gst_bin_add_many() transfers ownership of all elements to the pipeline, making it responsible for their lifecycle — including state transitions and resource cleanup. At this point, elements share the same execution context but are not yet connected.
6

Step 6 — Link Static Pipeline Elements

This establishes the following static connections:
The link between qtdemux and h264parse is intentionally omitted here. qtdemux exposes its output pads dynamically at runtime — this connection is established in Step 7 via dynamic pad handling.
7

Step 7 — Handle Dynamic Pads (Demuxer)

qtdemux determines the number and type of elementary streams only after parsing input data, dynamically creating output pads for each discovered stream. Since these pads do not exist at construction time, they cannot be linked statically.A callback is registered for the pad-added signal on the demuxer:
When a new pad is exposed at runtime, the callback inspects its type and links it to h264parse if it carries an H.264 video stream — all other stream types (e.g., audio) are ignored:
This completes the missing link:
The full data path is now established end-to-end:
8

Step 8 — Set Up Bus Message Handling

The application retrieves the pipeline bus and registers callbacks for error, EOS, and warning messages:
9

Step 9 — Handle Ctrl+C (Interrupt Signal)

A SIGINT handler is registered using g_unix_signal_add() to route the OS signal safely into the GLib main loop:
The signal handler checks the current pipeline state and responds accordingly:
Sending EOS before stopping is particularly important for file-based outputs where finalization steps (e.g., writing file headers/footers) must complete.
10

Step 10 — Start the Pipeline

This single call propagates the state transition to all contained elements. Each element progresses through intermediate READY and PAUSED states before reaching PLAYING, at which point data begins to flow from filesrc through to waylandsink.
11

Step 11 — Run the Main Loop

The application enters the GLib main loop, which:
  • Blocks the main thread for the duration of pipeline execution
  • Dispatches bus messages and invokes registered callbacks as events occur
  • Exits only when explicitly quit by the EOS callback, error handler, or interrupt signal handler
12

Step 12 — Handle End-of-Stream

When the pipeline processes the last buffer and EOS propagates to the sink, an EOS bus message is delivered to the registered callback:
The callback quits the main loop, allowing the main thread to resume and proceed with pipeline teardown and resource cleanup.
13

Step 13 — Handle Errors

If a fatal error occurs at any stage of pipeline execution, the error callback is invoked:
Errors are treated as fatal. The callback:
  1. Logs the error message and debug information for diagnostics
  2. Quits the main loop to initiate controlled shutdown
  3. The application subsequently transitions the pipeline to NULL and releases all resources
14

Step 14 — Teardown and Resource Cleanup

Once the main loop exits — whether due to EOS, error, or interrupt — the application performs an orderly teardown:
This sequence ensures all resources are released in the correct order and that the application exits in a well-defined, clean state.

Summary

This guide covered the full implementation of a GStreamer YOLOv8 object detection application across 14 steps: