#include #include #include #include #include #include #include "nvll_osd_struct.h" #include "nvll_osd_api.h" #include "gstdsosd.h" #include #include GST_DEBUG_CATEGORY_STATIC(gst_dsosd_debug); #define GST_CAT_DEFAULT gst_dsosd_debug static GQuark _dsmeta_quark = 0; /* Enum to identify properties */ enum { PROP_0, PROP_UNIQUE_ID, PROP_GPU_DEVICE_ID }; #define CHECK_NVDS_MEMORY_AND_GPUID(object, surface) \ ({ int _errtype=0;\ do { \ if ((surface->memType == NVBUF_MEM_DEFAULT || surface->memType == NVBUF_MEM_CUDA_DEVICE) && \ (surface->gpuId != object->gpu_id)) { \ GST_ELEMENT_ERROR (object, RESOURCE, FAILED, \ ("Input surface gpu-id doesnt match with configured gpu-id for element," \ " please allocate input using unified memory, or use same gpu-ids"),\ ("surface-gpu-id=%d,%s-gpu-id=%d",surface->gpuId,GST_ELEMENT_NAME(object),\ object->gpu_id)); \ _errtype = 1;\ } \ } while(0); \ _errtype; \ }) /* Default values for properties */ #define DEFAULT_UNIQUE_ID 15 #define DEFAULT_GPU_ID 0 #define RGB_BYTES_PER_PIXEL 3 #define RGBA_BYTES_PER_PIXEL 4 #define Y_BYTES_PER_PIXEL 1 #define UV_BYTES_PER_PIXEL 2 #define MIN_INPUT_OBJECT_WIDTH 16 #define MIN_INPUT_OBJECT_HEIGHT 16 #define CHECK_NPP_STATUS(npp_status,error_str) do { \ if ((npp_status) != NPP_SUCCESS) { \ g_print ("Error: %s in %s at line %d: NPP Error %d\n", \ error_str, __FILE__, __LINE__, npp_status); \ goto error; \ } \ } while (0) #define CHECK_CUDA_STATUS(cuda_status,error_str) do { \ if ((cuda_status) != cudaSuccess) { \ g_print ("Error: %s in %s at line %d (%s)\n", \ error_str, __FILE__, __LINE__, cudaGetErrorName(cuda_status)); \ goto error; \ } \ } while (0) /* 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_dsosd_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_dsosd_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_dsosd_parent_class parent_class G_DEFINE_TYPE(GstDsOsd, gst_dsosd, GST_TYPE_BASE_TRANSFORM); static void gst_dsosd_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_dsosd_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_dsosd_set_caps(GstBaseTransform * btrans, GstCaps * incaps, GstCaps * outcaps); static gboolean gst_dsosd_start(GstBaseTransform * btrans); static gboolean gst_dsosd_stop(GstBaseTransform * btrans); static GstFlowReturn gst_dsosd_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_dsosd_class_init(GstDsOsdClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBaseTransformClass *gstbasetransform_class; // Indicates we want to use DS buf api g_setenv("DS_NEW_BUFAPI", "1", TRUE); gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstbasetransform_class = (GstBaseTransformClass *) klass; /* Overide base class functions */ gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_dsosd_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_dsosd_get_property); gstbasetransform_class->set_caps = GST_DEBUG_FUNCPTR(gst_dsosd_set_caps); gstbasetransform_class->start = GST_DEBUG_FUNCPTR(gst_dsosd_start); gstbasetransform_class->stop = GST_DEBUG_FUNCPTR(gst_dsosd_stop); gstbasetransform_class->transform_ip = GST_DEBUG_FUNCPTR(gst_dsosd_transform_ip); /* Install properties */ g_object_class_install_property(gobject_class, PROP_UNIQUE_ID, g_param_spec_uint("unique-id", "Unique ID", "Unique ID for the element. Can be used to identify output of the" " element", 0, G_MAXUINT, DEFAULT_UNIQUE_ID, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property(gobject_class, PROP_GPU_DEVICE_ID, g_param_spec_uint("gpu-id", "Set GPU Device ID", "Set GPU Device ID", 0, G_MAXUINT, 0, GParamFlags (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_dsosd_src_template)); gst_element_class_add_pad_template(gstelement_class, gst_static_pad_template_get(&gst_dsosd_sink_template)); /* Set metadata describing the element */ gst_element_class_set_details_simple(gstelement_class, "DsOsd plugin", "DsOsd 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_dsosd_init(GstDsOsd * dsosd) { GstBaseTransform *btrans = GST_BASE_TRANSFORM(dsosd); /* 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 */ dsosd->unique_id = DEFAULT_UNIQUE_ID; dsosd->gpu_id = DEFAULT_GPU_ID; /* This quark is required to identify NvDsMeta when iterating through * the buffer metadatas */ if(!_dsmeta_quark) _dsmeta_quark = g_quark_from_static_string(NVDS_META_STRING); } /* Function called when a property of the element is set. Standard boilerplate. */ static void gst_dsosd_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstDsOsd *dsosd = GST_DSOSD(object); switch(prop_id) { case PROP_UNIQUE_ID: dsosd->unique_id = g_value_get_uint(value); break; case PROP_GPU_DEVICE_ID: dsosd->gpu_id = 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_dsosd_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstDsOsd *dsosd = GST_DSOSD(object); switch(prop_id) { case PROP_UNIQUE_ID: g_value_set_uint(value, dsosd->unique_id); break; case PROP_GPU_DEVICE_ID: g_value_set_uint(value, dsosd->gpu_id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } /** * Initialize all resources and start the output thread */ static gboolean gst_dsosd_start(GstBaseTransform * btrans) { GstDsOsd *dsosd = GST_DSOSD(btrans); NvBufSurfaceCreateParams create_params; GstQuery *queryparams = NULL; guint batch_size = 1; CHECK_CUDA_STATUS(cudaSetDevice(dsosd->gpu_id), "Unable to set cuda device"); dsosd->batch_size = batch_size; queryparams = gst_nvquery_batch_size_new(); if(gst_pad_peer_query(GST_BASE_TRANSFORM_SINK_PAD(btrans), queryparams) || gst_pad_peer_query(GST_BASE_TRANSFORM_SRC_PAD(btrans), queryparams)) { if(gst_nvquery_batch_size_parse(queryparams, &batch_size)) { dsosd->batch_size = batch_size; } } GST_DEBUG_OBJECT(dsosd, "Setting batch-size %d \n", dsosd->batch_size); gst_query_unref(queryparams); dsosd->nvosd_ctx = nvll_osd_create_context(); if(!dsosd->nvosd_ctx) goto error; GST_DEBUG_OBJECT(dsosd, "created CV Mat\n"); return TRUE; error: if(dsosd->nvosd_ctx) nvll_osd_destroy_context(dsosd->nvosd_ctx); return FALSE; } /** * Stop the output thread and free up all the resources */ static gboolean gst_dsosd_stop(GstBaseTransform * btrans) { GstDsOsd *dsosd = GST_DSOSD(btrans); if(dsosd->nvosd_ctx) nvll_osd_destroy_context(dsosd->nvosd_ctx); GST_DEBUG_OBJECT(dsosd, "ctx lib released \n"); return TRUE; } /** * Called when source / sink pad capabilities have been negotiated. */ static gboolean gst_dsosd_set_caps(GstBaseTransform * btrans, GstCaps * incaps, GstCaps * outcaps) { GstDsOsd *dsosd = GST_DSOSD(btrans); /* Save the input video information, since this will be required later. */ gst_video_info_from_caps(&dsosd->video_info, incaps); CHECK_CUDA_STATUS(cudaSetDevice(dsosd->gpu_id), "Unable to set cuda device"); return TRUE; error: return FALSE; } /** * Called when element recieves an input buffer from upstream element. */ static GstFlowReturn gst_dsosd_transform_ip(GstBaseTransform * btrans, GstBuffer * inbuf) { GstDsOsd *dsosd = GST_DSOSD(btrans); GstMapInfo in_map_info; GstFlowReturn flow_ret = GST_FLOW_ERROR; NvBufSurface *surface = NULL; NvDsBatchMeta *batch_meta = NULL; NvDsFrameMeta *frame_meta = NULL; NvDsMetaList * l_frame = NULL; guint i = 0; struct tm t; gchar date_time[64]; GTimeVal now; g_get_current_time(&now); strftime(date_time, sizeof(date_time), "%Y/%m/%d %H:%M:%S", localtime_r(&now.tv_sec, &t)); dsosd->frame_num++; if(gst_buffer_is_writable(inbuf)) { CHECK_CUDA_STATUS(cudaSetDevice(dsosd->gpu_id), "Unable to set cuda device"); memset(&in_map_info, 0, sizeof(in_map_info)); if(!gst_buffer_map(inbuf, &in_map_info, GST_MAP_READ)) { g_print("Error: Failed to map gst buffer\n"); goto error; } surface = (NvBufSurface *) in_map_info.data; GST_DEBUG_OBJECT(dsosd, "Processing Frame %" G_GUINT64_FORMAT " Surface %p\n", dsosd->frame_num, surface); if(CHECK_NVDS_MEMORY_AND_GPUID(dsosd, surface)) goto error; batch_meta = gst_buffer_get_nvds_batch_meta(inbuf); if(batch_meta == nullptr) { GST_ELEMENT_ERROR(dsosd, STREAM, FAILED, ("NvDsBatchMeta not found for input buffer."), (NULL)); return GST_FLOW_ERROR; } for(l_frame = batch_meta->frame_meta_list; l_frame != NULL; l_frame = l_frame->next) { frame_meta = (NvDsFrameMeta *)(l_frame->data); //#define NVOSD_MODE MODE_CPU //#define NVOSD_MODE MODE_GPU #define NVOSD_MODE MODE_HW #define NVOSD_NUM 1 nvll_osd_set_params( dsosd->nvosd_ctx , surface->surfaceList[i].width , surface->surfaceList[i].height); NvOSD_FrameTextParams *frame_text_params = &dsosd->frame_text_params; frame_text_params->buf_ptr = &(surface->surfaceList[i]); frame_text_params->num_strings = NVOSD_NUM; frame_text_params->mode = NVOSD_MODE; frame_text_params->text_params_list = (NvOSD_TextParams *)g_malloc(sizeof(NvOSD_TextParams) * frame_text_params->num_strings); for(guint j = 0; j < frame_text_params->num_strings; j ++) { NvOSD_TextParams *text_params = &frame_text_params->text_params_list[j]; text_params->display_text = g_strdup_printf("%s.%03ld", date_time, now.tv_usec / 1000); text_params->x_offset = 400; text_params->y_offset = 100 * j + 100; text_params->set_bg_clr = 1; text_params->text_bg_clr = (NvOSD_ColorParams) { 0, 0, 0, 1 }; text_params->font_params.font_name = g_strdup("Serif"); text_params->font_params.font_size = 11; text_params->font_params.font_color = (NvOSD_ColorParams) { 1, 1, 1, 1 }; } NvOSD_FrameRectParams *frame_rect_params = &dsosd->frame_rect_params; frame_rect_params->buf_ptr = &(surface->surfaceList[i]); frame_rect_params->num_rects = NVOSD_NUM; frame_rect_params->mode = NVOSD_MODE; frame_rect_params->rect_params_list = (NvOSD_RectParams *)g_malloc(sizeof(NvOSD_RectParams) * frame_rect_params->num_rects); for(guint j = 0; j < frame_rect_params->num_rects; j ++) { NvOSD_RectParams *rect_params = &frame_rect_params->rect_params_list[j]; rect_params->left = 300; rect_params->top = 100 * j + 100; rect_params->width = 100; rect_params->height = 100; rect_params->has_bg_color = 0; rect_params->bg_color = (NvOSD_ColorParams) { 0, 0, 0, 1 }; rect_params->border_width = 10; rect_params->border_color = (NvOSD_ColorParams) { 1, 0, 0, 1 }; } if(nvll_osd_put_text(dsosd->nvosd_ctx, frame_text_params)) { printf("failure to draw text"); } if(nvll_osd_draw_rectangles(dsosd->nvosd_ctx, frame_rect_params)) { printf("failure to draw rectangle"); } //g_free(frame_text_params->text_params_list); g_free(frame_rect_params->rect_params_list); i++; } flow_ret = GST_FLOW_OK; error: gst_buffer_unmap(inbuf, &in_map_info); } else { flow_ret = GST_FLOW_OK; } return flow_ret; } /** * Boiler plate for registering a plugin and an element. */ static gboolean dsosd_plugin_init(GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT(gst_dsosd_debug, "dsosd", 0, "dsosd plugin"); return gst_element_register(plugin, "dsosd", GST_RANK_PRIMARY, GST_TYPE_DSOSD); } GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, nvdsgst_dsosd, DESCRIPTION, dsosd_plugin_init, DS_VERSION, LICENSE, BINARY_PACKAGE, URL)