#include #include #include "gstmyosd.h" GST_DEBUG_CATEGORY_STATIC(gst_myosd_debug); #define GST_CAT_DEFAULT gst_myosd_debug /* Enum to identify properties */ enum { PROP_0, PROP_TEXT, PROP_X_OFFSET, PROP_Y_OFFSET, PROP_FONT_ALPHA, PROP_FONT, PROP_FONT_SIZE, PROP_FONT_COLOR }; /* Default values for properties */ #define DEFAULT_TEXT_CONTENT NULL #define DEFAULT_TEXT_FONT_ALPHA 255 #define DEFAULT_TEXT_FONT (gchar*)"Noto Serif CJK SC Light" #define DEFAULT_TEXT_FONT_SIZE 25 #define DEFAULT_TEXT_FONT_COLOR 16777215 #define GST_OMX_MEMORY_TYPE "openmax" #define GST_NV_FILTER_MEMORY_TYPE "nvfilter" #define GST_NV_V4L2_MEMORY_TYPE "V4l2Memory" #define GST_NVARGUS_MEMORY_TYPE "nvarguscam" /* By default NVIDIA Hardware allocated memory flows through the pipeline. We * will be processing on this type of memory only. */ #define GST_CAPS_FEATURE_MEMORY_NVMM "memory:NVMM" static GstStaticPadTemplate gst_myosd_sink_template = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_NVMM, "{ RGBA }"))); static GstStaticPadTemplate gst_myosd_src_template = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS(GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_NVMM, "{ RGBA }"))); /* Define our element type. Standard GObject/GStreamer boilerplate stuff */ #define gst_myosd_parent_class parent_class G_DEFINE_TYPE(GstMyOsd, gst_myosd, GST_TYPE_BASE_TRANSFORM); static void gst_myosd_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_myosd_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_myosd_set_caps(GstBaseTransform * btrans, GstCaps * incaps, GstCaps * outcaps); static gboolean gst_myosd_start(GstBaseTransform * btrans); static gboolean gst_myosd_stop(GstBaseTransform * btrans); static void set_text_params(GstMyOsd* osd); static GstFlowReturn gst_myosd_transform_ip(GstBaseTransform * btrans, GstBuffer * inbuf); /* Install properties, set sink and src pad capabilities, override the required * functions of the base class, These are common to all instances of the * element. */ static void gst_myosd_class_init(GstMyOsdClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBaseTransformClass *gstbasetransform_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstbasetransform_class = (GstBaseTransformClass *) klass; /* Overide base class functions */ gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_myosd_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_myosd_get_property); gstbasetransform_class->set_caps = GST_DEBUG_FUNCPTR(gst_myosd_set_caps); gstbasetransform_class->start = GST_DEBUG_FUNCPTR(gst_myosd_start); gstbasetransform_class->stop = GST_DEBUG_FUNCPTR(gst_myosd_stop); gstbasetransform_class->transform_ip = GST_DEBUG_FUNCPTR(gst_myosd_transform_ip); /* Install properties */ g_object_class_install_property (gobject_class, PROP_TEXT, g_param_spec_string ("text", "Text", "Text to be display", DEFAULT_TEXT_CONTENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_X_OFFSET, g_param_spec_int ("x-offset", "Set x offset", "Set x offset, positive number means from left to right, negative number means from right to left", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)); g_object_class_install_property (gobject_class, PROP_Y_OFFSET, g_param_spec_int ("y-offset", "Set y offset", "Set y offset, positive number means from top to bottom, negative number means from bottom to top", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)); g_object_class_install_property (gobject_class, PROP_FONT_ALPHA, g_param_spec_uint ("font-alpha", "Set Font Alpha", "Set Font Alpha", 0, G_MAXUINT, DEFAULT_TEXT_FONT_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)); g_object_class_install_property(gobject_class, PROP_FONT, g_param_spec_string ("font", "Font", "Pango font description of font to be used for rendering", DEFAULT_TEXT_FONT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_FONT_SIZE, g_param_spec_uint ("font-size", "Set Font Size", "Set Font Size", 0, G_MAXUINT, DEFAULT_TEXT_FONT_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)); g_object_class_install_property (gobject_class, PROP_FONT_COLOR, g_param_spec_uint ("font-color", "Set Font Color", "Set Font Color, use 3 bytes represent R:G:B", 0, G_MAXUINT, DEFAULT_TEXT_FONT_COLOR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)); /* Set sink and src pad capabilities */ gst_element_class_add_pad_template(gstelement_class, gst_static_pad_template_get(&gst_myosd_src_template)); gst_element_class_add_pad_template(gstelement_class, gst_static_pad_template_get(&gst_myosd_sink_template)); /* Set metadata describing the element */ gst_element_class_set_details_simple(gstelement_class, "MyOsd plugin", "MyOsd Plugin", "Process a 3rdparty osd algorithm on objects / full frame", "NVIDIA Corporation. Post on Deepstream for Tesla forum for any queries " "@ https://devtalk.nvidia.com/default/board/209/"); } static void gst_myosd_init(GstMyOsd * myosd) { GstBaseTransform *btrans = GST_BASE_TRANSFORM(myosd); /* We will not be generating a new buffer. Just adding / updating * metadata. */ gst_base_transform_set_in_place(GST_BASE_TRANSFORM(btrans), TRUE); /* We do not want to change the input caps. Set to passthrough. transform_ip * is still called. */ gst_base_transform_set_passthrough(GST_BASE_TRANSFORM(btrans), TRUE); /* Initialize all property variables to default values */ myosd->text = g_strdup(DEFAULT_TEXT_CONTENT); myosd->x_offset = 0; myosd->y_offset = 0; myosd->font_alpha = DEFAULT_TEXT_FONT_ALPHA; myosd->font = g_strdup(DEFAULT_TEXT_FONT); myosd->font_size = DEFAULT_TEXT_FONT_SIZE; myosd->font_color = DEFAULT_TEXT_FONT_COLOR; } /* Function called when a property of the element is set. Standard boilerplate. */ static void gst_myosd_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstMyOsd *myosd = GST_MYOSD(object); switch(prop_id) { case PROP_TEXT: if (!g_value_get_string (value)) { GST_WARNING ("Text cannot be NULL"); break; } g_free (myosd->text); myosd->text = g_strdup (g_value_get_string (value)); break; case PROP_X_OFFSET: myosd->x_offset = g_value_get_int (value); break; case PROP_Y_OFFSET: myosd->y_offset = g_value_get_int (value); break; case PROP_FONT_ALPHA: myosd->font_alpha = g_value_get_uint (value); break; case PROP_FONT: g_free (myosd->font); myosd->font = g_strdup(g_value_get_string(value)); break; case PROP_FONT_SIZE: myosd->font_size = g_value_get_uint (value); break; case PROP_FONT_COLOR: myosd->font_color = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } /* Function called when a property of the element is requested. Standard * boilerplate. */ static void gst_myosd_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstMyOsd *myosd = GST_MYOSD(object); switch(prop_id) { case PROP_TEXT: g_value_set_string(value, myosd->text); break; case PROP_X_OFFSET: g_value_set_int (value, myosd->x_offset); break; case PROP_Y_OFFSET: g_value_set_int (value, myosd->y_offset); break; case PROP_FONT_ALPHA: g_value_set_uint (value, myosd->font_alpha); break; case PROP_FONT: g_value_set_string (value, myosd->font); break; case PROP_FONT_SIZE: g_value_set_uint (value, myosd->font_size); break; case PROP_FONT_COLOR: g_value_set_uint (value, myosd->font_color); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } /**gst_myosd_start * Initialize all resources and start the output thread */ static gboolean gst_myosd_start(GstBaseTransform * btrans) { GstMyOsd *myosd = GST_MYOSD(btrans); myosd->nvosd_ctx = nvosd_create_context(); if(!myosd->nvosd_ctx) { GST_ERROR_OBJECT(myosd, "Create nvosd context failed."); goto error; } return TRUE; error: if(myosd->nvosd_ctx) nvosd_destroy_context(myosd->nvosd_ctx); return FALSE; } /** * Stop the output thread and free up all the resources */ static gboolean gst_myosd_stop(GstBaseTransform * btrans) { GstMyOsd *myosd = GST_MYOSD(btrans); if (myosd->text){ g_free(myosd->text); myosd->text = NULL; } if (myosd->font){ g_free(myosd->font); myosd->font = NULL; } if(myosd->nvosd_ctx) nvosd_destroy_context(myosd->nvosd_ctx); GST_DEBUG_OBJECT(myosd, "ctx lib released \n"); return TRUE; } /** * Called when source / sink pad capabilities have been negotiated. */ static gboolean gst_myosd_set_caps(GstBaseTransform * btrans, GstCaps * incaps, GstCaps * outcaps) { GstMyOsd *myosd = GST_MYOSD(btrans); /* Save the input video information, since this will be required later. */ gst_video_info_from_caps(&myosd->video_info, incaps); set_text_params(myosd); return TRUE; } /** * Set the text osd parameters */ static void set_text_params(GstMyOsd* osd) { osd->text_params.display_text = osd->text ? : strdup("nvosd overlay text"); osd->text_params.x_offset = osd->x_offset; osd->text_params.y_offset = osd->y_offset; osd->text_params.font_params.font_name = osd->font; osd->text_params.font_params.font_size = osd->font_size; osd->text_params.font_params.font_color.red = (osd->font_color>>16)/255.0; osd->text_params.font_params.font_color.green = ((osd->font_color & (0xff00))>>8)/255.0; osd->text_params.font_params.font_color.blue = (osd->font_color & (0xff))/255.0; osd->text_params.font_params.font_color.alpha = osd->font_alpha/255.0; osd->text_params.set_bg_clr = 1; osd->text_params.text_bg_clr.red = 0.0; osd->text_params.text_bg_clr.green = 0.0; osd->text_params.text_bg_clr.blue = 0.0; osd->text_params.text_bg_clr.alpha = 0.7; } /** * Called when element recieves an input buffer from upstream element. */ static GstFlowReturn gst_myosd_transform_ip(GstBaseTransform * btrans, GstBuffer * inbuf) { GstMyOsd *myosd = GST_MYOSD(btrans); GstMapInfo inmap; GstFlowReturn flow_ret = GST_FLOW_ERROR; GstMemory *inmem = NULL; gint ret = 0; gint dmabuf_fd = -1; inmem = gst_buffer_peek_memory(inbuf, 0); if (!inmem) { GST_ERROR_OBJECT(myosd, "no input memory block"); goto error; } if (!g_strcmp0(inmem->allocator->mem_type, GST_OMX_MEMORY_TYPE) || !g_strcmp0(inmem->allocator->mem_type, GST_NV_FILTER_MEMORY_TYPE) || !g_strcmp0(inmem->allocator->mem_type, GST_NVARGUS_MEMORY_TYPE) || !g_strcmp0(inmem->allocator->mem_type, GST_NV_V4L2_MEMORY_TYPE)) { if (!gst_buffer_map(inbuf, &inmap, GST_MAP_WRITE)) { GST_ERROR_OBJECT(myosd, "input buffer mapinfo failed"); goto error; } ret = ExtractFdFromNvBuffer(inmap.data, &dmabuf_fd); if (ret != 0) { GST_ERROR_OBJECT(myosd, "ExtractFdFromNvBuffer failed"); gst_buffer_unmap(inbuf, &inmap); goto error; } nvosd_put_text(myosd->nvosd_ctx, MODE_GPU, dmabuf_fd, 1, &myosd->text_params); gst_buffer_unmap(inbuf, &inmap); // release input_dmabuf_fds if (!g_strcmp0(inmem->allocator->mem_type, GST_OMX_MEMORY_TYPE)) { ret = NvReleaseFd(dmabuf_fd); if (ret != 0) { GST_ERROR_OBJECT(myosd, "NvReleaseFd failed"); goto error; } } flow_ret = GST_FLOW_OK; } error: return flow_ret; } /** * Boiler plate for registering a plugin and an element. */ static gboolean myosd_plugin_init(GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT(gst_myosd_debug, "myosd", 0, "myosd plugin"); return gst_element_register(plugin, "myosd", GST_RANK_PRIMARY, GST_TYPE_MYOSD); } GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, myosd, DESCRIPTION, myosd_plugin_init, "0.1.0", LICENSE, BINARY_PACKAGE, URL)