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