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#58eaa3c6edb7484955a5d8c32f47a60ba9501210 Lines: 91.9 % 320 294
Test Date: 2025-04-18 05:37:26 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          983 : 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           12 : gst_tensor_crop_class_init (GstTensorCropClass * klass)
     138              : {
     139              :   GObjectClass *object_class;
     140              :   GstElementClass *element_class;
     141              : 
     142           12 :   GST_DEBUG_CATEGORY_INIT (gst_tensor_crop_debug, "tensor_crop", 0,
     143              :       "Element to crop the regions of incoming tensor");
     144              : 
     145           12 :   object_class = (GObjectClass *) klass;
     146           12 :   element_class = (GstElementClass *) klass;
     147              : 
     148           12 :   object_class->set_property = gst_tensor_crop_set_property;
     149           12 :   object_class->get_property = gst_tensor_crop_get_property;
     150           12 :   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           12 :   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           12 :   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           12 :   element_class->change_state =
     175           12 :       GST_DEBUG_FUNCPTR (gst_tensor_crop_change_state);
     176              : 
     177           12 :   gst_element_class_add_pad_template (element_class,
     178              :       gst_static_pad_template_get (&src_template));
     179           12 :   gst_element_class_add_pad_template (element_class,
     180              :       gst_static_pad_template_get (&raw_template));
     181           12 :   gst_element_class_add_pad_template (element_class,
     182              :       gst_static_pad_template_get (&info_template));
     183              : 
     184           12 :   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           12 : }
     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              : 
     404           15 :       gst_event_parse_caps (event, &caps);
     405              : 
     406           15 :       ret = gst_tensors_config_from_caps (&cpad->config, caps, TRUE);
     407           15 :       gst_event_unref (event);
     408              : 
     409           15 :       return ret;
     410              :     }
     411           45 :     default:
     412           45 :       break;
     413              :   }
     414              : 
     415           45 :   return gst_collect_pads_event_default (pads, data, event, FALSE);
     416              : }
     417              : 
     418              : /**
     419              :  * @brief Set pad caps if not negotiated.
     420              :  */
     421              : static GstFlowReturn
     422           10 : gst_tensor_crop_negotiate (GstTensorCrop * self)
     423              : {
     424           10 :   if (!gst_pad_has_current_caps (self->sinkpad_raw)) {
     425            0 :     GST_ERROR_OBJECT (self,
     426              :         "The raw pad of tensor_crop '%s' does not have pad caps.",
     427              :         GST_ELEMENT_NAME (self));
     428            0 :     return GST_FLOW_NOT_NEGOTIATED;
     429              :   }
     430              : 
     431           10 :   if (!gst_pad_has_current_caps (self->sinkpad_info)) {
     432            0 :     GST_ERROR_OBJECT (self,
     433              :         "The info pad of tensor_crop '%s' does not have pad caps.",
     434              :         GST_ELEMENT_NAME (self));
     435            0 :     return GST_FLOW_NOT_NEGOTIATED;
     436              :   }
     437              : 
     438           10 :   if (!gst_pad_has_current_caps (self->srcpad)) {
     439              :     GstCaps *caps;
     440              :     GstSegment segment;
     441              :     GstTensorsConfig config;
     442              :     GstTensorCropPadData *cpad;
     443              :     GSList *walk;
     444              : 
     445            6 :     if (self->send_stream_start) {
     446              :       /**
     447              :        * Cannot use gst-pad util to get stream ID (multiple sink pads).
     448              :        * Create stream ID using first sink pad.
     449              :        */
     450            6 :       g_autofree gchar *sink_stream_id = gst_pad_get_stream_id (self->sinkpad_raw);
     451            6 :       g_autofree gchar *element_name = gst_element_get_name (self);
     452            6 :       g_autofree gchar *pad_name = gst_pad_get_name (self->srcpad);
     453           12 :       g_autofree gchar *stream_id = g_strdup_printf ("%s-%s-nnscrop-%s-%08x",
     454            6 :           GST_STR_NULL (sink_stream_id), element_name, pad_name, g_random_int ());
     455              : 
     456            6 :       gst_pad_push_event (self->srcpad, gst_event_new_stream_start (stream_id));
     457            6 :       self->send_stream_start = FALSE;
     458              :     }
     459              : 
     460              :     /**
     461              :      * Get config from collect-pads and set framerate.
     462              :      * Output is always flexible tensor.
     463              :      */
     464            6 :     gst_tensors_config_init (&config);
     465            6 :     config.info.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
     466              : 
     467            6 :     walk = self->collect->data;
     468           18 :     while (walk) {
     469           12 :       cpad = (GstTensorCropPadData *) walk->data;
     470              : 
     471           12 :       if (config.rate_n < 0 ||
     472            6 :           gst_util_fraction_compare (cpad->config.rate_n, cpad->config.rate_d,
     473              :               config.rate_n, config.rate_d) < 0) {
     474            6 :         config.rate_n = cpad->config.rate_n;
     475            6 :         config.rate_d = cpad->config.rate_d;
     476              :       }
     477              : 
     478           12 :       walk = g_slist_next (walk);
     479              :     }
     480              : 
     481            6 :     caps = gst_tensors_caps_from_config (&config);
     482            6 :     gst_pad_set_caps (self->srcpad, caps);
     483            6 :     gst_caps_unref (caps);
     484              : 
     485            6 :     gst_segment_init (&segment, GST_FORMAT_TIME);
     486            6 :     gst_pad_push_event (self->srcpad, gst_event_new_segment (&segment));
     487              :   }
     488              : 
     489           10 :   return GST_FLOW_OK;
     490              : }
     491              : 
     492              : /**
     493              :  * @brief Internal function to prepare output meta info.
     494              :  */
     495              : static gboolean
     496            6 : gst_tensor_crop_prepare_out_meta (GstTensorCrop * self, gpointer buffer,
     497              :     GstTensorMetaInfo * meta, GstTensorInfo * info, gboolean * is_flexible)
     498              : {
     499              :   GstCaps *caps;
     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              : 
     509            6 :   if (!gst_tensors_config_from_caps (&config, caps, TRUE)) {
     510            0 :     GST_ERROR_OBJECT (self, "Failed to get the config from caps.");
     511            0 :     goto done;
     512              :   }
     513              : 
     514              :   /**
     515              :    * @note tensor-crop handles single tensor. Parse first one.
     516              :    */
     517            6 :   _info = gst_tensors_info_get_nth_info (&config.info, 0);
     518            6 :   *is_flexible = gst_tensors_config_is_flexible (&config);
     519              : 
     520            6 :   if (*is_flexible) {
     521              :     /* meta from buffer */
     522            1 :     if (gst_tensor_meta_info_parse_header (meta, buffer)) {
     523            1 :       ret = gst_tensor_meta_info_convert (meta, info);
     524              :     }
     525              :   } else {
     526              :     /* meta from caps */
     527            5 :     ret = gst_tensor_info_convert_to_meta (_info, meta);
     528            5 :     gst_tensor_info_copy (info, _info);
     529              :   }
     530              : 
     531              :   /* output is flex tensor */
     532            6 :   meta->format = _NNS_TENSOR_FORMAT_FLEXIBLE;
     533              : 
     534            6 : done:
     535            6 :   gst_caps_unref (caps);
     536            6 :   gst_tensors_config_free (&config);
     537            6 :   return ret;
     538              : }
     539              : 
     540              : /**
     541              :  * @brief Internal function to parse buffer and fill crop info.
     542              :  */
     543              : static gboolean
     544            7 : gst_tensor_crop_get_crop_info (GstTensorCrop * self, GstBuffer * info,
     545              :     tensor_crop_info_s * cinfo)
     546              : {
     547              :   GstMemory *mem;
     548              :   GstMapInfo map;
     549              :   GstTensorMetaInfo meta;
     550              :   gsize hsize, dsize, esize;
     551              :   guint i, j;
     552              :   guint8 *pos, *src, *desc;
     553            7 :   gboolean ret = FALSE;
     554              : 
     555            7 :   i = gst_tensor_buffer_get_count (info);
     556            7 :   g_assert (i > 0);
     557            7 :   if (i > 1) {
     558            0 :     GST_WARNING_OBJECT (self,
     559              :         "Info buffer has %u memories, parse first one.", i);
     560              :   }
     561              : 
     562            7 :   mem = gst_tensor_buffer_get_nth_memory (info, 0);
     563            7 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
     564            0 :     GST_ERROR_OBJECT (self, "Failed to map the info buffer.");
     565            0 :     goto done;
     566              :   }
     567              : 
     568              :   /* parse crop-info from flex tensor */
     569            7 :   if (!gst_tensor_meta_info_parse_header (&meta, map.data)) {
     570            0 :     GST_ERROR_OBJECT (self, "Failed to get the meta from info buffer.");
     571            0 :     goto done;
     572              :   }
     573              : 
     574            7 :   hsize = gst_tensor_meta_info_get_header_size (&meta);
     575            7 :   dsize = gst_tensor_meta_info_get_data_size (&meta);
     576            7 :   esize = gst_tensor_get_element_size (meta.type);
     577              : 
     578            7 :   if (hsize + dsize != map.size) {
     579            1 :     GST_ERROR_OBJECT (self,
     580              :         "Invalid meta info, info buffer size is incorrect (received %zd, expected %zd).",
     581              :         map.size, hsize + dsize);
     582            1 :     goto done;
     583              :   }
     584              : 
     585              :   /**
     586              :    * @todo Add various mode to crop tensor.
     587              :    * Now tensor-crop handles NHWC data format only.
     588              :    */
     589            6 :   g_assert ((dsize % (esize * 4)) == 0);
     590              : 
     591            6 :   memset (cinfo, 0, sizeof (tensor_crop_info_s));
     592              : 
     593            6 :   cinfo->num = MIN (dsize / (esize * 4), NNS_TENSOR_SIZE_LIMIT);
     594              : 
     595           17 :   for (i = 0; i < cinfo->num; i++) {
     596           11 :     pos = map.data + hsize + (esize * 4 * i);
     597              : 
     598           55 :     for (j = 0; j < 4; j++) {
     599           44 :       src = pos + (esize * j);
     600           44 :       desc = (guint8 *) (&cinfo->region[i]) + sizeof (guint) * j;
     601              : 
     602           44 :       gst_tensor_data_raw_typecast (src, meta.type, desc, _NNS_UINT32);
     603              :     }
     604              :   }
     605              : 
     606            6 :   ret = TRUE;
     607              : 
     608            7 : done:
     609            7 :   if (mem) {
     610            7 :     gst_memory_unmap (mem, &map);
     611            7 :     gst_memory_unref (mem);
     612              :   }
     613              : 
     614            7 :   return ret;
     615              : }
     616              : 
     617              : /**
     618              :  * @brief Internal function to crop incoming buffer.
     619              :  */
     620              : static GstBuffer *
     621            6 : gst_tensor_crop_do_cropping (GstTensorCrop * self, GstBuffer * raw,
     622              :     tensor_crop_info_s * cinfo)
     623              : {
     624            6 :   GstBuffer *result = NULL;
     625              :   GstMemory *mem;
     626              :   GstMapInfo map;
     627              :   GstTensorMetaInfo meta;
     628              :   GstTensorInfo info;
     629              :   gboolean flexible;
     630              :   gsize hsize, esize, dsize;
     631              :   guint8 *cropped, *dpos, *desc, *src;
     632              :   guint i, j, ch, mw, mh, _x, _y, _w, _h;
     633              : 
     634            6 :   i = gst_tensor_buffer_get_count (raw);
     635            6 :   g_assert (i > 0);
     636            6 :   if (i > 1) {
     637            0 :     GST_WARNING_OBJECT (self,
     638              :         "Raw data buffer has %u memories, parse first one.", i);
     639              :   }
     640              : 
     641            6 :   mem = gst_tensor_buffer_get_nth_memory (raw, 0);
     642            6 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
     643            0 :     GST_ERROR_OBJECT (self, "Failed to map the raw buffer.");
     644            0 :     goto done;
     645              :   }
     646              : 
     647            6 :   if (!gst_tensor_crop_prepare_out_meta (self, map.data, &meta,
     648              :           &info, &flexible)) {
     649            0 :     GST_ERROR_OBJECT (self, "Failed to get the output meta.");
     650            0 :     goto done;
     651              :   }
     652              : 
     653            6 :   hsize = flexible ? gst_tensor_meta_info_get_header_size (&meta) : 0;
     654            6 :   dsize = gst_tensor_meta_info_get_data_size (&meta);
     655            6 :   dpos = map.data + hsize;
     656            6 :   if ((hsize + dsize) != map.size) {
     657            1 :     GST_ERROR_OBJECT (self,
     658              :         "Raw buffer has invalid data size (received %zd, expected %zd).",
     659              :         map.size, dsize);
     660            1 :     goto done;
     661              :   }
     662              : 
     663            5 :   result = gst_buffer_new ();
     664              : 
     665              :   /** @todo Add various mode to crop tensor. */
     666            5 :   ch = info.dimension[0];
     667            5 :   mw = info.dimension[1];
     668            5 :   mh = info.dimension[2];
     669            5 :   esize = gst_tensor_get_element_size (info.type);
     670            5 :   hsize = gst_tensor_meta_info_get_header_size (&meta);
     671              : 
     672           14 :   for (i = 0; i < cinfo->num; i++) {
     673              :     GstTensorInfo crop_info;
     674              :     GstMemory *crop_mem;
     675              : 
     676            9 :     _x = (cinfo->region[i].x < mw) ? cinfo->region[i].x : mw;
     677            9 :     _y = (cinfo->region[i].y < mh) ? cinfo->region[i].y : mh;
     678            9 :     _w = (_x + cinfo->region[i].w - 1 < mw) ? cinfo->region[i].w : (mw - _x);
     679            9 :     _h = (_y + cinfo->region[i].h - 1 < mh) ? cinfo->region[i].h : (mh - _y);
     680              : 
     681            9 :     g_assert (_w > 0 && _h > 0);
     682            9 :     dsize = hsize + (esize * ch * _w * _h);
     683            9 :     cropped = (guint8 *) g_malloc0 (dsize);
     684              : 
     685              :     /* set header for flex tensor */
     686            9 :     meta.dimension[1] = _w;
     687            9 :     meta.dimension[2] = _h;
     688            9 :     meta.dimension[3] = 1;
     689            9 :     gst_tensor_meta_info_update_header (&meta, cropped);
     690              : 
     691          232 :     for (j = 0; j < _h; j++) {
     692          223 :       src = dpos + esize * ch * (_x + (j + _y) * mw);
     693          223 :       desc = cropped + hsize + (esize * ch * _w) * j;
     694          223 :       memcpy (desc, src, (esize * ch * _w));
     695              :     }
     696              : 
     697              :     /* Prepare tensors-info to append memory into gst-buffer. */
     698            9 :     gst_tensor_meta_info_convert (&meta, &crop_info);
     699              :     crop_mem =
     700            9 :         gst_memory_new_wrapped (0, cropped, dsize, 0, dsize, cropped, g_free);
     701              : 
     702            9 :     gst_tensor_buffer_append_memory (result, crop_mem, &crop_info);
     703            9 :     gst_tensor_info_free (&crop_info);
     704              :   }
     705              : 
     706              :   /* set timestamp from raw buffer */
     707            5 :   gst_buffer_copy_into (result, raw, GST_BUFFER_COPY_METADATA, 0, -1);
     708              : 
     709            6 : done:
     710            6 :   if (mem) {
     711            6 :     gst_memory_unmap (mem, &map);
     712            6 :     gst_memory_unref (mem);
     713              :   }
     714              : 
     715            6 :   return result;
     716              : }
     717              : 
     718              : /**
     719              :  * @brief Internal function to transform the input buffer.
     720              :  */
     721              : static GstFlowReturn
     722            9 : gst_tensor_crop_chain (GstTensorCrop * self,
     723              :     GstCollectData * data_raw, GstCollectData * data_info)
     724              : {
     725              :   GstFlowReturn ret;
     726              :   GstBuffer *buf_raw, *buf_info, *result;
     727              :   GstTensorCropPadData *cpad;
     728              :   tensor_crop_info_s cinfo;
     729              :   gboolean drop_raw, drop_info;
     730              : 
     731           18 :   g_return_val_if_fail (data_raw && data_info, GST_FLOW_ERROR);
     732              : 
     733            9 :   buf_raw = gst_collect_pads_peek (self->collect, data_raw);
     734            9 :   buf_info = gst_collect_pads_peek (self->collect, data_info);
     735            9 :   drop_raw = (buf_raw != NULL);
     736            9 :   drop_info = (buf_info != NULL);
     737              : 
     738            9 :   if (!buf_raw || !buf_info) {
     739            0 :     ret = GST_FLOW_EOS;
     740            0 :     goto done;
     741              :   }
     742              : 
     743            9 :   cpad = (GstTensorCropPadData *) data_raw;
     744            9 :   buf_raw = gst_tensor_buffer_from_config (buf_raw, &cpad->config);
     745            9 :   cpad = (GstTensorCropPadData *) data_info;
     746            9 :   buf_info = gst_tensor_buffer_from_config (buf_info, &cpad->config);
     747              : 
     748              :   /**
     749              :    * The case when raw and info have different timestamp.
     750              :    * Compare timestamp and if time diff is less than lateness, crop raw buffer.
     751              :    */
     752            9 :   if (self->lateness >= 0) {
     753              :     GstClockTime ts_raw, ts_info, lateness;
     754              : 
     755            4 :     ts_raw = GST_BUFFER_TIMESTAMP (buf_raw);
     756            4 :     ts_info = GST_BUFFER_TIMESTAMP (buf_info);
     757            4 :     lateness = self->lateness * GST_MSECOND;
     758              : 
     759            4 :     if (GST_CLOCK_TIME_IS_VALID (ts_raw) && GST_CLOCK_TIME_IS_VALID (ts_info)) {
     760            4 :       if (((GstClockTime) ABS (GST_CLOCK_DIFF (ts_raw, ts_info))) > lateness) {
     761            2 :         GST_DEBUG_OBJECT (self, "Drop old buffer and wait for next.");
     762            2 :         GST_DEBUG_OBJECT (self, "Raw buffer ts: %" GST_TIME_FORMAT,
     763              :             GST_TIME_ARGS (ts_raw));
     764            2 :         GST_DEBUG_OBJECT (self, "Info buffer ts: %" GST_TIME_FORMAT,
     765              :             GST_TIME_ARGS (ts_info));
     766              : 
     767              :         /* clear old buffer and return ok to get next buffer */
     768            2 :         if (ts_raw > ts_info)
     769            1 :           drop_raw = FALSE;
     770              :         else
     771            1 :           drop_info = FALSE;
     772              : 
     773            2 :         ret = GST_FLOW_OK;
     774            2 :         goto done;
     775              :       }
     776              :     } else {
     777            0 :       GST_WARNING_OBJECT (self,
     778              :           "Incoming buffer has invalid timestamp, continue cropping data.");
     779              :     }
     780              :   }
     781              : 
     782            7 :   if (!gst_tensor_crop_get_crop_info (self, buf_info, &cinfo)) {
     783            1 :     ret = GST_FLOW_ERROR;
     784            1 :     goto done;
     785              :   }
     786              : 
     787            6 :   result = gst_tensor_crop_do_cropping (self, buf_raw, &cinfo);
     788            6 :   ret = gst_pad_push (self->srcpad, result);
     789              : 
     790            9 : done:
     791            9 :   if (buf_raw)
     792            9 :     gst_buffer_unref (buf_raw);
     793            9 :   if (buf_info)
     794            9 :     gst_buffer_unref (buf_info);
     795              : 
     796              :   /* clear buffer in collect pads */
     797            9 :   if (drop_raw)
     798            8 :     gst_buffer_unref (gst_collect_pads_pop (self->collect, data_raw));
     799            9 :   if (drop_info)
     800            8 :     gst_buffer_unref (gst_collect_pads_pop (self->collect, data_info));
     801              : 
     802            9 :   return ret;
     803              : }
     804              : 
     805              : /**
     806              :  * @brief Chain function called when the buffer is available on all of the collect pads.
     807              :  */
     808              : static GstFlowReturn
     809           10 : gst_tensor_crop_collected (GstCollectPads * pads, gpointer user_data)
     810              : {
     811              :   GstTensorCrop *self;
     812              :   GstCollectData *data_raw, *data_info;
     813              :   GSList *walk;
     814              :   GstFlowReturn ret;
     815              : 
     816           10 :   self = GST_TENSOR_CROP (user_data);
     817           10 :   data_raw = data_info = NULL;
     818              : 
     819           10 :   ret = gst_tensor_crop_negotiate (self);
     820           10 :   if (ret != GST_FLOW_OK)
     821            0 :     return ret;
     822              : 
     823           28 :   for (walk = pads->data; walk; walk = g_slist_next (walk)) {
     824              :     GstCollectData *data;
     825              : 
     826           19 :     data = (GstCollectData *) walk->data;
     827              : 
     828           19 :     if (GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_EOS)) {
     829            1 :       gst_pad_push_event (self->srcpad, gst_event_new_eos ());
     830            1 :       return GST_FLOW_EOS;
     831              :     }
     832              : 
     833           18 :     if (data->pad == self->sinkpad_raw) {
     834            9 :       data_raw = data;
     835            9 :     } else if (data->pad == self->sinkpad_info) {
     836            9 :       data_info = data;
     837              :     }
     838              :   }
     839              : 
     840            9 :   return gst_tensor_crop_chain (self, data_raw, data_info);
     841              : }
        

Generated by: LCOV version 2.0-1