This guide provides a comprehensive overview of how to develop AI applications using the Hailo GStreamer framework. It covers the underlying architecture and two primary development paths: basic callback customization and advanced pipeline modification.
Our framework is built on a stack of technologies, from the open-source GStreamer foundation to our custom Python tools. The diagram below illustrates how these layers build upon one another, with each layer providing a higher level of abstraction.
This diagram visually represents our technology stack. Understanding this stack is key to effective development.
At the base of our stack is GStreamer, a powerful open-source framework for creating streaming media applications. It provides a plugin-based architecture where elements are linked together to form a pipeline that defines a data flow. For more information, you can visit the GStreamer official documentation.
On top of GStreamer, we use Hailo's Tappas plugins. This is a library of C/C++ GStreamer elements specifically designed to interface with the Hailo AI accelerator. These high-performance elements are the bridge between the GStreamer framework and the Hailo hardware.
The framework provides many specialized GStreamer elements.
Click to see the list of Core Tappas Elements
-
AI/ML Processing Elements
- HailoNet: Runs neural network inference on input video frames using a Hailo device.(This element is released as part of HailoRT)
- HailoFilter: Applies
C++ post-processing functions to the raw output from
HailoNet, converting it into structured data like detection objects. - HailoOverlay: Draws the structured Hailo AI metadata (bounding boxes, masks, labels) onto the video frames for visualization.
-
Multi-Device & Routing Elements
- HailoMuxer: Combines multiple metadata streams into a single output stream.
- HailoRoundRobin: An element that provides muxing functionality. It receives input from one or more sink pads and forwards them into a single src pad in round-robin method.
- HailoStreamRouter: Routes video or metadata streams dynamically based on user-defined rules.
-
Cascading Networks Elements
- HailoCropper: Crops regions of interest from video frames for further processing based on the output of the previous network.
- HailoAggregator: Merges multiple input streams or metadata into a single output stream.
-
Tiling Elements
- HailoTileCropper: Crops tiled regions from input frames, to allow processing each tiles independently.
- HailoTileAggregator: Combines multiple cropped tiles into a single output frame.
-
Tracking & Monitoring Elements
- HailoTracker: Tracks detected objects across video frames, assigning unique IDs to each object.
- HailoDeviceStats: Reports real-time statistics and health information from the Hailo device.
For optimizing and debugging your GStreamer pipelines, we recommend using GstShark. It allows you to visualize queue levels, latency, and framerate to identify bottlenecks. See our GstShark Debugging Guide for installation and usage instructions.
This layer is developed in this repository to simplify the process of building and running applications on top of the GStreamer and TAPPAS foundation. It consists of three main components:
- The Application Runner (
gstreamer_app.py): This component features theGStreamerAppclass, which serves as the core engine of the application. It is responsible for managing the pipeline's lifecycle, handling bus messages (such as errors or End-Of-Stream), and integrating your Python callback functions. - The Pipeline Factory (
gstreamer_helper_pipelines.py): This module provides a set of Python functions that facilitate the creation of GStreamer pipeline strings in a modular and easily understandable manner. For a comprehensive reference of all available helper functions, see the GStreamer Helper Pipelines Reference. - Hailo Pipelines: These are pre-configured, ready-to-use AI pipelines that leverage the helper functions from the factory to form complete, executable applications for common scenarios like object detection or pose estimation. You can connect to their outputs with a simple callback, allowing you to easily integrate custom logic or processing steps.
For example,
hailo_apps/python/pipeline_apps/detection/detection_pipeline.py.
This is the quickest way to build an application. The core concept is to begin with one of our pre-built pipeline examples and incorporate your custom logic by writing a simple Python callback function. Each pipeline is designed to be executed with a straightforward callback.
We suggest using one of our example callback applications, such as hailo_apps/python/pipeline_apps/detection/detection.py, as your starting point. Each pipeline in this repository includes an example callback file (e.g., detection.py, pose_estimation.py, etc.). These files demonstrate the relevant callback code for that specific pipeline and can serve as a reference or starting point for your own application.
The GStreamer pipeline handles all complex tasks, including video decoding, inference, and rendering. Your Python callback function is invoked for each frame processed by the pipeline, receiving both the video frame and the AI metadata.
- Data Production: The pipeline processes a video frame, and the C++ post-processing library generates structured metadata (containing detections, etc.).
- Callback Invocation: An
identityelement in the pipeline intercepts the GStreamer buffer and triggers your Python function via thehandoffsignal, passing the element, buffer, and user data. - Your Custom Logic: Within your callback, you parse the metadata and perform any necessary actions.
IMPORTANT: The callback function must be non-blocking. Long-running tasks should be dispatched to a separate thread or process to prevent stalling the video pipeline.
A user application script typically has three parts: an optional custom data class, a callback function, and a main execution block.
This class lets you keep track of information between frames, such as the total number of people detected and the number of frames processed. By inheriting from app_callback_class, you get built-in features like automatic frame counting (via get_count()). You can add any attributes you want to store custom statistics or state.
Note: Frame counting is handled automatically by the framework. You do NOT need to call
user_data.increment()in your callback - the framework wraps your callback function and handles this for you. Simply useuser_data.get_count()to access the current frame number.
from hailo_apps.python.core.gstreamer.gstreamer_app import app_callback_class
class user_app_callback_class(app_callback_class):
def __init__(self):
super().__init__()
self.total_people = 0
self.total_frames = 0This function is called for every frame processed by the pipeline. This is where you put your custom logic for handling detections or other AI results. In this toy example, the callback function receives the GStreamer buffer, extracts detection results, counts how many people are detected in the frame, updates the running totals, and prints out statistics.
import os
os.environ["GST_PLUGIN_FEATURE_RANK"] = "vaapidecodebin:NONE" # Force software decoding to avoid VAAPI "views=2" bug causing black screens
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst
import hailo
def app_callback(element, buffer, user_data):
user_data.increment() # Count the number of frames
# buffer is passed directly
if buffer is None:
return
detections = hailo.get_roi_from_buffer(buffer).get_objects_typed(hailo.HAILO_DETECTION)
people_count = 0
for det in detections:
if det.get_label() == "person":
people_count = people_count + 1
user_data.total_people = user_data.total_people + people_count
user_data.total_frames = user_data.total_frames + 1
if user_data.total_frames > 0:
running_average = user_data.total_people / user_data.total_frames
else:
running_average = 0.0
string_to_print = (
"Frame count: " + str(user_data.get_count()) + "\n"
+ "People detected in frame: " + str(people_count) + "\n"
+ "Running average people per frame: " + str(round(running_average, 2)) + "\n"
)
for detection in detections:
string_to_print = string_to_print + (
"Detection: " + detection.get_label() + " Confidence: " + str(round(detection.get_confidence(), 2)) + "\n"
)
print(string_to_print)
returnThis part ties everything together. It creates an instance of your callback class, sets up the detection pipeline, and starts the application. This is the entry point of your script.
from hailo_apps.python.pipeline_apps.detection_simple.detection_simple_pipeline import GStreamerDetectionSimpleApp
if __name__ == "__main__":
user_data = user_app_callback_class()
app = GStreamerDetectionSimpleApp(app_callback, user_data)
app.run()Below is a complete, minimal example you can copy and run. It combines the custom class, callback function, and main block into a single script. This is a good starting point for your own application—just modify the callback logic as needed for your use case.
import os
os.environ["GST_PLUGIN_FEATURE_RANK"] = "vaapidecodebin:NONE"
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst
import hailo
from hailo_apps.python.core.gstreamer.gstreamer_app import app_callback_class
from hailo_apps.python.pipeline_apps.detection_simple.detection_simple_pipeline import GStreamerDetectionSimpleApp
class user_app_callback_class(app_callback_class):
def __init__(self):
super().__init__()
self.total_people = 0
self.total_frames = 0
def app_callback(pad, info, user_data):
# Note: Frame counting is handled automatically by the framework wrapper
buffer = info.get_buffer()
if buffer is None:
return Gst.PadProbeReturn.OK
detections = hailo.get_roi_from_buffer(buffer).get_objects_typed(hailo.HAILO_DETECTION)
people_count = 0
for det in detections:
if det.get_label() == "person":
people_count = people_count + 1
user_data.total_people = user_data.total_people + people_count
user_data.total_frames = user_data.total_frames + 1
if user_data.total_frames > 0:
running_average = user_data.total_people / user_data.total_frames
else:
running_average = 0.0
string_to_print = (
"Frame count: " + str(user_data.get_count()) + "\n"
+ "People detected in frame: " + str(people_count) + "\n"
+ "Running average people per frame: " + str(round(running_average, 2)) + "\n"
)
for detection in detections:
string_to_print = string_to_print + (
"Detection: " + detection.get_label() + " Confidence: " + str(round(detection.get_confidence(), 2)) + "\n"
)
print(string_to_print)
return Gst.PadProbeReturn.OK
if __name__ == "__main__":
user_data = user_app_callback_class()
app = GStreamerDetectionSimpleApp(app_callback, user_data)
app.run()This script demonstrates how to:
- Define a callback class to keep state.
- Implement a simple callback function to process detections and print statistics.
- Set up and run the detection pipeline.
To run your application, execute your Python script from the terminal. The application will automatically handle command-line arguments for inputs, HEF files, and other parameters. For example, to run the example code above paste it in 'example.py' and run the application with the USB camera as input:
python example.py --input usbYou can build your own pipelines from scratch or modify our examples to suit different data flow requirements.
Building a custom pipeline involves creating a Python class that inherits from our GStreamerApp base class. This approach allows you to leverage the core application logic (like window management and bus handling) while defining your own unique pipeline structure.
The process is straightforward:
-
Create a Class Inheriting from
GStreamerApp: Your application will be a new class that extendsGStreamerApp. In the constructor (__init__), you'll call the parent constructor and set up any application-specific parameters, such as HEF paths, video sources, or model thresholds. -
Override the
get_pipeline_stringMethod: This is where you define your GStreamer pipeline. You must implement this method in your class. Inside, you'll useself.get_source_pipeline()for the source (which automatically handles common settings like mirror/flip flags) and helper functions fromgstreamer_helper_pipelines.py(likeINFERENCE_PIPELINE,DISPLAY_PIPELINE, etc.) for the rest of the pipeline. -
Run Your Application: In your main execution block, you instantiate your new class and call its
run()method, which handles the pipeline setup and execution.
Here is a simplified example based on detection_simple_pipeline.py that illustrates the concept:
# Import necessary classes and pipeline helpers
from hailo_apps.python.core.gstreamer.gstreamer_app import GStreamerApp
from hailo_apps.python.core.gstreamer.gstreamer_helper_pipelines import (
INFERENCE_PIPELINE, DISPLAY_PIPELINE
)
# 1. Create a class that inherits from GStreamerApp
class MyCustomPipelineApp(GStreamerApp):
def __init__(self, args, user_data):
# Call the parent constructor
super().__init__(args, user_data)
# Set up application-specific properties
self.hef_path = "path/to/your/model.hef"
self.post_process_so = "path/to/your/postprocess.so"
# ... other parameters
# 2. Override this method to define your pipeline
def get_pipeline_string(self):
# Use self.get_source_pipeline() — automatically injects
# video_source, resolution, frame_rate, mirror/flip flags, etc.
source = self.get_source_pipeline()
infer = INFERENCE_PIPELINE(hef_path=self.hef_path, ...)
display = DISPLAY_PIPELINE(...)
# Link the components together with '!'
pipeline_string = f"{source} ! {infer} ! {display}"
print(pipeline_string)
return pipeline_string
# 3. Run the application
if __name__ == "__main__":
# app_callback and user_data are for the basic path; can be simple for this case
app = MyCustomPipelineApp(app_callback=dummy_callback, user_data=app_callback_class())
app.run()You have full control to reorder, remove, or add new GStreamer elements in the string returned by get_pipeline_string to create your desired data flow.
Note: For detailed information about all available helper functions and their parameters, see the GStreamer Helper Pipelines Reference.
The following patterns are examples for commonly used pipeline architectures. Most common patterns are already implemented in the GStreamerHelperPipelines. It is highly recommended to use the helper functions to build your pipeline, but you can also build your own pipeline string from scratch.
Use case: Run a single AI model (e.g., object detection) on a video stream.
flowchart LR
A[Source] --> B[Video conversion] --> C[hailonet] --> D[hailofilter] --> E[hailooverlay] --> F[Display]
A high level code example for building this pipeline using the helper functions:
from hailo_apps.python.core.gstreamer.gstreamer_helper_pipelines import (
SOURCE_PIPELINE, INFERENCE_PIPELINE, DISPLAY_PIPELINE
)
pipeline_string = (
f"{SOURCE_PIPELINE(video_source='input.mp4')} ! "
f"{INFERENCE_PIPELINE(hef_path='model.hef', post_process_so='post.so')} ! "
f"{DISPLAY_PIPELINE()}"
)See also: For complete examples and detailed function documentation, refer to the GStreamer Helper Pipelines Reference.
Use case: Run inference on a scaled-down version of the video for performance, but display the original high-res video with overlays.
flowchart LR
A[Source] --> C
subgraph INFERENCE_PIPELINE_WRAP
C[hailocropper]
C -- Resized Frame --> I[Inner Pipeline]
C -- Original Frame --> Q[Queue]
I --> AGG(hailoaggregator)
Q --> AGG
end
AGG -- Original Frame with AI Metadata --> F[hailooverlay] --> G[Display]
A high level code example for building this pipeline using the helper functions:
from hailo_apps.python.core.gstreamer.gstreamer_helper_pipelines import (
SOURCE_PIPELINE, INFERENCE_PIPELINE, INFERENCE_PIPELINE_WRAPPER, DISPLAY_PIPELINE
)
inner_infer = INFERENCE_PIPELINE(hef_path='model.hef', post_process_so='post.so')
inner_infer_wrapper = INFERENCE_PIPELINE_WRAPPER(inner_infer)
pipeline_string = (
f"{SOURCE_PIPELINE(video_source='input.mp4')} ! "
f"{inner_infer_wrapper} ! "
f"{DISPLAY_PIPELINE()}"
)See also: For detailed documentation on
INFERENCE_PIPELINE_WRAPPER, see the GStreamer Helper Pipelines Reference.
Use case: Run two or more models in series (e.g., detection followed by classification on detected regions).
flowchart LR
A[Source] --> B[NN1: Detection] --> C
subgraph CROPPER_PIPELINE
C[hailocropper]
C -- Cropped Detections --> D[NN2: Classification]
C -- Original Frame --> Q[Queue]
D --> AGG(hailoaggregator)
Q --> AGG
end
AGG -- Detections with class --> F[Display]
A high level code example for building this pipeline using the helper functions.
Note that the CROPPER_PIPELINE is a helper function that is used to crop the detections from the first network and pass them to the second network. You can control the cropper by passing a custom C++ function to it.
from hailo_apps.python.core.gstreamer.gstreamer_helper_pipelines import (
SOURCE_PIPELINE, INFERENCE_PIPELINE, CROPPER_PIPELINE, DISPLAY_PIPELINE
)
first_infer = INFERENCE_PIPELINE(hef_path='detector.hef', post_process_so='detector_post.so')
second_infer = INFERENCE_PIPELINE(hef_path='classifier.hef', post_process_so='classifier_post.so')
cropper = CROPPER_PIPELINE(second_infer, so_path='cropper.so', function_name='crop_func')
pipeline_string = (
f"{SOURCE_PIPELINE(video_source='input.mp4')} ! "
f"{first_infer} ! {cropper} ! {DISPLAY_PIPELINE()}"
)See also: For detailed documentation on
CROPPER_PIPELINEand its parameters, see the GStreamer Helper Pipelines Reference.
Use case: Run multiple models in parallel on the same input stream and combine their results.
flowchart TD
A[Source] --> T(tee)
T --> C1[NN Chain 1] --> M(hailomuxer)
T --> C2[NN Chain 2] --> M
M --> E[Overlay] --> F[Display]
A high level code example for building this pipeline using the helper functions.
from hailo_apps.python.core.gstreamer.gstreamer_helper_pipelines import (
SOURCE_PIPELINE, INFERENCE_PIPELINE, DISPLAY_PIPELINE
)
pipeline_string = (
f"{SOURCE_PIPELINE(video_source='input.mp4')} ! tee name=t "
f"t. ! {INFERENCE_PIPELINE(hef_path='model1.hef', post_process_so='post1.so')} ! hailomuxer name=m "
f"t. ! {INFERENCE_PIPELINE(hef_path='model2.hef', post_process_so='post2.so')} ! m. "
f"m. ! {DISPLAY_PIPELINE()}"
)Use case: Split a high-res frame into tiles, run inference on each, and aggregate results for display.
flowchart LR
A[Source] --> C
subgraph TILE_CROPPER_PIPELINE
C[hailotilecropper]
C -- Tile --> I[Inner Pipeline]
C -- Original Frame --> Q[Queue]
I --> AGG(hailotileaggregator)
Q --> AGG
end
AGG -- Original Frame with AI Metadata --> F[hailooverlay] --> G[Display]
A high level code example for building this pipeline using the helper functions:
from hailo_apps.python.core.gstreamer.gstreamer_helper_pipelines import (
SOURCE_PIPELINE, INFERENCE_PIPELINE, TILE_CROPPER_PIPELINE, DISPLAY_PIPELINE
)
tile_inference = INFERENCE_PIPELINE(hef_path='model.hef', post_process_so='post.so')
tile_cropper = TILE_CROPPER_PIPELINE(
inner_pipeline=tile_inference,
tiles_along_x_axis=4,
tiles_along_y_axis=3,
overlap_x_axis=0.1
)
pipeline_string = (
f"{SOURCE_PIPELINE(video_source='input.mp4')} ! "
f"{tile_cropper} ! {DISPLAY_PIPELINE()}"
)See also: For detailed documentation on
TILE_CROPPER_PIPELINEand its parameters, see the GStreamer Helper Pipelines Reference.
See Retraining your own models for more information.
Compiling your own models is out of the scope of this repository. See our hailo_model_zoo for additional supported models. For adding your own models, see our Data Flow Compiler (DFC) documentation in Hailo Developer Zone (Requires registration) for more information.
Once you have a new model, you will need to add a Post Process Function to the model. See Writing Your Own Post-Process for Hailo Apps for more information.