76 #define EVENT_NAME_UPDATE_MODEL "evt_update_model"
79 #define GST_CAT_DEFAULT gst_tensor_filter_debug
81 #define TF_MODELNAME(prop) \
82 ((prop)->model_files ? ((prop)->model_files[0]) : "[No Model File]")
87 #define CAPS_STRING GST_TENSOR_CAP_DEFAULT ";" GST_TENSORS_CAP_MAKE ("{ static, flexible }")
92 static GstStaticPadTemplate
sink_factory = GST_STATIC_PAD_TEMPLATE (
"sink",
100 static GstStaticPadTemplate
src_factory = GST_STATIC_PAD_TEMPLATE (
"src",
105 #define gst_tensor_filter_parent_class parent_class
113 #define LATENCY_REPORT_HEADROOM 0.05
120 #define LATENCY_REPORT_THRESHOLD 0.25
124 const GValue * value, GParamSpec * pspec);
126 GValue * value, GParamSpec * pspec);
131 GstBuffer * inbuf, GstBuffer * outbuf);
133 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
135 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
137 GstCaps * incaps, GstCaps * outcaps);
139 GstPadDirection direction, GstQuery * query);
141 GstPadDirection direction, GstCaps * caps, gsize size,
142 GstCaps * othercaps, gsize * othersize);
171 GObjectClass *gobject_class;
172 GstElementClass *gstelement_class;
173 GstBaseTransformClass *trans_class;
175 GST_DEBUG_CATEGORY_INIT (gst_tensor_filter_debug,
"tensor_filter", 0,
176 "Tensor filter to invoke neural network model");
178 trans_class = (GstBaseTransformClass *) klass;
179 gstelement_class = (GstElementClass *) trans_class;
180 gobject_class = (GObjectClass *) gstelement_class;
188 gst_element_class_set_details_simple (gstelement_class,
191 "Handles NN Frameworks (e.g., tensorflow) as Media Filters with other/tensor type stream",
192 "MyungJoo Ham <myungjoo.ham@samsung.com>");
194 gst_element_class_add_pad_template (gstelement_class,
196 gst_element_class_add_pad_template (gstelement_class,
200 trans_class->passthrough_on_same_caps =
FALSE;
206 trans_class->transform_caps =
212 trans_class->transform_size =
240 self->prev_ts = GST_CLOCK_TIME_NONE;
241 self->throttling_delay = 0;
242 self->throttling_accum = 0;
258 GST_OBJECT_LOCK (
self);
261 GST_OBJECT_UNLOCK (
self);
267 G_OBJECT_CLASS (parent_class)->finalize (
object);
292 (
"tensor_filter's core has inconsistent data. Please report to https://github.com/nnstreamer/nnstreamer/issues . The index argument (%u) of tensors is greater-than or equal-to the number of tensors (%u)",
305 const GValue * value, GParamSpec * pspec)
313 silent_debug (
self,
"Setting property for prop %d.\n", prop_id);
317 priv->
config_path = g_strdup (g_value_get_string (value));
323 G_OBJECT_WARN_INVALID_PROPERTY_ID (
object, prop_id, pspec);
331 GValue * value, GParamSpec * pspec)
339 silent_debug (
self,
"Getting property for prop %d.\n", prop_id);
347 G_OBJECT_WARN_INVALID_PROPERTY_ID (
object, prop_id, pspec);
359 GPtrArray *array = (GPtrArray *)
data;
361 void *tensor_data = (
void *) g_ptr_array_index (array, 1);
362 g_ptr_array_free (array,
TRUE);
375 GPtrArray *data_array = g_ptr_array_new ();
377 g_ptr_array_add (data_array, (gpointer)
self);
378 g_ptr_array_add (data_array, (gpointer)
data);
380 return gst_memory_new_wrapped (0,
data, size, 0, size, (gpointer) data_array,
399 gint64 *latency =
data;
400 gint64 *total_latency = user_data;
402 *total_latency += *latency;
405 #define THRESHOLD_DROP_OLD (2000)
406 #define THRESHOLD_CACHE_OLD (1000)
414 gint64 end_time = g_get_real_time ();
424 latency = g_new (gint64, 1);
430 g_free (g_queue_pop_head (recent_latencies));
431 g_queue_push_tail (recent_latencies, latency);
434 g_assert (g_queue_get_length (recent_latencies) != 0);
437 gint64 avg_latency = 0;
440 avg_latency /= g_queue_get_length (recent_latencies);
443 if (avg_latency <= INT32_MAX)
449 (*latency) / 1000.0);
453 gint throughput_int = -1;
461 if (throughput <= INT32_MAX)
462 throughput_int = (gint) throughput;
469 throughput_int / 1000.0);
506 gint64 estimated, reported;
509 GST_OBJECT_LOCK (
self);
512 GST_OBJECT_UNLOCK (
self);
516 deviation = (gdouble) ABS (estimated - reported) / reported;
522 (
"[%s] latency reported:%" G_GINT64_FORMAT
" estimated:%"
524 reported, estimated, deviation);
526 gst_element_post_message (GST_ELEMENT_CAST (
self),
527 gst_message_new_latency (GST_OBJECT_CAST (
self)));
545 GST_OBJECT_LOCK (trans);
547 if (self->throttling_delay != 0) {
548 GstClockTime curr_ts = GST_BUFFER_PTS (inbuf);
549 GstClockTime prev_ts =
self->prev_ts;
551 self->prev_ts = curr_ts;
553 if (GST_CLOCK_TIME_IS_VALID (prev_ts)) {
554 GstClockTimeDiff diff = curr_ts - prev_ts;
555 GstClockTimeDiff delay;
557 self->throttling_accum += diff;
560 delay = MAX (priv->
prop.
latency * 1000, self->throttling_delay);
562 if (self->throttling_accum < delay) {
563 GstClockTimeDiff duration = GST_BUFFER_DURATION (inbuf);
564 gdouble avg_rate = gst_guint64_to_gdouble (duration) /
565 gst_guint64_to_gdouble (delay);
571 GstPad *sinkpad = GST_BASE_TRANSFORM_SINK_PAD (&self->element);
572 GstEvent *
event = gst_event_new_qos (GST_QOS_TYPE_OVERFLOW,
573 avg_rate, (self->throttling_accum - delay), curr_ts);
575 gst_pad_push_event (sinkpad, event);
577 GST_OBJECT_UNLOCK (trans);
581 self->throttling_accum = 0;
585 GST_OBJECT_UNLOCK (trans);
594 GstBuffer * inbuf, GstBuffer * outbuf)
602 (
"The tensor_filter instance is not configured (pad caps not negotiated). Property info (framework = '%s', framework_opened = %d, model[0] = '%s', num-models = %d, custom_properties = '%s'.",
603 prop ?
prop->fwname :
"property info is NULL.",
607 prop ?
prop->custom_properties :
"property info is NULL."));
608 return GST_FLOW_NOT_NEGOTIATED;
610 if (G_UNLIKELY (!priv->
fw)) {
617 (
"Framework (filter subplugin) is not found or not configured: 'framework=%s'. Please check if the given framework name is correct and the given model path is consistent with the intended framework especially if 'framework=auto' is given. Please refer to the warning messages created when the framework property is set.",
620 (
"\nA corresponding nnstreamer extension (tensor_filter subplugin) is not installed or the framework property of tensor_filter is incorrect: [%s] is not found.\n\n",
622 return GST_FLOW_ERROR;
625 G_UNLIKELY (!(
prop->model_files &&
626 prop->num_models > 0 &&
prop->model_files[0]))) {
628 (
"For the framework='%s', its model filepath is not provided and this framework requires a model file. Thus, we cannot proceed with tensor_filter for inferences. Please provide a valid model file path.",
630 return GST_FLOW_ERROR;
635 (
"The tensor-filter subplugin for the framework='%s' does not have its mandatory methods (or callback functions). It appears that your subplugin implementation of '%s' is not completed. There is no 'invoke_NN (v1)' or 'invoke (v2)' methods available.",
637 return GST_FLOW_ERROR;
641 GST_STR_NULL (
prop->model_files[0]));
645 return GST_BASE_TRANSFORM_FLOW_DROPPED;
649 (
"The output buffer for the instance of tensor-filter subplugin (%s / %s) is null. Cannot proceed.",
651 return GST_FLOW_ERROR;
653 if (gst_buffer_get_size (outbuf) != 0) {
655 (
"The output buffer for the instance of tensor-filter subplugin (%s / %s) already has a content (buffer size = %zu). It should be 0.",
657 return GST_FLOW_ERROR;
672 for (i = 0; i < end_index; i++) {
673 if (trans_data->
mem[i]) {
674 gst_memory_unmap (trans_data->
mem[i], &trans_data->
info[i]);
675 gst_memory_unref (trans_data->
mem[i]);
687 gsize header_size = 0;
692 _meta = &trans_data->
meta[idx];
721 (
"Failed to allocate memory for internal data of tensor filter transform input data.");
731 if (!gst_memory_map (trans_data->
mem[i], &trans_data->
info[i],
734 (
"gst_tensor_filter_transform: For the given input buffer, tensor-filter (%s : %s) cannot map input memory from the buffer for reading. The %u-th memory chunk (%u-th tensor) has failed for memory map.\n",
763 guint invoke_num_tensors = 0;
770 (
"gst_tensor_filter_transform: Input buffer has invalid number of memory blocks (%u), which is expected to be %u (the number of tensors). Maybe, the pad capability is not consistent with the actual input stream.\n",
771 prop->input_meta.num_tensors,
prop->input_meta.num_tensors);
779 if (!invoke_tensors) {
781 (
"Failed to allocate memory for internal data of tensor filter transform invoke tensors. The number of invoke tensors: %u",
791 for (list = priv->
combi.
in_combi; list != NULL; list = list->next) {
792 i = GPOINTER_TO_UINT (list->data);
796 (
"gst_tensor_filter_transform: Invalid input combination ('input-combination' property) for the tensor-filter (%s:%s). The %u'th combination's index is %u, which is out of bound (>= %u = the number of memory chunks (tensors) of incoming buffer). Because of buffer index inconsistency, it cannot continue (cannot map the memory for the input buffer).\n",
806 (
"gst_tensor_filter_transform: With the given input combination ('input-combination' property) of the tensor-filter, the incoming buffer size of combination index %u (%u'th combination) is %zd, which is invalid and is expected to be %zd. Because of buffer size inconsistency, it cannot continue (cannot map the memory for the input buffer).\n",
807 i, info_idx, trans_data->
tensors[i].
size, expected);
812 invoke_tensors[info_idx++] = trans_data->
tensors[i];
815 for (i = 0; i <
prop->input_meta.num_tensors; i++) {
819 (
"gst_tensor_filter_transform: Input buffer size (%u'th memory chunk: %zd) is invalid, which is expected to be %zd, which is the frame size of the corresponding tensor. Maybe, the pad capability is not consistent with the actual input stream; if the size is supposed to change dynamically and the given neural network, framework, and the subpluigins can handle it, please consider using format=flexible.\n",
825 invoke_tensors[i] = trans_data->
tensors[i];
829 return invoke_tensors;
847 (
"Failed to allocate memory for internal data of tensor filter transform output data.");
858 (
"Dynamic Invoke of tensor filter is activated but the output of tensor filter is static tensors. Currently, only flexible tensors is supported as output of dynamic invoke. If you don't want to dynamic invoke, remove the invoke-dynamic option of tensor filter.");
880 for (i = 0; i <
prop->output_meta.num_tensors; i++) {
890 (
"gst_tensor_filter_transform: The configured output tensor information is invalid, at %u'th output tensor\n",
892 return GST_FLOW_ERROR;
900 gst_allocator_alloc (NULL, trans_data->
tensors[i].
size + hsize, NULL);
901 if (!trans_data->
mem[i]) {
903 (
"gst_tensor_filter_transform: cannot allocate memory for the output buffer (%u'th memory chunk for %u'th tensor), which requires %zd bytes. gst_allocate_alloc has returned Null. Out of memory?",
905 return GST_FLOW_ERROR;
907 if (!gst_memory_map (trans_data->
mem[i], &trans_data->
info[i],
910 (
"gst_tensor_filter_transform: For the given output buffer, allocated by gst_tensor_filter_transform, it cannot map output memory buffer for the %u'th memory chunk (%u'th output tensor) for write.\n",
912 return GST_FLOW_ERROR;
920 (&trans_data->
meta[i], trans_data->
info[i].data)) {
922 (
"gst_tensor_meta_info_update_header() has failed to update header for flexible format: invalid metadata or buffer for header is not available. This looks like an internal error of nnstreamer/tensor_filter. Please report to github.com/nnstreamer/nnstreamer/issues. %u'th output buffer has failed to update its header.\n",
924 return GST_FLOW_ERROR;
946 gst_memory_unmap (in_trans_data->
mem[i], &in_trans_data->
info[i]);
948 gst_memory_unref (in_trans_data->
mem[i]);
952 for (i = 0; i <
prop->output_meta.num_tensors; i++) {
953 gst_memory_unmap (out_trans_data->
mem[i], &out_trans_data->
info[i]);
955 gst_memory_unref (out_trans_data->
mem[i]);
959 if (invoke_res < 0) {
961 (
"Calling invoke function (inference instance) of the tensor-filter subplugin (%s for %s) has failed with error code (%d).\n",
963 return GST_FLOW_ERROR;
964 }
else if (invoke_res > 0) {
966 return GST_BASE_TRANSFORM_FLOW_DROPPED;
992 i = GPOINTER_TO_UINT (list->data);
1000 in_trans_data->
mem[i]);
1004 mem = gst_memory_share (in_trans_data->
mem[i], hsize, -1);
1006 mem = gst_memory_ref (in_trans_data->
mem[i]);
1009 gst_buffer_append_memory (outbuf, mem);
1013 for (i = 0; i < in_trans_data->
num_tensors; i++) {
1014 if (in_trans_data->
mem[i]) {
1015 gst_memory_unref (in_trans_data->
mem[i]);
1019 for (i = 0; i <
prop->output_meta.num_tensors; i++) {
1021 gboolean out_combi =
FALSE;
1024 if (i == GPOINTER_TO_UINT (list->data)) {
1035 gst_memory_unref (out_trans_data->
mem[i]);
1042 if (
prop->invoke_dynamic) {
1044 GstMemory *flex_mem;
1052 flex_mem = gst_memory_new_wrapped (0,
1057 out_trans_data->
mem[i] =
1059 gst_memory_unref (flex_mem);
1067 out_trans_data->
mem[i] =
1069 gst_memory_unref (mem);
1087 ml_logd (
"Suspend watchdog triggered. Unload the NN framework.");
1096 static GstFlowReturn
1098 GstBuffer * inbuf, GstBuffer * outbuf)
1102 gint invoke_res = -1;
1103 gboolean need_profiling;
1104 GstFlowReturn retval = GST_FLOW_OK;
1111 GST_OBJECT_LOCK (
self);
1113 GST_OBJECT_UNLOCK (
self);
1119 if (retval != GST_FLOW_OK)
1124 if (!in_trans_data) {
1125 return GST_FLOW_ERROR;
1130 if (!invoke_tensors) {
1135 if (!out_trans_data) {
1142 if (retval != GST_FLOW_OK) {
1153 if (need_profiling) {
1160 out_trans_data, invoke_res);
1161 if (retval != GST_FLOW_OK) {
1166 out_trans_data, outbuf);
1171 retval = GST_FLOW_ERROR;
1172 if (in_trans_data) {
1184 if (retval == GST_FLOW_OK && priv->
prop.
suspend != 0) {
1185 GST_OBJECT_LOCK (
self);
1188 ml_logw (
"Failed to feed watchdog. Suspend mode is not working.");
1190 GST_OBJECT_UNLOCK (
self);
1208 const GstCaps * incaps)
1212 GstStructure *structure;
1217 g_return_val_if_fail (incaps != NULL,
FALSE);
1233 structure = gst_caps_get_structure (incaps, 0);
1234 if (G_UNLIKELY (!structure)) {
1235 gchar *capstr = gst_caps_to_string (incaps);
1237 (
"%s:%u The input stream padcap is not appropriate. Cannot get structured information from the given padcap: '%s' (gst_caps_get_structure() has returned NULL.). Please check if the caps you've given is correct especially if you have applied caps-filter in front of tensor-filter (%s:%s).",
1238 __func__, __LINE__, capstr, GST_STR_NULL (
prop->fwname),
1244 gchar *capstr = gst_caps_to_string (incaps);
1246 (
"%s:%u The input stream padcap is not appropriate. It is not a valid tensor stream (other/tensors) or a tensor stream with a few required entries missing. For example, dimesion and type are missing for a static tensor (format=static or other/tensor (single tensor)). The given padcap is '%s' for tensor-filter (%s:%s).",
1247 __func__, __LINE__, capstr, GST_STR_NULL (
prop->fwname),
1258 gchar *capstr = gst_caps_to_string (incaps);
1260 (
"%s:%u The input stream padcaps cannot be validated. It is not a valid tensor stream for tensor_filter element. Input stream type for tensor_filter (%s:%s) is %s. tensor-filter could get config from the input padcap; however, it cannot be validated: framerate or format is not valid. Try a static tensor stream with a valid (0/1 is also valid!) framerate.",
1261 __func__, __LINE__, GST_STR_NULL (
prop->fwname),
1269 gchar *capstr = gst_caps_to_string (incaps);
1271 (
"%s:%u Failed to configure combined input info for tensor-filter (%s:%s). The given padcap is '%s'. User has specified input combination (refer to the previous error log), which shuffles the order of tensors, which is not compatible with the given model. Please check the padcap, input combination, and tensor/dimension requirement of your neural network model.",
1272 __func__, __LINE__, GST_STR_NULL (
prop->fwname),
1282 if (
prop->input_meta.num_tensors > 0) {
1288 GST_INFO_OBJECT (
self,
"The input tensor is flexible.");
1290 gchar *capstr = gst_caps_to_string (incaps);
1294 (
"%s:%u The input tensor of tensor_filter (%s:%s) is not compatible with the configured input information. Please check tensor-filter properties if you have given input dimensions explicitly; or the model properties if you have not given them. Check the input stream caps and related caps-filters, too. The given gstcap is %s, which is not compatible: %s",
1295 __func__, __LINE__, GST_STR_NULL (
prop->fwname),
1303 gchar *capstr = gst_caps_to_string (incaps);
1312 (
"%s:%u The input tensor of tensor_filter (%s:%s) is flexible (gstcap: '%s'), which requires either explicit type/dimension tensor-filter property values or static input dimension models. The current version of tensor-filter does not support flexible tensors for dynamic-input models without explicit input dimension declaration. Declare type/dimension/tensors with tensor-filter properties, or apply caps-filter in front of tensor-filter, or use static tensor streams.",
1313 __func__, __LINE__, GST_STR_NULL (
prop->fwname),
1325 if (!
prop->output_configured) {
1329 if (
prop->output_meta.num_tensors > 0) {
1332 &
prop->output_meta);
1334 (
"%s:%u The output tensor is not compatible with the configured tensor information. The configuration is usually set by tensor-filter properties declared by users or the given neural network itself. The following two tensor metadata are not compatible: %s.\n",
1335 __func__, __LINE__, cmpstr));
1347 if (!
prop->output_configured) {
1349 (
"%s:%u Failed to get output tensor info: not enough related information to configure output.\n",
1350 __func__, __LINE__));
1365 &
prop->output_meta, &out_config.
info)) {
1367 (
"%s:%u Failed to configure combined output info: please refer to the error message of gst_tensor_filter_common_get_combined_out_info(). ",
1368 __func__, __LINE__));
1406 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1414 GstStructure *structure;
1415 gboolean configured =
FALSE;
1422 if (priv->
fw == NULL)
1431 if (direction == GST_PAD_SINK)
1432 pad = GST_BASE_TRANSFORM_SRC_PAD (trans);
1434 pad = GST_BASE_TRANSFORM_SINK_PAD (trans);
1446 structure = gst_caps_get_structure (caps, 0);
1453 if (direction == GST_PAD_SINK) {
1459 if (
prop->output_configured) {
1466 &in_config.
info, &out_info);
1472 &in_config.
info, &out_info, &out_config.
info);
1489 if (direction == GST_PAD_SINK) {
1490 GstCaps *peer = gst_pad_peer_query_caps (pad, NULL);
1493 if (!gst_caps_is_any (peer))
1495 gst_caps_unref (peer);
1503 if (filter && gst_caps_get_size (filter) > 0) {
1504 GstCaps *intersection;
1512 gst_caps_intersect_full (
result, filter, GST_CAPS_INTERSECT_FIRST);
1529 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1537 silent_debug (
self,
"fixate_caps, direction = %d\n", direction);
1542 if (priv->
fw == NULL) {
1543 gst_caps_unref (othercaps);
1551 gst_caps_unref (othercaps);
1564 GstCaps * incaps, GstCaps * outcaps)
1568 GstStructure *structure;
1579 (
"Failed to configure tensor. Please refer to the error log of gst_tensor_filter_configure_tensor ()."));
1585 (
"Failed to validate input tensor configuration. Please refer to the error log of gst_tensors_config_validate(): %s",
1593 (
"Failed to validate output tensor configuration. Please refer to the error log of gst_tensors_config_validate(): %s",
1599 structure = gst_caps_get_structure (outcaps, 0);
1602 GST_INFO_OBJECT (
self,
"Output tensor is flexible.");
1608 (
"Set-caps failed. Invalid output config (padcaps) for tensor-filter (%s:%s): its format is static, but not equal to the internal configuration: %s\nThis might be an internal error. Please report to https://github.com/nnstreamer/nnstreamer/issues .",
1624 GstPadDirection direction, GstQuery * query)
1628 gboolean res =
FALSE;
1634 switch (GST_QUERY_TYPE (query)) {
1635 case GST_QUERY_LATENCY:
1637 GstClockTime min, max;
1642 GST_OBJECT_LOCK (
self);
1644 GST_OBJECT_UNLOCK (
self);
1647 if ((res = gst_pad_peer_query (GST_BASE_TRANSFORM (
self)->sinkpad,
1649 gst_query_parse_latency (query, &live, &min, &max);
1651 GST_DEBUG_OBJECT (
self,
"Peer latency: min %"
1652 GST_TIME_FORMAT
" max %" GST_TIME_FORMAT,
1653 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1655 latency = (gdouble) estimated *GST_USECOND *
1659 min += (gint64) latency;
1660 if (max != GST_CLOCK_TIME_NONE)
1661 max += (gint64) latency;
1663 GST_DEBUG_OBJECT (
self,
"Calculated total latency : min %"
1664 GST_TIME_FORMAT
" max %" GST_TIME_FORMAT,
1665 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1667 gst_query_set_latency (query, live, min, max);
1672 GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1679 GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1695 GstPadDirection direction, GstCaps * caps, gsize size,
1696 GstCaps * othercaps, gsize * othersize)
1729 switch (GST_EVENT_TYPE (event)) {
1730 case GST_EVENT_CUSTOM_DOWNSTREAM:
1732 const GstStructure *structure = gst_event_get_structure (event);
1734 if (structure == NULL ||
1738 const GValue *value =
1739 gst_structure_get_value (structure,
"model_files");
1740 if (value != NULL) {
1741 g_object_set (
self,
"model", value, NULL);
1746 gst_event_unref (event);
1754 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
1767 switch (GST_EVENT_TYPE (event)) {
1771 GstClockTimeDiff diff;
1772 gst_event_parse_qos (event, &
type, NULL, &diff, NULL);
1773 if (
type == GST_QOS_TYPE_THROTTLE && diff > 0) {
1774 GST_OBJECT_LOCK (trans);
1775 if (self->throttling_delay != 0)
1777 self->throttling_delay = MIN (self->throttling_delay, diff);
1779 self->throttling_delay = diff;
1780 GST_OBJECT_UNLOCK (trans);
1781 gst_event_unref (event);
1783 g_object_set (
self,
"latency", 1, NULL);
1793 return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
1809 if (priv->
fw == NULL)
1814 GST_OBJECT_LOCK (
self);
1816 ml_logw (
"Failed to create watchdog. Suspend mode is not working.");
1817 GST_OBJECT_UNLOCK (
self);
1822 ml_logw (
"Failed to feed watchdog. Suspend mode is not working.");
1824 GST_OBJECT_UNLOCK (
self);
1844 GST_OBJECT_LOCK (
self);
1847 GST_OBJECT_UNLOCK (
self);