LCOV - code coverage report
Current view: top level - nnstreamer-2.4.2/gst/nnstreamer/elements - gsttensor_crop.c (source / functions) Coverage Total Hit
Test: nnstreamer 2.4.2-0 nnstreamer/nnstreamer#eca68b8d050408568af95d831a8eef62aaee7784 Lines: 91.9 % 321 295
Test Date: 2025-03-14 05:36:58 Functions: 100.0 % 19 19

            Line data    Source code
       1              : /* SPDX-License-Identifier: LGPL-2.1-only */
       2              : /**
       3              :  * Copyright (C) 2021 Samsung Electronics Co., Ltd.
       4              :  *
       5              :  * @file        gsttensor_crop.c
       6              :  * @date        10 May 2021
       7              :  * @brief       GStreamer element to crop the regions of incoming tensor
       8              :  * @see         https://github.com/nnstreamer/nnstreamer
       9              :  * @author      Jaeyun Jung <jy1210.jung@samsung.com>
      10              :  * @bug         No known bugs except for NYI items
      11              :  */
      12              : 
      13              : /**
      14              :  * SECTION:element-tensor_crop
      15              :  *
      16              :  * tensor_crop is a GStreamer element to crop the regions of incoming tensor.
      17              :  *
      18              :  * tensor_crop has two always sink pads - raw and info.
      19              :  * The raw pad accepts tensor (other/tensor) which will be cropped with crop info.
      20              :  * The info pad has capability for flexible tensor stream (other/tensors-flexible), that can have a various buffer size for crop info.
      21              :  * Incoming buffer on info pad should be an array of crop info.
      22              :  * Note that NNStreamer supports maximum NNS_TENSOR_SIZE_LIMIT memory blocks in a buffer.
      23              :  * So, when incoming buffer on info pad has more than NNS_TENSOR_SIZE_LIMIT crop-info array, tensor_crop will ignore the data.
      24              :  *
      25              :  * The output is always in the format of other/tensors-flexible.
      26              :  *
      27              :  * <refsect2>
      28              :  * <title>Example launch line</title>
      29              :  * |[
      30              :  * gst-launch-1.0 tensor_crop name=crop ! (cropped tensors) ... \
      31              :  *     videotestsrc ! videoconvert ! video/x-raw,format=RGB ! tensor_converter ! tee name=t \
      32              :  *       t. ! queue ! crop.raw \
      33              :  *       t. ! queue ! (process raw video tensor and push buffer which includes crop info) ! crop.info
      34              :  * ]|
      35              :  * </refsect2>
      36              :  */
      37              : 
      38              : #ifdef HAVE_CONFIG_H
      39              : #include <config.h>
      40              : #endif
      41              : 
      42              : #include <string.h>
      43              : #include <nnstreamer_util.h>
      44              : #include "gsttensor_crop.h"
      45              : #include "tensor_data.h"
      46              : 
      47              : /**
      48              :  * @brief Internal data structure to describe tensor region.
      49              :  */
      50              : typedef struct
      51              : {
      52              :   guint x;
      53              :   guint y;
      54              :   guint w;
      55              :   guint h;
      56              : } tensor_region_s;
      57              : 
      58              : /**
      59              :  * @brief Internal data structure to describe cropping tensor data.
      60              :  * @todo Add various mode to crop tensor. Now tensor-crop handles NHWC data format only.
      61              :  */
      62              : typedef struct
      63              : {
      64              :   guint num;
      65              :   tensor_region_s region[NNS_TENSOR_SIZE_LIMIT];
      66              : } tensor_crop_info_s;
      67              : 
      68              : GST_DEBUG_CATEGORY_STATIC (gst_tensor_crop_debug);
      69              : #define GST_CAT_DEFAULT gst_tensor_crop_debug
      70              : 
      71              : /**
      72              :  * @brief tensor_crop properties
      73              :  */
      74              : enum
      75              : {
      76              :   PROP_0,
      77              :   PROP_LATENESS,
      78              :   PROP_SILENT
      79              : };
      80              : 
      81              : /**
      82              :  * @brief Flag to print minimized log.
      83              :  */
      84              : #define DEFAULT_SILENT TRUE
      85              : 
      86              : /**
      87              :  * @brief Default value to compare timestamp of raw and info buffer, in milliseconds (-1 means no synchronization).
      88              :  */
      89              : #define DEFAULT_LATENESS (-1)
      90              : 
      91              : /**
      92              :  * @brief Template for sink pad (raw data).
      93              :  */
      94              : static GstStaticPadTemplate raw_template = GST_STATIC_PAD_TEMPLATE ("raw",
      95              :     GST_PAD_SINK,
      96              :     GST_PAD_ALWAYS,
      97              :     GST_STATIC_CAPS (GST_TENSOR_CAP_DEFAULT ";"
      98              :         GST_TENSORS_CAP_MAKE ("{ static, flexible }")));
      99              : 
     100              : /**
     101              :  * @brief Template for sink pad (crop info).
     102              :  */
     103              : static GstStaticPadTemplate info_template = GST_STATIC_PAD_TEMPLATE ("info",
     104              :     GST_PAD_SINK,
     105              :     GST_PAD_ALWAYS,
     106              :     GST_STATIC_CAPS (GST_TENSORS_FLEX_CAP_DEFAULT));
     107              : 
     108              : /**
     109              :  * @brief Template for src pad.
     110              :  */
     111              : static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
     112              :     GST_PAD_SRC,
     113              :     GST_PAD_ALWAYS,
     114              :     GST_STATIC_CAPS (GST_TENSORS_FLEX_CAP_DEFAULT));
     115              : 
     116              : #define gst_tensor_crop_parent_class parent_class
     117          953 : G_DEFINE_TYPE (GstTensorCrop, gst_tensor_crop, GST_TYPE_ELEMENT);
     118              : 
     119              : static void gst_tensor_crop_finalize (GObject * object);
     120              : static void gst_tensor_crop_set_property (GObject * object, guint prop_id,
     121              :     const GValue * value, GParamSpec * pspec);
     122              : static void gst_tensor_crop_get_property (GObject * object, guint prop_id,
     123              :     GValue * value, GParamSpec * pspec);
     124              : static GstStateChangeReturn gst_tensor_crop_change_state (GstElement * element,
     125              :     GstStateChange transition);
     126              : static gboolean gst_tensor_crop_src_event (GstPad * pad, GstObject * parent,
     127              :     GstEvent * event);
     128              : static gboolean gst_tensor_crop_sink_event (GstCollectPads * pads,
     129              :     GstCollectData * data, GstEvent * event, gpointer user_data);
     130              : static GstFlowReturn gst_tensor_crop_collected (GstCollectPads * pads,
     131              :     gpointer user_data);
     132              : 
     133              : /**
     134              :  * @brief Initialize the tensor_crop's class.
     135              :  */
     136              : static void
     137            2 : gst_tensor_crop_class_init (GstTensorCropClass * klass)
     138              : {
     139              :   GObjectClass *object_class;
     140              :   GstElementClass *element_class;
     141              : 
     142            2 :   GST_DEBUG_CATEGORY_INIT (gst_tensor_crop_debug, "tensor_crop", 0,
     143              :       "Element to crop the regions of incoming tensor");
     144              : 
     145            2 :   object_class = (GObjectClass *) klass;
     146            2 :   element_class = (GstElementClass *) klass;
     147              : 
     148            2 :   object_class->set_property = gst_tensor_crop_set_property;
     149            2 :   object_class->get_property = gst_tensor_crop_get_property;
     150            2 :   object_class->finalize = gst_tensor_crop_finalize;
     151              : 
     152              :   /**
     153              :    * GstTensorCrop::lateness:
     154              :    *
     155              :    * The time difference between raw and info buffer, in milliseconds (-1 means no synchronization).
     156              :    * If raw and info buffers on the pads have different timestamp and time-diff is larger than 'lateness',
     157              :    * tensor-crop will drop old buffer and wait for next buffers.
     158              :    */
     159            2 :   g_object_class_install_property (object_class, PROP_LATENESS,
     160              :       g_param_spec_int ("lateness", "Lateness",
     161              :           "The time difference between raw and info buffer in milliseconds",
     162              :           -1, G_MAXINT, DEFAULT_LATENESS,
     163              :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     164              : 
     165              :   /**
     166              :    * GstTensorCrop::silent:
     167              :    *
     168              :    * The flag to enable/disable debugging messages.
     169              :    */
     170            2 :   g_object_class_install_property (object_class, PROP_SILENT,
     171              :       g_param_spec_boolean ("silent", "Silent", "Produce verbose output",
     172              :           DEFAULT_SILENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     173              : 
     174            2 :   element_class->change_state =
     175            2 :       GST_DEBUG_FUNCPTR (gst_tensor_crop_change_state);
     176              : 
     177            2 :   gst_element_class_add_pad_template (element_class,
     178              :       gst_static_pad_template_get (&src_template));
     179            2 :   gst_element_class_add_pad_template (element_class,
     180              :       gst_static_pad_template_get (&raw_template));
     181            2 :   gst_element_class_add_pad_template (element_class,
     182              :       gst_static_pad_template_get (&info_template));
     183              : 
     184            2 :   gst_element_class_set_static_metadata (element_class,
     185              :       "TensorCrop",
     186              :       "Filter/Tensor",
     187              :       "Element to crop the regions of incoming tensor",
     188              :       "Samsung Electronics Co., Ltd.");
     189            2 : }
     190              : 
     191              : /**
     192              :  * @brief Clear and reset old pad data.
     193              :  */
     194              : static void
     195           32 : gst_tensor_crop_pad_reset (GstTensorCropPadData * cpad)
     196              : {
     197           32 :   gst_tensors_config_free (&cpad->config);
     198           32 :   gst_tensors_config_init (&cpad->config);
     199           32 : }
     200              : 
     201              : /**
     202              :  * @brief Clear and reset old data in tensor_crop.
     203              :  */
     204              : static void
     205           16 : gst_tensor_crop_reset (GstTensorCrop * self)
     206              : {
     207              :   GstTensorCropPadData *cpad;
     208              :   GSList *walk;
     209              : 
     210           16 :   if (self->collect) {
     211           16 :     walk = self->collect->data;
     212              : 
     213           48 :     while (walk) {
     214           32 :       cpad = (GstTensorCropPadData *) walk->data;
     215              : 
     216           32 :       gst_tensor_crop_pad_reset (cpad);
     217           32 :       walk = g_slist_next (walk);
     218              :     }
     219              :   }
     220              : 
     221           16 :   self->send_stream_start = TRUE;
     222           16 : }
     223              : 
     224              : /**
     225              :  * @brief Initialize tensor_crop element.
     226              :  */
     227              : static void
     228            8 : gst_tensor_crop_init (GstTensorCrop * self)
     229              : {
     230              :   /* setup sink pad */
     231            8 :   self->sinkpad_raw = gst_pad_new_from_static_template (&raw_template, "raw");
     232            8 :   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad_raw);
     233              : 
     234            8 :   self->sinkpad_info =
     235            8 :       gst_pad_new_from_static_template (&info_template, "info");
     236            8 :   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad_info);
     237              : 
     238            8 :   self->collect = gst_collect_pads_new ();
     239            8 :   gst_collect_pads_set_function (self->collect,
     240            8 :       GST_DEBUG_FUNCPTR (gst_tensor_crop_collected), self);
     241            8 :   gst_collect_pads_set_event_function (self->collect,
     242            8 :       GST_DEBUG_FUNCPTR (gst_tensor_crop_sink_event), self);
     243              : 
     244            8 :   gst_collect_pads_add_pad (self->collect, self->sinkpad_raw,
     245              :       sizeof (GstTensorCropPadData), NULL, TRUE);
     246            8 :   gst_collect_pads_add_pad (self->collect, self->sinkpad_info,
     247              :       sizeof (GstTensorCropPadData), NULL, TRUE);
     248              : 
     249              :   /* setup src pad */
     250            8 :   self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
     251            8 :   gst_pad_set_event_function (self->srcpad,
     252              :       GST_DEBUG_FUNCPTR (gst_tensor_crop_src_event));
     253            8 :   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
     254              : 
     255              :   /* init properties */
     256            8 :   self->lateness = DEFAULT_LATENESS;
     257            8 :   self->silent = DEFAULT_SILENT;
     258            8 :   self->send_stream_start = TRUE;
     259            8 : }
     260              : 
     261              : /**
     262              :  * @brief Function to finalize instance.
     263              :  */
     264              : static void
     265            8 : gst_tensor_crop_finalize (GObject * object)
     266              : {
     267              :   GstTensorCrop *self;
     268              : 
     269            8 :   self = GST_TENSOR_CROP (object);
     270              : 
     271            8 :   gst_tensor_crop_reset (self);
     272              : 
     273            8 :   if (self->collect) {
     274            8 :     gst_object_unref (self->collect);
     275            8 :     self->collect = NULL;
     276              :   }
     277              : 
     278            8 :   G_OBJECT_CLASS (parent_class)->finalize (object);
     279            8 : }
     280              : 
     281              : /**
     282              :  * @brief Setter for tensor_crop properties.
     283              :  */
     284              : static void
     285            3 : gst_tensor_crop_set_property (GObject * object, guint prop_id,
     286              :     const GValue * value, GParamSpec * pspec)
     287              : {
     288              :   GstTensorCrop *self;
     289              : 
     290            3 :   self = GST_TENSOR_CROP (object);
     291              : 
     292            3 :   switch (prop_id) {
     293            2 :     case PROP_LATENESS:
     294            2 :       self->lateness = g_value_get_int (value);
     295            2 :       break;
     296            1 :     case PROP_SILENT:
     297            1 :       self->silent = g_value_get_boolean (value);
     298            1 :       break;
     299            0 :     default:
     300            0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     301            0 :       break;
     302              :   }
     303            3 : }
     304              : 
     305              : /**
     306              :  * @brief Getter for tensor_crop properties.
     307              :  */
     308              : static void
     309            4 : gst_tensor_crop_get_property (GObject * object, guint prop_id,
     310              :     GValue * value, GParamSpec * pspec)
     311              : {
     312              :   GstTensorCrop *self;
     313              : 
     314            4 :   self = GST_TENSOR_CROP (object);
     315              : 
     316            4 :   switch (prop_id) {
     317            2 :     case PROP_LATENESS:
     318            2 :       g_value_set_int (value, self->lateness);
     319            2 :       break;
     320            2 :     case PROP_SILENT:
     321            2 :       g_value_set_boolean (value, self->silent);
     322            2 :       break;
     323            0 :     default:
     324            0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     325            0 :       break;
     326              :   }
     327            4 : }
     328              : 
     329              : /**
     330              :  * @brief Handle state transition.
     331              :  */
     332              : static GstStateChangeReturn
     333           62 : gst_tensor_crop_change_state (GstElement * element, GstStateChange transition)
     334              : {
     335              :   GstTensorCrop *self;
     336              :   GstStateChangeReturn ret;
     337              : 
     338           62 :   self = GST_TENSOR_CROP (element);
     339              : 
     340           62 :   switch (transition) {
     341            8 :     case GST_STATE_CHANGE_READY_TO_PAUSED:
     342            8 :       gst_collect_pads_start (self->collect);
     343            8 :       break;
     344            8 :     case GST_STATE_CHANGE_PAUSED_TO_READY:
     345            8 :       gst_collect_pads_stop (self->collect);
     346            8 :       break;
     347           46 :     default:
     348           46 :       break;
     349              :   }
     350              : 
     351           62 :   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
     352              : 
     353           62 :   switch (transition) {
     354            8 :     case GST_STATE_CHANGE_PAUSED_TO_READY:
     355            8 :       gst_tensor_crop_reset (self);
     356            8 :       break;
     357           54 :     default:
     358           54 :       break;
     359              :   }
     360              : 
     361           62 :   return ret;
     362              : }
     363              : 
     364              : /**
     365              :  * @brief Handle event on src pad.
     366              :  */
     367              : static gboolean
     368            3 : gst_tensor_crop_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
     369              : {
     370            3 :   g_return_val_if_fail (event != NULL, FALSE);
     371              : 
     372            3 :   switch (GST_EVENT_TYPE (event)) {
     373            1 :     case GST_EVENT_SEEK:
     374              :       /* disable seeking */
     375            1 :       gst_event_unref (event);
     376            1 :       return FALSE;
     377            2 :     default:
     378            2 :       break;
     379              :   }
     380              : 
     381            2 :   return gst_pad_event_default (pad, parent, event);
     382              : }
     383              : 
     384              : /**
     385              :  * @brief Handle event on sink pad.
     386              :  */
     387              : static gboolean
     388           60 : gst_tensor_crop_sink_event (GstCollectPads * pads, GstCollectData * data,
     389              :     GstEvent * event, gpointer user_data)
     390              : {
     391              :   GstTensorCropPadData *cpad;
     392              :   UNUSED (user_data);
     393              : 
     394           60 :   g_return_val_if_fail (event != NULL, FALSE);
     395              : 
     396           60 :   cpad = (GstTensorCropPadData *) data;
     397              : 
     398           60 :   switch (GST_EVENT_TYPE (event)) {
     399           15 :     case GST_EVENT_CAPS:
     400              :     {
     401              :       GstCaps *caps;
     402              :       gboolean ret;
     403           15 :       gst_event_parse_caps (event, &caps);
     404              : 
     405           15 :       ret = gst_tensors_config_from_cap (&cpad->config, caps);
     406           15 :       gst_event_unref (event);
     407              : 
     408           15 :       return ret;
     409              :     }
     410           45 :     default:
     411           45 :       break;
     412              :   }
     413              : 
     414           45 :   return gst_collect_pads_event_default (pads, data, event, FALSE);
     415              : }
     416              : 
     417              : /**
     418              :  * @brief Set pad caps if not negotiated.
     419              :  */
     420              : static GstFlowReturn
     421           10 : gst_tensor_crop_negotiate (GstTensorCrop * self)
     422              : {
     423           10 :   if (!gst_pad_has_current_caps (self->sinkpad_raw)) {
     424            0 :     GST_ERROR_OBJECT (self,
     425              :         "The raw pad of tensor_crop '%s' does not have pad caps.",
     426              :         GST_ELEMENT_NAME (self));
     427            0 :     return GST_FLOW_NOT_NEGOTIATED;
     428              :   }
     429              : 
     430           10 :   if (!gst_pad_has_current_caps (self->sinkpad_info)) {
     431            0 :     GST_ERROR_OBJECT (self,
     432              :         "The info pad of tensor_crop '%s' does not have pad caps.",
     433              :         GST_ELEMENT_NAME (self));
     434            0 :     return GST_FLOW_NOT_NEGOTIATED;
     435              :   }
     436              : 
     437           10 :   if (!gst_pad_has_current_caps (self->srcpad)) {
     438              :     GstCaps *caps;
     439              :     GstSegment segment;
     440              :     GstTensorsConfig config;
     441              :     GstTensorCropPadData *cpad;
     442              :     GSList *walk;
     443              : 
     444            6 :     if (self->send_stream_start) {
     445              :       /**
     446              :        * Cannot use gst-pad util to get stream ID (multiple sink pads).
     447              :        * Create stream ID using first sink pad.
     448              :        */
     449            6 :       g_autofree gchar *sink_stream_id = gst_pad_get_stream_id (self->sinkpad_raw);
     450            6 :       g_autofree gchar *element_name = gst_element_get_name (self);
     451            6 :       g_autofree gchar *pad_name = gst_pad_get_name (self->srcpad);
     452           12 :       g_autofree gchar *stream_id = g_strdup_printf ("%s-%s-nnscrop-%s-%08x",
     453            6 :           GST_STR_NULL (sink_stream_id), element_name, pad_name, g_random_int ());
     454              : 
     455            6 :       gst_pad_push_event (self->srcpad, gst_event_new_stream_start (stream_id));
     456            6 :       self->send_stream_start = FALSE;
     457              :     }
     458              : 
     459              :     /**
     460              :      * Get config from collect-pads and set framerate.
     461              :      * Output is always flexible tensor.
     462              :      */
     463            6 :     gst_tensors_config_init (&config);
     464            6 :     config.info.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
     465              : 
     466            6 :     walk = self->collect->data;
     467           18 :     while (walk) {
     468           12 :       cpad = (GstTensorCropPadData *) walk->data;
     469              : 
     470           12 :       if (config.rate_n < 0 ||
     471            6 :           gst_util_fraction_compare (cpad->config.rate_n, cpad->config.rate_d,
     472              :               config.rate_n, config.rate_d) < 0) {
     473            6 :         config.rate_n = cpad->config.rate_n;
     474            6 :         config.rate_d = cpad->config.rate_d;
     475              :       }
     476              : 
     477           12 :       walk = g_slist_next (walk);
     478              :     }
     479              : 
     480            6 :     caps = gst_tensors_caps_from_config (&config);
     481            6 :     gst_pad_set_caps (self->srcpad, caps);
     482            6 :     gst_caps_unref (caps);
     483              : 
     484            6 :     gst_segment_init (&segment, GST_FORMAT_TIME);
     485            6 :     gst_pad_push_event (self->srcpad, gst_event_new_segment (&segment));
     486              :   }
     487              : 
     488           10 :   return GST_FLOW_OK;
     489              : }
     490              : 
     491              : /**
     492              :  * @brief Internal function to prepare output meta info.
     493              :  */
     494              : static gboolean
     495            6 : gst_tensor_crop_prepare_out_meta (GstTensorCrop * self, gpointer buffer,
     496              :     GstTensorMetaInfo * meta, GstTensorInfo * info, gboolean * is_flexible)
     497              : {
     498              :   GstCaps *caps;
     499              :   GstStructure *structure;
     500              :   GstTensorsConfig config;
     501              :   GstTensorInfo *_info;
     502            6 :   gboolean ret = FALSE;
     503              : 
     504            6 :   gst_tensor_meta_info_init (meta);
     505            6 :   gst_tensor_info_init (info);
     506              : 
     507            6 :   caps = gst_pad_get_current_caps (self->sinkpad_raw);
     508            6 :   structure = gst_caps_get_structure (caps, 0);
     509              : 
     510            6 :   if (!gst_tensors_config_from_structure (&config, structure)) {
     511            0 :     GST_ERROR_OBJECT (self, "Failed to get the config from caps.");
     512            0 :     goto done;
     513              :   }
     514              : 
     515              :   /**
     516              :    * @note tensor-crop handles single tensor. Parse first one.
     517              :    */
     518            6 :   _info = gst_tensors_info_get_nth_info (&config.info, 0);
     519            6 :   *is_flexible = gst_tensors_config_is_flexible (&config);
     520              : 
     521            6 :   if (*is_flexible) {
     522              :     /* meta from buffer */
     523            1 :     if (gst_tensor_meta_info_parse_header (meta, buffer)) {
     524            1 :       ret = gst_tensor_meta_info_convert (meta, info);
     525              :     }
     526              :   } else {
     527              :     /* meta from caps */
     528            5 :     ret = gst_tensor_info_convert_to_meta (_info, meta);
     529            5 :     gst_tensor_info_copy (info, _info);
     530              :   }
     531              : 
     532              :   /* output is flex tensor */
     533            6 :   meta->format = _NNS_TENSOR_FORMAT_FLEXIBLE;
     534              : 
     535            6 : done:
     536            6 :   gst_caps_unref (caps);
     537            6 :   gst_tensors_config_free (&config);
     538            6 :   return ret;
     539              : }
     540              : 
     541              : /**
     542              :  * @brief Internal function to parse buffer and fill crop info.
     543              :  */
     544              : static gboolean
     545            7 : gst_tensor_crop_get_crop_info (GstTensorCrop * self, GstBuffer * info,
     546              :     tensor_crop_info_s * cinfo)
     547              : {
     548              :   GstMemory *mem;
     549              :   GstMapInfo map;
     550              :   GstTensorMetaInfo meta;
     551              :   gsize hsize, dsize, esize;
     552              :   guint i, j;
     553              :   guint8 *pos, *src, *desc;
     554            7 :   gboolean ret = FALSE;
     555              : 
     556            7 :   i = gst_tensor_buffer_get_count (info);
     557            7 :   g_assert (i > 0);
     558            7 :   if (i > 1) {
     559            0 :     GST_WARNING_OBJECT (self,
     560              :         "Info buffer has %u memories, parse first one.", i);
     561              :   }
     562              : 
     563            7 :   mem = gst_tensor_buffer_get_nth_memory (info, 0);
     564            7 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
     565            0 :     GST_ERROR_OBJECT (self, "Failed to map the info buffer.");
     566            0 :     goto done;
     567              :   }
     568              : 
     569              :   /* parse crop-info from flex tensor */
     570            7 :   if (!gst_tensor_meta_info_parse_header (&meta, map.data)) {
     571            0 :     GST_ERROR_OBJECT (self, "Failed to get the meta from info buffer.");
     572            0 :     goto done;
     573              :   }
     574              : 
     575            7 :   hsize = gst_tensor_meta_info_get_header_size (&meta);
     576            7 :   dsize = gst_tensor_meta_info_get_data_size (&meta);
     577            7 :   esize = gst_tensor_get_element_size (meta.type);
     578              : 
     579            7 :   if (hsize + dsize != map.size) {
     580            1 :     GST_ERROR_OBJECT (self,
     581              :         "Invalid meta info, info buffer size is incorrect (received %zd, expected %zd).",
     582              :         map.size, hsize + dsize);
     583            1 :     goto done;
     584              :   }
     585              : 
     586              :   /**
     587              :    * @todo Add various mode to crop tensor.
     588              :    * Now tensor-crop handles NHWC data format only.
     589              :    */
     590            6 :   g_assert ((dsize % (esize * 4)) == 0);
     591              : 
     592            6 :   memset (cinfo, 0, sizeof (tensor_crop_info_s));
     593              : 
     594            6 :   cinfo->num = MIN (dsize / (esize * 4), NNS_TENSOR_SIZE_LIMIT);
     595              : 
     596           17 :   for (i = 0; i < cinfo->num; i++) {
     597           11 :     pos = map.data + hsize + (esize * 4 * i);
     598              : 
     599           55 :     for (j = 0; j < 4; j++) {
     600           44 :       src = pos + (esize * j);
     601           44 :       desc = (guint8 *) (&cinfo->region[i]) + sizeof (guint) * j;
     602              : 
     603           44 :       gst_tensor_data_raw_typecast (src, meta.type, desc, _NNS_UINT32);
     604              :     }
     605              :   }
     606              : 
     607            6 :   ret = TRUE;
     608              : 
     609            7 : done:
     610            7 :   if (mem) {
     611            7 :     gst_memory_unmap (mem, &map);
     612            7 :     gst_memory_unref (mem);
     613              :   }
     614              : 
     615            7 :   return ret;
     616              : }
     617              : 
     618              : /**
     619              :  * @brief Internal function to crop incoming buffer.
     620              :  */
     621              : static GstBuffer *
     622            6 : gst_tensor_crop_do_cropping (GstTensorCrop * self, GstBuffer * raw,
     623              :     tensor_crop_info_s * cinfo)
     624              : {
     625            6 :   GstBuffer *result = NULL;
     626              :   GstMemory *mem;
     627              :   GstMapInfo map;
     628              :   GstTensorMetaInfo meta;
     629              :   GstTensorInfo info;
     630              :   gboolean flexible;
     631              :   gsize hsize, esize, dsize;
     632              :   guint8 *cropped, *dpos, *desc, *src;
     633              :   guint i, j, ch, mw, mh, _x, _y, _w, _h;
     634              : 
     635            6 :   i = gst_tensor_buffer_get_count (raw);
     636            6 :   g_assert (i > 0);
     637            6 :   if (i > 1) {
     638            0 :     GST_WARNING_OBJECT (self,
     639              :         "Raw data buffer has %u memories, parse first one.", i);
     640              :   }
     641              : 
     642            6 :   mem = gst_tensor_buffer_get_nth_memory (raw, 0);
     643            6 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
     644            0 :     GST_ERROR_OBJECT (self, "Failed to map the raw buffer.");
     645            0 :     goto done;
     646              :   }
     647              : 
     648            6 :   if (!gst_tensor_crop_prepare_out_meta (self, map.data, &meta,
     649              :           &info, &flexible)) {
     650            0 :     GST_ERROR_OBJECT (self, "Failed to get the output meta.");
     651            0 :     goto done;
     652              :   }
     653              : 
     654            6 :   hsize = flexible ? gst_tensor_meta_info_get_header_size (&meta) : 0;
     655            6 :   dsize = gst_tensor_meta_info_get_data_size (&meta);
     656            6 :   dpos = map.data + hsize;
     657            6 :   if ((hsize + dsize) != map.size) {
     658            1 :     GST_ERROR_OBJECT (self,
     659              :         "Raw buffer has invalid data size (received %zd, expected %zd).",
     660              :         map.size, dsize);
     661            1 :     goto done;
     662              :   }
     663              : 
     664            5 :   result = gst_buffer_new ();
     665              : 
     666              :   /** @todo Add various mode to crop tensor. */
     667            5 :   ch = info.dimension[0];
     668            5 :   mw = info.dimension[1];
     669            5 :   mh = info.dimension[2];
     670            5 :   esize = gst_tensor_get_element_size (info.type);
     671            5 :   hsize = gst_tensor_meta_info_get_header_size (&meta);
     672              : 
     673           14 :   for (i = 0; i < cinfo->num; i++) {
     674              :     GstTensorInfo crop_info;
     675              :     GstMemory *crop_mem;
     676              : 
     677            9 :     _x = (cinfo->region[i].x < mw) ? cinfo->region[i].x : mw;
     678            9 :     _y = (cinfo->region[i].y < mh) ? cinfo->region[i].y : mh;
     679            9 :     _w = (_x + cinfo->region[i].w - 1 < mw) ? cinfo->region[i].w : (mw - _x);
     680            9 :     _h = (_y + cinfo->region[i].h - 1 < mh) ? cinfo->region[i].h : (mh - _y);
     681              : 
     682            9 :     g_assert (_w > 0 && _h > 0);
     683            9 :     dsize = hsize + (esize * ch * _w * _h);
     684            9 :     cropped = (guint8 *) g_malloc0 (dsize);
     685              : 
     686              :     /* set header for flex tensor */
     687            9 :     meta.dimension[1] = _w;
     688            9 :     meta.dimension[2] = _h;
     689            9 :     meta.dimension[3] = 1;
     690            9 :     gst_tensor_meta_info_update_header (&meta, cropped);
     691              : 
     692          232 :     for (j = 0; j < _h; j++) {
     693          223 :       src = dpos + esize * ch * (_x + (j + _y) * mw);
     694          223 :       desc = cropped + hsize + (esize * ch * _w) * j;
     695          223 :       memcpy (desc, src, (esize * ch * _w));
     696              :     }
     697              : 
     698              :     /* Prepare tensors-info to append memory into gst-buffer. */
     699            9 :     gst_tensor_meta_info_convert (&meta, &crop_info);
     700              :     crop_mem =
     701            9 :         gst_memory_new_wrapped (0, cropped, dsize, 0, dsize, cropped, g_free);
     702              : 
     703            9 :     gst_tensor_buffer_append_memory (result, crop_mem, &crop_info);
     704            9 :     gst_tensor_info_free (&crop_info);
     705              :   }
     706              : 
     707              :   /* set timestamp from raw buffer */
     708            5 :   gst_buffer_copy_into (result, raw, GST_BUFFER_COPY_METADATA, 0, -1);
     709              : 
     710            6 : done:
     711            6 :   if (mem) {
     712            6 :     gst_memory_unmap (mem, &map);
     713            6 :     gst_memory_unref (mem);
     714              :   }
     715              : 
     716            6 :   return result;
     717              : }
     718              : 
     719              : /**
     720              :  * @brief Internal function to transform the input buffer.
     721              :  */
     722              : static GstFlowReturn
     723            9 : gst_tensor_crop_chain (GstTensorCrop * self,
     724              :     GstCollectData * data_raw, GstCollectData * data_info)
     725              : {
     726              :   GstFlowReturn ret;
     727              :   GstBuffer *buf_raw, *buf_info, *result;
     728              :   GstTensorCropPadData *cpad;
     729              :   tensor_crop_info_s cinfo;
     730              :   gboolean drop_raw, drop_info;
     731              : 
     732           18 :   g_return_val_if_fail (data_raw && data_info, GST_FLOW_ERROR);
     733              : 
     734            9 :   buf_raw = gst_collect_pads_peek (self->collect, data_raw);
     735            9 :   buf_info = gst_collect_pads_peek (self->collect, data_info);
     736            9 :   drop_raw = (buf_raw != NULL);
     737            9 :   drop_info = (buf_info != NULL);
     738              : 
     739            9 :   if (!buf_raw || !buf_info) {
     740            0 :     ret = GST_FLOW_EOS;
     741            0 :     goto done;
     742              :   }
     743              : 
     744            9 :   cpad = (GstTensorCropPadData *) data_raw;
     745            9 :   buf_raw = gst_tensor_buffer_from_config (buf_raw, &cpad->config);
     746            9 :   cpad = (GstTensorCropPadData *) data_info;
     747            9 :   buf_info = gst_tensor_buffer_from_config (buf_info, &cpad->config);
     748              : 
     749              :   /**
     750              :    * The case when raw and info have different timestamp.
     751              :    * Compare timestamp and if time diff is less than lateness, crop raw buffer.
     752              :    */
     753            9 :   if (self->lateness >= 0) {
     754              :     GstClockTime ts_raw, ts_info, lateness;
     755              : 
     756            4 :     ts_raw = GST_BUFFER_TIMESTAMP (buf_raw);
     757            4 :     ts_info = GST_BUFFER_TIMESTAMP (buf_info);
     758            4 :     lateness = self->lateness * GST_MSECOND;
     759              : 
     760            4 :     if (GST_CLOCK_TIME_IS_VALID (ts_raw) && GST_CLOCK_TIME_IS_VALID (ts_info)) {
     761            4 :       if (((GstClockTime) ABS (GST_CLOCK_DIFF (ts_raw, ts_info))) > lateness) {
     762            2 :         GST_DEBUG_OBJECT (self, "Drop old buffer and wait for next.");
     763            2 :         GST_DEBUG_OBJECT (self, "Raw buffer ts: %" GST_TIME_FORMAT,
     764              :             GST_TIME_ARGS (ts_raw));
     765            2 :         GST_DEBUG_OBJECT (self, "Info buffer ts: %" GST_TIME_FORMAT,
     766              :             GST_TIME_ARGS (ts_info));
     767              : 
     768              :         /* clear old buffer and return ok to get next buffer */
     769            2 :         if (ts_raw > ts_info)
     770            1 :           drop_raw = FALSE;
     771              :         else
     772            1 :           drop_info = FALSE;
     773              : 
     774            2 :         ret = GST_FLOW_OK;
     775            2 :         goto done;
     776              :       }
     777              :     } else {
     778            0 :       GST_WARNING_OBJECT (self,
     779              :           "Incoming buffer has invalid timestamp, continue cropping data.");
     780              :     }
     781              :   }
     782              : 
     783            7 :   if (!gst_tensor_crop_get_crop_info (self, buf_info, &cinfo)) {
     784            1 :     ret = GST_FLOW_ERROR;
     785            1 :     goto done;
     786              :   }
     787              : 
     788            6 :   result = gst_tensor_crop_do_cropping (self, buf_raw, &cinfo);
     789            6 :   ret = gst_pad_push (self->srcpad, result);
     790              : 
     791            9 : done:
     792            9 :   if (buf_raw)
     793            9 :     gst_buffer_unref (buf_raw);
     794            9 :   if (buf_info)
     795            9 :     gst_buffer_unref (buf_info);
     796              : 
     797              :   /* clear buffer in collect pads */
     798            9 :   if (drop_raw)
     799            8 :     gst_buffer_unref (gst_collect_pads_pop (self->collect, data_raw));
     800            9 :   if (drop_info)
     801            8 :     gst_buffer_unref (gst_collect_pads_pop (self->collect, data_info));
     802              : 
     803            9 :   return ret;
     804              : }
     805              : 
     806              : /**
     807              :  * @brief Chain function called when the buffer is available on all of the collect pads.
     808              :  */
     809              : static GstFlowReturn
     810           10 : gst_tensor_crop_collected (GstCollectPads * pads, gpointer user_data)
     811              : {
     812              :   GstTensorCrop *self;
     813              :   GstCollectData *data_raw, *data_info;
     814              :   GSList *walk;
     815              :   GstFlowReturn ret;
     816              : 
     817           10 :   self = GST_TENSOR_CROP (user_data);
     818           10 :   data_raw = data_info = NULL;
     819              : 
     820           10 :   ret = gst_tensor_crop_negotiate (self);
     821           10 :   if (ret != GST_FLOW_OK)
     822            0 :     return ret;
     823              : 
     824           28 :   for (walk = pads->data; walk; walk = g_slist_next (walk)) {
     825              :     GstCollectData *data;
     826              : 
     827           19 :     data = (GstCollectData *) walk->data;
     828              : 
     829           19 :     if (GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_EOS)) {
     830            1 :       gst_pad_push_event (self->srcpad, gst_event_new_eos ());
     831            1 :       return GST_FLOW_EOS;
     832              :     }
     833              : 
     834           18 :     if (data->pad == self->sinkpad_raw) {
     835            9 :       data_raw = data;
     836            9 :     } else if (data->pad == self->sinkpad_info) {
     837            9 :       data_info = data;
     838              :     }
     839              :   }
     840              : 
     841            9 :   return gst_tensor_crop_chain (self, data_raw, data_info);
     842              : }
        

Generated by: LCOV version 2.0-1