GstCUDA Wrapper Usability

From RidgeRun Developer Wiki

Follow Us On Twitter LinkedIn Email Share this page



Previous: GstCUDA Wrapper - Getting Started Index Next: Performance Profiling





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)


Previous: GstCUDA Wrapper - Getting Started Index Next: Performance Profiling