LCOV - code coverage report
Current view: top level - nnstreamer-2.4.2/gst/nnstreamer - nnstreamer_plugin_api_impl.c (source / functions) Coverage Total Hit
Test: nnstreamer 2.4.2-0 nnstreamer/nnstreamer#58eaa3c6edb7484955a5d8c32f47a60ba9501210 Lines: 88.8 % 860 764
Test Date: 2025-04-18 05:37:26 Functions: 100.0 % 43 43

            Line data    Source code
       1              : /* SPDX-License-Identifier: LGPL-2.1-only */
       2              : /**
       3              :  * NNStreamer Common Header's Contents (pipeline extension)
       4              :  * Copyright (C) 2020 MyungJoo Ham <myungjoo.ham@samsung.com>
       5              :  */
       6              : /**
       7              :  * @file        nnstreamer_plugin_api_impl.c
       8              :  * @date        14 Apr 2020
       9              :  * @brief       Common data for NNStreamer, the GStreamer plugin for neural networks
      10              :  * @see         https://github.com/nnstreamer/nnstreamer
      11              :  * @author      MyungJoo Ham <myungjoo.ham@samsung.com>
      12              :  * @bug         No known bugs except for NYI items
      13              :  *
      14              :  */
      15              : 
      16              : #include <nnstreamer_util.h>
      17              : #include <string.h>
      18              : #include <tensor_common.h>
      19              : 
      20              : static const gchar *gst_tensor_time_sync_mode_string[] = {
      21              :   [SYNC_NOSYNC] = "nosync",
      22              :   [SYNC_SLOWEST] = "slowest",
      23              :   [SYNC_BASEPAD] = "basepad",
      24              :   [SYNC_REFRESH] = "refresh",
      25              :   [SYNC_END] = NULL
      26              : };
      27              : 
      28              : /**
      29              :  * @brief The old rank of tensor.
      30              :  */
      31              : #define NNS_TENSOR_RANK_LIMIT_PREV (4)
      32              : 
      33              : #define NNS_TENSOR_EXTRA_MAGIC 0xf00dc0de
      34              : 
      35              : /**
      36              :  * @brief Data structure to describe a "extra" tensor data.
      37              :  * This represents the information of the NNS_TENSOR_SIZE_LIMIT-th memory block for tensor stream.
      38              :  */
      39              : typedef struct
      40              : {
      41              :   uint32_t magic;
      42              :   uint32_t version;
      43              :   uint32_t num_extra_tensors;
      44              :   uint64_t reserved;
      45              :   GstTensorInfo infos[NNS_TENSOR_SIZE_EXTRA_LIMIT];
      46              : } GstTensorExtraInfo;
      47              : 
      48              : /**
      49              :  * @brief Check if given memory has extra tensors.
      50              :  * @param[in] map GstMapInfo of GstMemory to be checked.
      51              :  * @return TRUE if @map has extra tensors, otherwise FALSE.
      52              :  */
      53              : static gboolean
      54         2910 : gst_memory_map_is_extra_tensor (GstMapInfo * map)
      55              : {
      56              :   GstTensorExtraInfo *extra_info;
      57              :   gboolean is_extra;
      58              : 
      59         2910 :   g_return_val_if_fail (map != NULL, FALSE);
      60              : 
      61         2910 :   if (map->size < sizeof (GstTensorExtraInfo))
      62          380 :     return FALSE;
      63              : 
      64         2530 :   extra_info = (GstTensorExtraInfo *) map->data;
      65              : 
      66              :   /* check magic in header (extra info) of the memory */
      67         2530 :   is_extra = (extra_info && extra_info->magic == NNS_TENSOR_EXTRA_MAGIC);
      68              : 
      69         2530 :   return is_extra;
      70              : }
      71              : 
      72              : /**
      73              :  * @brief Initialize GstTensorExtraInfo structure with given @a memory.
      74              :  * @param[in/out] extra GstTensorExtraInfo to be initialized.
      75              :  * @param[in] reserved_size The memory size of extra memory block.
      76              :  */
      77              : static void
      78           28 : gst_tensor_extra_info_init (GstTensorExtraInfo * extra, gsize reserved_size)
      79              : {
      80              :   guint i;
      81              : 
      82           28 :   g_return_if_fail (extra != NULL);
      83              : 
      84           28 :   extra->magic = NNS_TENSOR_EXTRA_MAGIC;
      85           28 :   extra->version = 0;
      86           28 :   extra->num_extra_tensors = 0;
      87              : 
      88              :   /* set reserved size of NNS_TENSOR_SIZE_LIMIT-th memory */
      89           28 :   extra->reserved = reserved_size;
      90         6748 :   for (i = 0; i < NNS_TENSOR_SIZE_EXTRA_LIMIT; ++i) {
      91         6720 :     gst_tensor_info_init (&extra->infos[i]);
      92              :   }
      93              : }
      94              : 
      95              : /**
      96              :  * @brief Get the corresponding mode from the string value.
      97              :  * @param[in] str The string value for the mode.
      98              :  * @return Corresponding mode for the string. SYNC_END for errors.
      99              :  */
     100              : tensor_time_sync_mode
     101           66 : gst_tensor_time_sync_get_mode (const gchar * str)
     102              : {
     103              :   gint index;
     104              : 
     105           66 :   index = find_key_strv (gst_tensor_time_sync_mode_string, str);
     106              : 
     107           66 :   return (index < 0) ? SYNC_END : index;
     108              : }
     109              : 
     110              : /**
     111              :  * @brief Get the time-sync mode string.
     112              :  * @return Corresponding mode string.
     113              :  */
     114              : const gchar *
     115            2 : gst_tensor_time_sync_get_mode_string (tensor_time_sync_mode mode)
     116              : {
     117            2 :   return gst_tensor_time_sync_mode_string[mode];
     118              : }
     119              : 
     120              : /**
     121              :  * @brief Setup time sync option.
     122              :  * @param[in/out] filter "this" pointer. Sync mode & option MUST BE set already.
     123              :  * @return True if successfully set the option.
     124              :  */
     125              : gboolean
     126           95 : gst_tensor_time_sync_set_option_data (tensor_time_sync_data * sync)
     127              : {
     128           95 :   g_return_val_if_fail (sync != NULL, FALSE);
     129              : 
     130           95 :   if (sync->mode == SYNC_END || sync->option == NULL)
     131           66 :     return FALSE;
     132              : 
     133           29 :   switch (sync->mode) {
     134            0 :     case SYNC_NOSYNC:
     135            0 :       break;
     136            0 :     case SYNC_SLOWEST:
     137            0 :       break;
     138           29 :     case SYNC_BASEPAD:
     139              :     {
     140           29 :       g_auto (GStrv) strv = g_strsplit (sync->option, ":", 2);
     141              :       guint sink_id;
     142              :       guint duration;
     143              : 
     144           29 :       if (strv[0] != NULL)
     145           29 :         sink_id = (guint) g_ascii_strtoull (strv[0], NULL, 10);
     146              :       else
     147            0 :         sink_id = 0;
     148              : 
     149           29 :       if (strv[1] != NULL)
     150           27 :         duration = (guint) g_ascii_strtoull (strv[1], NULL, 10);
     151              :       else
     152            2 :         duration = G_MAXINT;
     153              : 
     154           29 :       sync->data_basepad.sink_id = sink_id;
     155           29 :       sync->data_basepad.duration = duration;
     156           29 :       break;
     157              :     }
     158            0 :     default:
     159              :       /* unknown mode */
     160            0 :       GST_WARNING ("Unknown mode = %d", sync->mode);
     161            0 :       return FALSE;
     162              :   }
     163              : 
     164           29 :   return TRUE;
     165              : }
     166              : 
     167              : /**
     168              :  * @brief Internal function to detect EOS using the number of empty pads.
     169              :  * @param[in] collect Collect pad.
     170              :  * @param[in] sync Synchronization option.
     171              :  * @param[in] empty The number of empty pads (pad has no buffer).
     172              :  * @return True if EOS.
     173              :  */
     174              : static gboolean
     175        13298 : _gst_tensor_time_sync_is_eos (GstCollectPads * collect,
     176              :     tensor_time_sync_data * sync, guint empty)
     177              : {
     178              :   guint total;
     179        13298 :   gboolean is_eos = FALSE;
     180              : 
     181        13298 :   total = g_slist_length (collect->data);
     182              : 
     183        13298 :   switch (sync->mode) {
     184        11378 :     case SYNC_REFRESH:
     185        11378 :       if (empty == total)
     186            2 :         is_eos = TRUE;
     187        11378 :       break;
     188         1920 :     default:
     189         1920 :       if (empty > 0)
     190          206 :         is_eos = TRUE;
     191         1920 :       break;
     192              :   }
     193              : 
     194        13298 :   return is_eos;
     195              : }
     196              : 
     197              : /**
     198              :  * @brief A function call to decide current timestamp among collected pads based on PTS.
     199              :  * It will decide current timestamp according to sync option.
     200              :  * GstMeta is also copied with same sync mode.
     201              :  */
     202              : gboolean
     203         6753 : gst_tensor_time_sync_get_current_time (GstCollectPads * collect,
     204              :     tensor_time_sync_data * sync, GstClockTime * current_time,
     205              :     GstBuffer * tensors_buf)
     206              : {
     207         6753 :   GSList *walk = NULL;
     208              :   guint count, empty_pad;
     209              : 
     210         6753 :   g_return_val_if_fail (collect != NULL, FALSE);
     211         6753 :   g_return_val_if_fail (sync != NULL, FALSE);
     212         6753 :   g_return_val_if_fail (current_time != NULL, FALSE);
     213              : 
     214         6753 :   walk = collect->data;
     215         6753 :   count = empty_pad = 0;
     216              : 
     217        21324 :   while (walk) {
     218              :     GstCollectData *data;
     219              :     GstBuffer *buf;
     220        14571 :     gboolean need_update = FALSE;
     221              : 
     222        14571 :     data = (GstCollectData *) walk->data;
     223        14571 :     buf = gst_collect_pads_peek (collect, data);
     224        14571 :     walk = g_slist_next (walk);
     225              : 
     226        14571 :     if (buf) {
     227         8358 :       switch (sync->mode) {
     228         7702 :         case SYNC_NOSYNC:
     229              :           /* fall-through */
     230              :         case SYNC_SLOWEST:
     231              :         case SYNC_REFRESH:
     232         7702 :           if (*current_time < GST_BUFFER_PTS (buf))
     233         6244 :             need_update = TRUE;
     234         7702 :           break;
     235          656 :         case SYNC_BASEPAD:
     236          656 :           if (count == sync->data_basepad.sink_id)
     237          296 :             need_update = TRUE;
     238          656 :           break;
     239            0 :         default:
     240            0 :           break;
     241              :       }
     242         8358 :       if (need_update) {
     243         6540 :         *current_time = GST_BUFFER_PTS (buf);
     244         6540 :         gst_buffer_copy_into (tensors_buf, buf, GST_BUFFER_COPY_METADATA,
     245              :             0, -1);
     246              :       }
     247         8358 :       gst_buffer_unref (buf);
     248              :     } else {
     249         6213 :       empty_pad++;
     250              :     }
     251              : 
     252        14571 :     count++;
     253              :   }
     254              : 
     255         6753 :   return _gst_tensor_time_sync_is_eos (collect, sync, empty_pad);
     256              : }
     257              : 
     258              : /**
     259              :  * @brief A function to be called while processing a flushing event.
     260              :  * It should clear old buffer and reset pad data.
     261              :  */
     262              : void
     263          152 : gst_tensor_time_sync_flush (GstCollectPads * collect)
     264              : {
     265              :   GSList *walk;
     266              :   GstTensorCollectPadData *pad;
     267              : 
     268          152 :   g_return_if_fail (collect != NULL);
     269              : 
     270          152 :   walk = collect->data;
     271          633 :   while (walk) {
     272          481 :     pad = (GstTensorCollectPadData *) walk->data;
     273              : 
     274          481 :     if (pad->buffer) {
     275          418 :       gst_buffer_unref (pad->buffer);
     276          418 :       pad->buffer = NULL;
     277              :     }
     278              : 
     279          481 :     walk = g_slist_next (walk);
     280              :   }
     281              : }
     282              : 
     283              : /**
     284              :  * @brief Internal function to update buffer in pad data based on the sync mode.
     285              :  */
     286              : static gboolean
     287         3779 : _gst_tensor_time_sync_buffer_update (GstCollectPads * collect,
     288              :     GstCollectData * data, GstClockTime current, GstClockTime base,
     289              :     tensor_time_sync_data * sync)
     290              : {
     291              :   GstTensorCollectPadData *pad;
     292              :   GstBuffer *buf;
     293              : 
     294         3779 :   pad = (GstTensorCollectPadData *) data;
     295              : 
     296         3779 :   buf = gst_collect_pads_peek (collect, data);
     297         3779 :   if (buf != NULL) {
     298         3645 :     if (GST_BUFFER_PTS (buf) < current) {
     299          671 :       gst_buffer_unref (buf);
     300          671 :       if (pad->buffer != NULL)
     301          669 :         gst_buffer_unref (pad->buffer);
     302          671 :       pad->buffer = gst_collect_pads_pop (collect, data);
     303          671 :       return FALSE;
     304              :     }
     305              : 
     306         4399 :     if ((sync->mode == SYNC_SLOWEST && pad->buffer != NULL &&
     307         1425 :             (ABS (GST_CLOCK_DIFF (current, GST_BUFFER_PTS (pad->buffer))) <
     308         1425 :                 ABS (GST_CLOCK_DIFF (current, GST_BUFFER_PTS (buf))))) ||
     309         3943 :         (sync->mode == SYNC_BASEPAD && pad->buffer != NULL &&
     310         1133 :             (((GstClockTime) ABS (GST_CLOCK_DIFF (current,
     311              :                             GST_BUFFER_PTS (buf)))) > base))) {
     312              :       /* keep last buffer */
     313              :     } else {
     314              :       /* update last buffer */
     315         2240 :       if (pad->buffer != NULL)
     316         1824 :         gst_buffer_unref (pad->buffer);
     317         2240 :       pad->buffer = gst_collect_pads_pop (collect, data);
     318              :     }
     319              : 
     320         2974 :     gst_buffer_unref (buf);
     321              :   }
     322              : 
     323         3108 :   return TRUE;
     324              : }
     325              : 
     326              : /**
     327              :  * @brief A function call to make tensors from collected pads.
     328              :  * It decide which buffer is going to be used according to sync option.
     329              :  * @return True to push buffer.
     330              :  */
     331              : gboolean
     332         7217 : gst_tensor_time_sync_buffer_from_collectpad (GstCollectPads * collect,
     333              :     tensor_time_sync_data * sync, GstClockTime current_time,
     334              :     GstBuffer * tensors_buf, GstTensorsConfig * configs, gboolean * is_eos)
     335              : {
     336         7217 :   GSList *walk = NULL;
     337              :   GstCollectData *data;
     338              :   GstTensorCollectPadData *pad;
     339         7217 :   GstBuffer *buf = NULL;
     340              :   GstMemory *mem;
     341         7217 :   gint old_numerator = G_MAXINT;
     342         7217 :   gint old_denominator = G_MAXINT;
     343              :   guint counting, empty_pad;
     344              :   GstTensorsConfig in_configs;
     345         7217 :   GstClockTime base_time = 0;
     346              :   GstTensorInfo *_info;
     347              :   guint i, j;
     348              :   GstMemory *in_mem[NNS_TENSOR_SIZE_LIMIT];
     349              :   tensor_format in_formats[NNS_TENSOR_SIZE_LIMIT];
     350              : 
     351        14434 :   g_return_val_if_fail (collect != NULL, FALSE);
     352         7217 :   g_return_val_if_fail (sync != NULL, FALSE);
     353         7217 :   g_return_val_if_fail (tensors_buf != NULL, FALSE);
     354         7217 :   g_return_val_if_fail (configs != NULL, FALSE);
     355         7217 :   g_return_val_if_fail (is_eos != NULL, FALSE);
     356              : 
     357         7217 :   walk = collect->data;
     358         7217 :   counting = empty_pad = 0;
     359              : 
     360         7217 :   if (sync->mode == SYNC_BASEPAD) {
     361          711 :     walk = g_slist_nth (walk, sync->data_basepad.sink_id);
     362          711 :     if (walk == NULL) {
     363            0 :       GST_ERROR_OBJECT (collect, "Cannot get GstCollectData from GSList");
     364            0 :       return FALSE;
     365              :     }
     366              : 
     367          711 :     data = (GstCollectData *) walk->data;
     368          711 :     pad = (GstTensorCollectPadData *) data;
     369              : 
     370          711 :     buf = gst_collect_pads_peek (collect, data);
     371          711 :     if (buf != NULL) {
     372          649 :       if (pad->buffer != NULL)
     373          624 :         base_time =
     374          624 :             MIN ((GstClockTimeDiff) sync->data_basepad.duration,
     375              :             ABS (GST_CLOCK_DIFF (GST_BUFFER_PTS (buf),
     376              :                     GST_BUFFER_PTS (pad->buffer))) - 1);
     377          649 :       gst_buffer_unref (buf);
     378              :     }
     379              :   }
     380              : 
     381         7217 :   walk = collect->data;
     382              : 
     383         7217 :   gst_tensors_config_init (&in_configs);
     384              : 
     385        21958 :   while (walk) {
     386        15413 :     gboolean configured = FALSE;
     387        15413 :     gboolean is_empty = FALSE;
     388              : 
     389        15413 :     data = (GstCollectData *) walk->data;
     390        15413 :     pad = (GstTensorCollectPadData *) data;
     391              : 
     392        15413 :     if (gst_pad_has_current_caps (data->pad)) {
     393        15412 :       GstCaps *caps = gst_pad_get_current_caps (data->pad);
     394              : 
     395        15412 :       gst_tensors_config_free (&in_configs);
     396        15412 :       configured = gst_tensors_config_from_caps (&in_configs, caps, TRUE);
     397              : 
     398        15412 :       gst_caps_unref (caps);
     399              :     }
     400              : 
     401              :     /**
     402              :      * This would be an internal logic error.
     403              :      * in_configs should be already confirmed valid at the negotiation phase
     404              :      * and this function should be called in a running pipeline.
     405              :      * If new sync mode is enabled (e.g., handle output when a pad gets new buffer),
     406              :      * this may cause unexpected exception.
     407              :      */
     408        15413 :     if (!configured) {
     409            1 :       return FALSE;
     410              :     }
     411              : 
     412        15412 :     if (in_configs.rate_d < old_denominator)
     413         7216 :       old_denominator = in_configs.rate_d;
     414        15412 :     if (in_configs.rate_n < old_numerator)
     415         7424 :       old_numerator = in_configs.rate_n;
     416              : 
     417        15412 :     walk = g_slist_next (walk);
     418              : 
     419        15412 :     switch (sync->mode) {
     420         3779 :       case SYNC_SLOWEST:
     421              :         /* fall-through */
     422              :       case SYNC_BASEPAD:
     423         3779 :         if (!_gst_tensor_time_sync_buffer_update (collect, data,
     424              :                 current_time, base_time, sync))
     425          671 :           return FALSE;
     426         3108 :         buf = gst_buffer_ref (pad->buffer);
     427         3108 :         is_empty = (buf == NULL);
     428         3108 :         break;
     429          257 :       case SYNC_NOSYNC:
     430          257 :         buf = gst_collect_pads_pop (collect, data);
     431          257 :         is_empty = (buf == NULL);
     432          257 :         break;
     433        11376 :       case SYNC_REFRESH:
     434        11376 :         buf = gst_collect_pads_pop (collect, data);
     435        11376 :         if (buf != NULL) {
     436         5689 :           if (pad->buffer != NULL) {
     437         5687 :             gst_buffer_unref (pad->buffer);
     438              :           }
     439         5689 :           pad->buffer = gst_buffer_ref (buf);
     440              :         } else {
     441         5687 :           if (pad->buffer == NULL) {
     442            0 :             *is_eos = FALSE;
     443            0 :             ml_logd ("Not the all buffers are arrived yet.");
     444            0 :             return FALSE;
     445              :           }
     446         5687 :           is_empty = TRUE;
     447         5687 :           buf = gst_buffer_ref (pad->buffer);
     448              :         }
     449        11376 :         break;
     450            0 :       default:
     451            0 :         break;
     452              :     }
     453              : 
     454        14741 :     if (GST_IS_BUFFER (buf)) {
     455        14741 :       guint32 n_tensor = gst_tensor_buffer_get_count (buf);
     456        14741 :       buf = gst_tensor_buffer_from_config (buf, &in_configs);
     457              : 
     458              :       /** These are internal logic error. If given inputs are incorrect,
     459              :           the negotiation should have been failed before this stage. */
     460        14741 :       if (gst_tensors_config_is_static (&in_configs))
     461        14715 :         g_assert (n_tensor == in_configs.info.num_tensors);
     462        14741 :       g_assert ((counting + n_tensor) <= NNS_TENSOR_SIZE_LIMIT);
     463              : 
     464        14741 :       if (gst_tensors_config_is_flexible (&in_configs))
     465           26 :         configs->info.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
     466              : 
     467        29862 :       for (i = 0; i < n_tensor; ++i) {
     468        15121 :         in_mem[counting] = gst_tensor_buffer_get_nth_memory (buf, i);
     469              : 
     470              :         /* set info */
     471        15121 :         gst_tensor_info_copy (gst_tensors_info_get_nth_info (&configs->info,
     472        15121 :                 counting), gst_tensors_info_get_nth_info (&in_configs.info, i));
     473        15121 :         in_formats[counting] = in_configs.info.format;
     474        15121 :         counting++;
     475              :       }
     476              : 
     477        14741 :       gst_buffer_unref (buf);
     478              :     }
     479        14741 :     if (is_empty)
     480         5687 :       empty_pad++;
     481              :   }
     482              : 
     483              :   /* append memories to output buffer */
     484        20870 :   for (i = 0; i < counting; i++) {
     485        14325 :     _info = gst_tensors_info_get_nth_info (&configs->info, i);
     486        14325 :     mem = in_mem[i];
     487              : 
     488        14325 :     if (gst_tensors_config_is_flexible (configs)) {
     489              :       /* append header if input tensor is not flexible */
     490           46 :       if (in_formats[i] != _NNS_TENSOR_FORMAT_FLEXIBLE) {
     491              :         GstTensorMetaInfo meta;
     492              : 
     493           20 :         gst_tensor_info_convert_to_meta (_info, &meta);
     494           20 :         mem = gst_tensor_meta_info_append_header (&meta, in_mem[i]);
     495           20 :         gst_memory_unref (in_mem[i]);
     496              :       }
     497              :     }
     498              : 
     499        14325 :     if (!gst_tensor_buffer_append_memory (tensors_buf, mem, _info)) {
     500            0 :       for (j = i + 1; j < counting; j++)
     501            0 :         gst_memory_unref (in_mem[j]);
     502              : 
     503            0 :       nns_loge ("Failed to append memory to buffer.");
     504            0 :       return FALSE;
     505              :     }
     506              :   }
     507              : 
     508         6545 :   configs->info.num_tensors = counting;
     509         6545 :   configs->rate_d = old_denominator;
     510         6545 :   configs->rate_n = old_numerator;
     511              : 
     512         6545 :   GST_BUFFER_PTS (tensors_buf) = current_time;
     513              : 
     514         6545 :   gst_tensors_config_free (&in_configs);
     515              : 
     516              :   /* check eos */
     517         6545 :   *is_eos = _gst_tensor_time_sync_is_eos (collect, sync, empty_pad);
     518         6545 :   return !(*is_eos);
     519              : }
     520              : 
     521              : /**
     522              :  * @brief Configure gst-buffer with tensors information.
     523              :  * NNStreamer handles single memory chunk as single tensor.
     524              :  * If incoming buffer has invalid memories, separate it and generate new gst-buffer using tensors information.
     525              :  * Note that this function always takes the ownership of input buffer.
     526              :  * @param in input buffer
     527              :  * @param config tensors config structure
     528              :  * @return Newly allocated buffer. Null if failed. Caller should unref the buffer using gst_buffer_unref().
     529              :  */
     530              : GstBuffer *
     531        36677 : gst_tensor_buffer_from_config (GstBuffer * in, GstTensorsConfig * config)
     532              : {
     533        36677 :   GstBuffer *out = NULL;
     534        36677 :   GstMemory *all = NULL;
     535              :   GstMapInfo map;
     536              :   guint i, num;
     537              :   gsize total, offset;
     538              :   gsize mem_size[NNS_TENSOR_MEMORY_MAX];
     539        36677 :   gboolean configured = FALSE;
     540        36677 :   gboolean is_extra = FALSE;
     541              : 
     542        36677 :   if (!GST_IS_BUFFER (in)) {
     543            1 :     nns_loge ("Failed to get tensor buffer, invalid input buffer.");
     544        36677 :     return NULL;
     545              :   }
     546              : 
     547        36676 :   if (!gst_tensors_config_validate (config)) {
     548            2 :     nns_loge ("Failed to get tensor buffer, invalid tensor configuration.");
     549            2 :     goto error;
     550              :   }
     551              : 
     552        36674 :   num = gst_buffer_n_memory (in);
     553        36674 :   total = gst_buffer_get_size (in);
     554              : 
     555              :   /* get memory size */
     556        36674 :   if (gst_tensors_config_is_static (config)) {
     557        36597 :     if (num == config->info.num_tensors) {
     558              :       /* Do nothing, pass input buffer. */
     559        36590 :       out = gst_buffer_ref (in);
     560        36590 :       goto done;
     561              :     }
     562              : 
     563            7 :     num = config->info.num_tensors;
     564            7 :     if ((is_extra = (num > NNS_TENSOR_MEMORY_MAX)))
     565            5 :       num = NNS_TENSOR_MEMORY_MAX;
     566           93 :     for (i = 0; i < num; i++)
     567           86 :       mem_size[i] = gst_tensors_info_get_size (&config->info, i);
     568            7 :     if (is_extra) {
     569            5 :       mem_size[num - 1] += sizeof (GstTensorExtraInfo);
     570           25 :       for (; i < config->info.num_tensors; i++)
     571           20 :         mem_size[num - 1] += gst_tensors_info_get_size (&config->info, i);
     572              :     }
     573              :   } else {
     574           77 :     if (num > 1) {
     575              :       /* Suppose it is already configured. */
     576           20 :       out = gst_buffer_ref (in);
     577           20 :       goto done;
     578              :     }
     579              : 
     580           57 :     if (!gst_buffer_map (in, &map, GST_MAP_READ)) {
     581            0 :       nns_loge ("Failed to get tensor buffer, cannot get the memory info.");
     582            0 :       goto error;
     583              :     }
     584              : 
     585           57 :     num = 0;
     586           57 :     offset = 0;
     587          116 :     while (offset < total) {
     588              :       GstTensorMetaInfo meta;
     589           59 :       gpointer h = map.data + offset;
     590              : 
     591           59 :       if (num >= NNS_TENSOR_MEMORY_MAX - 1) {
     592              :         /* Suppose remained memory may include extra tensors. */
     593            0 :         mem_size[num++] = total - offset;
     594            0 :         break;
     595              :       }
     596              : 
     597           59 :       gst_tensor_meta_info_parse_header (&meta, h);
     598           59 :       mem_size[num] = gst_tensor_meta_info_get_header_size (&meta);
     599           59 :       mem_size[num] += gst_tensor_meta_info_get_data_size (&meta);
     600              : 
     601           59 :       offset += mem_size[num];
     602           59 :       num++;
     603              :     }
     604              : 
     605           57 :     gst_buffer_unmap (in, &map);
     606              : 
     607           57 :     if (num == 1) {
     608              :       /* Do nothing, pass input buffer. */
     609           56 :       out = gst_buffer_ref (in);
     610           56 :       goto done;
     611              :     }
     612              :   }
     613              : 
     614              :   /* configure output buffer */
     615            8 :   out = gst_buffer_new ();
     616            8 :   all = gst_buffer_get_all_memory (in);
     617            8 :   offset = 0;
     618              : 
     619           95 :   for (i = 0; i < num; i++) {
     620              :     /* invalid memory size */
     621           88 :     if (offset + mem_size[i] > total) {
     622            1 :       nns_loge ("Failed to get tensor buffer, data size is mismatched.");
     623            1 :       goto error;
     624              :     }
     625              : 
     626           87 :     gst_buffer_append_memory (out, gst_memory_share (all, offset, mem_size[i]));
     627           87 :     offset += mem_size[i];
     628              :   }
     629              : 
     630            7 :   gst_buffer_copy_into (out, in, GST_BUFFER_COPY_METADATA, 0, -1);
     631              : 
     632        36673 : done:
     633        36673 :   configured = TRUE;
     634        36676 : error:
     635        36676 :   gst_buffer_unref (in);
     636              : 
     637        36676 :   if (all)
     638            8 :     gst_memory_unref (all);
     639              : 
     640        36676 :   if (!configured) {
     641            3 :     if (out) {
     642            1 :       gst_buffer_unref (out);
     643            1 :       out = NULL;
     644              :     }
     645              :   }
     646              : 
     647        36676 :   return out;
     648              : }
     649              : 
     650              : /**
     651              :  * @brief Internal struct to handle aggregation data in hash table.
     652              :  */
     653              : typedef struct
     654              : {
     655              :   GstAdapter *adapter;
     656              : } gst_tensor_aggregation_data_s;
     657              : 
     658              : #define AGGREGATION_DEFAULT_KEY 0xC0FFEEU
     659              : 
     660              : /**
     661              :  * @brief Internal function to free aggregation data.
     662              :  */
     663              : static void
     664          868 : gst_tensor_aggregation_free_data (gpointer data)
     665              : {
     666              :   gst_tensor_aggregation_data_s *aggr;
     667              : 
     668          868 :   aggr = (gst_tensor_aggregation_data_s *) data;
     669          868 :   if (aggr) {
     670          868 :     gst_adapter_clear (aggr->adapter);
     671          868 :     g_object_unref (aggr->adapter);
     672              : 
     673          868 :     g_free (aggr);
     674              :   }
     675          868 : }
     676              : 
     677              : /**
     678              :  * @brief Internal function to add new aggregation data.
     679              :  */
     680              : static gst_tensor_aggregation_data_s *
     681          925 : gst_tensor_aggregation_add_data (GHashTable * table, const guint32 key)
     682              : {
     683              :   gst_tensor_aggregation_data_s *aggr;
     684              :   guint32 hashkey;
     685              : 
     686          925 :   g_return_val_if_fail (table != NULL, NULL);
     687          925 :   if (key == 0)
     688            0 :     hashkey = AGGREGATION_DEFAULT_KEY;
     689              :   else
     690          925 :     hashkey = key;
     691          925 :   aggr = g_new0 (gst_tensor_aggregation_data_s, 1);
     692          925 :   aggr->adapter = gst_adapter_new ();
     693              : 
     694          925 :   g_hash_table_insert (table, GINT_TO_POINTER (hashkey), aggr);
     695          925 :   return aggr;
     696              : }
     697              : 
     698              : /**
     699              :  * @brief Internal function to get aggregation data.
     700              :  */
     701              : static gst_tensor_aggregation_data_s *
     702          303 : gst_tensor_aggregation_get_data (GHashTable * table, const guint32 key)
     703              : {
     704          303 :   g_return_val_if_fail (table != NULL, NULL);
     705              : 
     706          315 :   return (gst_tensor_aggregation_data_s *) g_hash_table_lookup (table,
     707           12 :       GINT_TO_POINTER (key == 0 ? AGGREGATION_DEFAULT_KEY : key));
     708              : }
     709              : 
     710              : /**
     711              :  * @brief Internal function to remove all buffers from aggregation data.
     712              :  */
     713              : static void
     714         3491 : gst_tensor_aggregation_clear_internal (gpointer key, gpointer value,
     715              :     gpointer user_data)
     716              : {
     717              :   gst_tensor_aggregation_data_s *aggr;
     718              : 
     719              :   UNUSED (key);
     720              :   UNUSED (user_data);
     721              : 
     722         3491 :   aggr = (gst_tensor_aggregation_data_s *) value;
     723         3491 :   if (aggr) {
     724         3491 :     gst_adapter_clear (aggr->adapter);
     725              :   }
     726         3491 : }
     727              : 
     728              : /**
     729              :  * @brief Gets new hash table for tensor aggregation.
     730              :  * @return Newly allocated hash table, caller should release this using g_hash_table_destroy().
     731              :  */
     732              : GHashTable *
     733          922 : gst_tensor_aggregation_init (void)
     734              : {
     735              :   GHashTable *table;
     736              : 
     737          922 :   table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
     738              :       gst_tensor_aggregation_free_data);
     739              : 
     740              :   /**
     741              :    * Add default adapter (for the case if buffer has no specific id).
     742              :    * If gst-buffer has tensor-meta which includes client-id,
     743              :    * e.g., aggregation frames from multiple clients on query-server pipeline,
     744              :    * nnstreamer element should parse meta and request adapter with this id.
     745              :    * However, on normal pipeline, gst-buffer does not contain tensor-meta,
     746              :    * then the element may request adapter with null key string.
     747              :    */
     748          922 :   gst_tensor_aggregation_add_data (table, AGGREGATION_DEFAULT_KEY);
     749              : 
     750          922 :   return table;
     751              : }
     752              : 
     753              : /**
     754              :  * @brief Clears buffers from adapter.
     755              :  * @param table a hash table instance initialized with gst_tensor_aggregation_init()
     756              :  * @param key the key to look up (set null to get default adapter)
     757              :  */
     758              : void
     759            1 : gst_tensor_aggregation_clear (GHashTable * table, const guint32 key)
     760              : {
     761              :   gst_tensor_aggregation_data_s *aggr;
     762              : 
     763            1 :   g_return_if_fail (table != NULL);
     764              : 
     765            1 :   aggr = gst_tensor_aggregation_get_data (table, key);
     766            1 :   gst_tensor_aggregation_clear_internal (NULL, aggr, NULL);
     767              : }
     768              : 
     769              : /**
     770              :  * @brief Clears buffers from all adapters in hash table.
     771              :  * @param table a hash table instance initialized with gst_tensor_aggregation_init()
     772              :  */
     773              : void
     774         3485 : gst_tensor_aggregation_clear_all (GHashTable * table)
     775              : {
     776         3485 :   g_hash_table_foreach (table, gst_tensor_aggregation_clear_internal, NULL);
     777         3485 : }
     778              : 
     779              : /**
     780              :  * @brief Gets adapter from hash table.
     781              :  * @param table a hash table instance initialized with gst_tensor_aggregation_init()
     782              :  * @param key the key to look up (set null to get default adapter)
     783              :  * @return gst-adapter instance. DO NOT release this instance.
     784              :  */
     785              : GstAdapter *
     786          303 : gst_tensor_aggregation_get_adapter (GHashTable * table, const guint32 key)
     787              : {
     788              :   gst_tensor_aggregation_data_s *aggr;
     789              : 
     790          303 :   g_return_val_if_fail (table != NULL, NULL);
     791              : 
     792          302 :   aggr = gst_tensor_aggregation_get_data (table, key);
     793          302 :   if (!aggr) {
     794              :     /*append new data */
     795            3 :     aggr = gst_tensor_aggregation_add_data (table, key);
     796              :   }
     797              : 
     798          302 :   return aggr->adapter;
     799              : }
     800              : 
     801              : /**
     802              :  * @brief Internal function to check tensor dimensions to append old caps for backward compatibility (rank 4).
     803              :  */
     804              : static gboolean
     805        10628 : _append_prev_caps (const GstTensorsConfig * config)
     806              : {
     807              :   GstTensorsInfo *info;
     808              :   GstTensorInfo *_info;
     809              :   guint i, min_rank;
     810              : 
     811        10628 :   g_return_val_if_fail (config != NULL, FALSE);
     812              : 
     813        10628 :   info = (GstTensorsInfo *) (&config->info);
     814        10628 :   if (!gst_tensors_info_validate (info))
     815         4719 :     return FALSE;
     816              : 
     817        12834 :   for (i = 0; i < info->num_tensors; i++) {
     818         6991 :     _info = gst_tensors_info_get_nth_info (info, i);
     819              : 
     820         6991 :     min_rank = gst_tensor_dimension_get_min_rank (_info->dimension);
     821              : 
     822         6991 :     if (min_rank > NNS_TENSOR_RANK_LIMIT_PREV)
     823           66 :       return FALSE;
     824              :   }
     825              : 
     826         5843 :   return TRUE;
     827              : }
     828              : 
     829              : /**
     830              :  * @brief Internal function to get caps for single tensor from config.
     831              :  */
     832              : static GstCaps *
     833         5659 : _get_tensor_caps (const GstTensorsConfig * config)
     834              : {
     835              :   GstCaps *caps, *prev1, *prev2;
     836              :   GstTensorsInfo *info;
     837              :   GstTensorInfo *_info;
     838              :   gboolean append_prev;
     839              : 
     840         5659 :   g_return_val_if_fail (config != NULL, NULL);
     841              : 
     842         5659 :   info = (GstTensorsInfo *) (&config->info);
     843         5659 :   if (info->num_tensors > 1)
     844          103 :     return NULL;
     845              : 
     846         5556 :   caps = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
     847         5556 :   _info = gst_tensors_info_get_nth_info (info, 0);
     848              : 
     849              :   /* caps for backward compatibility */
     850         5556 :   prev1 = prev2 = NULL;
     851         5556 :   append_prev = _append_prev_caps (config);
     852         5556 :   if (append_prev) {
     853         3182 :     prev1 = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
     854         3182 :     prev2 = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
     855              :   }
     856              : 
     857         5556 :   if (gst_tensor_dimension_is_valid (_info->dimension)) {
     858         3353 :     g_autofree gchar *dim_str =
     859         3353 :         gst_tensor_get_dimension_string (_info->dimension);
     860              : 
     861         3353 :     gst_caps_set_simple (caps, "dimension", G_TYPE_STRING, dim_str, NULL);
     862              : 
     863         3353 :     if (append_prev) {
     864         3182 :       g_autofree gchar *dim_prev1 =
     865         3182 :           gst_tensor_get_rank_dimension_string (_info->dimension,
     866              :           NNS_TENSOR_RANK_LIMIT_PREV, FALSE);
     867         3182 :       g_autofree gchar *dim_prev2 =
     868         3182 :           gst_tensor_get_rank_dimension_string (_info->dimension,
     869              :           NNS_TENSOR_RANK_LIMIT_PREV, TRUE);
     870              : 
     871         3182 :       gst_caps_set_simple (prev1, "dimension", G_TYPE_STRING, dim_prev1, NULL);
     872         3182 :       gst_caps_set_simple (prev2, "dimension", G_TYPE_STRING, dim_prev2, NULL);
     873              :     }
     874              :   }
     875              : 
     876         5556 :   if (_info->type != _NNS_END) {
     877         3753 :     const gchar *type_str = gst_tensor_get_type_string (_info->type);
     878              : 
     879         3753 :     gst_caps_set_simple (caps, "type", G_TYPE_STRING, type_str, NULL);
     880              : 
     881         3753 :     if (append_prev) {
     882         3182 :       gst_caps_set_simple (prev1, "type", G_TYPE_STRING, type_str, NULL);
     883         3182 :       gst_caps_set_simple (prev2, "type", G_TYPE_STRING, type_str, NULL);
     884              :     }
     885              :   }
     886              : 
     887         5556 :   if (config->rate_n >= 0 && config->rate_d > 0) {
     888         2065 :     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
     889         2065 :         config->rate_n, config->rate_d, NULL);
     890              : 
     891         2065 :     if (append_prev) {
     892         2000 :       gst_caps_set_simple (prev1, "framerate", GST_TYPE_FRACTION,
     893         2000 :           config->rate_n, config->rate_d, NULL);
     894         2000 :       gst_caps_set_simple (prev2, "framerate", GST_TYPE_FRACTION,
     895         2000 :           config->rate_n, config->rate_d, NULL);
     896              :     }
     897              :   }
     898              : 
     899         5556 :   if (append_prev)
     900         3182 :     caps = gst_caps_merge (caps, gst_caps_merge (prev1, prev2));
     901              : 
     902         5556 :   return caps;
     903              : }
     904              : 
     905              : /**
     906              :  * @brief Internal function to get caps for multi tensors from config.
     907              :  */
     908              : static GstCaps *
     909         5072 : _get_tensors_caps (const GstTensorsConfig * config)
     910              : {
     911              :   GstCaps *caps, *prev1, *prev2;
     912              :   gboolean append_prev;
     913              : 
     914         5072 :   g_return_val_if_fail (config != NULL, NULL);
     915              : 
     916         5072 :   caps = gst_caps_from_string (GST_TENSORS_CAP_DEFAULT);
     917              : 
     918              :   /* caps for backward compatibility */
     919         5072 :   prev1 = prev2 = NULL;
     920         5072 :   append_prev = _append_prev_caps (config);
     921         5072 :   if (append_prev) {
     922         2661 :     prev1 = gst_caps_from_string (GST_TENSORS_CAP_DEFAULT);
     923         2661 :     prev2 = gst_caps_from_string (GST_TENSORS_CAP_DEFAULT);
     924              :   }
     925              : 
     926         5072 :   if (config->info.num_tensors > 0) {
     927         2847 :     g_autofree gchar *type_str =
     928         2847 :         gst_tensors_info_get_types_string (&config->info);
     929         2847 :     g_autofree gchar *dim_str =
     930         2847 :         gst_tensors_info_get_dimensions_string (&config->info);
     931              : 
     932         2847 :     gst_caps_set_simple (caps, "num_tensors", G_TYPE_INT,
     933         2847 :         config->info.num_tensors, NULL);
     934         2847 :     gst_caps_set_simple (caps, "dimensions", G_TYPE_STRING, dim_str, NULL);
     935         2847 :     gst_caps_set_simple (caps, "types", G_TYPE_STRING, type_str, NULL);
     936              : 
     937         2847 :     if (append_prev) {
     938         2661 :       g_autofree gchar *dim_prev1 =
     939         2661 :           gst_tensors_info_get_rank_dimensions_string (&config->info,
     940              :           NNS_TENSOR_RANK_LIMIT_PREV, FALSE);
     941         2661 :       g_autofree gchar *dim_prev2 =
     942         2661 :           gst_tensors_info_get_rank_dimensions_string (&config->info,
     943              :           NNS_TENSOR_RANK_LIMIT_PREV, TRUE);
     944              : 
     945         2661 :       gst_caps_set_simple (prev1, "num_tensors", G_TYPE_INT,
     946         2661 :           config->info.num_tensors, NULL);
     947         2661 :       gst_caps_set_simple (prev1, "dimensions", G_TYPE_STRING, dim_prev1, NULL);
     948         2661 :       gst_caps_set_simple (prev1, "types", G_TYPE_STRING, type_str, NULL);
     949              : 
     950         2661 :       gst_caps_set_simple (prev2, "num_tensors", G_TYPE_INT,
     951         2661 :           config->info.num_tensors, NULL);
     952         2661 :       gst_caps_set_simple (prev2, "dimensions", G_TYPE_STRING, dim_prev2, NULL);
     953         2661 :       gst_caps_set_simple (prev2, "types", G_TYPE_STRING, type_str, NULL);
     954              :     }
     955              :   }
     956              : 
     957         5072 :   if (config->rate_n >= 0 && config->rate_d > 0) {
     958         1495 :     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
     959         1495 :         config->rate_n, config->rate_d, NULL);
     960              : 
     961         1495 :     if (append_prev) {
     962         1439 :       gst_caps_set_simple (prev1, "framerate", GST_TYPE_FRACTION,
     963         1439 :           config->rate_n, config->rate_d, NULL);
     964         1439 :       gst_caps_set_simple (prev2, "framerate", GST_TYPE_FRACTION,
     965         1439 :           config->rate_n, config->rate_d, NULL);
     966              :     }
     967              :   }
     968              : 
     969         5072 :   if (append_prev)
     970         2661 :     caps = gst_caps_merge (caps, gst_caps_merge (prev1, prev2));
     971              : 
     972         5072 :   return caps;
     973              : }
     974              : 
     975              : /**
     976              :  * @brief Internal function to get caps for flexible tensor from config.
     977              :  */
     978              : static GstCaps *
     979         2863 : _get_flexible_caps (const GstTensorsConfig * config)
     980              : {
     981              :   GstCaps *caps;
     982              : 
     983         2863 :   caps = gst_caps_from_string (GST_TENSORS_FLEX_CAP_DEFAULT);
     984              : 
     985         2863 :   if (config->rate_n >= 0 && config->rate_d > 0) {
     986          862 :     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
     987          862 :         config->rate_n, config->rate_d, NULL);
     988              :   }
     989              : 
     990         2863 :   return caps;
     991              : }
     992              : 
     993              : /**
     994              :  * @brief Check given mimetype is tensor stream.
     995              :  * @param structure structure to be interpreted
     996              :  * @return TRUE if mimetype is tensor stream
     997              :  */
     998              : gboolean
     999        18421 : gst_structure_is_tensor_stream (const GstStructure * structure)
    1000              : {
    1001              :   const gchar *name;
    1002              : 
    1003        18421 :   name = gst_structure_get_name (structure);
    1004        18421 :   g_return_val_if_fail (name != NULL, FALSE);
    1005              : 
    1006        32680 :   return (g_str_equal (name, NNS_MIMETYPE_TENSOR) ||
    1007        14259 :       g_str_equal (name, NNS_MIMETYPE_TENSORS));
    1008              : }
    1009              : 
    1010              : /**
    1011              :  * @brief Get media type from structure
    1012              :  * @param structure structure to be interpreted
    1013              :  * @return corresponding media type (returns _NNS_MEDIA_INVALID for unsupported type)
    1014              :  */
    1015              : media_type
    1016        20367 : gst_structure_get_media_type (const GstStructure * structure)
    1017              : {
    1018              :   const gchar *name;
    1019              : 
    1020        20367 :   name = gst_structure_get_name (structure);
    1021              : 
    1022        20367 :   g_return_val_if_fail (name != NULL, _NNS_MEDIA_INVALID);
    1023              : 
    1024        20367 :   if (g_str_has_prefix (name, "video/")) {
    1025         2790 :     return _NNS_VIDEO;
    1026              :   }
    1027              : 
    1028        17577 :   if (g_str_has_prefix (name, "audio/")) {
    1029         2206 :     return _NNS_AUDIO;
    1030              :   }
    1031              : 
    1032        15371 :   if (g_str_has_prefix (name, "text/")) {
    1033         2179 :     return _NNS_TEXT;
    1034              :   }
    1035              : 
    1036        13192 :   if (g_str_equal (name, "application/octet-stream")) {
    1037         4454 :     return _NNS_OCTET;
    1038              :   }
    1039              : 
    1040         8738 :   if (gst_structure_is_tensor_stream (structure)) {
    1041         2174 :     return _NNS_TENSOR;
    1042              :   }
    1043              : 
    1044              :   /* unknown or unsupported type */
    1045         6564 :   return _NNS_MEDIA_INVALID;
    1046              : }
    1047              : 
    1048              : /**
    1049              :  * @brief Parse caps from peer pad and set tensors config.
    1050              :  * @param pad GstPad to get the capabilities
    1051              :  * @param config tensors config structure to be filled
    1052              :  * @param is_fixed flag to be updated when peer caps is fixed (not mandatory, do nothing when the param is null)
    1053              :  * @return TRUE if successfully configured from peer
    1054              :  */
    1055              : gboolean
    1056         4313 : gst_tensors_config_from_peer (GstPad * pad, GstTensorsConfig * config,
    1057              :     gboolean * is_fixed)
    1058              : {
    1059              :   GstCaps *peer_caps;
    1060              :   GstStructure *structure;
    1061         4313 :   gboolean ret = FALSE;
    1062              : 
    1063         4313 :   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
    1064         4313 :   g_return_val_if_fail (config != NULL, FALSE);
    1065              : 
    1066         4313 :   gst_tensors_config_init (config);
    1067              : 
    1068         4313 :   if ((peer_caps = gst_pad_peer_query_caps (pad, NULL))) {
    1069         4313 :     if (gst_caps_get_size (peer_caps) > 0) {
    1070         2208 :       structure = gst_caps_get_structure (peer_caps, 0);
    1071         2208 :       ret = gst_tensors_config_from_structure (config, structure);
    1072              :     }
    1073              : 
    1074         4313 :     if (ret && is_fixed)
    1075           30 :       *is_fixed = gst_caps_is_fixed (peer_caps);
    1076              : 
    1077         4313 :     gst_caps_unref (peer_caps);
    1078              :   }
    1079              : 
    1080         4313 :   return ret;
    1081              : }
    1082              : 
    1083              : /**
    1084              :  * @brief Check whether two structures have the same dimension
    1085              :  */
    1086              : static gboolean
    1087          248 : _is_structure_dimension_same (GstStructure * st1, GstStructure * st2,
    1088              :     const gchar * fieldname)
    1089              : {
    1090              :   const char *dim_str1;
    1091              :   const char *dim_str2;
    1092              : 
    1093          248 :   g_return_val_if_fail (gst_structure_has_field (st1, fieldname), FALSE);
    1094          248 :   g_return_val_if_fail (gst_structure_has_field (st2, fieldname), FALSE);
    1095              : 
    1096          248 :   dim_str1 = gst_structure_get_string (st1, fieldname);
    1097          248 :   dim_str2 = gst_structure_get_string (st2, fieldname);
    1098              : 
    1099          248 :   return gst_tensor_dimension_string_is_equal (dim_str1, dim_str2);
    1100              : }
    1101              : 
    1102              : /**
    1103              :  * @brief Update caps dimensions for negotiation
    1104              :  * @param caps caps to compare and update
    1105              :  * @param filter caps to compare
    1106              :  */
    1107              : void
    1108         1635 : gst_tensor_caps_update_dimension (GstCaps * caps, GstCaps * filter)
    1109              : {
    1110              :   GstStructure *st_caps, *st_filter;
    1111              :   guint i, j;
    1112              : 
    1113         1635 :   g_return_if_fail (GST_IS_CAPS (caps));
    1114         1635 :   g_return_if_fail (GST_IS_CAPS (filter));
    1115              : 
    1116         4978 :   for (i = 0; i < gst_caps_get_size (caps); i++) {
    1117         3343 :     st_caps = gst_caps_get_structure (caps, i);
    1118              : 
    1119         3343 :     if (!gst_structure_is_tensor_stream (st_caps))
    1120            0 :       continue;
    1121              : 
    1122         9642 :     for (j = 0; j < gst_caps_get_size (filter); j++) {
    1123         6299 :       st_filter = gst_caps_get_structure (filter, j);
    1124              : 
    1125         6299 :       if (!gst_structure_is_tensor_stream (st_filter))
    1126            0 :         continue;
    1127              : 
    1128              :       /* other/tensor */
    1129         6299 :       if (gst_structure_has_field (st_caps, "dimension")
    1130         3425 :           && gst_structure_has_field (st_filter, "dimension")) {
    1131              :         /* update dimensions for negotiation */
    1132          236 :         if (_is_structure_dimension_same (st_caps, st_filter, "dimension")) {
    1133          118 :           gst_structure_set (st_caps, "dimension", G_TYPE_STRING,
    1134              :               gst_structure_get_string (st_filter, "dimension"), NULL);
    1135              :         }
    1136              :       }
    1137              :       /* other/tensors */
    1138         6181 :       else if (gst_structure_has_field (st_caps, "dimensions")
    1139         1866 :           && gst_structure_has_field (st_filter, "dimensions")) {
    1140              :         /* update dimensions for negotiation */
    1141          127 :         if (_is_structure_dimension_same (st_caps, st_filter, "dimensions")) {
    1142          118 :           gst_structure_set (st_caps, "dimensions", G_TYPE_STRING,
    1143              :               gst_structure_get_string (st_filter, "dimensions"), NULL);
    1144              :         }
    1145              :       }
    1146              :     }
    1147              :   }
    1148              : }
    1149              : 
    1150              : /**
    1151              :  * @brief  Try intersecting @caps1 and @caps2 for tensor stream
    1152              :  * @param caps1 a GstCaps to intersect
    1153              :  * @param caps2 a GstCaps to intersect
    1154              :  * @return TRUE if intersection would be not empty.
    1155              :  */
    1156              : gboolean
    1157           14 : gst_tensor_caps_can_intersect (GstCaps * caps1, GstCaps * caps2)
    1158              : {
    1159              :   GstStructure *structure1;
    1160              :   GstStructure *structure2;
    1161              :   GstStructure *structure_copy1;
    1162              :   GstStructure *structure_copy2;
    1163              : 
    1164              :   const gchar *name1;
    1165              :   const gchar *name2;
    1166              : 
    1167              :   gboolean intersectable;
    1168              : 
    1169           14 :   if (gst_caps_can_intersect (caps1, caps2))
    1170           10 :     return TRUE;
    1171              : 
    1172            4 :   structure1 = gst_caps_get_structure (caps1, 0);
    1173            4 :   structure2 = gst_caps_get_structure (caps2, 0);
    1174              : 
    1175            4 :   if (!gst_structure_is_tensor_stream (structure1)
    1176            4 :       || !gst_structure_is_tensor_stream (structure2))
    1177            0 :     return FALSE;
    1178              : 
    1179            4 :   name1 = gst_structure_get_name (structure1);
    1180            4 :   name2 = gst_structure_get_name (structure2);
    1181              : 
    1182            4 :   if (!g_str_equal (name1, name2))
    1183            1 :     return FALSE;
    1184              : 
    1185              :   /* other/tensor */
    1186            3 :   if (g_str_equal (name1, NNS_MIMETYPE_TENSOR)) {
    1187            3 :     if (gst_structure_has_field (structure1, "dimension")
    1188            3 :         && gst_structure_has_field (structure2, "dimension")) {
    1189            3 :       if (!_is_structure_dimension_same (structure1, structure2, "dimension"))
    1190            1 :         return FALSE;
    1191              :     }
    1192              :   }
    1193              :   /* other/tensors */
    1194            0 :   else if (gst_structure_has_field (structure1, "dimensions")
    1195            0 :       && gst_structure_has_field (structure2, "dimensions")) {
    1196            0 :     if (!_is_structure_dimension_same (structure1, structure2, "dimensions"))
    1197            0 :       return FALSE;
    1198              :   }
    1199              : 
    1200            2 :   structure_copy1 = gst_structure_copy (structure1);
    1201            2 :   structure_copy2 = gst_structure_copy (structure2);
    1202              : 
    1203            2 :   gst_structure_remove_field (structure_copy1, "dimension");
    1204            2 :   gst_structure_remove_field (structure_copy1, "dimensions");
    1205            2 :   gst_structure_remove_field (structure_copy2, "dimension");
    1206            2 :   gst_structure_remove_field (structure_copy2, "dimensions");
    1207              : 
    1208              :   intersectable =
    1209            2 :       gst_structure_can_intersect (structure_copy1, structure_copy2);
    1210              : 
    1211            2 :   gst_structure_free (structure_copy1);
    1212            2 :   gst_structure_free (structure_copy2);
    1213              : 
    1214            2 :   return intersectable;
    1215              : }
    1216              : 
    1217              : /**
    1218              :  * @brief Get pad caps from tensors config and caps of the peer connected to the pad.
    1219              :  * @param pad GstPad to get possible caps
    1220              :  * @param config tensors config structure
    1221              :  * @return caps for given config. Caller is responsible for unreffing the returned caps.
    1222              :  */
    1223              : GstCaps *
    1224         1209 : gst_tensor_pad_caps_from_config (GstPad * pad, const GstTensorsConfig * config)
    1225              : {
    1226         1209 :   GstCaps *caps = NULL;
    1227              :   GstCaps *templ;
    1228              :   gboolean is_flexible, peer_is_flexible, peer_has_tensor_caps;
    1229              :   GstCaps *peer_caps;
    1230              : 
    1231         1209 :   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
    1232         1209 :   g_return_val_if_fail (config != NULL, NULL);
    1233              : 
    1234         1209 :   templ = gst_pad_get_pad_template_caps (pad);
    1235              : 
    1236              :   /* check peer caps */
    1237         1209 :   peer_is_flexible = peer_has_tensor_caps = FALSE;
    1238              : 
    1239         1209 :   peer_caps = gst_pad_peer_query_caps (pad, NULL);
    1240         1209 :   if (peer_caps && gst_caps_get_size (peer_caps) > 0) {
    1241              :     GstCaps *tmp;
    1242              :     GstStructure *st;
    1243              :     GstTensorsConfig peer_config;
    1244              : 
    1245          913 :     tmp = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
    1246          913 :     peer_has_tensor_caps = gst_caps_can_intersect (tmp, peer_caps);
    1247          913 :     gst_caps_unref (tmp);
    1248              : 
    1249          913 :     st = gst_caps_get_structure (peer_caps, 0);
    1250          913 :     if (gst_tensors_config_from_structure (&peer_config, st))
    1251          913 :       peer_is_flexible = gst_tensors_config_is_flexible (&peer_config);
    1252          913 :     gst_tensors_config_free (&peer_config);
    1253              :   }
    1254              : 
    1255              :   /* other/tensors (flexible) */
    1256         1209 :   is_flexible = gst_tensors_config_is_flexible (config);
    1257              : 
    1258         1209 :   if (is_flexible || peer_is_flexible) {
    1259           70 :     caps = _get_flexible_caps (config);
    1260           70 :     goto intersectable;
    1261              :   }
    1262              : 
    1263              :   /* other/tensor */
    1264         1139 :   if (config->info.num_tensors == 1 && peer_has_tensor_caps) {
    1265          728 :     caps = _get_tensor_caps (config);
    1266          728 :     if (peer_caps)
    1267          728 :       gst_tensor_caps_update_dimension (caps, peer_caps);
    1268              : 
    1269          728 :     if (gst_caps_can_intersect (caps, templ))
    1270          724 :       goto done;
    1271              : 
    1272            4 :     gst_caps_unref (caps);
    1273              :   }
    1274              : 
    1275              :   /* other/tensors (static) */
    1276          415 :   caps = _get_tensors_caps (config);
    1277          415 :   if (peer_caps)
    1278          415 :     gst_tensor_caps_update_dimension (caps, peer_caps);
    1279              : 
    1280            0 : intersectable:
    1281          485 :   if (!gst_caps_can_intersect (caps, templ)) {
    1282            0 :     gst_caps_unref (caps);
    1283            0 :     caps = NULL;
    1284              :   }
    1285              : 
    1286          485 : done:
    1287         1209 :   gst_caps_unref (templ);
    1288         1209 :   if (peer_caps)
    1289         1209 :     gst_caps_unref (peer_caps);
    1290         1209 :   caps = gst_caps_truncate (caps);
    1291         1209 :   return caps;
    1292              : }
    1293              : 
    1294              : /**
    1295              :  * @brief Get all possible caps from tensors config. Unlike gst_tensor_pad_caps_from_config(), this function does not check peer caps.
    1296              :  * @param pad GstPad to get possible caps
    1297              :  * @param config tensors config structure
    1298              :  * @return caps for given config. Caller is responsible for unreffing the returned caps.
    1299              :  */
    1300              : GstCaps *
    1301         2072 : gst_tensor_pad_possible_caps_from_config (GstPad * pad,
    1302              :     const GstTensorsConfig * config)
    1303              : {
    1304              :   GstCaps *caps, *tmp;
    1305              :   GstCaps *templ;
    1306              : 
    1307         2072 :   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
    1308         2072 :   g_return_val_if_fail (config != NULL, NULL);
    1309              : 
    1310         2072 :   caps = gst_caps_new_empty ();
    1311         2072 :   templ = gst_pad_get_pad_template_caps (pad);
    1312              : 
    1313              :   /* append caps for static tensor */
    1314         2072 :   if (gst_tensors_config_is_static (config)) {
    1315              :     /* other/tensor */
    1316         1952 :     if ((tmp = _get_tensor_caps (config)) != NULL) {
    1317         1849 :       if (gst_caps_can_intersect (tmp, templ))
    1318         1801 :         gst_caps_append (caps, tmp);
    1319              :       else
    1320           48 :         gst_caps_unref (tmp);
    1321              :     }
    1322              : 
    1323              :     /* other/tensors */
    1324         1952 :     if ((tmp = _get_tensors_caps (config)) != NULL) {
    1325         1952 :       if (gst_caps_can_intersect (tmp, templ))
    1326         1952 :         gst_caps_append (caps, tmp);
    1327              :       else
    1328            0 :         gst_caps_unref (tmp);
    1329              :     }
    1330              :   }
    1331              : 
    1332              :   /* caps for flexible tensor */
    1333         2072 :   if ((tmp = _get_flexible_caps (config)) != NULL) {
    1334         2072 :     if (gst_caps_can_intersect (tmp, templ))
    1335         1979 :       gst_caps_append (caps, tmp);
    1336              :     else
    1337           93 :       gst_caps_unref (tmp);
    1338              :   }
    1339              : 
    1340              :   /* if no possible caps for given config, return null. */
    1341         2072 :   if (gst_caps_is_empty (caps)) {
    1342            0 :     gst_caps_unref (caps);
    1343            0 :     caps = NULL;
    1344              :   }
    1345              : 
    1346         2072 :   gst_caps_unref (templ);
    1347         2072 :   return caps;
    1348              : }
    1349              : 
    1350              : /**
    1351              :  * @brief Get tensor format of current pad caps.
    1352              :  * @param pad GstPad to check current caps.
    1353              :  * @return The tensor_format of current pad caps.
    1354              :  *
    1355              :  * If pad does not have tensor caps return _NNS_TENSOR_FORMAT_END
    1356              :  */
    1357              : tensor_format
    1358       127456 : gst_tensor_pad_get_format (GstPad * pad)
    1359              : {
    1360              :   GstCaps *caps;
    1361       127456 :   tensor_format ret = _NNS_TENSOR_FORMAT_END;
    1362              : 
    1363       127456 :   g_return_val_if_fail (GST_IS_PAD (pad), _NNS_TENSOR_FORMAT_END);
    1364              : 
    1365       127456 :   caps = gst_pad_get_current_caps (pad);
    1366       127456 :   if (caps) {
    1367              :     GstTensorsConfig config;
    1368              : 
    1369       127443 :     if (gst_tensors_config_from_caps (&config, caps, TRUE)) {
    1370       127332 :       ret = config.info.format;
    1371              :     }
    1372       127443 :     gst_caps_unref (caps);
    1373       127443 :     gst_tensors_config_free (&config);
    1374              :   }
    1375              : 
    1376       127456 :   return ret;
    1377              : }
    1378              : 
    1379              : /**
    1380              :  * @brief Get caps from tensors config (for other/tensors)
    1381              :  * @param config tensors config info
    1382              :  * @return caps for given config
    1383              :  */
    1384              : GstCaps *
    1385         3427 : gst_tensors_caps_from_config (const GstTensorsConfig * config)
    1386              : {
    1387              :   GstCaps *caps;
    1388              : 
    1389         3427 :   g_return_val_if_fail (config != NULL, NULL);
    1390              : 
    1391         3426 :   if (gst_tensors_config_is_flexible (config)) {
    1392          721 :     caps = _get_flexible_caps (config);
    1393              :   } else {
    1394         2705 :     caps = _get_tensors_caps (config);
    1395              :   }
    1396              : 
    1397         3426 :   caps = gst_caps_truncate (caps);
    1398              : 
    1399         3426 :   return caps;
    1400              : }
    1401              : 
    1402              : /**
    1403              :  * @brief Get tensor caps from tensors config
    1404              :  * @param config tensors config info
    1405              :  * @return caps for given config
    1406              :  */
    1407              : GstCaps *
    1408         2980 : gst_tensor_caps_from_config (const GstTensorsConfig * config)
    1409              : {
    1410              :   GstCaps *caps;
    1411              : 
    1412         2980 :   g_return_val_if_fail (config != NULL, NULL);
    1413              : 
    1414         2979 :   caps = _get_tensor_caps (config);
    1415         2979 :   caps = gst_caps_truncate (caps);
    1416              : 
    1417         2979 :   return caps;
    1418              : }
    1419              : 
    1420              : /**
    1421              :  * @brief Parse structure and set tensors config (for other/tensors)
    1422              :  * @param config tensors config structure to be filled
    1423              :  * @param structure structure to be interpreted
    1424              :  * @return TRUE if no error
    1425              :  */
    1426              : gboolean
    1427       156883 : gst_tensors_config_from_structure (GstTensorsConfig * config,
    1428              :     const GstStructure * structure)
    1429              : {
    1430              :   const gchar *name;
    1431       156883 :   tensor_format format = _NNS_TENSOR_FORMAT_STATIC;
    1432              : 
    1433       156883 :   g_return_val_if_fail (config != NULL, FALSE);
    1434       156881 :   gst_tensors_config_init (config);
    1435              : 
    1436       156881 :   g_return_val_if_fail (structure != NULL, FALSE);
    1437              : 
    1438       156880 :   name = gst_structure_get_name (structure);
    1439              : 
    1440       156880 :   if (g_str_equal (name, NNS_MIMETYPE_TENSOR)) {
    1441              :     /* other/tensor is always static */
    1442       143501 :     config->info.num_tensors = 1;
    1443              : 
    1444       143501 :     if (gst_structure_has_field (structure, "dimension")) {
    1445       137640 :       const gchar *dim_str = gst_structure_get_string (structure, "dimension");
    1446       137640 :       gst_tensor_parse_dimension (dim_str, config->info.info[0].dimension);
    1447              :     }
    1448              : 
    1449       143501 :     if (gst_structure_has_field (structure, "type")) {
    1450       137783 :       const gchar *type_str = gst_structure_get_string (structure, "type");
    1451       137783 :       config->info.info[0].type = gst_tensor_get_type (type_str);
    1452              :     }
    1453        13379 :   } else if (g_str_equal (name, NNS_MIMETYPE_TENSORS)) {
    1454        13268 :     if (gst_structure_has_field (structure, "format")) {
    1455              :       const gchar *format_str;
    1456              : 
    1457        13266 :       format_str = gst_structure_get_string (structure, "format");
    1458        13266 :       format = gst_tensor_get_format (format_str);
    1459              : 
    1460        13266 :       if (format == _NNS_TENSOR_FORMAT_END) {
    1461         1482 :         GST_INFO
    1462              :             ("Invalid format %s, it should be one of %s. Suppose tensor format is static.",
    1463              :             _STR_NULL (format_str), GST_TENSOR_FORMAT_ALL);
    1464              :       } else {
    1465        11784 :         config->info.format = format;
    1466              :       }
    1467              :     }
    1468              : 
    1469        13268 :     if (config->info.format == _NNS_TENSOR_FORMAT_STATIC) {
    1470        11284 :       gst_structure_get_int (structure, "num_tensors",
    1471        11284 :           (gint *) (&config->info.num_tensors));
    1472              : 
    1473              :       /* parse dimensions */
    1474        11284 :       if (gst_structure_has_field (structure, "dimensions")) {
    1475              :         const gchar *dims_str;
    1476              :         guint num_dims;
    1477              : 
    1478         9043 :         dims_str = gst_structure_get_string (structure, "dimensions");
    1479              :         num_dims =
    1480         9043 :             gst_tensors_info_parse_dimensions_string (&config->info, dims_str);
    1481              : 
    1482         9043 :         if (config->info.num_tensors != num_dims) {
    1483            8 :           nns_logw ("Invalid param, dimensions (%d) tensors (%d)\n",
    1484              :               num_dims, config->info.num_tensors);
    1485              :         }
    1486              :       }
    1487              : 
    1488              :       /* parse types */
    1489        11284 :       if (gst_structure_has_field (structure, "types")) {
    1490              :         const gchar *types_str;
    1491              :         guint num_types;
    1492              : 
    1493         8995 :         types_str = gst_structure_get_string (structure, "types");
    1494              :         num_types =
    1495         8995 :             gst_tensors_info_parse_types_string (&config->info, types_str);
    1496              : 
    1497         8995 :         if (config->info.num_tensors != num_types) {
    1498            0 :           nns_logw ("Invalid param, types (%d) tensors (%d)\n",
    1499              :               num_types, config->info.num_tensors);
    1500              :         }
    1501              :       }
    1502              :     }
    1503              :   } else {
    1504          111 :     nns_logw ("Unsupported type = %s\n", name ? name : "Unknown");
    1505          111 :     return FALSE;
    1506              :   }
    1507              : 
    1508       156769 :   if (gst_structure_has_field (structure, "framerate")) {
    1509       156750 :     gst_structure_get_fraction (structure, "framerate", &config->rate_n,
    1510       156750 :         &config->rate_d);
    1511              :   }
    1512              : 
    1513       156769 :   return TRUE;
    1514              : }
    1515              : 
    1516              : /**
    1517              :  * @brief Parse caps and set tensors config (for other/tensors)
    1518              :  * @param[out] config tensors config structure to be filled
    1519              :  * @param[in] caps incoming capability
    1520              :  * @param[in] validate TRUE to validate configuration
    1521              :  * @return TRUE/FALSE (if successfully configured, return TRUE)
    1522              :  */
    1523              : gboolean
    1524       147885 : gst_tensors_config_from_caps (GstTensorsConfig * config, const GstCaps * caps,
    1525              :     const gboolean validate)
    1526              : {
    1527              :   GstStructure *structure;
    1528       147885 :   gboolean ret = FALSE;
    1529              : 
    1530       147885 :   gst_tensors_config_init (config);
    1531              : 
    1532       147885 :   if (validate && !gst_caps_is_fixed (caps)) {
    1533          524 :     nns_logw ("GstCaps is not fixed.");
    1534          524 :     return FALSE;
    1535              :   }
    1536              : 
    1537       147361 :   structure = gst_caps_get_structure (caps, 0);
    1538       147361 :   ret = gst_tensors_config_from_structure (config, structure);
    1539              : 
    1540       147361 :   if (ret && validate) {
    1541       144515 :     ret = gst_tensors_config_validate (config);
    1542              :   }
    1543              : 
    1544       147361 :   if (!ret) {
    1545          114 :     gst_tensors_config_free (config);
    1546              :   }
    1547              : 
    1548       147361 :   return ret;
    1549              : }
    1550              : 
    1551              : /**
    1552              :  * @brief Parse memory and fill the tensor meta.
    1553              :  * @param[out] meta tensor meta structure to be filled
    1554              :  * @param[in] mem pointer to GstMemory to be parsed
    1555              :  * @return TRUE if successfully set the meta
    1556              :  */
    1557              : gboolean
    1558        60318 : gst_tensor_meta_info_parse_memory (GstTensorMetaInfo * meta, GstMemory * mem)
    1559              : {
    1560              :   GstMapInfo map;
    1561              :   gsize hsize, msize;
    1562              :   gboolean ret;
    1563              : 
    1564       120635 :   g_return_val_if_fail (mem != NULL, FALSE);
    1565        60317 :   g_return_val_if_fail (meta != NULL, FALSE);
    1566              : 
    1567        60316 :   gst_tensor_meta_info_init (meta);
    1568              : 
    1569              :   /* Check header size of tensor-meta. */
    1570        60316 :   hsize = gst_tensor_meta_info_get_header_size (meta);
    1571        60316 :   msize = gst_memory_get_sizes (mem, NULL, NULL);
    1572        60316 :   if (msize < hsize)
    1573         8480 :     return FALSE;
    1574              : 
    1575        51836 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
    1576            0 :     nns_loge ("Failed to get the meta, cannot map the memory.");
    1577            0 :     return FALSE;
    1578              :   }
    1579              : 
    1580        51836 :   ret = gst_tensor_meta_info_parse_header (meta, map.data);
    1581              : 
    1582        51836 :   gst_memory_unmap (mem, &map);
    1583        51836 :   return ret;
    1584              : }
    1585              : 
    1586              : /**
    1587              :  * @brief Append header to memory.
    1588              :  * @param[in] meta tensor meta structure
    1589              :  * @param[in] mem pointer to GstMemory
    1590              :  * @return Newly allocated GstMemory (Caller should free returned memory using gst_memory_unref())
    1591              :  */
    1592              : GstMemory *
    1593          282 : gst_tensor_meta_info_append_header (GstTensorMetaInfo * meta, GstMemory * mem)
    1594              : {
    1595          282 :   GstMemory *new_mem = NULL;
    1596              :   gsize msize, hsize;
    1597              :   GstMapInfo old_map, new_map;
    1598              : 
    1599          563 :   g_return_val_if_fail (mem != NULL, NULL);
    1600          281 :   g_return_val_if_fail (gst_tensor_meta_info_validate (meta), NULL);
    1601              : 
    1602          280 :   if (!gst_memory_map (mem, &old_map, GST_MAP_READ)) {
    1603            0 :     nns_loge ("Failed to append header, cannot map the old memory.");
    1604            0 :     return NULL;
    1605              :   }
    1606              : 
    1607              :   /* memory size (header + old memory) */
    1608          280 :   hsize = gst_tensor_meta_info_get_header_size (meta);
    1609          280 :   msize = hsize + old_map.size;
    1610              : 
    1611          280 :   new_mem = gst_allocator_alloc (NULL, msize, NULL);
    1612          280 :   if (!gst_memory_map (new_mem, &new_map, GST_MAP_WRITE)) {
    1613            0 :     nns_loge ("Failed to append header, cannot map the new memory.");
    1614            0 :     gst_memory_unmap (mem, &old_map);
    1615            0 :     gst_memory_unref (new_mem);
    1616            0 :     return NULL;
    1617              :   }
    1618              : 
    1619              :   /* set header and copy old data */
    1620          280 :   gst_tensor_meta_info_update_header (meta, new_map.data);
    1621          280 :   memcpy (new_map.data + hsize, old_map.data, old_map.size);
    1622              : 
    1623          280 :   gst_memory_unmap (mem, &old_map);
    1624          280 :   gst_memory_unmap (new_mem, &new_map);
    1625          280 :   return new_mem;
    1626              : }
    1627              : 
    1628              : /**
    1629              :  * @brief Get the nth GstMemory from given @a buffer.
    1630              :  * @param[in] buffer GstBuffer to be parsed.
    1631              :  * @param[in] index Index of GstMemory to be returned.
    1632              :  * @return GstMemory if found, otherwise NULL (Caller should free returned memory using gst_memory_unref()).
    1633              :  */
    1634              : GstMemory *
    1635        62523 : gst_tensor_buffer_get_nth_memory (GstBuffer * buffer, const guint index)
    1636              : {
    1637              :   guint i, num_tensors;
    1638              :   gsize offset;
    1639        62523 :   GstMemory *extra_tensors_memory, *res_mem = NULL;
    1640              :   GstMapInfo extra_tensors_map;
    1641              :   GstTensorExtraInfo *extra_info;
    1642              : 
    1643        62523 :   if (!GST_IS_BUFFER (buffer)) {
    1644            0 :     nns_loge ("Failed to parse GstBuffer (invalid input buffer).");
    1645        62523 :     return NULL;
    1646              :   }
    1647              : 
    1648        62523 :   num_tensors = gst_tensor_buffer_get_count (buffer);
    1649        62523 :   if (index >= num_tensors) {
    1650            0 :     nns_loge ("Invalid index %u, the number of tensors in the buffer is %u.",
    1651              :         index, num_tensors);
    1652            0 :     return NULL;
    1653              :   }
    1654              : 
    1655              :   /* If num_tensors is less than or equal to NNS_TENSOR_MEMORY_MAX, it's trivial. */
    1656        62523 :   if (num_tensors <= NNS_TENSOR_MEMORY_MAX || index < NNS_TENSOR_MEMORY_MAX - 1) {
    1657        61902 :     return gst_buffer_get_memory (buffer, index);
    1658              :   }
    1659              : 
    1660              :   /* If num_tensors is greater than NNS_TENSOR_MEMORY_MAX, we need to parse extra info. */
    1661              :   extra_tensors_memory =
    1662          621 :       gst_buffer_peek_memory (buffer, NNS_TENSOR_MEMORY_MAX - 1);
    1663          621 :   if (!extra_tensors_memory) {
    1664            0 :     nns_loge ("Failed to get %d-th memory", NNS_TENSOR_MEMORY_MAX);
    1665            0 :     return NULL;
    1666              :   }
    1667              : 
    1668          621 :   if (!gst_memory_map (extra_tensors_memory, &extra_tensors_map, GST_MAP_READ)) {
    1669            0 :     nns_loge ("Failed to map %d-th memory", NNS_TENSOR_MEMORY_MAX);
    1670            0 :     return NULL;
    1671              :   }
    1672              : 
    1673              :   /* check header (extra info) of the memory */
    1674          621 :   if (!gst_memory_map_is_extra_tensor (&extra_tensors_map)) {
    1675            0 :     nns_loge ("Invalid extra header");
    1676            0 :     goto done;
    1677              :   }
    1678              : 
    1679              :   /* parse the memory */
    1680          621 :   extra_info = (GstTensorExtraInfo *) extra_tensors_map.data;
    1681          621 :   offset = sizeof (GstTensorExtraInfo);
    1682              : 
    1683              :   /* If index is NNS_TENSOR_MEMORY_MAX - 1 */
    1684          621 :   if (index == NNS_TENSOR_MEMORY_MAX - 1) {
    1685              :     res_mem =
    1686           29 :         gst_memory_share (extra_tensors_memory, offset, extra_info->reserved);
    1687           29 :     goto done;
    1688              :   }
    1689              : 
    1690          592 :   offset += extra_info->reserved;
    1691              : 
    1692        31720 :   for (i = 1; i <= index - NNS_TENSOR_MEMORY_MAX; ++i) {
    1693        31128 :     offset += gst_tensor_info_get_size (&extra_info->infos[i - 1]);
    1694              :   }
    1695              : 
    1696              :   /* wrap it as GstMemory */
    1697              :   res_mem =
    1698          592 :       gst_memory_share (extra_tensors_memory, offset,
    1699          592 :       gst_tensor_info_get_size (&extra_info->infos[index -
    1700              :               NNS_TENSOR_MEMORY_MAX]));
    1701              : 
    1702          621 : done:
    1703          621 :   gst_memory_unmap (extra_tensors_memory, &extra_tensors_map);
    1704          621 :   return res_mem;
    1705              : }
    1706              : 
    1707              : /**
    1708              :  * @brief Append @a memory to given @a buffer.
    1709              :  * @param[in/out] buffer GstBuffer to be appended.
    1710              :  * @param[in] memory GstMemory to append. This function takes ownership of this, even if it returns failure.
    1711              :  * @param[in] info GstTensorInfo of given @a memory.
    1712              :  * @return TRUE if successfully appended, otherwise FALSE.
    1713              :  */
    1714              : gboolean
    1715        60293 : gst_tensor_buffer_append_memory (GstBuffer * buffer, GstMemory * memory,
    1716              :     const GstTensorInfo * info)
    1717              : {
    1718              :   guint num_mems, new_mem_index;
    1719        60293 :   GstMemory *new_memory = NULL, *last_memory = NULL;
    1720              :   gsize offset, new_mem_size, last_mem_size;
    1721              :   GstMapInfo new_memory_map, last_memory_map, incoming_memory_map;
    1722              :   GstTensorExtraInfo *extra_info;
    1723              :   GstTensorMetaInfo meta;
    1724              :   gboolean is_extra, is_static;
    1725        60293 :   gboolean appended = FALSE;
    1726              : 
    1727        60293 :   if (!GST_IS_BUFFER (buffer)) {
    1728            0 :     nns_loge ("Failed to append memory, given buffer is invalid.");
    1729            0 :     goto failed;
    1730              :   }
    1731              : 
    1732        60293 :   if (!memory) {
    1733            0 :     nns_loge ("Failed to append memory, given memory is NULL.");
    1734            0 :     goto failed;
    1735              :   }
    1736              : 
    1737        60293 :   if (gst_tensor_meta_info_parse_memory (&meta, memory)) {
    1738          558 :     is_static = (meta.format == _NNS_TENSOR_FORMAT_STATIC);
    1739              :   } else {
    1740              :     /* Suppose given memory is static tensor. */
    1741        59735 :     is_static = TRUE;
    1742              : 
    1743              :     /* Error case if given tensor-info is invalid. */
    1744        59735 :     if (!gst_tensor_info_validate (info)) {
    1745            0 :       nns_loge ("Failed to get tensor info (invalid input info).");
    1746            0 :       goto failed;
    1747              :     }
    1748              :   }
    1749              : 
    1750        60293 :   num_mems = gst_buffer_n_memory (buffer);
    1751              : 
    1752              :   /* trivial call to gst_buffer_append_memory */
    1753        60293 :   if (num_mems < NNS_TENSOR_MEMORY_MAX) {
    1754        59705 :     gst_buffer_append_memory (buffer, memory);
    1755        60293 :     return TRUE;
    1756              :   }
    1757              : 
    1758              :   /* given buffer has NNS_TENSOR_MEMORY_MAX memory blocks */
    1759          588 :   last_memory = gst_buffer_peek_memory (buffer, num_mems - 1);
    1760          588 :   if (!last_memory) {
    1761            0 :     nns_loge ("Failed to get last memory");
    1762            0 :     goto failed;
    1763              :   }
    1764              : 
    1765          588 :   if (!gst_memory_map (last_memory, &last_memory_map, GST_MAP_READ)) {
    1766            0 :     nns_loge ("Failed to map last memory");
    1767            0 :     last_memory = NULL;
    1768            0 :     goto failed;
    1769              :   }
    1770              : 
    1771          588 :   new_mem_size = last_mem_size = gst_memory_get_sizes (last_memory, NULL, NULL);
    1772              : 
    1773              :   /* if the memory does not have proper header, append it */
    1774          588 :   is_extra = gst_memory_map_is_extra_tensor (&last_memory_map);
    1775          588 :   if (!is_extra) {
    1776           28 :     new_mem_size += sizeof (GstTensorExtraInfo);
    1777              :   }
    1778              : 
    1779          588 :   new_mem_size += gst_memory_get_sizes (memory, NULL, NULL);
    1780              : 
    1781          588 :   new_memory = gst_allocator_alloc (NULL, new_mem_size, NULL);
    1782          588 :   if (!new_memory) {
    1783            0 :     nns_loge ("Failed to allocate memory for extra tensors.");
    1784            0 :     goto failed;
    1785              :   }
    1786              : 
    1787          588 :   if (!gst_memory_map (new_memory, &new_memory_map, GST_MAP_WRITE)) {
    1788            0 :     nns_loge ("Failed to map extra memory");
    1789            0 :     gst_memory_unref (new_memory);
    1790            0 :     new_memory = NULL;
    1791            0 :     goto failed;
    1792              :   }
    1793              : 
    1794          588 :   if (!gst_memory_map (memory, &incoming_memory_map, GST_MAP_READ)) {
    1795            0 :     nns_loge ("Failed to map incoming memory");
    1796            0 :     goto failed;
    1797              :   }
    1798              : 
    1799          588 :   extra_info = (GstTensorExtraInfo *) new_memory_map.data;
    1800              : 
    1801              :   /* if the last_memory does not have proper header, append it */
    1802          588 :   if (!is_extra) {
    1803           28 :     gst_tensor_extra_info_init (extra_info, last_mem_size);
    1804           28 :     offset = sizeof (GstTensorExtraInfo);
    1805              :   } else {
    1806          560 :     offset = 0;
    1807              :   }
    1808              : 
    1809              :   /* copy last_memory into new_memory */
    1810          588 :   memcpy (new_memory_map.data + offset, last_memory_map.data,
    1811              :       last_memory_map.size);
    1812              : 
    1813              :   /* copy incoming_memory into new_memory */
    1814          588 :   new_mem_index = extra_info->num_extra_tensors;
    1815          588 :   extra_info->num_extra_tensors += 1;
    1816              : 
    1817              :   /* Copy tensor info into extra. */
    1818          588 :   if (is_static) {
    1819          588 :     gst_tensor_info_copy (&extra_info->infos[new_mem_index], info);
    1820              : 
    1821              :     /**
    1822              :      * Free the name string, cause it does not freed by gstreamer.
    1823              :      * @todo Make custom gst_allocator later?
    1824              :      */
    1825          588 :     g_clear_pointer (&extra_info->infos[new_mem_index].name, g_free);
    1826              :   } else {
    1827            0 :     gst_tensor_meta_info_convert (&meta, &extra_info->infos[new_mem_index]);
    1828              :   }
    1829              : 
    1830          588 :   memcpy (new_memory_map.data + offset + last_memory_map.size,
    1831          588 :       incoming_memory_map.data, incoming_memory_map.size);
    1832              : 
    1833          588 :   gst_memory_unmap (memory, &incoming_memory_map);
    1834          588 :   gst_memory_unmap (last_memory, &last_memory_map);
    1835          588 :   last_memory = NULL;
    1836              : 
    1837          588 :   gst_buffer_replace_memory (buffer, num_mems - 1, new_memory);
    1838          588 :   appended = TRUE;
    1839              : 
    1840          588 : failed:
    1841          588 :   if (new_memory) {
    1842          588 :     gst_memory_unmap (new_memory, &new_memory_map);
    1843          588 :     if (!appended)
    1844            0 :       gst_memory_unref (new_memory);
    1845              :   }
    1846              : 
    1847          588 :   if (last_memory)
    1848            0 :     gst_memory_unmap (last_memory, &last_memory_map);
    1849              : 
    1850              :   /* Release incoming memory even if failed to append it into buffer. */
    1851          588 :   if (memory)
    1852          588 :     gst_memory_unref (memory);
    1853              : 
    1854          588 :   return appended;
    1855              : }
    1856              : 
    1857              : /**
    1858              :  * @brief Get the number of tensors in the buffer.
    1859              :  */
    1860              : guint
    1861       122330 : gst_tensor_buffer_get_count (GstBuffer * buffer)
    1862              : {
    1863              :   guint num_mems;
    1864              :   GstMemory *mem;
    1865              :   GstMapInfo map;
    1866              :   GstTensorExtraInfo *extra_info;
    1867              : 
    1868       244660 :   g_return_val_if_fail (buffer != NULL, 0);
    1869              : 
    1870       122330 :   num_mems = gst_buffer_n_memory (buffer);
    1871       122330 :   if (num_mems < NNS_TENSOR_MEMORY_MAX) {
    1872       120629 :     return num_mems;
    1873              :   }
    1874              : 
    1875              :   /* num_mems == NNS_TENSOR_MEMORY_MAX */
    1876         1701 :   mem = gst_buffer_peek_memory (buffer, num_mems - 1);
    1877         1701 :   if (!mem) {
    1878            0 :     nns_loge ("Failed to get the last memory.");
    1879            0 :     return 0;
    1880              :   }
    1881              : 
    1882         1701 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
    1883            0 :     nns_loge ("Failed to map the last memory.");
    1884            0 :     return 0;
    1885              :   }
    1886              : 
    1887         1701 :   if (gst_memory_map_is_extra_tensor (&map)) {
    1888         1349 :     extra_info = (GstTensorExtraInfo *) map.data;
    1889         1349 :     num_mems = extra_info->num_extra_tensors + NNS_TENSOR_MEMORY_MAX;
    1890              :   } else {
    1891          352 :     nns_logi ("The last memory does not have extra tensors header. "
    1892              :         "Assuming the number of tensors is %d.", num_mems);
    1893              :   }
    1894              : 
    1895         1701 :   gst_memory_unmap (mem, &map);
    1896              : 
    1897         1701 :   return num_mems;
    1898              : }
    1899              : 
    1900              : /**
    1901              :  * @brief Sets the value of a property based on the specified property value and GParamSpec.
    1902              :  *
    1903              :  * @param prop_value A pointer to the GValue where the property value will be set.
    1904              :  * @param param_spec A pointer to the GParamSpec that describes the property.
    1905              :  * @param property_value A string representing the value to be set for the property.
    1906              :  *
    1907              :  * @note This API is intended to be used by gst_tensor_parse_config_file ()
    1908              :  */
    1909              : static void
    1910           34 : set_property_value (GValue * prop_value, const GParamSpec * param_spec,
    1911              :     const gchar * property_value)
    1912              : {
    1913           34 :   GType value_type = G_PARAM_SPEC_VALUE_TYPE (param_spec);
    1914              : 
    1915           34 :   g_value_init (prop_value, value_type);
    1916              : 
    1917           34 :   if (value_type == G_TYPE_BOOLEAN) {
    1918            0 :     gboolean value = g_ascii_strcasecmp (property_value, "true") == 0;
    1919            0 :     g_value_set_boolean (prop_value, value);
    1920           34 :   } else if (value_type == G_TYPE_INT) {
    1921            0 :     gint value = atoi (property_value);
    1922            0 :     g_value_set_int (prop_value, value);
    1923           34 :   } else if (value_type == G_TYPE_UINT) {
    1924            0 :     guint value = atoi (property_value);
    1925            0 :     g_value_set_uint (prop_value, value);
    1926           34 :   } else if (value_type == G_TYPE_FLOAT) {
    1927            0 :     gfloat value = atof (property_value);
    1928            0 :     g_value_set_float (prop_value, value);
    1929           34 :   } else if (value_type == G_TYPE_DOUBLE) {
    1930            0 :     gdouble value = atof (property_value);
    1931            0 :     g_value_set_double (prop_value, value);
    1932              :   } else {
    1933           34 :     g_value_set_string (prop_value, property_value); /** default is string */
    1934              :   }
    1935           34 : }
    1936              : 
    1937              : /**
    1938              :  * @brief Parses a configuration file and sets the corresponding properties on a GObject.
    1939              :  *
    1940              :  * This function reads the contents of the configuration file located at the given path
    1941              :  * and sets the properties of the specified GObject based on the configuration data.
    1942              :  *
    1943              :  * @param config_path The path to the configuration file.
    1944              :  * @param object      The GObject on which to set the properties.
    1945              :  *
    1946              :  * @note The responsibility of managing the memory of the GObject passed as a parameter
    1947              :  *       lies outside this function.
    1948              :  */
    1949              : void
    1950            9 : gst_tensor_parse_config_file (const gchar * config_path, const GObject * object)
    1951              : {
    1952            9 :   g_autofree gchar *config_data = NULL;
    1953            9 :   g_auto (GStrv) lines = NULL;
    1954            9 :   GStrv line = NULL;
    1955            9 :   GError *error = NULL;
    1956            9 :   GObjectClass *g_object_class = G_OBJECT_GET_CLASS (object);
    1957              : 
    1958            9 :   if (!g_file_get_contents (config_path, &config_data, NULL, &error)) {
    1959            0 :     GST_DEBUG ("Failed to read config file: %s\n", error->message);
    1960            0 :     g_error_free (error);
    1961            0 :     return;
    1962              :   }
    1963              : 
    1964            9 :   lines = g_strsplit (config_data, "\n", -1);
    1965              : 
    1966              :   /** Iterate over each line */
    1967           52 :   for (line = lines; *line; ++line) {
    1968           43 :     g_auto (GStrv) parts = g_strsplit (*line, "=", 2);
    1969              : 
    1970           43 :     if (g_strv_length (parts) == 2) {
    1971           68 :       g_autofree gchar *property_name = g_strstrip (g_strdup (parts[0]));
    1972           68 :       g_autofree gchar *property_value = g_strstrip (g_strdup (parts[1]));
    1973              : 
    1974              :       GParamSpec *pdata =
    1975           34 :           g_object_class_find_property (g_object_class, property_name);
    1976              : 
    1977           34 :       if (pdata != NULL) {
    1978           34 :         GValue prop_value = G_VALUE_INIT;
    1979           34 :         set_property_value (&prop_value, pdata, property_value);
    1980           34 :         g_object_set_property (G_OBJECT (object), pdata->name, &prop_value);
    1981           34 :         g_value_unset (&prop_value);
    1982              :       }
    1983              :     }
    1984              :   }
    1985              : }
        

Generated by: LCOV version 2.0-1