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 1197 : 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 14 : gst_tensor_if_cv_get_type (void)
146 : {
147 : static GType mode_type = 0;
148 :
149 14 : 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 14 : mode_type = g_enum_register_static ("tensor_if_compared_value", mode_types);
158 : }
159 :
160 14 : 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 14 : gst_tensor_if_op_get_type (void)
170 : {
171 : static GType mode_type = 0;
172 :
173 14 : 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 14 : mode_type = g_enum_register_static ("tensor_if_operator", mode_types);
190 : }
191 :
192 14 : 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 28 : gst_tensor_if_act_get_type (void)
202 : {
203 : static GType mode_type = 0;
204 :
205 28 : 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 14 : mode_type = g_enum_register_static ("tensor_if_behavior", mode_types);
213 : }
214 :
215 28 : return mode_type;
216 : }
217 :
218 : /**
219 : * @brief initialize the tensor_if's class (GST Standard)
220 : */
221 : static void
222 14 : gst_tensor_if_class_init (GstTensorIfClass * klass)
223 : {
224 : GObjectClass *gobject_class;
225 : GstElementClass *gstelement_class;
226 :
227 14 : GST_DEBUG_CATEGORY_INIT (gst_tensor_if_debug, "tensor_if", 0,
228 : "Tensor if to control streams based on tensor(s) values");
229 :
230 14 : gobject_class = (GObjectClass *) klass;
231 14 : gstelement_class = (GstElementClass *) klass;
232 :
233 14 : parent_class = g_type_class_peek_parent (klass);
234 :
235 14 : gobject_class->set_property = gst_tensor_if_set_property;
236 14 : gobject_class->get_property = gst_tensor_if_get_property;
237 14 : gobject_class->dispose = gst_tensor_if_dispose;
238 :
239 14 : gst_tensor_if_install_properties (gobject_class);
240 :
241 14 : 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 14 : gst_element_class_add_pad_template (gstelement_class,
248 : gst_static_pad_template_get (&src_factory));
249 14 : gst_element_class_add_pad_template (gstelement_class,
250 : gst_static_pad_template_get (&sink_factory));
251 14 : }
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 14 : gst_tensor_if_install_properties (GObjectClass * gobject_class)
628 : {
629 14 : 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 14 : 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 14 : 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 14 : 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 14 : 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 14 : 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 14 : 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 14 : 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 14 : 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 14 : }
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 :
686 25 : gst_event_parse_caps (event, &caps);
687 25 : if (!gst_tensors_config_from_caps (&tensor_if->in_config, caps, TRUE)) {
688 0 : GST_ERROR_OBJECT (tensor_if, "Failed to parse caps.\n");
689 0 : gst_event_unref (event);
690 0 : return FALSE;
691 : }
692 25 : break;
693 : }
694 72 : default:
695 72 : break;
696 : }
697 :
698 97 : return gst_pad_event_default (pad, parent, event);
699 : }
700 :
701 : /**
702 : * @brief Checking if the source pad is created and if not, create TensorPad
703 : * @param tensor_if TensorIf Object
704 : * @param config Tensors Config Data
705 : * @param nth source ordering
706 : * @return TensorPad if pad is already created, then return created pad.
707 : * If not return new pad after creation.
708 : */
709 : static GstTensorPad *
710 33 : gst_tensor_if_get_tensor_pad (GstTensorIf * tensor_if,
711 : GstTensorsConfig * config, gboolean * created, guint nth)
712 : {
713 : GSList *walk;
714 : GstPad *pad;
715 : GstTensorPad *tensorpad;
716 : gchar *name;
717 33 : GstCaps *caps = NULL;
718 :
719 33 : walk = tensor_if->srcpads;
720 41 : while (walk) {
721 26 : GstTensorPad *pad = (GstTensorPad *) walk->data;
722 26 : if (nth == pad->nth) {
723 18 : if (created) {
724 18 : *created = FALSE;
725 : }
726 18 : return pad;
727 : }
728 8 : walk = walk->next;
729 : }
730 :
731 15 : tensorpad = g_new0 (GstTensorPad, 1);
732 15 : g_assert (tensorpad != NULL);
733 15 : GST_DEBUG_OBJECT (tensor_if, "creating pad: %d(%dth)",
734 : tensor_if->num_srcpads, nth);
735 :
736 15 : name = g_strdup_printf ("src_%d", nth);
737 15 : pad = gst_pad_new_from_static_template (&src_factory, name);
738 15 : g_free (name);
739 :
740 15 : tensorpad->pad = pad;
741 15 : tensorpad->nth = nth;
742 15 : tensorpad->last_ret = GST_FLOW_OK;
743 15 : tensorpad->last_ts = GST_CLOCK_TIME_NONE;
744 :
745 15 : tensor_if->srcpads = g_slist_append (tensor_if->srcpads, tensorpad);
746 15 : tensor_if->num_srcpads++;
747 :
748 15 : gst_pad_use_fixed_caps (pad);
749 15 : gst_pad_set_active (pad, TRUE);
750 15 : gst_element_add_pad (GST_ELEMENT_CAST (tensor_if), pad);
751 :
752 15 : caps = gst_tensor_pad_caps_from_config (pad, config);
753 :
754 15 : silent_debug_caps (tensor_if, caps, "out caps");
755 15 : gst_pad_set_caps (pad, caps);
756 :
757 15 : gst_caps_unref (caps);
758 :
759 15 : if (created) {
760 15 : *created = TRUE;
761 : }
762 :
763 15 : return tensorpad;
764 : }
765 :
766 : /**
767 : * @brief Check the status among sources in if
768 : * @param tensor_if TensorIf Object
769 : * @param TensorPad Tensorpad
770 : * @param ret return status of current pad
771 : * @return return status after check sources
772 : */
773 : static GstFlowReturn
774 33 : gst_tensor_if_combine_flows (GstTensorIf * tensor_if,
775 : GstTensorPad * pad, GstFlowReturn ret)
776 : {
777 : GSList *walk;
778 33 : pad->last_ret = ret;
779 :
780 33 : if (ret != GST_FLOW_NOT_LINKED)
781 33 : goto done;
782 :
783 0 : for (walk = tensor_if->srcpads; walk; walk = g_slist_next (walk)) {
784 0 : GstTensorPad *opad = (GstTensorPad *) walk->data;
785 0 : ret = opad->last_ret;
786 0 : if (ret != GST_FLOW_NOT_LINKED)
787 0 : goto done;
788 : }
789 0 : done:
790 33 : return ret;
791 : }
792 :
793 : /**
794 : * @brief Macro for operator function.
795 : */
796 : #define operator_func(cv,t,op,sv1,sv2,ret) do { \
797 : switch (op) { \
798 : case TIFOP_EQ: ret = (cv._##t == sv1._##t) ? TRUE : FALSE; break; \
799 : case TIFOP_NE: ret = (cv._##t != sv1._##t) ? TRUE : FALSE; break; \
800 : case TIFOP_GT: ret = (cv._##t > sv1._##t) ? TRUE : FALSE; break; \
801 : case TIFOP_GE: ret = (cv._##t >= sv1._##t) ? TRUE : FALSE; break; \
802 : case TIFOP_LT: ret = (cv._##t < sv1._##t) ? TRUE : FALSE; break; \
803 : case TIFOP_LE: ret = (cv._##t <= sv1._##t) ? TRUE : FALSE; break; \
804 : case TIFOP_RANGE_INCLUSIVE: \
805 : ret = (sv1._##t <= cv._##t && cv._##t <= sv2._##t) ? TRUE : FALSE; break; \
806 : case TIFOP_RANGE_EXCLUSIVE: \
807 : ret = (sv1._##t < cv._##t && cv._##t < sv2._##t) ? TRUE : FALSE; break; \
808 : case TIFOP_NOT_IN_RANGE_INCLUSIVE: \
809 : ret = (cv._##t < sv1._##t && sv2._##t < cv._##t) ? TRUE : FALSE; break; \
810 : case TIFOP_NOT_IN_RANGE_EXCLUSIVE: \
811 : ret = (cv._##t <= sv1._##t && sv2._##t <= cv._##t) ? TRUE : FALSE; break; \
812 : default: break; \
813 : } \
814 : } while (0)
815 :
816 : /**
817 : * @brief Get comparison value
818 : */
819 : static gboolean
820 34 : gst_tensor_if_get_comparison_result (GstTensorIf * tensor_if,
821 : tensor_data_s * cv, gboolean * result)
822 : {
823 34 : gboolean ret = FALSE;
824 : tensor_data_s svtc_1, svtc_2;
825 :
826 34 : svtc_1.type = tensor_if->sv->type;
827 34 : svtc_1.data = tensor_if->sv->data[0];
828 34 : gst_tensor_data_typecast (&svtc_1, cv->type);
829 :
830 34 : svtc_2.type = tensor_if->sv->type;
831 34 : svtc_2.data = tensor_if->sv->data[1];
832 34 : gst_tensor_data_typecast (&svtc_2, cv->type);
833 :
834 34 : switch (cv->type) {
835 5 : case _NNS_INT32:
836 5 : operator_func (cv->data, int32_t, tensor_if->op, svtc_1.data, svtc_2.data,
837 : ret);
838 5 : break;
839 1 : case _NNS_UINT32:
840 1 : operator_func (cv->data, uint32_t, tensor_if->op, svtc_1.data,
841 : svtc_2.data, ret);
842 1 : break;
843 1 : case _NNS_INT16:
844 1 : operator_func (cv->data, int16_t, tensor_if->op, svtc_1.data, svtc_2.data,
845 : ret);
846 1 : break;
847 1 : case _NNS_UINT16:
848 1 : operator_func (cv->data, uint16_t, tensor_if->op, svtc_1.data,
849 : svtc_2.data, ret);
850 1 : break;
851 1 : case _NNS_INT8:
852 1 : operator_func (cv->data, int8_t, tensor_if->op, svtc_1.data, svtc_2.data,
853 : ret);
854 1 : break;
855 21 : case _NNS_UINT8:
856 21 : operator_func (cv->data, uint8_t, tensor_if->op, svtc_1.data, svtc_2.data,
857 : ret);
858 21 : break;
859 1 : case _NNS_FLOAT64:
860 1 : operator_func (cv->data, double, tensor_if->op, svtc_1.data, svtc_2.data,
861 : ret);
862 1 : break;
863 1 : case _NNS_FLOAT32:
864 1 : operator_func (cv->data, float, tensor_if->op, svtc_1.data, svtc_2.data,
865 : ret);
866 1 : break;
867 1 : case _NNS_INT64:
868 1 : operator_func (cv->data, int64_t, tensor_if->op, svtc_1.data, svtc_2.data,
869 : ret);
870 1 : break;
871 1 : case _NNS_UINT64:
872 1 : operator_func (cv->data, uint64_t, tensor_if->op, svtc_1.data,
873 : svtc_2.data, ret);
874 1 : break;
875 0 : default:
876 0 : GST_ERROR_OBJECT (tensor_if, "Unknown tensor type %d", cv->type);
877 34 : return FALSE;
878 : }
879 34 : *result = ret;
880 34 : return TRUE;
881 : }
882 :
883 : /**
884 : * @brief Calculate average value of the nth tensor
885 : */
886 : static gboolean
887 18 : gst_tensor_if_get_tensor_average (GstTensorIf * tensor_if,
888 : GstBuffer * buf, tensor_data_s * cv, guint nth)
889 : {
890 : GstTensorInfo *_info;
891 : GstMemory *in_mem;
892 : GstMapInfo in_info;
893 18 : gdouble *avg = NULL;
894 : tensor_type type;
895 :
896 18 : in_mem = gst_tensor_buffer_get_nth_memory (buf, nth);
897 18 : if (!gst_memory_map (in_mem, &in_info, GST_MAP_READ)) {
898 0 : GST_WARNING_OBJECT (tensor_if, "Failed to map the input buffer.");
899 0 : gst_memory_unref (in_mem);
900 18 : return FALSE;
901 : }
902 :
903 18 : _info = gst_tensors_info_get_nth_info (&tensor_if->in_config.info, nth);
904 18 : type = _info->type;
905 :
906 18 : gst_tensor_data_raw_average (in_info.data, in_info.size, type, &avg);
907 :
908 18 : gst_memory_unmap (in_mem, &in_info);
909 18 : gst_memory_unref (in_mem);
910 :
911 18 : gst_tensor_data_set (cv, _NNS_FLOAT64, avg);
912 18 : gst_tensor_data_typecast (cv, type);
913 :
914 18 : g_free (avg);
915 18 : return TRUE;
916 : }
917 :
918 : /**
919 : * @brief Calculate compared value
920 : */
921 : static gboolean
922 38 : gst_tensor_if_calculate_cv (GstTensorIf * tensor_if, GstBuffer * buf,
923 : tensor_data_s * cv)
924 : {
925 : GstTensorInfo *_info;
926 :
927 38 : switch (tensor_if->cv) {
928 18 : case TIFCV_A_VALUE:
929 : {
930 : GstMemory *in_mem;
931 : GstMapInfo in_info;
932 : GList *list;
933 18 : uint32_t idx = 0, nth, i, offset = 1;
934 : tensor_dim target;
935 : const uint32_t *in_dim;
936 : tensor_type in_type;
937 :
938 18 : if (g_list_length (tensor_if->cv_option) != NNS_TENSOR_RANK_LIMIT + 1) {
939 1 : GST_ERROR_OBJECT (tensor_if,
940 : "Please specify a proper 'compared-value-option' property, e.g., 0:1:2:3,0");
941 2 : return FALSE;
942 : }
943 289 : for (list = tensor_if->cv_option; list->next != NULL; list = list->next) {
944 272 : target[idx++] = GPOINTER_TO_INT (list->data);
945 : }
946 :
947 17 : nth = GPOINTER_TO_INT (list->data);
948 17 : if (gst_tensor_buffer_get_count (buf) <= nth) {
949 1 : GST_ERROR_OBJECT (tensor_if, "Index should be lower than buffer size");
950 1 : return FALSE;
951 : }
952 :
953 16 : _info = gst_tensors_info_get_nth_info (&tensor_if->in_config.info, nth);
954 16 : in_type = _info->type;
955 16 : in_dim = _info->dimension;
956 :
957 16 : in_mem = gst_tensor_buffer_get_nth_memory (buf, nth);
958 16 : if (!gst_memory_map (in_mem, &in_info, GST_MAP_READ)) {
959 0 : GST_WARNING_OBJECT (tensor_if, "Failed to map the input buffer.");
960 0 : gst_memory_unref (in_mem);
961 0 : return FALSE;
962 : }
963 :
964 : /* Find data index for mem access */
965 16 : idx = target[0];
966 256 : for (i = 1; i < NNS_TENSOR_RANK_LIMIT; i++) {
967 240 : offset *= in_dim[i - 1];
968 240 : idx += (target[i]) * offset;
969 : }
970 :
971 16 : idx *= gst_tensor_get_element_size (in_type);
972 :
973 16 : gst_tensor_data_set (cv, in_type, in_info.data + idx);
974 16 : gst_memory_unmap (in_mem, &in_info);
975 16 : gst_memory_unref (in_mem);
976 16 : break;
977 : }
978 20 : case TIFCV_TENSOR_AVERAGE_VALUE:
979 : {
980 : uint32_t nth;
981 20 : if (g_list_length (tensor_if->cv_option) != 1) {
982 1 : GST_ERROR_OBJECT (tensor_if,
983 : "Please specify a proper 'compared-value-option' property, For TENSOR_AVERAGE_VALUE, specify only one tensor. Tensors is not supported.");
984 1 : return FALSE;
985 : }
986 19 : nth = GPOINTER_TO_INT (tensor_if->cv_option->data);
987 19 : if (gst_tensor_buffer_get_count (buf) <= nth) {
988 1 : GST_ERROR_OBJECT (tensor_if, "Index should be lower than buffer size");
989 1 : return FALSE;
990 : }
991 18 : return gst_tensor_if_get_tensor_average (tensor_if, buf, cv, nth);
992 : }
993 0 : default:
994 0 : GST_ERROR_OBJECT (tensor_if,
995 : "Compared value is not supported yet or not defined");
996 0 : return FALSE;
997 : }
998 16 : return TRUE;
999 : }
1000 :
1001 : /**
1002 : * @brief Registers a callback for tensor_if custom condition
1003 : * @return 0 if success. -ERRNO if error.
1004 : */
1005 : int
1006 5 : nnstreamer_if_custom_register (const gchar * name, tensor_if_custom func,
1007 : void *data)
1008 : {
1009 : custom_cb_s *ptr;
1010 :
1011 5 : g_return_val_if_fail (name && strlen (name), -EINVAL);
1012 4 : g_return_val_if_fail (func, -EINVAL);
1013 :
1014 3 : if (!(ptr = g_try_new0 (custom_cb_s, 1)))
1015 0 : return -ENOMEM;
1016 :
1017 3 : ptr->func = func;
1018 3 : ptr->data = data;
1019 :
1020 3 : if (register_subplugin (NNS_IF_CUSTOM, name, ptr))
1021 2 : return 0;
1022 :
1023 1 : g_free (ptr);
1024 1 : return -EINVAL;
1025 : }
1026 :
1027 : /**
1028 : * @brief Unregisters a callback for tensor_if custom condition
1029 : * @return 0 if success. -ERRNO if error.
1030 : */
1031 : int
1032 4 : nnstreamer_if_custom_unregister (const gchar * name)
1033 : {
1034 : custom_cb_s *ptr;
1035 :
1036 4 : ptr = (custom_cb_s *) get_subplugin (NNS_IF_CUSTOM, name);
1037 4 : if (!unregister_subplugin (NNS_IF_CUSTOM, name)) {
1038 2 : ml_loge ("Failed to unregister custom callback %s.", name);
1039 2 : return -EINVAL;
1040 : }
1041 2 : g_free (ptr);
1042 :
1043 2 : return 0;
1044 : }
1045 :
1046 : /**
1047 : * @brief Determining whether a given condition is true or false
1048 : * @param tensor_if TensorIf Object
1049 : * @param buf gstbuffer from sink pad
1050 : * @return return TRUE if no error
1051 : */
1052 : static gboolean
1053 40 : gst_tensor_if_check_condition (GstTensorIf * tensor_if, GstBuffer * buf,
1054 : gboolean * result)
1055 : {
1056 40 : gboolean ret = FALSE;
1057 :
1058 40 : if (tensor_if->cv == TIFCV_CUSTOM) {
1059 : GstMemory *in_mem[NNS_TENSOR_SIZE_LIMIT];
1060 : GstMapInfo in_info[NNS_TENSOR_SIZE_LIMIT];
1061 : GstTensorMemory in_tensors[NNS_TENSOR_SIZE_LIMIT];
1062 : guint i, j;
1063 :
1064 2 : if (!tensor_if->custom_configured) {
1065 0 : nns_loge ("custom condition of the tensor_if is not configured.");
1066 0 : return FALSE;
1067 : }
1068 :
1069 6 : for (i = 0; i < tensor_if->in_config.info.num_tensors; i++) {
1070 4 : in_mem[i] = gst_tensor_buffer_get_nth_memory (buf, i);
1071 4 : if (!gst_memory_map (in_mem[i], &in_info[i], GST_MAP_READ)) {
1072 0 : for (j = 0; j < i; j++) {
1073 0 : gst_memory_unmap (in_mem[j], &in_info[j]);
1074 0 : gst_memory_unref (in_mem[j]);
1075 : }
1076 0 : gst_memory_unref (in_mem[i]);
1077 :
1078 0 : GST_WARNING_OBJECT (tensor_if, "Cannot map input buffer(%d)\n", i);
1079 0 : return FALSE;
1080 : }
1081 4 : in_tensors[i].data = in_info[i].data;
1082 4 : in_tensors[i].size = in_info[i].size;
1083 : }
1084 :
1085 2 : ret = tensor_if->custom.func (&tensor_if->in_config.info, in_tensors,
1086 : tensor_if->custom.data, result);
1087 :
1088 6 : for (i = 0; i < tensor_if->in_config.info.num_tensors; i++) {
1089 4 : gst_memory_unmap (in_mem[i], &in_info[i]);
1090 4 : gst_memory_unref (in_mem[i]);
1091 : }
1092 : } else {
1093 38 : tensor_data_s cv = {.type = _NNS_END,.data._uint8_t = 0 };
1094 38 : if (!gst_tensor_if_calculate_cv (tensor_if, buf, &cv)) {
1095 4 : GST_ERROR_OBJECT (tensor_if, " failed to calculate compared value");
1096 4 : return FALSE;
1097 : }
1098 34 : ret = gst_tensor_if_get_comparison_result (tensor_if, &cv, result);
1099 : }
1100 :
1101 36 : return ret;
1102 : }
1103 :
1104 : /**
1105 : * @brief chain function for sink (gst element vmethod)
1106 : */
1107 : static GstFlowReturn
1108 40 : gst_tensor_if_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
1109 : {
1110 : guint num_tensors, i;
1111 40 : GstFlowReturn res = GST_FLOW_OK;
1112 40 : GstTensorIf *tensor_if = GST_TENSOR_IF (parent);
1113 40 : gboolean condition_result = FALSE;
1114 40 : tensor_if_behavior curr_act = TIFB_PASSTHROUGH;
1115 40 : tensor_if_srcpads which_srcpad = TIFSP_THEN_PAD;
1116 40 : GList *curr_act_option = NULL;
1117 : GstTensorsConfig *config;
1118 : GstTensorPad *srcpad;
1119 40 : GstBuffer *outbuf = NULL;
1120 40 : GstMemory *mem = NULL;
1121 : gboolean created;
1122 : GstClockTime ts;
1123 : UNUSED (pad);
1124 :
1125 40 : num_tensors = tensor_if->in_config.info.num_tensors;
1126 40 : GST_DEBUG_OBJECT (tensor_if, " Number of Tensors: %u", num_tensors);
1127 : /* supposed n memory blocks in buffer */
1128 40 : g_assert (gst_tensor_buffer_get_count (buf) == num_tensors);
1129 :
1130 40 : if (!gst_tensor_if_check_condition (tensor_if, buf, &condition_result)) {
1131 4 : GST_ERROR_OBJECT (tensor_if, " Failed to check condition");
1132 40 : return GST_FLOW_ERROR;
1133 : }
1134 :
1135 36 : if (condition_result) {
1136 25 : curr_act = tensor_if->act_then;
1137 25 : curr_act_option = tensor_if->then_option;
1138 25 : which_srcpad = TIFSP_THEN_PAD;
1139 : } else {
1140 11 : curr_act = tensor_if->act_else;
1141 11 : curr_act_option = tensor_if->else_option;
1142 11 : which_srcpad = TIFSP_ELSE_PAD;
1143 : }
1144 :
1145 36 : config = &tensor_if->out_config[which_srcpad];
1146 :
1147 36 : if (config->info.num_tensors == 0) {
1148 20 : config->rate_n = tensor_if->in_config.rate_n;
1149 20 : config->rate_d = tensor_if->in_config.rate_d;
1150 : }
1151 :
1152 36 : switch (curr_act) {
1153 13 : case TIFB_PASSTHROUGH:
1154 13 : if (config->info.num_tensors == 0) {
1155 4 : gst_tensors_info_copy (&config->info, &tensor_if->in_config.info);
1156 : }
1157 13 : outbuf = gst_buffer_ref (buf);
1158 :
1159 13 : break;
1160 20 : case TIFB_TENSORPICK:
1161 : {
1162 : GList *list;
1163 20 : gint info_idx = 0;
1164 : GstTensorInfo *dest, *src;
1165 :
1166 20 : outbuf = gst_buffer_new ();
1167 40 : for (list = curr_act_option; list != NULL; list = list->next) {
1168 20 : i = GPOINTER_TO_INT (list->data);
1169 20 : src = gst_tensors_info_get_nth_info (&tensor_if->in_config.info, i);
1170 :
1171 20 : if (config->info.num_tensors == 0) {
1172 13 : dest = gst_tensors_info_get_nth_info (&config->info, info_idx);
1173 13 : info_idx++;
1174 :
1175 13 : gst_tensor_info_copy (dest, src);
1176 : }
1177 :
1178 20 : mem = gst_tensor_buffer_get_nth_memory (buf, i);
1179 20 : gst_tensor_buffer_append_memory (outbuf, mem, src);
1180 : }
1181 20 : config->info.num_tensors = info_idx;
1182 20 : break;
1183 : }
1184 3 : case TIFB_SKIP:
1185 3 : goto done;
1186 0 : default:
1187 0 : GST_DEBUG_OBJECT (tensor_if, " Not defined behavior");
1188 0 : break;
1189 : }
1190 :
1191 : srcpad =
1192 33 : gst_tensor_if_get_tensor_pad (tensor_if, config, &created, which_srcpad);
1193 :
1194 33 : if (created) {
1195 : GstSegment segment;
1196 15 : gst_segment_init (&segment, GST_FORMAT_TIME);
1197 15 : gst_pad_push_event (srcpad->pad, gst_event_new_segment (&segment));
1198 : }
1199 :
1200 33 : outbuf = gst_buffer_make_writable (outbuf);
1201 :
1202 : /* metadata from incoming buffer */
1203 33 : gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
1204 :
1205 33 : ts = GST_BUFFER_TIMESTAMP (buf);
1206 33 : if (srcpad->last_ts == GST_CLOCK_TIME_NONE || srcpad->last_ts != ts) {
1207 24 : srcpad->last_ts = ts;
1208 : } else {
1209 9 : GST_DEBUG_OBJECT (tensor_if, "invalid timestamp %" GST_TIME_FORMAT,
1210 : GST_TIME_ARGS (ts));
1211 : }
1212 :
1213 33 : res = gst_pad_push (srcpad->pad, outbuf);
1214 33 : res = gst_tensor_if_combine_flows (tensor_if, srcpad, res);
1215 :
1216 36 : done:
1217 36 : gst_buffer_unref (buf);
1218 36 : return res;
1219 : }
|