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#33f8a040b33adcdfcbb5c331bf727be211eaffb0 Lines: 89.0 % 879 782
Test Date: 2025-08-01 05:43:06 Functions: 100.0 % 44 44

            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        13269 : _gst_tensor_time_sync_is_eos (GstCollectPads * collect,
     176              :     tensor_time_sync_data * sync, guint empty)
     177              : {
     178              :   guint total;
     179        13269 :   gboolean is_eos = FALSE;
     180              : 
     181        13269 :   total = g_slist_length (collect->data);
     182              : 
     183        13269 :   switch (sync->mode) {
     184        11342 :     case SYNC_REFRESH:
     185        11342 :       if (empty == total)
     186            2 :         is_eos = TRUE;
     187        11342 :       break;
     188         1927 :     default:
     189         1927 :       if (empty > 0)
     190          213 :         is_eos = TRUE;
     191         1927 :       break;
     192              :   }
     193              : 
     194        13269 :   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         6742 : gst_tensor_time_sync_get_current_time (GstCollectPads * collect,
     204              :     tensor_time_sync_data * sync, GstClockTime * current_time,
     205              :     GstBuffer * tensors_buf)
     206              : {
     207         6742 :   GSList *walk = NULL;
     208              :   guint count, empty_pad;
     209              : 
     210         6742 :   g_return_val_if_fail (collect != NULL, FALSE);
     211         6742 :   g_return_val_if_fail (sync != NULL, FALSE);
     212         6742 :   g_return_val_if_fail (current_time != NULL, FALSE);
     213              : 
     214         6742 :   walk = collect->data;
     215         6742 :   count = empty_pad = 0;
     216              : 
     217        21294 :   while (walk) {
     218              :     GstCollectData *data;
     219              :     GstBuffer *buf;
     220        14552 :     gboolean need_update = FALSE;
     221              : 
     222        14552 :     data = (GstCollectData *) walk->data;
     223        14552 :     buf = gst_collect_pads_peek (collect, data);
     224        14552 :     walk = g_slist_next (walk);
     225              : 
     226        14552 :     if (buf) {
     227         8340 :       switch (sync->mode) {
     228         7682 :         case SYNC_NOSYNC:
     229              :           /* fall-through */
     230              :         case SYNC_SLOWEST:
     231              :         case SYNC_REFRESH:
     232         7682 :           if (*current_time < GST_BUFFER_PTS (buf))
     233         6226 :             need_update = TRUE;
     234         7682 :           break;
     235          658 :         case SYNC_BASEPAD:
     236          658 :           if (count == sync->data_basepad.sink_id)
     237          296 :             need_update = TRUE;
     238          658 :           break;
     239            0 :         default:
     240            0 :           break;
     241              :       }
     242         8340 :       if (need_update) {
     243         6522 :         *current_time = GST_BUFFER_PTS (buf);
     244         6522 :         gst_buffer_copy_into (tensors_buf, buf, GST_BUFFER_COPY_METADATA,
     245              :             0, -1);
     246              :       }
     247         8340 :       gst_buffer_unref (buf);
     248              :     } else {
     249         6212 :       empty_pad++;
     250              :     }
     251              : 
     252        14552 :     count++;
     253              :   }
     254              : 
     255         6742 :   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         3781 : _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         3781 :   pad = (GstTensorCollectPadData *) data;
     295              : 
     296         3781 :   buf = gst_collect_pads_peek (collect, data);
     297         3781 :   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         3110 :   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         7199 : 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         7199 :   GSList *walk = NULL;
     337              :   GstCollectData *data;
     338              :   GstTensorCollectPadData *pad;
     339         7199 :   GstBuffer *buf = NULL;
     340              :   GstMemory *mem;
     341         7199 :   gint old_numerator = G_MAXINT;
     342         7199 :   gint old_denominator = G_MAXINT;
     343              :   guint counting, empty_pad;
     344              :   GstTensorsConfig in_configs;
     345         7199 :   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        14398 :   g_return_val_if_fail (collect != NULL, FALSE);
     352         7199 :   g_return_val_if_fail (sync != NULL, FALSE);
     353         7199 :   g_return_val_if_fail (tensors_buf != NULL, FALSE);
     354         7199 :   g_return_val_if_fail (configs != NULL, FALSE);
     355         7199 :   g_return_val_if_fail (is_eos != NULL, FALSE);
     356              : 
     357         7199 :   walk = collect->data;
     358         7199 :   counting = empty_pad = 0;
     359              : 
     360         7199 :   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         7199 :   walk = collect->data;
     382              : 
     383         7199 :   gst_tensors_config_init (&in_configs);
     384              : 
     385        21906 :   while (walk) {
     386        15379 :     gboolean configured = FALSE;
     387        15379 :     gboolean is_empty = FALSE;
     388              : 
     389        15379 :     data = (GstCollectData *) walk->data;
     390        15379 :     pad = (GstTensorCollectPadData *) data;
     391              : 
     392        15379 :     if (gst_pad_has_current_caps (data->pad)) {
     393        15378 :       GstCaps *caps = gst_pad_get_current_caps (data->pad);
     394              : 
     395        15378 :       gst_tensors_config_free (&in_configs);
     396        15378 :       configured = gst_tensors_config_from_caps (&in_configs, caps, TRUE);
     397              : 
     398        15378 :       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        15379 :     if (!configured) {
     409            1 :       return FALSE;
     410              :     }
     411              : 
     412        15378 :     if (in_configs.rate_d < old_denominator)
     413         7198 :       old_denominator = in_configs.rate_d;
     414        15378 :     if (in_configs.rate_n < old_numerator)
     415         7406 :       old_numerator = in_configs.rate_n;
     416              : 
     417        15378 :     walk = g_slist_next (walk);
     418              : 
     419        15378 :     switch (sync->mode) {
     420         3781 :       case SYNC_SLOWEST:
     421              :         /* fall-through */
     422              :       case SYNC_BASEPAD:
     423         3781 :         if (!_gst_tensor_time_sync_buffer_update (collect, data,
     424              :                 current_time, base_time, sync))
     425          671 :           return FALSE;
     426         3110 :         buf = gst_buffer_ref (pad->buffer);
     427         3110 :         is_empty = (buf == NULL);
     428         3110 :         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        11340 :       case SYNC_REFRESH:
     434        11340 :         buf = gst_collect_pads_pop (collect, data);
     435        11340 :         if (buf != NULL) {
     436         5671 :           if (pad->buffer != NULL) {
     437         5669 :             gst_buffer_unref (pad->buffer);
     438              :           }
     439         5671 :           pad->buffer = gst_buffer_ref (buf);
     440              :         } else {
     441         5669 :           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         5669 :           is_empty = TRUE;
     447         5669 :           buf = gst_buffer_ref (pad->buffer);
     448              :         }
     449        11340 :         break;
     450            0 :       default:
     451            0 :         break;
     452              :     }
     453              : 
     454        14707 :     if (GST_IS_BUFFER (buf)) {
     455        14707 :       guint32 n_tensor = gst_tensor_buffer_get_count (buf);
     456        14707 :       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        14707 :       if (gst_tensors_config_is_static (&in_configs))
     461        14681 :         g_assert (n_tensor == in_configs.info.num_tensors);
     462        14707 :       g_assert ((counting + n_tensor) <= NNS_TENSOR_SIZE_LIMIT);
     463              : 
     464        14707 :       if (gst_tensors_config_is_flexible (&in_configs))
     465           26 :         configs->info.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
     466              : 
     467        29794 :       for (i = 0; i < n_tensor; ++i) {
     468        15087 :         in_mem[counting] = gst_tensor_buffer_get_nth_memory (buf, i);
     469              : 
     470              :         /* set info */
     471        15087 :         gst_tensor_info_copy (gst_tensors_info_get_nth_info (&configs->info,
     472        15087 :                 counting), gst_tensors_info_get_nth_info (&in_configs.info, i));
     473        15087 :         in_formats[counting] = in_configs.info.format;
     474        15087 :         counting++;
     475              :       }
     476              : 
     477        14707 :       gst_buffer_unref (buf);
     478              :     }
     479        14707 :     if (is_empty)
     480         5669 :       empty_pad++;
     481              :   }
     482              : 
     483              :   /* append memories to output buffer */
     484        20816 :   for (i = 0; i < counting; i++) {
     485        14289 :     _info = gst_tensors_info_get_nth_info (&configs->info, i);
     486        14289 :     mem = in_mem[i];
     487              : 
     488        14289 :     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        14289 :     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         6527 :   configs->info.num_tensors = counting;
     509         6527 :   configs->rate_d = old_denominator;
     510         6527 :   configs->rate_n = old_numerator;
     511              : 
     512         6527 :   GST_BUFFER_PTS (tensors_buf) = current_time;
     513              : 
     514         6527 :   gst_tensors_config_free (&in_configs);
     515              : 
     516              :   /* check eos */
     517         6527 :   *is_eos = _gst_tensor_time_sync_is_eos (collect, sync, empty_pad);
     518         6527 :   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        37057 : gst_tensor_buffer_from_config (GstBuffer * in, GstTensorsConfig * config)
     532              : {
     533        37057 :   GstBuffer *out = NULL;
     534        37057 :   GstMemory *all = NULL;
     535              :   GstMapInfo map;
     536              :   guint i, num;
     537              :   gsize total, offset;
     538              :   gsize mem_size[NNS_TENSOR_MEMORY_MAX];
     539        37057 :   gboolean configured = FALSE;
     540        37057 :   gboolean is_extra = FALSE;
     541              : 
     542        37057 :   if (!GST_IS_BUFFER (in)) {
     543            1 :     nns_loge ("Failed to get tensor buffer, invalid input buffer.");
     544        37057 :     return NULL;
     545              :   }
     546              : 
     547        37056 :   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        37054 :   num = gst_buffer_n_memory (in);
     553        37054 :   total = gst_buffer_get_size (in);
     554              : 
     555              :   /* get memory size */
     556        37054 :   if (gst_tensors_config_is_static (config)) {
     557        36975 :     if (num == config->info.num_tensors) {
     558              :       /* Do nothing, pass input buffer. */
     559        36968 :       out = gst_buffer_ref (in);
     560        36968 :       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           79 :     if (num > 1) {
     575              :       /* Suppose it is already configured. */
     576           20 :       out = gst_buffer_ref (in);
     577           20 :       goto done;
     578              :     }
     579              : 
     580           59 :     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           59 :     num = 0;
     586           59 :     offset = 0;
     587          120 :     while (offset < total) {
     588              :       GstTensorMetaInfo meta;
     589           61 :       gpointer h = map.data + offset;
     590              : 
     591           61 :       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           61 :       gst_tensor_meta_info_parse_header (&meta, h);
     598           61 :       mem_size[num] = gst_tensor_meta_info_get_header_size (&meta);
     599           61 :       mem_size[num] += gst_tensor_meta_info_get_data_size (&meta);
     600              : 
     601           61 :       offset += mem_size[num];
     602           61 :       num++;
     603              :     }
     604              : 
     605           59 :     gst_buffer_unmap (in, &map);
     606              : 
     607           59 :     if (num == 1) {
     608              :       /* Do nothing, pass input buffer. */
     609           58 :       out = gst_buffer_ref (in);
     610           58 :       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        37053 : done:
     633        37053 :   configured = TRUE;
     634        37056 : error:
     635        37056 :   gst_buffer_unref (in);
     636              : 
     637        37056 :   if (all)
     638            8 :     gst_memory_unref (all);
     639              : 
     640        37056 :   if (!configured) {
     641            3 :     if (out) {
     642            1 :       gst_buffer_unref (out);
     643            1 :       out = NULL;
     644              :     }
     645              :   }
     646              : 
     647        37056 :   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              : /**
     659              :  * @brief Internal function to free aggregation data.
     660              :  */
     661              : static void
     662          868 : gst_tensor_aggregation_free_data (gpointer data)
     663              : {
     664              :   gst_tensor_aggregation_data_s *aggr;
     665              : 
     666          868 :   aggr = (gst_tensor_aggregation_data_s *) data;
     667          868 :   if (aggr) {
     668          868 :     gst_adapter_clear (aggr->adapter);
     669          868 :     g_object_unref (aggr->adapter);
     670              : 
     671          868 :     g_free (aggr);
     672              :   }
     673          868 : }
     674              : 
     675              : /**
     676              :  * @brief Internal function to add new aggregation data.
     677              :  */
     678              : static gst_tensor_aggregation_data_s *
     679          927 : gst_tensor_aggregation_add_data (GHashTable * table, const gint64 key)
     680              : {
     681              :   gst_tensor_aggregation_data_s *aggr;
     682              :   gint64 *hashkey;
     683              : 
     684          927 :   g_return_val_if_fail (table != NULL, NULL);
     685              : 
     686          927 :   hashkey = g_new (gint64, 1);
     687          927 :   *hashkey = key;
     688              : 
     689          927 :   aggr = g_new0 (gst_tensor_aggregation_data_s, 1);
     690          927 :   aggr->adapter = gst_adapter_new ();
     691              : 
     692          927 :   g_hash_table_insert (table, hashkey, aggr);
     693          927 :   return aggr;
     694              : }
     695              : 
     696              : /**
     697              :  * @brief Internal function to get aggregation data.
     698              :  */
     699              : static gst_tensor_aggregation_data_s *
     700          294 : gst_tensor_aggregation_get_data (GHashTable * table, const gint64 key)
     701              : {
     702          294 :   g_return_val_if_fail (table != NULL, NULL);
     703              : 
     704          294 :   return (gst_tensor_aggregation_data_s *) g_hash_table_lookup (table, &key);
     705              : }
     706              : 
     707              : /**
     708              :  * @brief Internal function to remove all buffers from aggregation data.
     709              :  */
     710              : static void
     711         3493 : gst_tensor_aggregation_clear_internal (gpointer key, gpointer value,
     712              :     gpointer user_data)
     713              : {
     714              :   gst_tensor_aggregation_data_s *aggr;
     715              : 
     716              :   UNUSED (key);
     717              :   UNUSED (user_data);
     718              : 
     719         3493 :   aggr = (gst_tensor_aggregation_data_s *) value;
     720         3493 :   if (aggr) {
     721         3493 :     gst_adapter_clear (aggr->adapter);
     722              :   }
     723         3493 : }
     724              : 
     725              : /**
     726              :  * @brief Gets new hash table for tensor aggregation.
     727              :  * @return Newly allocated hash table, caller should release this using g_hash_table_destroy().
     728              :  */
     729              : GHashTable *
     730          924 : gst_tensor_aggregation_init (void)
     731              : {
     732              :   GHashTable *table;
     733              : 
     734          924 :   table = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free,
     735              :       gst_tensor_aggregation_free_data);
     736              : 
     737              :   /**
     738              :    * Add default adapter (for the case if buffer has no specific id).
     739              :    * If gst-buffer has tensor-meta which includes client-id,
     740              :    * e.g., aggregation frames from multiple clients on query-server pipeline,
     741              :    * nnstreamer element should parse meta and request adapter with this id.
     742              :    * However, on normal pipeline, gst-buffer does not contain tensor-meta,
     743              :    * then the element may request adapter with null key string.
     744              :    */
     745          924 :   gst_tensor_aggregation_add_data (table, 0);
     746              : 
     747          924 :   return table;
     748              : }
     749              : 
     750              : /**
     751              :  * @brief Clears buffers from adapter.
     752              :  * @param table a hash table instance initialized with gst_tensor_aggregation_init()
     753              :  * @param key the key to look up (set 0 to get default adapter)
     754              :  */
     755              : void
     756            1 : gst_tensor_aggregation_clear (GHashTable * table, const gint64 key)
     757              : {
     758              :   gst_tensor_aggregation_data_s *aggr;
     759              : 
     760            1 :   g_return_if_fail (table != NULL);
     761            1 :   g_return_if_fail (key >= 0);
     762              : 
     763            1 :   aggr = gst_tensor_aggregation_get_data (table, key);
     764            1 :   gst_tensor_aggregation_clear_internal (NULL, aggr, NULL);
     765              : }
     766              : 
     767              : /**
     768              :  * @brief Clears buffers from all adapters in hash table.
     769              :  * @param table a hash table instance initialized with gst_tensor_aggregation_init()
     770              :  */
     771              : void
     772         3487 : gst_tensor_aggregation_clear_all (GHashTable * table)
     773              : {
     774         3487 :   g_hash_table_foreach (table, gst_tensor_aggregation_clear_internal, NULL);
     775         3487 : }
     776              : 
     777              : /**
     778              :  * @brief Gets adapter from hash table.
     779              :  * @param table a hash table instance initialized with gst_tensor_aggregation_init()
     780              :  * @param key the key to look up (set 0 to get default adapter)
     781              :  * @return gst-adapter instance. DO NOT release this instance.
     782              :  */
     783              : GstAdapter *
     784          294 : gst_tensor_aggregation_get_adapter (GHashTable * table, const gint64 key)
     785              : {
     786              :   gst_tensor_aggregation_data_s *aggr;
     787              : 
     788          294 :   g_return_val_if_fail (table != NULL, NULL);
     789          293 :   g_return_val_if_fail (key >= 0, NULL);
     790              : 
     791          293 :   aggr = gst_tensor_aggregation_get_data (table, key);
     792          293 :   if (!aggr) {
     793              :     /*append new data */
     794            3 :     aggr = gst_tensor_aggregation_add_data (table, key);
     795              :   }
     796              : 
     797          293 :   return aggr->adapter;
     798              : }
     799              : 
     800              : /**
     801              :  * @brief Internal function to check tensor dimensions to append old caps for backward compatibility (rank 4).
     802              :  */
     803              : static gboolean
     804        14339 : _append_prev_caps (const GstTensorsConfig * config)
     805              : {
     806              :   GstTensorsInfo *info;
     807              :   GstTensorInfo *_info;
     808              :   guint i, min_rank;
     809              : 
     810        14339 :   g_return_val_if_fail (config != NULL, FALSE);
     811              : 
     812        14339 :   info = (GstTensorsInfo *) (&config->info);
     813        14339 :   if (!gst_tensors_info_validate (info))
     814         7086 :     return FALSE;
     815              : 
     816        15512 :   for (i = 0; i < info->num_tensors; i++) {
     817         8341 :     _info = gst_tensors_info_get_nth_info (info, i);
     818              : 
     819         8341 :     min_rank = gst_tensor_dimension_get_min_rank (_info->dimension);
     820              : 
     821         8341 :     if (min_rank > NNS_TENSOR_RANK_LIMIT_PREV)
     822           82 :       return FALSE;
     823              :   }
     824              : 
     825         7171 :   return TRUE;
     826              : }
     827              : 
     828              : /**
     829              :  * @brief Internal function to check tensors-info string includes item (dimension, type, or name).
     830              :  */
     831              : static gboolean
     832        12784 : _is_empty_info_string (const gchar * str)
     833              : {
     834        12784 :   gboolean is_empty = TRUE;
     835              : 
     836        12784 :   if (str) {
     837              :     gchar **str_array;
     838              :     guint i, num;
     839              : 
     840        12784 :     str_array = g_strsplit (str, ",", -1);
     841        12784 :     num = g_strv_length (str_array);
     842              : 
     843        12784 :     for (i = 0; i < num; i++) {
     844         8242 :       g_strstrip (str_array[i]);
     845              : 
     846         8242 :       if (str_array[i] && str_array[i][0] != '\0') {
     847         8242 :         is_empty = FALSE;
     848         8242 :         break;
     849              :       }
     850              :     }
     851              : 
     852        12784 :     g_strfreev (str_array);
     853              :   }
     854              : 
     855        12784 :   return is_empty;
     856              : }
     857              : 
     858              : /**
     859              :  * @brief Internal function to get caps for single tensor from config.
     860              :  */
     861              : static GstCaps *
     862         7358 : _get_tensor_caps (const GstTensorsConfig * config)
     863              : {
     864              :   GstCaps *caps, *prev1, *prev2;
     865              :   GstTensorsInfo *info;
     866              :   GstTensorInfo *_info;
     867              :   gboolean append_prev;
     868              : 
     869         7358 :   g_return_val_if_fail (config != NULL, NULL);
     870              : 
     871         7358 :   info = (GstTensorsInfo *) (&config->info);
     872         7358 :   if (info->num_tensors > 1)
     873          135 :     return NULL;
     874              : 
     875         7223 :   caps = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
     876         7223 :   _info = gst_tensors_info_get_nth_info (info, 0);
     877              : 
     878              :   /* caps for backward compatibility */
     879         7223 :   prev1 = prev2 = NULL;
     880         7223 :   append_prev = _append_prev_caps (config);
     881         7223 :   if (append_prev) {
     882         3647 :     prev1 = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
     883         3647 :     prev2 = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
     884              :   }
     885              : 
     886         7223 :   if (gst_tensor_dimension_is_valid (_info->dimension)) {
     887         4021 :     g_autofree gchar *dim_str =
     888         4021 :         gst_tensor_get_dimension_string (_info->dimension);
     889              : 
     890         4021 :     gst_caps_set_simple (caps, "dimension", G_TYPE_STRING, dim_str, NULL);
     891              : 
     892         4021 :     if (append_prev) {
     893         3647 :       g_autofree gchar *dim_prev1 =
     894         3647 :           gst_tensor_get_rank_dimension_string (_info->dimension,
     895              :           NNS_TENSOR_RANK_LIMIT_PREV, FALSE);
     896         3647 :       g_autofree gchar *dim_prev2 =
     897         3647 :           gst_tensor_get_rank_dimension_string (_info->dimension,
     898              :           NNS_TENSOR_RANK_LIMIT_PREV, TRUE);
     899              : 
     900         3647 :       gst_caps_set_simple (prev1, "dimension", G_TYPE_STRING, dim_prev1, NULL);
     901         3647 :       gst_caps_set_simple (prev2, "dimension", G_TYPE_STRING, dim_prev2, NULL);
     902              :     }
     903              :   }
     904              : 
     905         7223 :   if (_info->type != _NNS_END) {
     906         4439 :     const gchar *type_str = gst_tensor_get_type_string (_info->type);
     907              : 
     908         4439 :     gst_caps_set_simple (caps, "type", G_TYPE_STRING, type_str, NULL);
     909              : 
     910         4439 :     if (append_prev) {
     911         3647 :       gst_caps_set_simple (prev1, "type", G_TYPE_STRING, type_str, NULL);
     912         3647 :       gst_caps_set_simple (prev2, "type", G_TYPE_STRING, type_str, NULL);
     913              :     }
     914              :   }
     915              : 
     916         7223 :   if (config->rate_n >= 0 && config->rate_d > 0) {
     917         2529 :     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
     918         2529 :         config->rate_n, config->rate_d, NULL);
     919              : 
     920         2529 :     if (append_prev) {
     921         2363 :       gst_caps_set_simple (prev1, "framerate", GST_TYPE_FRACTION,
     922         2363 :           config->rate_n, config->rate_d, NULL);
     923         2363 :       gst_caps_set_simple (prev2, "framerate", GST_TYPE_FRACTION,
     924         2363 :           config->rate_n, config->rate_d, NULL);
     925              :     }
     926              :   }
     927              : 
     928         7223 :   if (append_prev)
     929         3647 :     caps = gst_caps_merge (caps, gst_caps_merge (prev1, prev2));
     930              : 
     931         7223 :   return caps;
     932              : }
     933              : 
     934              : /**
     935              :  * @brief Internal function to get caps for multi tensors from config.
     936              :  */
     937              : static GstCaps *
     938         7116 : _get_tensors_caps (const GstTensorsConfig * config)
     939              : {
     940              :   GstCaps *caps, *prev1, *prev2;
     941              :   gboolean append_prev;
     942              : 
     943         7116 :   g_return_val_if_fail (config != NULL, NULL);
     944              : 
     945         7116 :   caps = gst_caps_from_string (GST_TENSORS_CAP_DEFAULT);
     946              : 
     947              :   /* caps for backward compatibility */
     948         7116 :   prev1 = prev2 = NULL;
     949         7116 :   append_prev = _append_prev_caps (config);
     950         7116 :   if (append_prev) {
     951         3524 :     prev1 = gst_caps_from_string (GST_TENSORS_CAP_DEFAULT);
     952         3524 :     prev2 = gst_caps_from_string (GST_TENSORS_CAP_DEFAULT);
     953              :   }
     954              : 
     955         7116 :   if (config->info.num_tensors > 0) {
     956         6392 :     g_autofree gchar *type_str =
     957         6392 :         gst_tensors_info_get_types_string (&config->info);
     958         6392 :     g_autofree gchar *dim_str =
     959         6392 :         gst_tensors_info_get_dimensions_string (&config->info);
     960         6392 :     gboolean has_type = !_is_empty_info_string (type_str);
     961         6392 :     gboolean has_dim = !_is_empty_info_string (dim_str);
     962              : 
     963         6392 :     gst_caps_set_simple (caps, "num_tensors", G_TYPE_INT,
     964         6392 :         config->info.num_tensors, NULL);
     965         6392 :     if (has_dim) {
     966         3912 :       gst_caps_set_simple (caps, "dimensions", G_TYPE_STRING, dim_str, NULL);
     967              :     }
     968         6392 :     if (has_type) {
     969         4330 :       gst_caps_set_simple (caps, "types", G_TYPE_STRING, type_str, NULL);
     970              :     }
     971              : 
     972         6392 :     if (append_prev) {
     973         3524 :       gst_caps_set_simple (prev1, "num_tensors", G_TYPE_INT,
     974         3524 :           config->info.num_tensors, NULL);
     975         3524 :       if (has_dim) {
     976         3524 :         g_autofree gchar *dimstr =
     977         3524 :             gst_tensors_info_get_rank_dimensions_string (&config->info,
     978              :             NNS_TENSOR_RANK_LIMIT_PREV, FALSE);
     979              : 
     980         3524 :         gst_caps_set_simple (prev1, "dimensions", G_TYPE_STRING, dimstr, NULL);
     981              :       }
     982         3524 :       if (has_type) {
     983         3524 :         gst_caps_set_simple (prev1, "types", G_TYPE_STRING, type_str, NULL);
     984              :       }
     985              : 
     986         3524 :       gst_caps_set_simple (prev2, "num_tensors", G_TYPE_INT,
     987         3524 :           config->info.num_tensors, NULL);
     988         3524 :       if (has_dim) {
     989         3524 :         g_autofree gchar *dimstr =
     990         3524 :             gst_tensors_info_get_rank_dimensions_string (&config->info,
     991              :             NNS_TENSOR_RANK_LIMIT_PREV, TRUE);
     992              : 
     993         3524 :         gst_caps_set_simple (prev2, "dimensions", G_TYPE_STRING, dimstr, NULL);
     994              :       }
     995         3524 :       if (has_type) {
     996         3524 :         gst_caps_set_simple (prev2, "types", G_TYPE_STRING, type_str, NULL);
     997              :       }
     998              :     }
     999              :   }
    1000              : 
    1001         7116 :   if (config->rate_n >= 0 && config->rate_d > 0) {
    1002         2377 :     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
    1003         2377 :         config->rate_n, config->rate_d, NULL);
    1004              : 
    1005         2377 :     if (append_prev) {
    1006         2201 :       gst_caps_set_simple (prev1, "framerate", GST_TYPE_FRACTION,
    1007         2201 :           config->rate_n, config->rate_d, NULL);
    1008         2201 :       gst_caps_set_simple (prev2, "framerate", GST_TYPE_FRACTION,
    1009         2201 :           config->rate_n, config->rate_d, NULL);
    1010              :     }
    1011              :   }
    1012              : 
    1013         7116 :   if (append_prev)
    1014         3524 :     caps = gst_caps_merge (caps, gst_caps_merge (prev1, prev2));
    1015              : 
    1016         7116 :   return caps;
    1017              : }
    1018              : 
    1019              : /**
    1020              :  * @brief Internal function to get caps for flexible tensor from config.
    1021              :  */
    1022              : static GstCaps *
    1023         7813 : _get_flexible_caps (const GstTensorsConfig * config)
    1024              : {
    1025              :   GstCaps *caps;
    1026              : 
    1027         7813 :   caps = gst_caps_from_string (GST_TENSORS_FLEX_CAP_DEFAULT);
    1028              : 
    1029         7813 :   if (config->rate_n >= 0 && config->rate_d > 0) {
    1030         2105 :     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
    1031         2105 :         config->rate_n, config->rate_d, NULL);
    1032              :   }
    1033              : 
    1034         7813 :   return caps;
    1035              : }
    1036              : 
    1037              : /**
    1038              :  * @brief Check given mimetype is tensor stream.
    1039              :  * @param structure structure to be interpreted
    1040              :  * @return TRUE if mimetype is tensor stream
    1041              :  */
    1042              : gboolean
    1043        19155 : gst_structure_is_tensor_stream (const GstStructure * structure)
    1044              : {
    1045              :   const gchar *name;
    1046              : 
    1047        19155 :   name = gst_structure_get_name (structure);
    1048        19155 :   g_return_val_if_fail (name != NULL, FALSE);
    1049              : 
    1050        34094 :   return (g_str_equal (name, NNS_MIMETYPE_TENSOR) ||
    1051        14939 :       g_str_equal (name, NNS_MIMETYPE_TENSORS));
    1052              : }
    1053              : 
    1054              : /**
    1055              :  * @brief Get media type from structure
    1056              :  * @param structure structure to be interpreted
    1057              :  * @return corresponding media type (returns _NNS_MEDIA_INVALID for unsupported type)
    1058              :  */
    1059              : media_type
    1060        20151 : gst_structure_get_media_type (const GstStructure * structure)
    1061              : {
    1062              :   const gchar *name;
    1063              : 
    1064        20151 :   name = gst_structure_get_name (structure);
    1065              : 
    1066        20151 :   g_return_val_if_fail (name != NULL, _NNS_MEDIA_INVALID);
    1067              : 
    1068        20151 :   if (g_str_has_prefix (name, "video/")) {
    1069         2764 :     return _NNS_VIDEO;
    1070              :   }
    1071              : 
    1072        17387 :   if (g_str_has_prefix (name, "audio/")) {
    1073         2182 :     return _NNS_AUDIO;
    1074              :   }
    1075              : 
    1076        15205 :   if (g_str_has_prefix (name, "text/")) {
    1077         2155 :     return _NNS_TEXT;
    1078              :   }
    1079              : 
    1080        13050 :   if (g_str_equal (name, "application/octet-stream")) {
    1081         4408 :     return _NNS_OCTET;
    1082              :   }
    1083              : 
    1084         8642 :   if (gst_structure_is_tensor_stream (structure)) {
    1085         2150 :     return _NNS_TENSOR;
    1086              :   }
    1087              : 
    1088              :   /* unknown or unsupported type */
    1089         6492 :   return _NNS_MEDIA_INVALID;
    1090              : }
    1091              : 
    1092              : /**
    1093              :  * @brief Parse caps from peer pad and set tensors config.
    1094              :  * @param pad GstPad to get the capabilities
    1095              :  * @param config tensors config structure to be filled
    1096              :  * @param is_fixed flag to be updated when peer caps is fixed (not mandatory, do nothing when the param is null)
    1097              :  * @return TRUE if successfully configured from peer
    1098              :  */
    1099              : gboolean
    1100         4294 : gst_tensors_config_from_peer (GstPad * pad, GstTensorsConfig * config,
    1101              :     gboolean * is_fixed)
    1102              : {
    1103              :   GstCaps *peer_caps;
    1104              :   GstStructure *structure;
    1105         4294 :   gboolean ret = FALSE;
    1106              : 
    1107         4294 :   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
    1108         4294 :   g_return_val_if_fail (config != NULL, FALSE);
    1109              : 
    1110         4294 :   gst_tensors_config_init (config);
    1111              : 
    1112         4294 :   if ((peer_caps = gst_pad_peer_query_caps (pad, NULL))) {
    1113         4294 :     if (gst_caps_get_size (peer_caps) > 0) {
    1114         2184 :       structure = gst_caps_get_structure (peer_caps, 0);
    1115         2184 :       ret = gst_tensors_config_from_structure (config, structure);
    1116              :     }
    1117              : 
    1118         4294 :     if (ret && is_fixed)
    1119           30 :       *is_fixed = gst_caps_is_fixed (peer_caps);
    1120              : 
    1121         4294 :     gst_caps_unref (peer_caps);
    1122              :   }
    1123              : 
    1124         4294 :   return ret;
    1125              : }
    1126              : 
    1127              : /**
    1128              :  * @brief Check whether two structures have the same dimension
    1129              :  */
    1130              : static gboolean
    1131          261 : _is_structure_dimension_same (GstStructure * st1, GstStructure * st2,
    1132              :     const gchar * fieldname)
    1133              : {
    1134              :   const char *dim_str1;
    1135              :   const char *dim_str2;
    1136              : 
    1137          261 :   g_return_val_if_fail (gst_structure_has_field (st1, fieldname), FALSE);
    1138          261 :   g_return_val_if_fail (gst_structure_has_field (st2, fieldname), FALSE);
    1139              : 
    1140          261 :   dim_str1 = gst_structure_get_string (st1, fieldname);
    1141          261 :   dim_str2 = gst_structure_get_string (st2, fieldname);
    1142              : 
    1143          261 :   return gst_tensor_dimension_string_is_equal (dim_str1, dim_str2);
    1144              : }
    1145              : 
    1146              : /**
    1147              :  * @brief Update caps dimensions for negotiation
    1148              :  * @param caps caps to compare and update
    1149              :  * @param filter caps to compare
    1150              :  */
    1151              : void
    1152         1639 : gst_tensor_caps_update_dimension (GstCaps * caps, GstCaps * filter)
    1153              : {
    1154              :   GstStructure *st_caps, *st_filter;
    1155              :   guint i, j;
    1156              : 
    1157         1639 :   g_return_if_fail (GST_IS_CAPS (caps));
    1158         1639 :   g_return_if_fail (GST_IS_CAPS (filter));
    1159              : 
    1160         5006 :   for (i = 0; i < gst_caps_get_size (caps); i++) {
    1161         3367 :     st_caps = gst_caps_get_structure (caps, i);
    1162              : 
    1163         3367 :     if (!gst_structure_is_tensor_stream (st_caps))
    1164            0 :       continue;
    1165              : 
    1166        10472 :     for (j = 0; j < gst_caps_get_size (filter); j++) {
    1167         7105 :       st_filter = gst_caps_get_structure (filter, j);
    1168              : 
    1169         7105 :       if (!gst_structure_is_tensor_stream (st_filter))
    1170            0 :         continue;
    1171              : 
    1172              :       /* other/tensor */
    1173         7105 :       if (gst_structure_has_field (st_caps, "dimension")
    1174         3876 :           && gst_structure_has_field (st_filter, "dimension")) {
    1175              :         /* update dimensions for negotiation */
    1176          232 :         if (_is_structure_dimension_same (st_caps, st_filter, "dimension")) {
    1177          116 :           gst_structure_set (st_caps, "dimension", G_TYPE_STRING,
    1178              :               gst_structure_get_string (st_filter, "dimension"), NULL);
    1179              :         }
    1180              :       }
    1181              :       /* other/tensors */
    1182         6989 :       else if (gst_structure_has_field (st_caps, "dimensions")
    1183         2104 :           && gst_structure_has_field (st_filter, "dimensions")) {
    1184              :         /* update dimensions for negotiation */
    1185          142 :         if (_is_structure_dimension_same (st_caps, st_filter, "dimensions")) {
    1186          141 :           gst_structure_set (st_caps, "dimensions", G_TYPE_STRING,
    1187              :               gst_structure_get_string (st_filter, "dimensions"), NULL);
    1188              :         }
    1189              :       }
    1190              :     }
    1191              :   }
    1192              : }
    1193              : 
    1194              : /**
    1195              :  * @brief  Try intersecting @caps1 and @caps2 for tensor stream
    1196              :  * @param caps1 a GstCaps to intersect
    1197              :  * @param caps2 a GstCaps to intersect
    1198              :  * @return TRUE if intersection would be not empty.
    1199              :  */
    1200              : gboolean
    1201           14 : gst_tensor_caps_can_intersect (GstCaps * caps1, GstCaps * caps2)
    1202              : {
    1203              :   GstStructure *structure1;
    1204              :   GstStructure *structure2;
    1205              :   GstStructure *structure_copy1;
    1206              :   GstStructure *structure_copy2;
    1207              : 
    1208              :   const gchar *name1;
    1209              :   const gchar *name2;
    1210              : 
    1211              :   gboolean intersectable;
    1212              : 
    1213           14 :   if (gst_caps_can_intersect (caps1, caps2))
    1214           10 :     return TRUE;
    1215              : 
    1216            4 :   structure1 = gst_caps_get_structure (caps1, 0);
    1217            4 :   structure2 = gst_caps_get_structure (caps2, 0);
    1218              : 
    1219            4 :   if (!gst_structure_is_tensor_stream (structure1)
    1220            4 :       || !gst_structure_is_tensor_stream (structure2))
    1221            0 :     return FALSE;
    1222              : 
    1223            4 :   name1 = gst_structure_get_name (structure1);
    1224            4 :   name2 = gst_structure_get_name (structure2);
    1225              : 
    1226            4 :   if (!g_str_equal (name1, name2))
    1227            1 :     return FALSE;
    1228              : 
    1229              :   /* other/tensor */
    1230            3 :   if (g_str_equal (name1, NNS_MIMETYPE_TENSOR)) {
    1231            3 :     if (gst_structure_has_field (structure1, "dimension")
    1232            3 :         && gst_structure_has_field (structure2, "dimension")) {
    1233            3 :       if (!_is_structure_dimension_same (structure1, structure2, "dimension"))
    1234            1 :         return FALSE;
    1235              :     }
    1236              :   }
    1237              :   /* other/tensors */
    1238            0 :   else if (gst_structure_has_field (structure1, "dimensions")
    1239            0 :       && gst_structure_has_field (structure2, "dimensions")) {
    1240            0 :     if (!_is_structure_dimension_same (structure1, structure2, "dimensions"))
    1241            0 :       return FALSE;
    1242              :   }
    1243              : 
    1244            2 :   structure_copy1 = gst_structure_copy (structure1);
    1245            2 :   structure_copy2 = gst_structure_copy (structure2);
    1246              : 
    1247            2 :   gst_structure_remove_field (structure_copy1, "dimension");
    1248            2 :   gst_structure_remove_field (structure_copy1, "dimensions");
    1249            2 :   gst_structure_remove_field (structure_copy2, "dimension");
    1250            2 :   gst_structure_remove_field (structure_copy2, "dimensions");
    1251              : 
    1252              :   intersectable =
    1253            2 :       gst_structure_can_intersect (structure_copy1, structure_copy2);
    1254              : 
    1255            2 :   gst_structure_free (structure_copy1);
    1256            2 :   gst_structure_free (structure_copy2);
    1257              : 
    1258            2 :   return intersectable;
    1259              : }
    1260              : 
    1261              : /**
    1262              :  * @brief Get pad caps from tensors config and caps of the peer connected to the pad.
    1263              :  * @param pad GstPad to get possible caps
    1264              :  * @param config tensors config structure
    1265              :  * @return caps for given config. Caller is responsible for unreffing the returned caps.
    1266              :  */
    1267              : GstCaps *
    1268         1209 : gst_tensor_pad_caps_from_config (GstPad * pad, const GstTensorsConfig * config)
    1269              : {
    1270         1209 :   GstCaps *caps = NULL;
    1271              :   GstCaps *templ;
    1272              :   gboolean is_flexible, peer_is_flexible, peer_has_tensor_caps;
    1273              :   GstCaps *peer_caps;
    1274              : 
    1275         2418 :   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
    1276         1209 :   g_return_val_if_fail (config != NULL, NULL);
    1277              : 
    1278         1209 :   templ = gst_pad_get_pad_template_caps (pad);
    1279              : 
    1280              :   /* check peer caps */
    1281         1209 :   peer_is_flexible = peer_has_tensor_caps = FALSE;
    1282              : 
    1283         1209 :   peer_caps = gst_pad_peer_query_caps (pad, NULL);
    1284         1209 :   if (peer_caps && gst_caps_get_size (peer_caps) > 0) {
    1285              :     GstCaps *tmp;
    1286              :     GstStructure *st;
    1287              :     GstTensorsConfig peer_config;
    1288              : 
    1289          912 :     tmp = gst_caps_from_string (GST_TENSOR_CAP_DEFAULT);
    1290          912 :     peer_has_tensor_caps = gst_caps_can_intersect (tmp, peer_caps);
    1291          912 :     gst_caps_unref (tmp);
    1292              : 
    1293          912 :     st = gst_caps_get_structure (peer_caps, 0);
    1294          912 :     if (gst_tensors_config_from_structure (&peer_config, st))
    1295          912 :       peer_is_flexible = gst_tensors_config_is_flexible (&peer_config);
    1296          912 :     gst_tensors_config_free (&peer_config);
    1297              :   }
    1298              : 
    1299              :   /* other/tensors (flexible) */
    1300         1209 :   is_flexible = gst_tensors_config_is_flexible (config);
    1301              : 
    1302         1209 :   if (is_flexible || peer_is_flexible) {
    1303           71 :     caps = _get_flexible_caps (config);
    1304           71 :     goto intersectable;
    1305              :   }
    1306              : 
    1307              :   /* other/tensor */
    1308         1138 :   if (config->info.num_tensors == 1 && peer_has_tensor_caps) {
    1309          732 :     caps = _get_tensor_caps (config);
    1310          732 :     if (peer_caps)
    1311          732 :       gst_tensor_caps_update_dimension (caps, peer_caps);
    1312              : 
    1313          732 :     if (gst_caps_can_intersect (caps, templ))
    1314          728 :       goto done;
    1315              : 
    1316            4 :     gst_caps_unref (caps);
    1317              :   }
    1318              : 
    1319              :   /* other/tensors (static) */
    1320          410 :   caps = _get_tensors_caps (config);
    1321          410 :   if (peer_caps)
    1322          410 :     gst_tensor_caps_update_dimension (caps, peer_caps);
    1323              : 
    1324            0 : intersectable:
    1325          481 :   if (!gst_caps_can_intersect (caps, templ)) {
    1326            0 :     g_clear_pointer (&caps, gst_caps_unref);
    1327              :   }
    1328              : 
    1329          481 : done:
    1330         1209 :   g_clear_pointer (&templ, gst_caps_unref);
    1331         1209 :   g_clear_pointer (&peer_caps, gst_caps_unref);
    1332              : 
    1333         1209 :   return gst_caps_truncate (caps);
    1334              : }
    1335              : 
    1336              : /**
    1337              :  * @brief Get all possible caps from tensors config. Unlike gst_tensor_pad_caps_from_config(), this function does not check peer caps.
    1338              :  * @param pad GstPad to get possible caps
    1339              :  * @param config tensors config structure
    1340              :  * @return caps for given config. Caller is responsible for unreffing the returned caps.
    1341              :  */
    1342              : GstCaps *
    1343         7735 : gst_tensor_pad_possible_caps_from_config (GstPad * pad,
    1344              :     const GstTensorsConfig * config)
    1345              : {
    1346              :   GstCaps *caps, *tmp;
    1347              :   GstCaps *templ;
    1348              : 
    1349        15470 :   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
    1350         7735 :   g_return_val_if_fail (config != NULL, NULL);
    1351              : 
    1352         7735 :   caps = gst_caps_new_empty ();
    1353         7735 :   templ = gst_pad_get_pad_template_caps (pad);
    1354              : 
    1355              :   /* append caps for static tensor */
    1356         7735 :   if (gst_tensors_config_is_static (config)) {
    1357              :     /* other/tensor */
    1358         6607 :     if ((tmp = _get_tensor_caps (config)) != NULL) {
    1359         6472 :       if (gst_caps_can_intersect (tmp, templ))
    1360         6425 :         gst_caps_append (caps, tmp);
    1361              :       else
    1362           47 :         gst_caps_unref (tmp);
    1363              :     }
    1364              : 
    1365              :     /* other/tensors */
    1366         6607 :     if ((tmp = _get_tensors_caps (config)) != NULL) {
    1367         6607 :       if (gst_caps_can_intersect (tmp, templ))
    1368         6607 :         gst_caps_append (caps, tmp);
    1369              :       else
    1370            0 :         gst_caps_unref (tmp);
    1371              :     }
    1372              :   }
    1373              : 
    1374              :   /* caps for flexible tensor */
    1375         7735 :   if ((tmp = _get_flexible_caps (config)) != NULL) {
    1376         7735 :     if (gst_caps_can_intersect (tmp, templ))
    1377         7642 :       gst_caps_append (caps, tmp);
    1378              :     else
    1379           93 :       gst_caps_unref (tmp);
    1380              :   }
    1381              : 
    1382              :   /* if no possible caps for given config, return null. */
    1383         7735 :   if (gst_caps_is_empty (caps)) {
    1384            0 :     g_clear_pointer (&caps, gst_caps_unref);
    1385              :   }
    1386              : 
    1387         7735 :   g_clear_pointer (&templ, gst_caps_unref);
    1388         7735 :   return caps;
    1389              : }
    1390              : 
    1391              : /**
    1392              :  * @brief Get tensor format of current pad caps.
    1393              :  * @param pad GstPad to check current caps.
    1394              :  * @return The tensor_format of current pad caps.
    1395              :  *
    1396              :  * If pad does not have tensor caps return _NNS_TENSOR_FORMAT_END
    1397              :  */
    1398              : tensor_format
    1399       129475 : gst_tensor_pad_get_format (GstPad * pad)
    1400              : {
    1401              :   GstCaps *caps;
    1402       129475 :   tensor_format ret = _NNS_TENSOR_FORMAT_END;
    1403              : 
    1404       129475 :   g_return_val_if_fail (GST_IS_PAD (pad), _NNS_TENSOR_FORMAT_END);
    1405              : 
    1406       129475 :   caps = gst_pad_get_current_caps (pad);
    1407       129475 :   if (caps) {
    1408              :     GstTensorsConfig config;
    1409              : 
    1410       129462 :     if (gst_tensors_config_from_caps (&config, caps, TRUE)) {
    1411       129351 :       ret = config.info.format;
    1412              :     }
    1413       129462 :     gst_caps_unref (caps);
    1414       129462 :     gst_tensors_config_free (&config);
    1415              :   }
    1416              : 
    1417       129475 :   return ret;
    1418              : }
    1419              : 
    1420              : /**
    1421              :  * @brief Get caps from tensors config (for other/tensors)
    1422              :  * @param config tensors config info
    1423              :  * @return caps for given config
    1424              :  */
    1425              : GstCaps *
    1426          107 : gst_tensors_caps_from_config (const GstTensorsConfig * config)
    1427              : {
    1428              :   GstCaps *caps;
    1429              : 
    1430          107 :   g_return_val_if_fail (config != NULL, NULL);
    1431              : 
    1432          106 :   if (gst_tensors_config_is_flexible (config)) {
    1433            7 :     caps = _get_flexible_caps (config);
    1434              :   } else {
    1435           99 :     caps = _get_tensors_caps (config);
    1436              :   }
    1437              : 
    1438          106 :   caps = gst_caps_truncate (caps);
    1439              : 
    1440          106 :   return caps;
    1441              : }
    1442              : 
    1443              : /**
    1444              :  * @brief Get tensor caps from tensors config
    1445              :  * @param config tensors config info
    1446              :  * @return caps for given config
    1447              :  */
    1448              : GstCaps *
    1449           20 : gst_tensor_caps_from_config (const GstTensorsConfig * config)
    1450              : {
    1451              :   GstCaps *caps;
    1452              : 
    1453           20 :   g_return_val_if_fail (config != NULL, NULL);
    1454              : 
    1455           19 :   caps = _get_tensor_caps (config);
    1456           19 :   caps = gst_caps_truncate (caps);
    1457              : 
    1458           19 :   return caps;
    1459              : }
    1460              : 
    1461              : /**
    1462              :  * @brief Parse structure and set tensors config (for other/tensors)
    1463              :  * @param config tensors config structure to be filled
    1464              :  * @param structure structure to be interpreted
    1465              :  * @return TRUE if no error
    1466              :  */
    1467              : gboolean
    1468       159779 : gst_tensors_config_from_structure (GstTensorsConfig * config,
    1469              :     const GstStructure * structure)
    1470              : {
    1471              :   const gchar *name;
    1472              : 
    1473       159779 :   g_return_val_if_fail (config != NULL, FALSE);
    1474       159777 :   gst_tensors_config_init (config);
    1475              : 
    1476       159777 :   g_return_val_if_fail (structure != NULL, FALSE);
    1477              : 
    1478       159776 :   name = gst_structure_get_name (structure);
    1479              : 
    1480       159776 :   if (g_str_equal (name, NNS_MIMETYPE_TENSOR)) {
    1481              :     /* other/tensor is always static */
    1482       146019 :     config->info.num_tensors = 1;
    1483              : 
    1484       146019 :     if (gst_structure_has_field (structure, "dimension")) {
    1485       140052 :       const gchar *dim_str = gst_structure_get_string (structure, "dimension");
    1486       140052 :       gst_tensor_parse_dimension (dim_str, config->info.info[0].dimension);
    1487              :     }
    1488              : 
    1489       146019 :     if (gst_structure_has_field (structure, "type")) {
    1490       140225 :       const gchar *type_str = gst_structure_get_string (structure, "type");
    1491       140225 :       config->info.info[0].type = gst_tensor_get_type (type_str);
    1492              :     }
    1493        13757 :   } else if (g_str_equal (name, NNS_MIMETYPE_TENSORS)) {
    1494        13646 :     tensor_format format = _NNS_TENSOR_FORMAT_END;
    1495              : 
    1496        13646 :     if (gst_structure_has_field (structure, "format")) {
    1497              :       const gchar *format_str;
    1498              : 
    1499        13646 :       format_str = gst_structure_get_string (structure, "format");
    1500        13646 :       format = gst_tensor_get_format (format_str);
    1501              : 
    1502        13646 :       if (format == _NNS_TENSOR_FORMAT_END) {
    1503         1651 :         GST_INFO
    1504              :             ("Invalid format %s, it should be one of %s. Suppose tensor format is unknown.",
    1505              :             _STR_NULL (format_str), GST_TENSOR_FORMAT_ALL);
    1506              :       }
    1507            0 :     } else if (gst_structure_has_field (structure, "num_tensors")) {
    1508            0 :         GST_WARNING
    1509              :             ("Found 'num_tensors' field but format is not described. Suppose tensor format is static.");
    1510            0 :         format = _NNS_TENSOR_FORMAT_STATIC;
    1511              :     }
    1512              : 
    1513        13646 :     config->info.format = format;
    1514              : 
    1515        13646 :     if (config->info.format == _NNS_TENSOR_FORMAT_STATIC) {
    1516         9702 :       gst_structure_get_int (structure, "num_tensors",
    1517         9702 :           (gint *) (&config->info.num_tensors));
    1518              : 
    1519              :       /* parse dimensions */
    1520         9702 :       if (gst_structure_has_field (structure, "dimensions")) {
    1521              :         const gchar *dims_str;
    1522              :         guint num_dims;
    1523              : 
    1524         8761 :         dims_str = gst_structure_get_string (structure, "dimensions");
    1525              :         num_dims =
    1526         8761 :             gst_tensors_info_parse_dimensions_string (&config->info, dims_str);
    1527              : 
    1528         8761 :         if (config->info.num_tensors != num_dims) {
    1529            0 :           nns_logw ("Invalid param, dimensions (%d) tensors (%d)\n",
    1530              :               num_dims, config->info.num_tensors);
    1531              :         }
    1532              :       }
    1533              : 
    1534              :       /* parse types */
    1535         9702 :       if (gst_structure_has_field (structure, "types")) {
    1536              :         const gchar *types_str;
    1537              :         guint num_types;
    1538              : 
    1539         8855 :         types_str = gst_structure_get_string (structure, "types");
    1540              :         num_types =
    1541         8855 :             gst_tensors_info_parse_types_string (&config->info, types_str);
    1542              : 
    1543         8855 :         if (config->info.num_tensors != num_types) {
    1544            0 :           nns_logw ("Invalid param, types (%d) tensors (%d)\n",
    1545              :               num_types, config->info.num_tensors);
    1546              :         }
    1547              :       }
    1548              :     }
    1549              :   } else {
    1550          111 :     nns_logw ("Unsupported type = %s\n", name ? name : "Unknown");
    1551          111 :     return FALSE;
    1552              :   }
    1553              : 
    1554       159665 :   if (gst_structure_has_field (structure, "framerate")) {
    1555       159646 :     gst_structure_get_fraction (structure, "framerate", &config->rate_n,
    1556       159646 :         &config->rate_d);
    1557              :   }
    1558              : 
    1559       159665 :   return TRUE;
    1560              : }
    1561              : 
    1562              : /**
    1563              :  * @brief Parse caps and set tensors config (for other/tensors)
    1564              :  * @param[out] config tensors config structure to be filled
    1565              :  * @param[in] caps incoming capability
    1566              :  * @param[in] validate TRUE to validate configuration
    1567              :  * @return TRUE/FALSE (if successfully configured, return TRUE)
    1568              :  */
    1569              : gboolean
    1570       149946 : gst_tensors_config_from_caps (GstTensorsConfig * config, const GstCaps * caps,
    1571              :     const gboolean validate)
    1572              : {
    1573              :   GstStructure *structure;
    1574       149946 :   gboolean ret = FALSE;
    1575              : 
    1576       149946 :   gst_tensors_config_init (config);
    1577              : 
    1578       149946 :   if (validate && !gst_caps_is_fixed (caps)) {
    1579          551 :     nns_logw ("GstCaps is not fixed.");
    1580          551 :     return FALSE;
    1581              :   }
    1582              : 
    1583       149395 :   structure = gst_caps_get_structure (caps, 0);
    1584       149395 :   ret = gst_tensors_config_from_structure (config, structure);
    1585              : 
    1586       149395 :   if (ret && validate) {
    1587       146513 :     ret = gst_tensors_config_validate (config);
    1588              :   }
    1589              : 
    1590       149395 :   if (!ret) {
    1591          114 :     gst_tensors_config_free (config);
    1592              :   }
    1593              : 
    1594       149395 :   return ret;
    1595              : }
    1596              : 
    1597              : /**
    1598              :  * @brief Parse memory and fill the tensor meta.
    1599              :  * @param[out] meta tensor meta structure to be filled
    1600              :  * @param[in] mem pointer to GstMemory to be parsed
    1601              :  * @return TRUE if successfully set the meta
    1602              :  */
    1603              : gboolean
    1604        61105 : gst_tensor_meta_info_parse_memory (GstTensorMetaInfo * meta, GstMemory * mem)
    1605              : {
    1606              :   GstMapInfo map;
    1607              :   gsize hsize, msize;
    1608              :   gboolean ret;
    1609              : 
    1610       122209 :   g_return_val_if_fail (mem != NULL, FALSE);
    1611        61104 :   g_return_val_if_fail (meta != NULL, FALSE);
    1612              : 
    1613        61103 :   gst_tensor_meta_info_init (meta);
    1614              : 
    1615              :   /* Check header size of tensor-meta. */
    1616        61103 :   hsize = gst_tensor_meta_info_get_header_size (meta);
    1617        61103 :   msize = gst_memory_get_sizes (mem, NULL, NULL);
    1618        61103 :   if (msize < hsize)
    1619         8462 :     return FALSE;
    1620              : 
    1621        52641 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
    1622            0 :     nns_loge ("Failed to get the meta, cannot map the memory.");
    1623            0 :     return FALSE;
    1624              :   }
    1625              : 
    1626        52641 :   ret = gst_tensor_meta_info_parse_header (meta, map.data);
    1627              : 
    1628        52641 :   gst_memory_unmap (mem, &map);
    1629        52641 :   return ret;
    1630              : }
    1631              : 
    1632              : /**
    1633              :  * @brief Append header to memory.
    1634              :  * @param[in] meta tensor meta structure
    1635              :  * @param[in] mem pointer to GstMemory
    1636              :  * @return Newly allocated GstMemory (Caller should free returned memory using gst_memory_unref())
    1637              :  */
    1638              : GstMemory *
    1639          284 : gst_tensor_meta_info_append_header (GstTensorMetaInfo * meta, GstMemory * mem)
    1640              : {
    1641          284 :   GstMemory *new_mem = NULL;
    1642              :   gsize msize, hsize;
    1643              :   GstMapInfo old_map, new_map;
    1644              : 
    1645          567 :   g_return_val_if_fail (mem != NULL, NULL);
    1646          283 :   g_return_val_if_fail (gst_tensor_meta_info_validate (meta), NULL);
    1647              : 
    1648          282 :   if (!gst_memory_map (mem, &old_map, GST_MAP_READ)) {
    1649            0 :     nns_loge ("Failed to append header, cannot map the old memory.");
    1650            0 :     return NULL;
    1651              :   }
    1652              : 
    1653              :   /* memory size (header + old memory) */
    1654          282 :   hsize = gst_tensor_meta_info_get_header_size (meta);
    1655          282 :   msize = hsize + old_map.size;
    1656              : 
    1657          282 :   new_mem = gst_allocator_alloc (NULL, msize, NULL);
    1658          282 :   if (!gst_memory_map (new_mem, &new_map, GST_MAP_WRITE)) {
    1659            0 :     nns_loge ("Failed to append header, cannot map the new memory.");
    1660            0 :     gst_memory_unmap (mem, &old_map);
    1661            0 :     gst_memory_unref (new_mem);
    1662            0 :     return NULL;
    1663              :   }
    1664              : 
    1665              :   /* set header and copy old data */
    1666          282 :   gst_tensor_meta_info_update_header (meta, new_map.data);
    1667          282 :   memcpy (new_map.data + hsize, old_map.data, old_map.size);
    1668              : 
    1669          282 :   gst_memory_unmap (mem, &old_map);
    1670          282 :   gst_memory_unmap (new_mem, &new_map);
    1671          282 :   return new_mem;
    1672              : }
    1673              : 
    1674              : /**
    1675              :  * @brief Get the nth GstMemory from given @a buffer.
    1676              :  * @param[in] buffer GstBuffer to be parsed.
    1677              :  * @param[in] index Index of GstMemory to be returned.
    1678              :  * @return GstMemory if found, otherwise NULL (Caller should free returned memory using gst_memory_unref()).
    1679              :  */
    1680              : GstMemory *
    1681        63302 : gst_tensor_buffer_get_nth_memory (GstBuffer * buffer, const guint index)
    1682              : {
    1683              :   guint i, num_tensors;
    1684              :   gsize offset;
    1685        63302 :   GstMemory *extra_tensors_memory, *res_mem = NULL;
    1686              :   GstMapInfo extra_tensors_map;
    1687              :   GstTensorExtraInfo *extra_info;
    1688              : 
    1689        63302 :   if (!GST_IS_BUFFER (buffer)) {
    1690            0 :     nns_loge ("Failed to parse GstBuffer (invalid input buffer).");
    1691        63302 :     return NULL;
    1692              :   }
    1693              : 
    1694        63302 :   num_tensors = gst_tensor_buffer_get_count (buffer);
    1695        63302 :   if (index >= num_tensors) {
    1696            0 :     nns_loge ("Invalid index %u, the number of tensors in the buffer is %u.",
    1697              :         index, num_tensors);
    1698            0 :     return NULL;
    1699              :   }
    1700              : 
    1701              :   /* If num_tensors is less than or equal to NNS_TENSOR_MEMORY_MAX, it's trivial. */
    1702        63302 :   if (num_tensors <= NNS_TENSOR_MEMORY_MAX || index < NNS_TENSOR_MEMORY_MAX - 1) {
    1703        62681 :     return gst_buffer_get_memory (buffer, index);
    1704              :   }
    1705              : 
    1706              :   /* If num_tensors is greater than NNS_TENSOR_MEMORY_MAX, we need to parse extra info. */
    1707              :   extra_tensors_memory =
    1708          621 :       gst_buffer_peek_memory (buffer, NNS_TENSOR_MEMORY_MAX - 1);
    1709          621 :   if (!extra_tensors_memory) {
    1710            0 :     nns_loge ("Failed to get %d-th memory", NNS_TENSOR_MEMORY_MAX);
    1711            0 :     return NULL;
    1712              :   }
    1713              : 
    1714          621 :   if (!gst_memory_map (extra_tensors_memory, &extra_tensors_map, GST_MAP_READ)) {
    1715            0 :     nns_loge ("Failed to map %d-th memory", NNS_TENSOR_MEMORY_MAX);
    1716            0 :     return NULL;
    1717              :   }
    1718              : 
    1719              :   /* check header (extra info) of the memory */
    1720          621 :   if (!gst_memory_map_is_extra_tensor (&extra_tensors_map)) {
    1721            0 :     nns_loge ("Invalid extra header");
    1722            0 :     goto done;
    1723              :   }
    1724              : 
    1725              :   /* parse the memory */
    1726          621 :   extra_info = (GstTensorExtraInfo *) extra_tensors_map.data;
    1727          621 :   offset = sizeof (GstTensorExtraInfo);
    1728              : 
    1729              :   /* If index is NNS_TENSOR_MEMORY_MAX - 1 */
    1730          621 :   if (index == NNS_TENSOR_MEMORY_MAX - 1) {
    1731              :     res_mem =
    1732           29 :         gst_memory_share (extra_tensors_memory, offset, extra_info->reserved);
    1733           29 :     goto done;
    1734              :   }
    1735              : 
    1736          592 :   offset += extra_info->reserved;
    1737              : 
    1738        31720 :   for (i = 1; i <= index - NNS_TENSOR_MEMORY_MAX; ++i) {
    1739        31128 :     offset += gst_tensor_info_get_size (&extra_info->infos[i - 1]);
    1740              :   }
    1741              : 
    1742              :   /* wrap it as GstMemory */
    1743              :   res_mem =
    1744          592 :       gst_memory_share (extra_tensors_memory, offset,
    1745          592 :       gst_tensor_info_get_size (&extra_info->infos[index -
    1746              :               NNS_TENSOR_MEMORY_MAX]));
    1747              : 
    1748          621 : done:
    1749          621 :   gst_memory_unmap (extra_tensors_memory, &extra_tensors_map);
    1750          621 :   return res_mem;
    1751              : }
    1752              : 
    1753              : /**
    1754              :  * @brief Append @a memory to given @a buffer.
    1755              :  * @param[in/out] buffer GstBuffer to be appended.
    1756              :  * @param[in] memory GstMemory to append. This function takes ownership of this, even if it returns failure.
    1757              :  * @param[in] info GstTensorInfo of given @a memory.
    1758              :  * @return TRUE if successfully appended, otherwise FALSE.
    1759              :  */
    1760              : gboolean
    1761        61080 : gst_tensor_buffer_append_memory (GstBuffer * buffer, GstMemory * memory,
    1762              :     const GstTensorInfo * info)
    1763              : {
    1764              :   guint num_mems, new_mem_index;
    1765        61080 :   GstMemory *new_memory = NULL, *last_memory = NULL;
    1766              :   gsize offset, new_mem_size, last_mem_size;
    1767              :   GstMapInfo new_memory_map, last_memory_map, incoming_memory_map;
    1768              :   GstTensorExtraInfo *extra_info;
    1769              :   GstTensorMetaInfo meta;
    1770              :   gboolean is_extra, is_static;
    1771        61080 :   gboolean appended = FALSE;
    1772              : 
    1773        61080 :   if (!GST_IS_BUFFER (buffer)) {
    1774            0 :     nns_loge ("Failed to append memory, given buffer is invalid.");
    1775            0 :     goto failed;
    1776              :   }
    1777              : 
    1778        61080 :   if (!memory) {
    1779            0 :     nns_loge ("Failed to append memory, given memory is NULL.");
    1780            0 :     goto failed;
    1781              :   }
    1782              : 
    1783        61080 :   if (gst_tensor_meta_info_parse_memory (&meta, memory)) {
    1784          563 :     is_static = (meta.format == _NNS_TENSOR_FORMAT_STATIC);
    1785              :   } else {
    1786              :     /* Suppose given memory is static tensor. */
    1787        60517 :     is_static = TRUE;
    1788              : 
    1789              :     /* Error case if given tensor-info is invalid. */
    1790        60517 :     if (!gst_tensor_info_validate (info)) {
    1791            0 :       nns_loge ("Failed to get tensor info (invalid input info).");
    1792            0 :       goto failed;
    1793              :     }
    1794              :   }
    1795              : 
    1796        61080 :   num_mems = gst_buffer_n_memory (buffer);
    1797              : 
    1798              :   /* trivial call to gst_buffer_append_memory */
    1799        61080 :   if (num_mems < NNS_TENSOR_MEMORY_MAX) {
    1800        60492 :     gst_buffer_append_memory (buffer, memory);
    1801        61080 :     return TRUE;
    1802              :   }
    1803              : 
    1804              :   /* given buffer has NNS_TENSOR_MEMORY_MAX memory blocks */
    1805          588 :   last_memory = gst_buffer_peek_memory (buffer, num_mems - 1);
    1806          588 :   if (!last_memory) {
    1807            0 :     nns_loge ("Failed to get last memory");
    1808            0 :     goto failed;
    1809              :   }
    1810              : 
    1811          588 :   if (!gst_memory_map (last_memory, &last_memory_map, GST_MAP_READ)) {
    1812            0 :     nns_loge ("Failed to map last memory");
    1813            0 :     last_memory = NULL;
    1814            0 :     goto failed;
    1815              :   }
    1816              : 
    1817          588 :   new_mem_size = last_mem_size = gst_memory_get_sizes (last_memory, NULL, NULL);
    1818              : 
    1819              :   /* if the memory does not have proper header, append it */
    1820          588 :   is_extra = gst_memory_map_is_extra_tensor (&last_memory_map);
    1821          588 :   if (!is_extra) {
    1822           28 :     new_mem_size += sizeof (GstTensorExtraInfo);
    1823              :   }
    1824              : 
    1825          588 :   new_mem_size += gst_memory_get_sizes (memory, NULL, NULL);
    1826              : 
    1827          588 :   new_memory = gst_allocator_alloc (NULL, new_mem_size, NULL);
    1828          588 :   if (!new_memory) {
    1829            0 :     nns_loge ("Failed to allocate memory for extra tensors.");
    1830            0 :     goto failed;
    1831              :   }
    1832              : 
    1833          588 :   if (!gst_memory_map (new_memory, &new_memory_map, GST_MAP_WRITE)) {
    1834            0 :     nns_loge ("Failed to map extra memory");
    1835            0 :     gst_memory_unref (new_memory);
    1836            0 :     new_memory = NULL;
    1837            0 :     goto failed;
    1838              :   }
    1839              : 
    1840          588 :   if (!gst_memory_map (memory, &incoming_memory_map, GST_MAP_READ)) {
    1841            0 :     nns_loge ("Failed to map incoming memory");
    1842            0 :     goto failed;
    1843              :   }
    1844              : 
    1845          588 :   extra_info = (GstTensorExtraInfo *) new_memory_map.data;
    1846              : 
    1847              :   /* if the last_memory does not have proper header, append it */
    1848          588 :   if (!is_extra) {
    1849           28 :     gst_tensor_extra_info_init (extra_info, last_mem_size);
    1850           28 :     offset = sizeof (GstTensorExtraInfo);
    1851              :   } else {
    1852          560 :     offset = 0;
    1853              :   }
    1854              : 
    1855              :   /* copy last_memory into new_memory */
    1856          588 :   memcpy (new_memory_map.data + offset, last_memory_map.data,
    1857              :       last_memory_map.size);
    1858              : 
    1859              :   /* copy incoming_memory into new_memory */
    1860          588 :   new_mem_index = extra_info->num_extra_tensors;
    1861          588 :   extra_info->num_extra_tensors += 1;
    1862              : 
    1863              :   /* Copy tensor info into extra. */
    1864          588 :   if (is_static) {
    1865          588 :     gst_tensor_info_copy (&extra_info->infos[new_mem_index], info);
    1866              : 
    1867              :     /**
    1868              :      * Free the name string, cause it does not freed by gstreamer.
    1869              :      * @todo Make custom gst_allocator later?
    1870              :      */
    1871          588 :     g_clear_pointer (&extra_info->infos[new_mem_index].name, g_free);
    1872              :   } else {
    1873            0 :     gst_tensor_meta_info_convert (&meta, &extra_info->infos[new_mem_index]);
    1874              :   }
    1875              : 
    1876          588 :   memcpy (new_memory_map.data + offset + last_memory_map.size,
    1877          588 :       incoming_memory_map.data, incoming_memory_map.size);
    1878              : 
    1879          588 :   gst_memory_unmap (memory, &incoming_memory_map);
    1880          588 :   gst_memory_unmap (last_memory, &last_memory_map);
    1881          588 :   last_memory = NULL;
    1882              : 
    1883          588 :   gst_buffer_replace_memory (buffer, num_mems - 1, new_memory);
    1884          588 :   appended = TRUE;
    1885              : 
    1886          588 : failed:
    1887          588 :   if (new_memory) {
    1888          588 :     gst_memory_unmap (new_memory, &new_memory_map);
    1889          588 :     if (!appended)
    1890            0 :       gst_memory_unref (new_memory);
    1891              :   }
    1892              : 
    1893          588 :   if (last_memory)
    1894            0 :     gst_memory_unmap (last_memory, &last_memory_map);
    1895              : 
    1896              :   /* Release incoming memory even if failed to append it into buffer. */
    1897          588 :   if (memory)
    1898          588 :     gst_memory_unref (memory);
    1899              : 
    1900          588 :   return appended;
    1901              : }
    1902              : 
    1903              : /**
    1904              :  * @brief Get the number of tensors in the buffer.
    1905              :  */
    1906              : guint
    1907       123901 : gst_tensor_buffer_get_count (GstBuffer * buffer)
    1908              : {
    1909              :   guint num_mems;
    1910              :   GstMemory *mem;
    1911              :   GstMapInfo map;
    1912              :   GstTensorExtraInfo *extra_info;
    1913              : 
    1914       247802 :   g_return_val_if_fail (buffer != NULL, 0);
    1915              : 
    1916       123901 :   num_mems = gst_buffer_n_memory (buffer);
    1917       123901 :   if (num_mems < NNS_TENSOR_MEMORY_MAX) {
    1918       122200 :     return num_mems;
    1919              :   }
    1920              : 
    1921              :   /* num_mems == NNS_TENSOR_MEMORY_MAX */
    1922         1701 :   mem = gst_buffer_peek_memory (buffer, num_mems - 1);
    1923         1701 :   if (!mem) {
    1924            0 :     nns_loge ("Failed to get the last memory.");
    1925            0 :     return 0;
    1926              :   }
    1927              : 
    1928         1701 :   if (!gst_memory_map (mem, &map, GST_MAP_READ)) {
    1929            0 :     nns_loge ("Failed to map the last memory.");
    1930            0 :     return 0;
    1931              :   }
    1932              : 
    1933         1701 :   if (gst_memory_map_is_extra_tensor (&map)) {
    1934         1349 :     extra_info = (GstTensorExtraInfo *) map.data;
    1935         1349 :     num_mems = extra_info->num_extra_tensors + NNS_TENSOR_MEMORY_MAX;
    1936              :   } else {
    1937          352 :     nns_logi ("The last memory does not have extra tensors header. "
    1938              :         "Assuming the number of tensors is %d.", num_mems);
    1939              :   }
    1940              : 
    1941         1701 :   gst_memory_unmap (mem, &map);
    1942              : 
    1943         1701 :   return num_mems;
    1944              : }
    1945              : 
    1946              : /**
    1947              :  * @brief Sets the value of a property based on the specified property value and GParamSpec.
    1948              :  *
    1949              :  * @param prop_value A pointer to the GValue where the property value will be set.
    1950              :  * @param param_spec A pointer to the GParamSpec that describes the property.
    1951              :  * @param property_value A string representing the value to be set for the property.
    1952              :  *
    1953              :  * @note This API is intended to be used by gst_tensor_parse_config_file ()
    1954              :  */
    1955              : static void
    1956           34 : set_property_value (GValue * prop_value, const GParamSpec * param_spec,
    1957              :     const gchar * property_value)
    1958              : {
    1959           34 :   GType value_type = G_PARAM_SPEC_VALUE_TYPE (param_spec);
    1960              : 
    1961           34 :   g_value_init (prop_value, value_type);
    1962              : 
    1963           34 :   if (value_type == G_TYPE_BOOLEAN) {
    1964            0 :     gboolean value = g_ascii_strcasecmp (property_value, "true") == 0;
    1965            0 :     g_value_set_boolean (prop_value, value);
    1966           34 :   } else if (value_type == G_TYPE_INT) {
    1967            0 :     gint value = atoi (property_value);
    1968            0 :     g_value_set_int (prop_value, value);
    1969           34 :   } else if (value_type == G_TYPE_UINT) {
    1970            0 :     guint value = atoi (property_value);
    1971            0 :     g_value_set_uint (prop_value, value);
    1972           34 :   } else if (value_type == G_TYPE_FLOAT) {
    1973            0 :     gfloat value = atof (property_value);
    1974            0 :     g_value_set_float (prop_value, value);
    1975           34 :   } else if (value_type == G_TYPE_DOUBLE) {
    1976            0 :     gdouble value = atof (property_value);
    1977            0 :     g_value_set_double (prop_value, value);
    1978              :   } else {
    1979           34 :     g_value_set_string (prop_value, property_value); /** default is string */
    1980              :   }
    1981           34 : }
    1982              : 
    1983              : /**
    1984              :  * @brief Parses a configuration file and sets the corresponding properties on a GObject.
    1985              :  *
    1986              :  * This function reads the contents of the configuration file located at the given path
    1987              :  * and sets the properties of the specified GObject based on the configuration data.
    1988              :  *
    1989              :  * @param config_path The path to the configuration file.
    1990              :  * @param object      The GObject on which to set the properties.
    1991              :  *
    1992              :  * @note The responsibility of managing the memory of the GObject passed as a parameter
    1993              :  *       lies outside this function.
    1994              :  */
    1995              : void
    1996            9 : gst_tensor_parse_config_file (const gchar * config_path, const GObject * object)
    1997              : {
    1998            9 :   g_autofree gchar *config_data = NULL;
    1999            9 :   g_auto (GStrv) lines = NULL;
    2000            9 :   GStrv line = NULL;
    2001            9 :   GError *error = NULL;
    2002            9 :   GObjectClass *g_object_class = G_OBJECT_GET_CLASS (object);
    2003              : 
    2004            9 :   if (!g_file_get_contents (config_path, &config_data, NULL, &error)) {
    2005            0 :     GST_DEBUG ("Failed to read config file: %s\n", error->message);
    2006            0 :     g_error_free (error);
    2007            0 :     return;
    2008              :   }
    2009              : 
    2010            9 :   lines = g_strsplit (config_data, "\n", -1);
    2011              : 
    2012              :   /** Iterate over each line */
    2013           52 :   for (line = lines; *line; ++line) {
    2014           43 :     g_auto (GStrv) parts = g_strsplit (*line, "=", 2);
    2015              : 
    2016           43 :     if (g_strv_length (parts) == 2) {
    2017           68 :       g_autofree gchar *property_name = g_strstrip (g_strdup (parts[0]));
    2018           68 :       g_autofree gchar *property_value = g_strstrip (g_strdup (parts[1]));
    2019              : 
    2020              :       GParamSpec *pdata =
    2021           34 :           g_object_class_find_property (g_object_class, property_name);
    2022              : 
    2023           34 :       if (pdata != NULL) {
    2024           34 :         GValue prop_value = G_VALUE_INIT;
    2025           34 :         set_property_value (&prop_value, pdata, property_value);
    2026           34 :         g_object_set_property (G_OBJECT (object), pdata->name, &prop_value);
    2027           34 :         g_value_unset (&prop_value);
    2028              :       }
    2029              :     }
    2030              :   }
    2031              : }
        

Generated by: LCOV version 2.0-1