Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1-only */
2 : /**
3 : * GStreamer/NNStreamer Tensor-IF
4 : * Copyright (C) 2020 MyungJoo Ham <myungjoo.ham@samsung.com>
5 : */
6 : /**
7 : * @file gsttensor_if.c
8 : * @date 08 April 2020
9 : * @brief GStreamer plugin to control flow based on tensor values
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 : * SECTION:element-tensor_if
17 : *
18 : * A filter that controls its src-pad based on the values (other/tensor(s))
19 : * of its sink-pad.
20 : * For example, you may skip frames if there is no object detected with
21 : * high confidence.
22 : *
23 : * The format of statement with tensor-if is:
24 : * if (Compared_Value OPERATOR Supplied_Value(s))) then THEN else ELSE
25 : * Compared_Value and Supplied_Value are the operands.
26 : * Compared_Value is a value from input tensor(s).
27 : * Supplied_Value is a value from tensor-if properties.
28 : *
29 : * If the given if-condition is simple enough (e.g., if a specific element
30 : * is between a given range in a tensor frame), it can be expressed as:
31 : * <refsect2>
32 : * <title>Example launch line with simple if condition</title>
33 : * gst-launch ... (some tensor stream) !
34 : * tensor_if name=tif
35 : * compared-value=A_VALUE compared-value-option=3:4:2:5,0
36 : * operator=RANGE_INCLUSIVE
37 : * supplied-value=10,100
38 : * then=PASSTHROUGH
39 : * else=TENSORPICK
40 : * else-option=1
41 : * tif.src_0 ! queue ! (tensor(s) stream for TRUE action) ...
42 : * tif.src_1 ! queue ! (tensor(s) stream for FALSE action) ...
43 : * </refsect2>
44 : *
45 : * However, if the if-condition is complex and cannot be expressed with
46 : * tensor-if expressions, you may create a corresponding custom filter
47 : * with tensor-filter, whose output is other/tensors with an additional tensor
48 : * that is "1:1:1:1, uint8", which is 1 (true) or 0 (false) as the
49 : * first tensor of other/tensors and the input tensor/tensors.
50 : * Then, you can create a pipeline as follows:
51 : * <refsect2>
52 : * <title>Example launch line with complex if condition</title>
53 : * gst-launch ... (some tensor stream)
54 : * ! tensor_filter framework=custom name=your_code.so
55 : * ! tensor_if compared-value=A_VALUE
56 : * compared-value-option=0:0:0:0,0 # 1st tensor's [0][0][0][0].
57 : * operator=EQ
58 : * supplied-value=1
59 : * then=PASSTHROUGH # or whatsoever you want
60 : * else=SKIP # or whatsoever you want
61 : * ! tensor_demux name=d
62 : * d.src_0 ! queue ! fakesink # throw away the 1/0 value.
63 : * d.src_1 ! queue ! do whatever you want here...
64 : * ...
65 : * </refsect2>
66 : */
67 : #ifdef HAVE_CONFIG_H
68 : #include <config.h>
69 : #endif
70 :
71 : #include <nnstreamer_log.h>
72 : #include <string.h>
73 :
74 : #include <nnstreamer_subplugin.h>
75 : #include <nnstreamer_util.h>
76 : #include "gsttensor_if.h"
77 :
78 : /**
79 : * @brief Macro for debug mode.
80 : */
81 : #ifndef DBG
82 : #define DBG (!tensor_if->silent)
83 : #endif
84 :
85 : /**
86 : * @brief tensor_if properties
87 : */
88 : enum
89 : {
90 : PROP_0,
91 : PROP_SILENT,
92 : PROP_CV, /**< Compared Value, operand 1 (from input tensor(s)) */
93 : PROP_CV_OPTION, /**< Compared Value Option */
94 : PROP_OP, /**< Operator */
95 : PROP_SV, /**< Supplied Value, operand 2 (from the properties) */
96 : PROP_THEN, /**< Action if it is TRUE */
97 : PROP_THEN_OPTION, /**< Option for TRUE Action */
98 : PROP_ELSE, /**< Action if it is FALSE */
99 : PROP_ELSE_OPTION, /**< Option for FALSE Action */
100 : };
101 :
102 : GST_DEBUG_CATEGORY_STATIC (gst_tensor_if_debug);
103 : #define GST_CAT_DEFAULT gst_tensor_if_debug
104 :
105 : #define CAPS_STRING GST_TENSOR_CAP_DEFAULT "; " GST_TENSORS_CAP_DEFAULT
106 : /**
107 : * @brief The capabilities of the inputs
108 : */
109 : static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
110 : GST_PAD_SINK,
111 : GST_PAD_ALWAYS,
112 : GST_STATIC_CAPS (CAPS_STRING));
113 :
114 : /**
115 : * @brief The capabilities of the outputs
116 : */
117 : static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
118 : GST_PAD_SRC,
119 : GST_PAD_SOMETIMES,
120 : GST_STATIC_CAPS (CAPS_STRING));
121 :
122 : #define gst_tensor_if_parent_class parent_class
123 1167 : G_DEFINE_TYPE (GstTensorIf, gst_tensor_if, GST_TYPE_ELEMENT);
124 :
125 : /* GObject vmethod implementations */
126 : static void gst_tensor_if_set_property (GObject * object, guint prop_id,
127 : const GValue * value, GParamSpec * pspec);
128 : static void gst_tensor_if_get_property (GObject * object, guint prop_id,
129 : GValue * value, GParamSpec * pspec);
130 :
131 : static GstFlowReturn gst_tensor_if_chain (GstPad * pad, GstObject * parent,
132 : GstBuffer * buf);
133 : static gboolean gst_tensor_if_event (GstPad * pad, GstObject * parent,
134 : GstEvent * event);
135 : static void gst_tensor_if_dispose (GObject * object);
136 :
137 : static void gst_tensor_if_install_properties (GObjectClass * gobject_class);
138 :
139 : #define GST_TYPE_TENSOR_IF_CV (gst_tensor_if_cv_get_type ())
140 : /**
141 : * @brief A private function to register GEnumValue array for the 'compared-value' property
142 : * to a GType and return it
143 : */
144 : static GType
145 4 : gst_tensor_if_cv_get_type (void)
146 : {
147 : static GType mode_type = 0;
148 :
149 4 : if (mode_type == 0) {
150 : static GEnumValue mode_types[] = {
151 : {TIFCV_A_VALUE, "A_VALUE", "Decide based on a single scalar value"},
152 : {TIFCV_TENSOR_AVERAGE_VALUE, "TENSOR_AVERAGE_VALUE",
153 : "Decide based on a average value of a specific tensor"},
154 : {TIFCV_CUSTOM, "CUSTOM", "Decide based on a user defined callback"},
155 : {0, NULL, NULL},
156 : };
157 4 : mode_type = g_enum_register_static ("tensor_if_compared_value", mode_types);
158 : }
159 :
160 4 : return mode_type;
161 : }
162 :
163 : #define GST_TYPE_TENSOR_IF_OP (gst_tensor_if_op_get_type ())
164 : /**
165 : * @brief A private function to register GEnumValue array for the 'operator' property
166 : * to a GType and return it
167 : */
168 : static GType
169 4 : gst_tensor_if_op_get_type (void)
170 : {
171 : static GType mode_type = 0;
172 :
173 4 : if (mode_type == 0) {
174 : static GEnumValue mode_types[] = {
175 : {TIFOP_EQ, "EQ", "eqaual"},
176 : {TIFOP_NE, "NE", "not_eqaual"},
177 : {TIFOP_GT, "GT", "greater_than"},
178 : {TIFOP_GE, "GE", "greater_or_equal"},
179 : {TIFOP_LT, "LT", "less_than"},
180 : {TIFOP_LE, "LE", "less_or_equal"},
181 : {TIFOP_RANGE_INCLUSIVE, "RANGE_INCLUSIVE", "range inclusive"},
182 : {TIFOP_RANGE_EXCLUSIVE, "RANGE_EXCLUSIVE", "range exclusive"},
183 : {TIFOP_NOT_IN_RANGE_INCLUSIVE, "NOT_IN_RANGE_INCLUSIVE",
184 : "not in range inclusive"},
185 : {TIFOP_NOT_IN_RANGE_EXCLUSIVE, "NOT_IN_RANGE_EXCLUSIVE",
186 : "not in range exclusive"},
187 : {0, NULL, NULL},
188 : };
189 4 : mode_type = g_enum_register_static ("tensor_if_operator", mode_types);
190 : }
191 :
192 4 : return mode_type;
193 : }
194 :
195 : #define GST_TYPE_TENSOR_IF_ACT (gst_tensor_if_act_get_type ())
196 : /**
197 : * @brief A private function to register GEnumValue array for the 'then' and 'else' properties
198 : * to a GType and return it
199 : */
200 : static GType
201 8 : gst_tensor_if_act_get_type (void)
202 : {
203 : static GType mode_type = 0;
204 :
205 8 : if (mode_type == 0) {
206 : static GEnumValue mode_types[] = {
207 : {TIFB_PASSTHROUGH, "PASSTHROUGH", "passthrough"},
208 : {TIFB_SKIP, "SKIP", "skip"},
209 : {TIFB_TENSORPICK, "TENSORPICK", "tensorpick"},
210 : {0, NULL, NULL},
211 : };
212 4 : mode_type = g_enum_register_static ("tensor_if_behavior", mode_types);
213 : }
214 :
215 8 : return mode_type;
216 : }
217 :
218 : /**
219 : * @brief initialize the tensor_if's class (GST Standard)
220 : */
221 : static void
222 4 : gst_tensor_if_class_init (GstTensorIfClass * klass)
223 : {
224 : GObjectClass *gobject_class;
225 : GstElementClass *gstelement_class;
226 :
227 4 : GST_DEBUG_CATEGORY_INIT (gst_tensor_if_debug, "tensor_if", 0,
228 : "Tensor if to control streams based on tensor(s) values");
229 :
230 4 : gobject_class = (GObjectClass *) klass;
231 4 : gstelement_class = (GstElementClass *) klass;
232 :
233 4 : parent_class = g_type_class_peek_parent (klass);
234 :
235 4 : gobject_class->set_property = gst_tensor_if_set_property;
236 4 : gobject_class->get_property = gst_tensor_if_get_property;
237 4 : gobject_class->dispose = gst_tensor_if_dispose;
238 :
239 4 : gst_tensor_if_install_properties (gobject_class);
240 :
241 4 : gst_element_class_set_details_simple (gstelement_class,
242 : "TensorIf",
243 : "Filter/Tensor",
244 : "Controls streams based on the tensor(s) values",
245 : "MyungJoo Ham <myungjoo.ham@samsung.com>");
246 :
247 4 : gst_element_class_add_pad_template (gstelement_class,
248 : gst_static_pad_template_get (&src_factory));
249 4 : gst_element_class_add_pad_template (gstelement_class,
250 : gst_static_pad_template_get (&sink_factory));
251 4 : }
252 :
253 : /**
254 : * @brief initialize the new element (GST Standard)
255 : * instantiate pads and add them to element
256 : * set pad callback functions
257 : * initialize instance structure
258 : */
259 : static void
260 17 : gst_tensor_if_init (GstTensorIf * tensor_if)
261 : {
262 17 : tensor_if->silent = TRUE;
263 17 : gst_tensors_config_init (&tensor_if->in_config);
264 17 : gst_tensors_config_init (&tensor_if->out_config[0]);
265 17 : gst_tensors_config_init (&tensor_if->out_config[1]);
266 :
267 17 : tensor_if->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
268 17 : gst_element_add_pad (GST_ELEMENT_CAST (tensor_if), tensor_if->sinkpad);
269 17 : gst_pad_set_chain_function (tensor_if->sinkpad,
270 : GST_DEBUG_FUNCPTR (gst_tensor_if_chain));
271 17 : gst_pad_set_event_function (tensor_if->sinkpad,
272 : GST_DEBUG_FUNCPTR (gst_tensor_if_event));
273 :
274 17 : tensor_if->num_srcpads = 0;
275 17 : tensor_if->srcpads = NULL;
276 17 : tensor_if->cv_option = NULL;
277 17 : tensor_if->then_option = NULL;
278 17 : tensor_if->else_option = NULL;
279 17 : memset (tensor_if->sv, 0, sizeof (tensor_if_sv_s) * 2);
280 17 : memset (&tensor_if->custom, 0, sizeof (custom_cb_s));
281 17 : tensor_if->custom_configured = FALSE;
282 :
283 17 : g_mutex_init (&tensor_if->lock);
284 17 : }
285 :
286 : /**
287 : * @brief function to remove srcpad list
288 : */
289 : static void
290 13 : gst_tensor_if_remove_src_pads (GstTensorIf * tensor_if)
291 : {
292 28 : while (tensor_if->srcpads != NULL) {
293 15 : GstTensorPad *tensor_pad = tensor_if->srcpads->data;
294 15 : gst_element_remove_pad (GST_ELEMENT (tensor_if), tensor_pad->pad);
295 15 : g_free (tensor_pad);
296 15 : tensor_if->srcpads =
297 15 : g_slist_delete_link (tensor_if->srcpads, tensor_if->srcpads);
298 : }
299 13 : tensor_if->srcpads = NULL;
300 13 : tensor_if->num_srcpads = 0;
301 13 : }
302 :
303 : /**
304 : * @brief dispose function for tensor if (gst element vmethod)
305 : */
306 : static void
307 13 : gst_tensor_if_dispose (GObject * object)
308 : {
309 13 : GstTensorIf *tensor_if = GST_TENSOR_IF (object);
310 13 : g_mutex_clear (&tensor_if->lock);
311 :
312 13 : gst_tensor_if_remove_src_pads (tensor_if);
313 13 : g_list_free (tensor_if->cv_option);
314 13 : g_list_free (tensor_if->then_option);
315 13 : g_list_free (tensor_if->else_option);
316 13 : g_free (tensor_if->custom.name);
317 13 : tensor_if->custom.func = NULL;
318 13 : tensor_if->custom.data = NULL;
319 13 : tensor_if->custom_configured = FALSE;
320 :
321 13 : G_OBJECT_CLASS (parent_class)->dispose (object);
322 13 : }
323 :
324 : /**
325 : * @brief Convert GValue to GList according to delimiters
326 : */
327 : static void
328 36 : gst_tensor_if_set_property_glist (const GValue * value, GList ** prop_list,
329 : const gchar * delimiters)
330 : {
331 : gint64 val;
332 36 : const gchar *param = g_value_get_string (value);
333 36 : gchar **strv = g_strsplit_set (param, delimiters, -1);
334 36 : gint i, num = g_strv_length (strv);
335 :
336 36 : g_list_free (*prop_list);
337 36 : *prop_list = NULL;
338 :
339 94 : for (i = 0; i < num; i++) {
340 58 : val = g_ascii_strtoll (strv[i], NULL, 10);
341 58 : if (errno == ERANGE) {
342 0 : ml_loge ("Overflow occurred during converting %s to a gint64 value",
343 : strv[i]);
344 : }
345 58 : *prop_list = g_list_append (*prop_list, GINT_TO_POINTER (val));
346 : }
347 36 : g_strfreev (strv);
348 36 : }
349 :
350 : /**
351 : * @brief Convert GValue to GList for cv option
352 : */
353 : static void
354 18 : gst_tensor_if_set_property_cv_option (const GValue * value, GList ** prop_list)
355 : {
356 : gint64 val;
357 : gint length, i;
358 18 : const gchar *param = g_value_get_string (value);
359 18 : gchar **strv = g_strsplit_set (param, ",", -1);
360 18 : GValue tmp = G_VALUE_INIT;
361 :
362 18 : length = g_strv_length (strv);
363 :
364 18 : if (length > 2) {
365 0 : ml_loge
366 : ("Invalid compared value option. It should be in the form of 'IDX_DIM0: ... :INDEX_DIM_LAST,nth-tensor'(A_VALUE) or 'nth-tensor' (TENSOR_AVERAGE_VALUE)");
367 0 : g_strfreev (strv);
368 0 : return;
369 : }
370 :
371 18 : g_value_init (&tmp, G_TYPE_STRING);
372 18 : g_value_set_string (&tmp, strv[0]);
373 :
374 18 : gst_tensor_if_set_property_glist (&tmp, prop_list, ":");
375 :
376 : /* A_VALUE */
377 18 : if (length == 2) {
378 8 : length = g_list_length (*prop_list);
379 :
380 : /* append zero value for undefined dimensions */
381 109 : for (i = length; i < NNS_TENSOR_RANK_LIMIT; i++) {
382 101 : *prop_list = g_list_append (*prop_list, GINT_TO_POINTER (0));
383 : }
384 :
385 8 : val = g_ascii_strtoll (strv[1], NULL, 10);
386 8 : if (errno == ERANGE) {
387 0 : ml_loge ("Overflow occurred during converting %s to a gint64 value",
388 : strv[1]);
389 : }
390 8 : *prop_list = g_list_append (*prop_list, GINT_TO_POINTER (val));
391 : }
392 18 : g_strfreev (strv);
393 18 : g_value_reset (&tmp);
394 : }
395 :
396 : /**
397 : * @brief Convert GValue to GList according to delimiters
398 : */
399 : static void
400 19 : gst_tensor_if_set_property_supplied_value (const GValue * value,
401 : tensor_if_sv_s * sv, const gchar * delimiters)
402 : {
403 : gint i;
404 19 : gboolean is_float = FALSE;
405 19 : const gchar *param = g_value_get_string (value);
406 19 : gchar **strv = g_strsplit_set (param, delimiters, -1);
407 19 : gint num = g_strv_length (strv);
408 :
409 19 : if (!param) {
410 0 : ml_loge ("Invalid supplied value. The value is NULL.");
411 0 : return;
412 : }
413 :
414 19 : if (strchr (param, '.') || strchr (param, 'E') || strchr (param, 'e')) {
415 2 : is_float = TRUE;
416 : }
417 :
418 19 : sv->num = num;
419 40 : for (i = 0; i < num; i++) {
420 21 : if (is_float) {
421 2 : sv->type = _NNS_FLOAT64;
422 2 : sv->data[i]._double = g_ascii_strtod (strv[i], NULL);
423 : } else {
424 19 : sv->type = _NNS_INT64;
425 19 : sv->data[i]._int64_t = g_ascii_strtoll (strv[i], NULL, 10);
426 : }
427 : }
428 19 : g_strfreev (strv);
429 : }
430 :
431 : /**
432 : * @brief Set custom compared value property
433 : */
434 : static void
435 36 : gst_tensor_if_configure_custom_prop (GstTensorIf * self)
436 : {
437 36 : if (!self->custom.name)
438 16 : return;
439 :
440 20 : if (self->cv == TIFCV_CUSTOM) {
441 2 : const custom_cb_s *ptr = get_subplugin (NNS_IF_CUSTOM, self->custom.name);
442 2 : if (!ptr) {
443 1 : nns_logw ("Failed to find custom subplugin of the tensor_if");
444 1 : return;
445 : }
446 1 : self->custom_configured = TRUE;
447 1 : self->custom.func = (*ptr).func;
448 1 : self->custom.data = (*ptr).data;
449 : }
450 : }
451 :
452 : /**
453 : * @brief Setter for tensor_if properties.
454 : */
455 : static void
456 130 : gst_tensor_if_set_property (GObject * object, guint prop_id,
457 : const GValue * value, GParamSpec * pspec)
458 : {
459 130 : GstTensorIf *self = GST_TENSOR_IF (object);
460 :
461 130 : switch (prop_id) {
462 18 : case PROP_CV:
463 18 : self->cv = g_value_get_enum (value);
464 18 : gst_tensor_if_configure_custom_prop (self);
465 18 : break;
466 18 : case PROP_CV_OPTION:
467 18 : g_free (self->custom.name);
468 18 : self->custom.name = g_value_dup_string (value);
469 18 : gst_tensor_if_configure_custom_prop (self);
470 18 : gst_tensor_if_set_property_cv_option (value, &self->cv_option);
471 18 : break;
472 18 : case PROP_OP:
473 18 : self->op = g_value_get_enum (value);
474 18 : break;
475 19 : case PROP_SV:
476 19 : gst_tensor_if_set_property_supplied_value (value, self->sv, ",");
477 19 : break;
478 18 : case PROP_THEN:
479 18 : self->act_then = g_value_get_enum (value);
480 18 : break;
481 9 : case PROP_THEN_OPTION:
482 9 : gst_tensor_if_set_property_glist (value, &self->then_option, ",");
483 9 : break;
484 18 : case PROP_ELSE:
485 18 : self->act_else = g_value_get_enum (value);
486 18 : break;
487 9 : case PROP_ELSE_OPTION:
488 9 : gst_tensor_if_set_property_glist (value, &self->else_option, ",");
489 9 : break;
490 3 : case PROP_SILENT:
491 3 : self->silent = g_value_get_boolean (value);
492 3 : break;
493 0 : default:
494 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
495 0 : break;
496 : }
497 130 : }
498 :
499 : /**
500 : * @brief Convert GList to GValue
501 : */
502 : static void
503 4 : gst_tensor_if_property_to_string (GValue * value, GList * prop_list,
504 : guint prop_id)
505 : {
506 : GList *list;
507 : gchar *p;
508 : GPtrArray *arr;
509 : gchar **strings;
510 : guint len;
511 :
512 4 : if (prop_list == NULL) {
513 0 : g_value_set_string (value, "");
514 0 : return;
515 : }
516 :
517 4 : arr = g_ptr_array_new ();
518 24 : for (list = prop_list; list != NULL; list = list->next) {
519 20 : g_ptr_array_add (arr, g_strdup_printf ("%i", GPOINTER_TO_INT (list->data)));
520 : }
521 4 : g_ptr_array_add (arr, NULL);
522 :
523 4 : len = arr->len;
524 :
525 5 : if (prop_id == PROP_CV_OPTION && len % (NNS_TENSOR_RANK_LIMIT + 2) == 0) {
526 : gchar *dim;
527 1 : gchar *tensor = (gchar *) g_ptr_array_index (arr, len - 2);
528 1 : g_ptr_array_remove_index (arr, len - 2);
529 1 : strings = (gchar **) g_ptr_array_free (arr, FALSE);
530 1 : dim = g_strjoinv (":", strings);
531 1 : p = g_strjoin (",", dim, tensor, NULL);
532 1 : g_free (dim);
533 1 : g_free (tensor);
534 : } else {
535 3 : strings = (gchar **) g_ptr_array_free (arr, FALSE);
536 3 : p = g_strjoinv (",", strings);
537 : }
538 :
539 4 : g_strfreev (strings);
540 4 : g_value_take_string (value, p);
541 : }
542 :
543 : /**
544 : * @brief Convert GValue to supplied value according to delimiters
545 : */
546 : static void
547 3 : gst_tensor_if_get_property_supplied_value (GValue * value, tensor_if_sv_s * sv)
548 : {
549 : guint i;
550 : gchar *p;
551 : GPtrArray *arr;
552 : gchar **strings;
553 :
554 3 : if (sv == NULL || sv->num == 0) {
555 0 : g_value_set_string (value, "");
556 0 : return;
557 : }
558 :
559 3 : arr = g_ptr_array_new ();
560 7 : for (i = 0; i < sv->num; i++) {
561 4 : if (sv->type == _NNS_FLOAT64) {
562 1 : g_ptr_array_add (arr, g_strdup_printf ("%lf", sv->data[i]._double));
563 : } else {
564 3 : g_ptr_array_add (arr, g_strdup_printf ("%ld",
565 3 : (long int) sv->data[i]._int64_t));
566 : }
567 : }
568 3 : g_ptr_array_add (arr, NULL);
569 3 : strings = (gchar **) g_ptr_array_free (arr, FALSE);
570 3 : p = g_strjoinv (",", strings);
571 3 : g_strfreev (strings);
572 3 : g_value_take_string (value, p);
573 : }
574 :
575 : /**
576 : * @brief Getter for tensor_if properties.
577 : */
578 : static void
579 19 : gst_tensor_if_get_property (GObject * object, guint prop_id,
580 : GValue * value, GParamSpec * pspec)
581 : {
582 19 : GstTensorIf *self = GST_TENSOR_IF (object);
583 :
584 19 : switch (prop_id) {
585 3 : case PROP_CV:
586 3 : g_value_set_enum (value, self->cv);
587 3 : break;
588 4 : case PROP_CV_OPTION:
589 4 : if (self->cv == TIFCV_CUSTOM) {
590 2 : g_value_set_string (value, self->custom.name ? self->custom.name : "");
591 : } else {
592 2 : gst_tensor_if_property_to_string (value, self->cv_option, prop_id);
593 : }
594 4 : break;
595 2 : case PROP_OP:
596 2 : g_value_set_enum (value, self->op);
597 2 : break;
598 3 : case PROP_SV:
599 3 : gst_tensor_if_get_property_supplied_value (value, self->sv);
600 3 : break;
601 2 : case PROP_THEN:
602 2 : g_value_set_enum (value, self->act_then);
603 2 : break;
604 1 : case PROP_THEN_OPTION:
605 1 : gst_tensor_if_property_to_string (value, self->then_option, prop_id);
606 1 : break;
607 2 : case PROP_ELSE:
608 2 : g_value_set_enum (value, self->act_else);
609 2 : break;
610 1 : case PROP_ELSE_OPTION:
611 1 : gst_tensor_if_property_to_string (value, self->else_option, prop_id);
612 1 : break;
613 1 : case PROP_SILENT:
614 1 : g_value_set_boolean (value, self->silent);
615 1 : break;
616 0 : default:
617 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
618 0 : break;
619 : }
620 19 : }
621 :
622 : /**
623 : * @brief Installs all the properties for tensor_if
624 : * @param[in] gobject_class Glib object class whose properties will be set
625 : */
626 : static void
627 4 : gst_tensor_if_install_properties (GObjectClass * gobject_class)
628 : {
629 4 : g_object_class_install_property (gobject_class, PROP_SILENT,
630 : g_param_spec_boolean ("silent", "Silent", "Produce verbose output",
631 : FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
632 :
633 4 : g_object_class_install_property (gobject_class, PROP_CV,
634 : g_param_spec_enum ("compared-value", "CV",
635 : "Compared value from input tensor(s)", GST_TYPE_TENSOR_IF_CV,
636 : TIFCV_A_VALUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
637 :
638 4 : g_object_class_install_property (gobject_class, PROP_CV_OPTION,
639 : g_param_spec_string ("compared-value-option", "CV_OPTION",
640 : "Specify an element of the nth tensor or pick tensor ", "",
641 : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
642 :
643 4 : g_object_class_install_property (gobject_class, PROP_SV,
644 : g_param_spec_string ("supplied-value", "SV",
645 : " Supplied Value by user ", "",
646 : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
647 :
648 4 : g_object_class_install_property (gobject_class, PROP_OP,
649 : g_param_spec_enum ("operator", "OP", "Comparison Operator",
650 : GST_TYPE_TENSOR_IF_OP, TIFOP_EQ,
651 : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
652 :
653 4 : g_object_class_install_property (gobject_class, PROP_THEN,
654 : g_param_spec_enum ("then", "THEN", "Action if it is TRUE",
655 : GST_TYPE_TENSOR_IF_ACT, TIFB_PASSTHROUGH,
656 : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
657 :
658 4 : g_object_class_install_property (gobject_class, PROP_THEN_OPTION,
659 : g_param_spec_string ("then-option", "THEN_OPTION",
660 : "Pick tensor ", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
661 :
662 4 : g_object_class_install_property (gobject_class, PROP_ELSE,
663 : g_param_spec_enum ("else", "ELSE", "Action if it is FALSE",
664 : GST_TYPE_TENSOR_IF_ACT, TIFB_SKIP,
665 : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
666 :
667 4 : g_object_class_install_property (gobject_class, PROP_ELSE_OPTION,
668 : g_param_spec_string ("else-option", "ELSE_OPTION",
669 : "Pick tensor ", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
670 4 : }
671 :
672 : /**
673 : * @brief event function for sink (gst element vmethod)
674 : */
675 : static gboolean
676 97 : gst_tensor_if_event (GstPad * pad, GstObject * parent, GstEvent * event)
677 : {
678 : GstTensorIf *tensor_if;
679 97 : tensor_if = GST_TENSOR_IF (parent);
680 :
681 97 : switch (GST_EVENT_TYPE (event)) {
682 25 : case GST_EVENT_CAPS:
683 : {
684 : GstCaps *caps;
685 25 : gst_event_parse_caps (event, &caps);
686 25 : if (!gst_tensors_config_from_cap (&tensor_if->in_config, caps)) {
687 0 : GST_ERROR_OBJECT (tensor_if, "Failed to parse caps.\n");
688 0 : gst_event_unref (event);
689 0 : return FALSE;
690 : }
691 25 : break;
692 : }
693 72 : default:
694 72 : break;
695 : }
696 :
697 97 : return gst_pad_event_default (pad, parent, event);
698 : }
699 :
700 : /**
701 : * @brief Checking if the source pad is created and if not, create TensorPad
702 : * @param tensor_if TensorIf Object
703 : * @param config Tensors Config Data
704 : * @param nth source ordering
705 : * @return TensorPad if pad is already created, then return created pad.
706 : * If not return new pad after creation.
707 : */
708 : static GstTensorPad *
709 33 : gst_tensor_if_get_tensor_pad (GstTensorIf * tensor_if,
710 : GstTensorsConfig * config, gboolean * created, guint nth)
711 : {
712 : GSList *walk;
713 : GstPad *pad;
714 : GstTensorPad *tensorpad;
715 : gchar *name;
716 33 : GstCaps *caps = NULL;
717 :
718 33 : walk = tensor_if->srcpads;
719 41 : while (walk) {
720 26 : GstTensorPad *pad = (GstTensorPad *) walk->data;
721 26 : if (nth == pad->nth) {
722 18 : if (created) {
723 18 : *created = FALSE;
724 : }
725 18 : return pad;
726 : }
727 8 : walk = walk->next;
728 : }
729 :
730 15 : tensorpad = g_new0 (GstTensorPad, 1);
731 15 : g_assert (tensorpad != NULL);
732 15 : GST_DEBUG_OBJECT (tensor_if, "creating pad: %d(%dth)",
733 : tensor_if->num_srcpads, nth);
734 :
735 15 : name = g_strdup_printf ("src_%d", nth);
736 15 : pad = gst_pad_new_from_static_template (&src_factory, name);
737 15 : g_free (name);
738 :
739 15 : tensorpad->pad = pad;
740 15 : tensorpad->nth = nth;
741 15 : tensorpad->last_ret = GST_FLOW_OK;
742 15 : tensorpad->last_ts = GST_CLOCK_TIME_NONE;
743 :
744 15 : tensor_if->srcpads = g_slist_append (tensor_if->srcpads, tensorpad);
745 15 : tensor_if->num_srcpads++;
746 :
747 15 : gst_pad_use_fixed_caps (pad);
748 15 : gst_pad_set_active (pad, TRUE);
749 15 : gst_element_add_pad (GST_ELEMENT_CAST (tensor_if), pad);
750 :
751 15 : caps = gst_tensor_pad_caps_from_config (pad, config);
752 :
753 15 : silent_debug_caps (tensor_if, caps, "out caps");
754 15 : gst_pad_set_caps (pad, caps);
755 :
756 15 : gst_caps_unref (caps);
757 :
758 15 : if (created) {
759 15 : *created = TRUE;
760 : }
761 :
762 15 : return tensorpad;
763 : }
764 :
765 : /**
766 : * @brief Check the status among sources in if
767 : * @param tensor_if TensorIf Object
768 : * @param TensorPad Tensorpad
769 : * @param ret return status of current pad
770 : * @return return status after check sources
771 : */
772 : static GstFlowReturn
773 33 : gst_tensor_if_combine_flows (GstTensorIf * tensor_if,
774 : GstTensorPad * pad, GstFlowReturn ret)
775 : {
776 : GSList *walk;
777 33 : pad->last_ret = ret;
778 :
779 33 : if (ret != GST_FLOW_NOT_LINKED)
780 33 : goto done;
781 :
782 0 : for (walk = tensor_if->srcpads; walk; walk = g_slist_next (walk)) {
783 0 : GstTensorPad *opad = (GstTensorPad *) walk->data;
784 0 : ret = opad->last_ret;
785 0 : if (ret != GST_FLOW_NOT_LINKED)
786 0 : goto done;
787 : }
788 0 : done:
789 33 : return ret;
790 : }
791 :
792 : /**
793 : * @brief Macro for operator function.
794 : */
795 : #define operator_func(cv,t,op,sv1,sv2,ret) do { \
796 : switch (op) { \
797 : case TIFOP_EQ: ret = (cv._##t == sv1._##t) ? TRUE : FALSE; break; \
798 : case TIFOP_NE: ret = (cv._##t != sv1._##t) ? TRUE : FALSE; break; \
799 : case TIFOP_GT: ret = (cv._##t > sv1._##t) ? TRUE : FALSE; break; \
800 : case TIFOP_GE: ret = (cv._##t >= sv1._##t) ? TRUE : FALSE; break; \
801 : case TIFOP_LT: ret = (cv._##t < sv1._##t) ? TRUE : FALSE; break; \
802 : case TIFOP_LE: ret = (cv._##t <= sv1._##t) ? TRUE : FALSE; break; \
803 : case TIFOP_RANGE_INCLUSIVE: \
804 : ret = (sv1._##t <= cv._##t && cv._##t <= sv2._##t) ? TRUE : FALSE; break; \
805 : case TIFOP_RANGE_EXCLUSIVE: \
806 : ret = (sv1._##t < cv._##t && cv._##t < sv2._##t) ? TRUE : FALSE; break; \
807 : case TIFOP_NOT_IN_RANGE_INCLUSIVE: \
808 : ret = (cv._##t < sv1._##t && sv2._##t < cv._##t) ? TRUE : FALSE; break; \
809 : case TIFOP_NOT_IN_RANGE_EXCLUSIVE: \
810 : ret = (cv._##t <= sv1._##t && sv2._##t <= cv._##t) ? TRUE : FALSE; break; \
811 : default: break; \
812 : } \
813 : } while (0)
814 :
815 : /**
816 : * @brief Get comparison value
817 : */
818 : static gboolean
819 34 : gst_tensor_if_get_comparison_result (GstTensorIf * tensor_if,
820 : tensor_data_s * cv, gboolean * result)
821 : {
822 34 : gboolean ret = FALSE;
823 : tensor_data_s svtc_1, svtc_2;
824 :
825 34 : svtc_1.type = tensor_if->sv->type;
826 34 : svtc_1.data = tensor_if->sv->data[0];
827 34 : gst_tensor_data_typecast (&svtc_1, cv->type);
828 :
829 34 : svtc_2.type = tensor_if->sv->type;
830 34 : svtc_2.data = tensor_if->sv->data[1];
831 34 : gst_tensor_data_typecast (&svtc_2, cv->type);
832 :
833 34 : switch (cv->type) {
834 5 : case _NNS_INT32:
835 5 : operator_func (cv->data, int32_t, tensor_if->op, svtc_1.data, svtc_2.data,
836 : ret);
837 5 : break;
838 1 : case _NNS_UINT32:
839 1 : operator_func (cv->data, uint32_t, tensor_if->op, svtc_1.data,
840 : svtc_2.data, ret);
841 1 : break;
842 1 : case _NNS_INT16:
843 1 : operator_func (cv->data, int16_t, tensor_if->op, svtc_1.data, svtc_2.data,
844 : ret);
845 1 : break;
846 1 : case _NNS_UINT16:
847 1 : operator_func (cv->data, uint16_t, tensor_if->op, svtc_1.data,
848 : svtc_2.data, ret);
849 1 : break;
850 1 : case _NNS_INT8:
851 1 : operator_func (cv->data, int8_t, tensor_if->op, svtc_1.data, svtc_2.data,
852 : ret);
853 1 : break;
854 21 : case _NNS_UINT8:
855 21 : operator_func (cv->data, uint8_t, tensor_if->op, svtc_1.data, svtc_2.data,
856 : ret);
857 21 : break;
858 1 : case _NNS_FLOAT64:
859 1 : operator_func (cv->data, double, tensor_if->op, svtc_1.data, svtc_2.data,
860 : ret);
861 1 : break;
862 1 : case _NNS_FLOAT32:
863 1 : operator_func (cv->data, float, tensor_if->op, svtc_1.data, svtc_2.data,
864 : ret);
865 1 : break;
866 1 : case _NNS_INT64:
867 1 : operator_func (cv->data, int64_t, tensor_if->op, svtc_1.data, svtc_2.data,
868 : ret);
869 1 : break;
870 1 : case _NNS_UINT64:
871 1 : operator_func (cv->data, uint64_t, tensor_if->op, svtc_1.data,
872 : svtc_2.data, ret);
873 1 : break;
874 0 : default:
875 0 : GST_ERROR_OBJECT (tensor_if, "Unknown tensor type %d", cv->type);
876 34 : return FALSE;
877 : }
878 34 : *result = ret;
879 34 : return TRUE;
880 : }
881 :
882 : /**
883 : * @brief Calculate average value of the nth tensor
884 : */
885 : static gboolean
886 18 : gst_tensor_if_get_tensor_average (GstTensorIf * tensor_if,
887 : GstBuffer * buf, tensor_data_s * cv, guint nth)
888 : {
889 : GstTensorInfo *_info;
890 : GstMemory *in_mem;
891 : GstMapInfo in_info;
892 18 : gdouble *avg = NULL;
893 : tensor_type type;
894 :
895 18 : in_mem = gst_tensor_buffer_get_nth_memory (buf, nth);
896 18 : if (!gst_memory_map (in_mem, &in_info, GST_MAP_READ)) {
897 0 : GST_WARNING_OBJECT (tensor_if, "Failed to map the input buffer.");
898 0 : gst_memory_unref (in_mem);
899 18 : return FALSE;
900 : }
901 :
902 18 : _info = gst_tensors_info_get_nth_info (&tensor_if->in_config.info, nth);
903 18 : type = _info->type;
904 :
905 18 : gst_tensor_data_raw_average (in_info.data, in_info.size, type, &avg);
906 :
907 18 : gst_memory_unmap (in_mem, &in_info);
908 18 : gst_memory_unref (in_mem);
909 :
910 18 : gst_tensor_data_set (cv, _NNS_FLOAT64, avg);
911 18 : gst_tensor_data_typecast (cv, type);
912 :
913 18 : g_free (avg);
914 18 : return TRUE;
915 : }
916 :
917 : /**
918 : * @brief Calculate compared value
919 : */
920 : static gboolean
921 38 : gst_tensor_if_calculate_cv (GstTensorIf * tensor_if, GstBuffer * buf,
922 : tensor_data_s * cv)
923 : {
924 : GstTensorInfo *_info;
925 :
926 38 : switch (tensor_if->cv) {
927 18 : case TIFCV_A_VALUE:
928 : {
929 : GstMemory *in_mem;
930 : GstMapInfo in_info;
931 : GList *list;
932 18 : uint32_t idx = 0, nth, i, offset = 1;
933 : tensor_dim target;
934 : const uint32_t *in_dim;
935 : tensor_type in_type;
936 :
937 18 : if (g_list_length (tensor_if->cv_option) != NNS_TENSOR_RANK_LIMIT + 1) {
938 1 : GST_ERROR_OBJECT (tensor_if,
939 : "Please specify a proper 'compared-value-option' property, e.g., 0:1:2:3,0");
940 2 : return FALSE;
941 : }
942 289 : for (list = tensor_if->cv_option; list->next != NULL; list = list->next) {
943 272 : target[idx++] = GPOINTER_TO_INT (list->data);
944 : }
945 :
946 17 : nth = GPOINTER_TO_INT (list->data);
947 17 : if (gst_tensor_buffer_get_count (buf) <= nth) {
948 1 : GST_ERROR_OBJECT (tensor_if, "Index should be lower than buffer size");
949 1 : return FALSE;
950 : }
951 :
952 16 : _info = gst_tensors_info_get_nth_info (&tensor_if->in_config.info, nth);
953 16 : in_type = _info->type;
954 16 : in_dim = _info->dimension;
955 :
956 16 : in_mem = gst_tensor_buffer_get_nth_memory (buf, nth);
957 16 : if (!gst_memory_map (in_mem, &in_info, GST_MAP_READ)) {
958 0 : GST_WARNING_OBJECT (tensor_if, "Failed to map the input buffer.");
959 0 : gst_memory_unref (in_mem);
960 0 : return FALSE;
961 : }
962 :
963 : /* Find data index for mem access */
964 16 : idx = target[0];
965 256 : for (i = 1; i < NNS_TENSOR_RANK_LIMIT; i++) {
966 240 : offset *= in_dim[i - 1];
967 240 : idx += (target[i]) * offset;
968 : }
969 :
970 16 : idx *= gst_tensor_get_element_size (in_type);
971 :
972 16 : gst_tensor_data_set (cv, in_type, in_info.data + idx);
973 16 : gst_memory_unmap (in_mem, &in_info);
974 16 : gst_memory_unref (in_mem);
975 16 : break;
976 : }
977 20 : case TIFCV_TENSOR_AVERAGE_VALUE:
978 : {
979 : uint32_t nth;
980 20 : if (g_list_length (tensor_if->cv_option) != 1) {
981 1 : GST_ERROR_OBJECT (tensor_if,
982 : "Please specify a proper 'compared-value-option' property, For TENSOR_AVERAGE_VALUE, specify only one tensor. Tensors is not supported.");
983 1 : return FALSE;
984 : }
985 19 : nth = GPOINTER_TO_INT (tensor_if->cv_option->data);
986 19 : if (gst_tensor_buffer_get_count (buf) <= nth) {
987 1 : GST_ERROR_OBJECT (tensor_if, "Index should be lower than buffer size");
988 1 : return FALSE;
989 : }
990 18 : return gst_tensor_if_get_tensor_average (tensor_if, buf, cv, nth);
991 : }
992 0 : default:
993 0 : GST_ERROR_OBJECT (tensor_if,
994 : "Compared value is not supported yet or not defined");
995 0 : return FALSE;
996 : }
997 16 : return TRUE;
998 : }
999 :
1000 : /**
1001 : * @brief Registers a callback for tensor_if custom condition
1002 : * @return 0 if success. -ERRNO if error.
1003 : */
1004 : int
1005 5 : nnstreamer_if_custom_register (const gchar * name, tensor_if_custom func,
1006 : void *data)
1007 : {
1008 : custom_cb_s *ptr;
1009 :
1010 5 : g_return_val_if_fail (name && strlen (name), -EINVAL);
1011 4 : g_return_val_if_fail (func, -EINVAL);
1012 :
1013 3 : if (!(ptr = g_try_new0 (custom_cb_s, 1)))
1014 0 : return -ENOMEM;
1015 :
1016 3 : ptr->func = func;
1017 3 : ptr->data = data;
1018 :
1019 3 : if (register_subplugin (NNS_IF_CUSTOM, name, ptr))
1020 2 : return 0;
1021 :
1022 1 : g_free (ptr);
1023 1 : return -EINVAL;
1024 : }
1025 :
1026 : /**
1027 : * @brief Unregisters a callback for tensor_if custom condition
1028 : * @return 0 if success. -ERRNO if error.
1029 : */
1030 : int
1031 4 : nnstreamer_if_custom_unregister (const gchar * name)
1032 : {
1033 : custom_cb_s *ptr;
1034 :
1035 4 : ptr = (custom_cb_s *) get_subplugin (NNS_IF_CUSTOM, name);
1036 4 : if (!unregister_subplugin (NNS_IF_CUSTOM, name)) {
1037 2 : ml_loge ("Failed to unregister custom callback %s.", name);
1038 2 : return -EINVAL;
1039 : }
1040 2 : g_free (ptr);
1041 :
1042 2 : return 0;
1043 : }
1044 :
1045 : /**
1046 : * @brief Determining whether a given condition is true or false
1047 : * @param tensor_if TensorIf Object
1048 : * @param buf gstbuffer from sink pad
1049 : * @return return TRUE if no error
1050 : */
1051 : static gboolean
1052 40 : gst_tensor_if_check_condition (GstTensorIf * tensor_if, GstBuffer * buf,
1053 : gboolean * result)
1054 : {
1055 40 : gboolean ret = FALSE;
1056 :
1057 40 : if (tensor_if->cv == TIFCV_CUSTOM) {
1058 : GstMemory *in_mem[NNS_TENSOR_SIZE_LIMIT];
1059 : GstMapInfo in_info[NNS_TENSOR_SIZE_LIMIT];
1060 : GstTensorMemory in_tensors[NNS_TENSOR_SIZE_LIMIT];
1061 : guint i, j;
1062 :
1063 2 : if (!tensor_if->custom_configured) {
1064 0 : nns_loge ("custom condition of the tensor_if is not configured.");
1065 0 : return FALSE;
1066 : }
1067 :
1068 6 : for (i = 0; i < tensor_if->in_config.info.num_tensors; i++) {
1069 4 : in_mem[i] = gst_tensor_buffer_get_nth_memory (buf, i);
1070 4 : if (!gst_memory_map (in_mem[i], &in_info[i], GST_MAP_READ)) {
1071 0 : for (j = 0; j < i; j++) {
1072 0 : gst_memory_unmap (in_mem[j], &in_info[j]);
1073 0 : gst_memory_unref (in_mem[j]);
1074 : }
1075 0 : gst_memory_unref (in_mem[i]);
1076 :
1077 0 : GST_WARNING_OBJECT (tensor_if, "Cannot map input buffer(%d)\n", i);
1078 0 : return FALSE;
1079 : }
1080 4 : in_tensors[i].data = in_info[i].data;
1081 4 : in_tensors[i].size = in_info[i].size;
1082 : }
1083 :
1084 2 : ret = tensor_if->custom.func (&tensor_if->in_config.info, in_tensors,
1085 : tensor_if->custom.data, result);
1086 :
1087 6 : for (i = 0; i < tensor_if->in_config.info.num_tensors; i++) {
1088 4 : gst_memory_unmap (in_mem[i], &in_info[i]);
1089 4 : gst_memory_unref (in_mem[i]);
1090 : }
1091 : } else {
1092 38 : tensor_data_s cv = {.type = _NNS_END,.data._uint8_t = 0 };
1093 38 : if (!gst_tensor_if_calculate_cv (tensor_if, buf, &cv)) {
1094 4 : GST_ERROR_OBJECT (tensor_if, " failed to calculate compared value");
1095 4 : return FALSE;
1096 : }
1097 34 : ret = gst_tensor_if_get_comparison_result (tensor_if, &cv, result);
1098 : }
1099 :
1100 36 : return ret;
1101 : }
1102 :
1103 : /**
1104 : * @brief chain function for sink (gst element vmethod)
1105 : */
1106 : static GstFlowReturn
1107 40 : gst_tensor_if_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
1108 : {
1109 : guint num_tensors, i;
1110 40 : GstFlowReturn res = GST_FLOW_OK;
1111 40 : GstTensorIf *tensor_if = GST_TENSOR_IF (parent);
1112 40 : gboolean condition_result = FALSE;
1113 40 : tensor_if_behavior curr_act = TIFB_PASSTHROUGH;
1114 40 : tensor_if_srcpads which_srcpad = TIFSP_THEN_PAD;
1115 40 : GList *curr_act_option = NULL;
1116 : GstTensorsConfig *config;
1117 : GstTensorPad *srcpad;
1118 40 : GstBuffer *outbuf = NULL;
1119 40 : GstMemory *mem = NULL;
1120 : gboolean created;
1121 : GstClockTime ts;
1122 : UNUSED (pad);
1123 :
1124 40 : num_tensors = tensor_if->in_config.info.num_tensors;
1125 40 : GST_DEBUG_OBJECT (tensor_if, " Number of Tensors: %u", num_tensors);
1126 : /* supposed n memory blocks in buffer */
1127 40 : g_assert (gst_tensor_buffer_get_count (buf) == num_tensors);
1128 :
1129 40 : if (!gst_tensor_if_check_condition (tensor_if, buf, &condition_result)) {
1130 4 : GST_ERROR_OBJECT (tensor_if, " Failed to check condition");
1131 40 : return GST_FLOW_ERROR;
1132 : }
1133 :
1134 36 : if (condition_result) {
1135 25 : curr_act = tensor_if->act_then;
1136 25 : curr_act_option = tensor_if->then_option;
1137 25 : which_srcpad = TIFSP_THEN_PAD;
1138 : } else {
1139 11 : curr_act = tensor_if->act_else;
1140 11 : curr_act_option = tensor_if->else_option;
1141 11 : which_srcpad = TIFSP_ELSE_PAD;
1142 : }
1143 :
1144 36 : config = &tensor_if->out_config[which_srcpad];
1145 :
1146 36 : if (config->info.num_tensors == 0) {
1147 20 : config->rate_n = tensor_if->in_config.rate_n;
1148 20 : config->rate_d = tensor_if->in_config.rate_d;
1149 : }
1150 :
1151 36 : switch (curr_act) {
1152 13 : case TIFB_PASSTHROUGH:
1153 13 : if (config->info.num_tensors == 0) {
1154 4 : gst_tensors_info_copy (&config->info, &tensor_if->in_config.info);
1155 : }
1156 13 : outbuf = gst_buffer_ref (buf);
1157 :
1158 13 : break;
1159 20 : case TIFB_TENSORPICK:
1160 : {
1161 : GList *list;
1162 20 : gint info_idx = 0;
1163 : GstTensorInfo *dest, *src;
1164 :
1165 20 : outbuf = gst_buffer_new ();
1166 40 : for (list = curr_act_option; list != NULL; list = list->next) {
1167 20 : i = GPOINTER_TO_INT (list->data);
1168 20 : src = gst_tensors_info_get_nth_info (&tensor_if->in_config.info, i);
1169 :
1170 20 : if (config->info.num_tensors == 0) {
1171 13 : dest = gst_tensors_info_get_nth_info (&config->info, info_idx);
1172 13 : info_idx++;
1173 :
1174 13 : gst_tensor_info_copy (dest, src);
1175 : }
1176 :
1177 20 : mem = gst_tensor_buffer_get_nth_memory (buf, i);
1178 20 : gst_tensor_buffer_append_memory (outbuf, mem, src);
1179 : }
1180 20 : config->info.num_tensors = info_idx;
1181 20 : break;
1182 : }
1183 3 : case TIFB_SKIP:
1184 3 : goto done;
1185 0 : default:
1186 0 : GST_DEBUG_OBJECT (tensor_if, " Not defined behavior");
1187 0 : break;
1188 : }
1189 :
1190 : srcpad =
1191 33 : gst_tensor_if_get_tensor_pad (tensor_if, config, &created, which_srcpad);
1192 :
1193 33 : if (created) {
1194 : GstSegment segment;
1195 15 : gst_segment_init (&segment, GST_FORMAT_TIME);
1196 15 : gst_pad_push_event (srcpad->pad, gst_event_new_segment (&segment));
1197 : }
1198 :
1199 33 : outbuf = gst_buffer_make_writable (outbuf);
1200 :
1201 : /* metadata from incoming buffer */
1202 33 : gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
1203 :
1204 33 : ts = GST_BUFFER_TIMESTAMP (buf);
1205 33 : if (srcpad->last_ts == GST_CLOCK_TIME_NONE || srcpad->last_ts != ts) {
1206 24 : srcpad->last_ts = ts;
1207 : } else {
1208 9 : GST_DEBUG_OBJECT (tensor_if, "invalid timestamp %" GST_TIME_FORMAT,
1209 : GST_TIME_ARGS (ts));
1210 : }
1211 :
1212 33 : res = gst_pad_push (srcpad->pad, outbuf);
1213 33 : res = gst_tensor_if_combine_flows (tensor_if, srcpad, res);
1214 :
1215 36 : done:
1216 36 : gst_buffer_unref (buf);
1217 36 : return res;
1218 : }
|