GstCUDA Wrapper Usability
The GstCudaWrapper provides a compatibility layer that allows developers to create CUDA-accelerated GStreamer elements using a RidgeRun-style API, while internally relying on the open-source GStreamer CUDA framework.
This page explains:
- The core data structures exposed by the wrapper
- The execution model of a SISO (Single Input Single Output) element
- How to create a new SISO element using the wrapper
- How the wrapper maps RidgeRun GstCUDA concepts to GStreamer CUDA
GstCudaWrapper Programming Model
The wrapper follows the same design philosophy as RidgeRun GstCUDA:
- Elements operate on GPU memory
- CUDA execution is abstracted from GStreamer mechanics
- Developers focus on algorithm logic, not buffer handling
From the developer’s point of view, a wrapper-based element behaves like a traditional RidgeRun GstCUDA element.
Core Data Structures
GstRRCudaWrapperFrame
The GstRRCudaWrapperFrame represents a video frame backed by CUDA memory.
It encapsulates:
- A pointer to GPU-accessible memory
- Frame dimensions (width, height)
- Pixel format
- CUDA stream association
This structure replaces direct access to GstBuffer and hides whether the underlying memory is:
- Unified Memory (RidgeRun backend)
- CudaMemory (GStreamer CUDA backend)
The wrapper guarantees a consistent interface regardless of backend.
GstRRCudaWrapperStream
The GstRRCudaWrapperStream abstracts a CUDA stream (cudaStream_t).
Key properties:
- Each element instance owns or reuses a stream
- Synchronization is handled by the wrapper
- Developers should assume all operations occur asynchronously unless stated otherwise
This enables efficient pipelining without explicit stream management in element code.
GstRRCudaWrapperFormat
The GstRRCudaWrapperFormat enumeration defines supported pixel formats.
It mirrors RidgeRun GstCUDA formats and is mapped internally to:
- GStreamer CUDA formats
- Supported caps in the negotiation phase
This allows elements to declare formats once and remain backend-agnostic.
The documentation regarding data structures is available here.
SISO Element Execution Model
A SISO element:
- Receives one input buffer
- Produces one output buffer
- Applies a CUDA-based transformation
The wrapper implements this model by mapping to:
- GstRRCudaBaseFilter (GStreamer CUDA)
- GstBaseTransform (GStreamer core)
The developer does not interact with these base classes directly.
Basic Element
Please, find a basic element below:
#include <cuda_runtime.h>
#include <gst/gst.h>
#include <rrcudawrapper/gstrrcudawrapperbasetransform.h>
G_BEGIN_DECLS
#define GST_TYPE_RR_CUDA_WRAPPER_FILTER (gst_rr_cuda_wrapper_filter_get_type())
G_DECLARE_FINAL_TYPE(GstRRCudaWrapperFilter, gst_rr_cuda_wrapper_filter,
GST_RR_CUDA_WRAPPER, FILTER, GstRRCudaWrapperBaseTransform)
G_END_DECLS
GST_DEBUG_CATEGORY_STATIC(gst_rr_cuda_wrapper_filter_debug_category);
#define GST_CAT_DEFAULT gst_rr_cuda_wrapper_filter_debug_category
/* prototypes */
static void gst_rr_cuda_wrapper_filter_set_property(GObject* object,
guint property_id,
const GValue* value,
GParamSpec* pspec);
static void gst_rr_cuda_wrapper_filter_get_property(GObject* object,
guint property_id,
GValue* value,
GParamSpec* pspec);
static GstFlowReturn gst_rr_cuda_wrapper_filter_transform_frame(
GstRRCudaWrapperBaseTransform* trans, GstRRCudaWrapperFrame* inframe,
GstRRCudaWrapperFrame* outframe);
static GstFlowReturn gst_rr_cuda_wrapper_filter_transform_frame_ip(
GstRRCudaWrapperBaseTransform* trans, GstRRCudaWrapperFrame* frame);
static gboolean gst_rr_cuda_wrapper_filter_start(
GstRRCudaWrapperBaseTransform* trans);
enum {
PROP_0,
PROP_IN_PLACE,
};
#define DEFAULT_PROP_IN_PLACE FALSE
#define FORMAT_FILTER_CAPS GST_RR_CUDA_WRAPPER_VIDEO_CAPS("{RGBA, I420}")
struct _GstRRCudaWrapperFilter {
GstRRCudaWrapperBaseTransform base;
gboolean in_place;
};
G_DEFINE_TYPE_WITH_CODE(
GstRRCudaWrapperFilter, gst_rr_cuda_wrapper_filter,
GST_TYPE_RR_CUDA_WRAPPER_BASE_TRANSFORM,
GST_DEBUG_CATEGORY_INIT(gst_rr_cuda_wrapper_filter_debug_category,
"rrcudawrapperfilter", 0,
"debug category for rrcudawrapperfilter element"));
static void gst_rr_cuda_wrapper_filter_class_init(
GstRRCudaWrapperFilterClass* klass) {
GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
GstRRCudaWrapperBaseTransformClass* cuda_base_transform_class =
GST_RR_CUDA_WRAPPER_BASE_TRANSFORM_CLASS(klass);
gst_element_class_add_pad_template(
GST_ELEMENT_CLASS(klass),
gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS,
gst_caps_from_string(FORMAT_FILTER_CAPS)));
gst_element_class_add_pad_template(
GST_ELEMENT_CLASS(klass),
gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
gst_caps_from_string(FORMAT_FILTER_CAPS)));
gst_element_class_set_static_metadata(
GST_ELEMENT_CLASS(klass), "GstRRCuda Wrapper Filter", "filter",
"Example implementation of a filter using GstRRCuda Wrapper",
"RidgeRun <support@ridgerun.com>");
gobject_class->set_property = gst_rr_cuda_wrapper_filter_set_property;
gobject_class->get_property = gst_rr_cuda_wrapper_filter_get_property;
g_object_class_install_property(
gobject_class, PROP_IN_PLACE,
g_param_spec_boolean(
"in-place", "in-place mode",
"Uses the in-place mode to check the functionality. ",
DEFAULT_PROP_IN_PLACE,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
cuda_base_transform_class->transform_frame =
GST_DEBUG_FUNCPTR(gst_rr_cuda_wrapper_filter_transform_frame);
cuda_base_transform_class->transform_frame_ip =
GST_DEBUG_FUNCPTR(gst_rr_cuda_wrapper_filter_transform_frame_ip);
cuda_base_transform_class->start =
GST_DEBUG_FUNCPTR(gst_rr_cuda_wrapper_filter_start);
}
static void gst_rr_cuda_wrapper_filter_init(GstRRCudaWrapperFilter* self) {
self->in_place = DEFAULT_PROP_IN_PLACE;
}
static void gst_rr_cuda_wrapper_filter_set_property(GObject* object,
guint property_id,
const GValue* value,
GParamSpec* pspec) {
GstRRCudaWrapperFilter* self = GST_RR_CUDA_WRAPPER_FILTER(object);
GST_DEBUG_OBJECT(self, "set_property");
switch (property_id) {
case PROP_IN_PLACE:
self->in_place = g_value_get_boolean(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void gst_rr_cuda_wrapper_filter_get_property(GObject* object,
guint property_id,
GValue* value,
GParamSpec* pspec) {
GstRRCudaWrapperFilter* self = GST_RR_CUDA_WRAPPER_FILTER(object);
GST_DEBUG_OBJECT(self, "get_property");
switch (property_id) {
case PROP_IN_PLACE:
g_value_set_boolean(value, self->in_place);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static gboolean gst_rr_cuda_wrapper_filter_start(
GstRRCudaWrapperBaseTransform* trans) {
GstRRCudaWrapperFilter* self = GST_RR_CUDA_WRAPPER_FILTER(trans);
gst_base_transform_set_in_place(GST_BASE_TRANSFORM(trans), self->in_place);
return TRUE;
}
static GstFlowReturn gst_rr_cuda_wrapper_filter_transform_frame(
GstRRCudaWrapperBaseTransform* trans, GstRRCudaWrapperFrame* inframe,
GstRRCudaWrapperFrame* outframe) {
GstRRCudaWrapperFilter* self = GST_RR_CUDA_WRAPPER_FILTER(trans);
cudaStream_t* stream = NULL;
gsize input_size = 0;
gsize output_size = 0;
GST_DEBUG_OBJECT(self, "transform_frame");
input_size = gst_buffer_get_size(inframe->ref);
output_size = gst_buffer_get_size(outframe->ref);
if (input_size != output_size) {
GST_ERROR_OBJECT(self, "Output size: %lu Input size: %lu", output_size,
input_size);
return GST_FLOW_ERROR;
}
/* Copy frame since it is not in-place */
stream = (cudaStream_t*)(inframe->cuda_data.stream);
for (int i = 0; i < inframe->cuda_data.num_planes; ++i) {
gpointer in_data = inframe->cuda_data.channels[i].data;
gpointer out_data = outframe->cuda_data.channels[i].data;
output_size = outframe->cuda_data.channels[i].pitch * outframe->cuda_data.channels[i].height;
cudaMemcpyAsync(out_data, in_data, output_size, cudaMemcpyDeviceToDevice,
*stream);
}
cudaStreamSynchronize(*stream);
return GST_FLOW_OK;
}
static GstFlowReturn gst_rr_cuda_wrapper_filter_transform_frame_ip(
GstRRCudaWrapperBaseTransform* trans, GstRRCudaWrapperFrame* frame) {
GstRRCudaWrapperFilter* self = GST_RR_CUDA_WRAPPER_FILTER(trans);
GST_DEBUG_OBJECT(self, "transform_frame_ip");
return GST_FLOW_OK;
}
static gboolean plugin_init(GstPlugin* plugin) {
return gst_element_register(plugin, "rrcudawrapperfilter", GST_RANK_NONE,
GST_TYPE_RR_CUDA_WRAPPER_FILTER);
}
GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, rrcudawrapperfilter,
"Example Cuda Wrapper Filter", plugin_init, VERSION,
"Proprietary", PACKAGE_NAME, GST_PACKAGE_ORIGIN)