diff --git a/girs/Gst-1.0.gir b/girs/Gst-1.0.gir index 7918b15f74..372c57b785 100644 --- a/girs/Gst-1.0.gir +++ b/girs/Gst-1.0.gir @@ -22114,6 +22114,16 @@ In hindsight, this tag should have been called "memory-layout". + + Check if the transform type is clearing the content of the meta without +freeing it. + + + + a transform type + + + Check if the transform type is a copy transform diff --git a/girs/GstAnalytics-1.0.gir b/girs/GstAnalytics-1.0.gir new file mode 100644 index 0000000000..ddf1359c2f --- /dev/null +++ b/girs/GstAnalytics-1.0.gir @@ -0,0 +1,1025 @@ + + + + + + + + Type of analytics meta data + + + + + + + + + Handle containing data required to use gst_analytics_cls_mtd APIs. This type +is generally expected to be allocated on the stack. + + + Instance identifier. + + + + Instance of #GstAnalyticsRelationMeta where the analysis-metadata +identified by @id is stored. + + + + + + index of the class associated with @quarks ( and label) or + a negative value on failure. + + + + + Instance handle + + + + Quark of the class +Get index of class represented by @quark + + + + + + + + Number of classes in this classification instance + + + + + Instance handle +Get number of classes + + + + + + Get confidence level for class at @index + + + confidence level for @index, <0.0 if the call failed. + + + + + instance handle + + + + Object class index + + + + + + + + Quark of this class (label) associated with @index + + + + + Instance handle + + + + index of the class +Get quark of the class at @index + + + + + + Get the static string representing #GstAnalyticsMtd type. + + + #GstAnalyticsMtd type name. + + + + + Get a quark identifying #GstAnalyticsMtd type. + + + Quark of #GstAnalyticsMtd type + + + + + + + + + + + + + + + + + A wildcard matching any type of analysis + + + + + Handle containing data required to use gst_analytics_mtd API. This type +is generally expected to be allocated on the stack. + + + Instance identifier. + + + + Instance of #GstAnalyticsRelationMeta where the analysis-metadata +identified by @id is stored. + + + + + + Id of @instance + + + + + Instance of #GstAnalyticsMtd +Get instance id + + + + + + @instance Instance of #GstAnalyticsRelatableMtd +Get instance size + + + Size (in bytes) of this instance or 0 on failure. + + + + + + + + + + + + quark associated with type. + + + + + Instance of #GstAnalyticsMtd +Get analysis result type. + + + + + + + Handle containing data required to use gst_analytics_od_mtd APIs. This type +is generally expected to be allocated on the stack. + + + Instance identifier. + + + + Instance of #GstAnalyticsRelationMeta where the analysis-metadata +identified by @id is stored. + + + + Retrieve location and location confidence level. + + + TRUE on success, otherwise FALSE. + + + + + instance + + + + x component of upper-left corner of the object location + + + + y component of upper-left corner of the object location + + + + bounding box width of the object location + + + + bounding box height of the object location + + + + Confidence on object location + + + + + + + + Quark different from on success and 0 on failure. + + + + + Instance handle +Quark of the class of object associated with this location. + + + + + + Get a text representing object-detection metadata type. + + + #GstAnalyticsMtd type name. + + + + + Get a quark that represent object-detection metadata type + + + Quark of #GstAnalyticsMtd type + + + + + + + + No relation + + + First analysis-meta is part of second analysis-meta + + + First analysis-meta contain second analysis-meta. + + + + + reserved + + + Only use for criteria. + + + + Base structure for analysis-metadata that can be placed in relation. Only +other analysis-metadata based on GstAnalyticsRelatableMtdData should +directly use this structure. + + + Identify the type of analysis-metadata + + + + Instance identifier. + + + + Size in bytes of the instance + + + + + + + + + + + + + + + + + + + + + + + + + Add analytic classification metadata to @instance. + + + Added successfully + + + + + Instance of #GstAnalyticsRelationMeta where to add classification instance + + + + length of @confidence_levels + + + + confidence levels + + + + + + labels of this + classification. Order define index, quark, labels relation. This array + need to exist as long has this classification meta exist. + + + + + + Handle updated to newly added classification meta. + + + + + + Add a relatable metadata to @meta. This method is meant to be used by +new struct sub-classing GstAnalyticsRelatableMtd. + + + New GstAnalyticsRelatableMtdData instance. + +Since 1.24 + + + + + Instance + + + + Type of relatable (#GstAnalyticsRelatableMtd) + + + + Size required + + + + Updated handle + + + + + + + + Added successfully + + + + + Instance of #GstAnalyticsRelationMeta where to add classification instance + + + + Quark of the object type + + + + x component of bounding box upper-left corner + + + + y component of bounding box upper-left corner + + + + bounding box width + + + + bounding box height + + + + confidence level on the object location + + + + Handle updated with newly added object detection + meta. Add an object-detetion metadata to @instance. + + + + + + Add analytic classification metadata to @instance. + + + Added successfully + + + + + Instance of #GstAnalyticsRelationMeta where to add classification instance + + + + confidence levels + + + + labels of this + classification. Order define index, quark, labels relation. This array + need to exist as long has this classification meta exist. + + + + Handle updated to newly added classification meta. + + + + + + + + Added successfully + + + + + Instance of GstAnalyticsRelationMeta where to add tracking mtd + + + + Tracking id + + + + Timestamp of first time the object was observed. + + + + Handle updated with newly added tracking meta. +Add an analytic tracking metadata to @instance. + + + + + + Verify existence of relation(s) between @an_meta_first_d and +@an_meta_second_id according to relation condition @cond_types. It optionally +also return a shortest path of relations ( compliant with @cond_types) +between @an_meta_first_id and @an_meta_second_id. + + + TRUE if a relation between exit between @an_meta_first_id and + @an_meta_second_id, otherwise FALSE. + +Since 1.24 + + + + + a #GstAnalyticsRelationMeta describing analysis-meta + relation + + + + First analysis-meta + + + + Second analysis-meta + + + + Maximum number of relation between @an_meta_first_id and + @an_meta_second_id. + A value of 1 mean only only consider direct relation. + + + + condition on relation types. + + + + + If not NULL this list will be filled with relation path between + @an_meta_first_id and + @an_meta_second_id. List value should be access with GSList API. Use + GPOINTER_TO_INT(iter->data) where iter is a GSList element to get + analysis-meta id on the relation path. Free this list with g_slist_free + (@relations_path) after using. + + + + + + + + Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, +otherwise this method return FALSE and @rlt is invalid. + + + TRUE if successful. + +Since 1.24 + + + + + Instance of #GstAnalyticsRelationMeta + + + + Id of #GstAnalyticsClsMtd instance to retrieve + + + + Will be filled with relatable + meta + + + + + + + + TRUE if @rlt_mtd was updated, other wise FALSE + +Since 1.24 + + + + + GstAnalyticsRelationMeta instance where to query for + GstAnalyticsRelatableMtd. + + + + Id of GstAnalyticsMtd involved in relation to query + + + + Type of relation to filter on. + + + + Type of GstAnalyticsMtd to filter on + + + + Opaque data to store state of the query. + If @state point to NULL, the first analytics-metadata directly related + to @an_meta_id will be set in @rlt_mtd. Doesn't need to be free. + + + + Handle updated to directly related relatable meta. + + + + + + Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, +otherwise this method return FALSE and @rlt is invalid. + + + TRUE if successful. + +Since 1.24 + + + + + Instance of GstAnalyticsRelationMeta + + + + Id of GstAnalyticsMtd instance to retrieve + + + + Filter on a specific type of analysis, use + %GST_ANALYTICS_MTD_TYPE_ANY to match any type + + + + Will be filled with relatable + meta + + + + + + + + Instance of GstAnalyticsRelatableMtdData + +Since 1.24 + + + + + Instance of GstAnalyticsRelationMeta + + + + Id of GstAnalyticsMtd instance to retrieve + + + + + + Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, +otherwise this method return FALSE and @rlt is invalid. + + + TRUE if successful. + +Since 1.24 + + + + + + + + Id of #GstAnalyticsOdMtd instance to retrieve + + + + Will be filled with relatable + meta + + + + + + + + relation description between first and second analysis-meta. + +Get relations between first and second analysis-meta. +Ids (@an_meta_first_id and @an_meta_second_id) must be from a call to +@gst_analytics_mtd_get_id (handle). + + + + + a #GstAnalyticsRelationMeta + + + + Id of first analysis-meta + + + + Id of second analysis-meta + + + + + + Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, +otherwise this method return FALSE and @rlt is invalid. + + + TRUE if successful. + +Since 1.24 + + + + + + + + Id of GstAnalyticsMtd instance to retrieve + + + + Will be filled with relatable + meta + + + + + + + + FALSE if end was reached and iteration is completed. + +Since 1.24 + + + + + Instance of GstAnalyticsRelationMeta + + + + Opaque data to store iteration state, initialize to NULL, no need to + free it. + + + + Type of GstAnalyticsMtd to iterate on or use + %GST_ANALYTICS_MTD_TYPE_ANY for any. + + + + Handle updated to iterated GstAnalyticsRelatableMtd. + + + + + + Sets the relation (#GstAnalyticsRelTypes) between @an_meta_first and + @an_meta_second. +Ids must have been obtained a call to + @gst_analytics_mtd_get_id(handle). + + + TRUE on success and FALSE on failure. + + + + + Parameter to receive new maximum number of + analysis-meta described by relation. + + + + a #GstAnalyticsRelTypes defining relation between two analysis-meta + + + + first meta id + + + + second meta id + + + + + + + + + + + + + GstAnalyticsRelationMeta initialization parameters. + + + Initial relations order. + + + + Buffer size in bytes to store relatable metadata + + + + + Store information on results of object tracking + + + Instance identifier. + + + + Instance of #GstAnalyticsRelationMeta where the analysis-metadata +identified by @id is stored. + + + + Retrieve tracking information. + + + Successfully retrieved info. + + + + + Instance of tracking metadata + + + + Updated tracking id + + + + Updated timestamp of the tracking first observation. + + + + Updated timestamp of the tracking last observation. + + + + Has the tracking been lost + + + + + + + + Update successful + + + + + Instance of GstAnalyticsTrackingMtd. +Set tracking to lost + + + + + + + + + + + + GstAnalyticsTrackingMtd instance + + + + Timestamp of last time this object was tracked + + + + + + + + + + + + + + Quark representing the type of GstAnalyticsRelatableMtd + +Get the quark identifying the relatable type + + + + + + Attach a analysis-results-meta-relation meta (#GstAnalyticsRelationMeta)to @buffer. + +A #GstAnalyticsRelationMeta is a metadata describing relation between other +analysis meta. It's more efficient to use #gst_buffer_add_analytics_relation_meta_full +and providing the maximum number of analysis meta that will attached to a buffer. + + + Newly attached #GstAnalyticsRelationMeta + +Since 1.24 + + + + + a #GstBuffer + + + + + + Attache a analysis-results relation-meta (#GstAnalyticsRelationMeta) to @buffer. + +A #GstAnalyticsRelationMeta is a metadata describing relation between other +analysis meta. + + + Newly attached #GstAnalyticsRelationMeta + +Since 1.24 + + + + + a #GstBuffer + + + + Initialization parameters + + + + + + Retrives the meta or %NULL if it doesn't exist + + + The #GstAnalyticsRelationMeta if there is one + + + + + a #GstBuffer + + + + + + Get the static string representing #GstAnalyticsMtd type. + + + #GstAnalyticsMtd type name. + + + + + Get a quark identifying #GstAnalyticsMtd type. + + + Quark of #GstAnalyticsMtd type + + + + + Get a text representing object-detection metadata type. + + + #GstAnalyticsMtd type name. + + + + + Get a quark that represent object-detection metadata type + + + Quark of #GstAnalyticsMtd type + + + + + @instance Instance of #GstAnalyticsRelationMeta +Get number of relatable meta attached to instance + + + Number of analysis-meta attached to this +instance. + + + + + + + + + + + + GType of GstAnalyticsRelationMeta + + + + + + + + + + + + + + + + + + + Quark representing the type of GstAnalyticsRelatableMtd + +Get the quark identifying the relatable type + + + + + diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/analytics-meta-prelude.h b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/analytics-meta-prelude.h new file mode 100644 index 0000000000..6b4ae3bf68 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/analytics-meta-prelude.h @@ -0,0 +1,36 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * analytics-meta-prelude.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Publicn + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ANALYTICS_META_PRELUDE_H__ +#define __GST_ANALYTICS_META_PRELUDE_H__ + +#include + +#ifndef GST_ANALYTICS_META_API +# ifdef BUILDING_GST_ANALYTIC_META +# define GST_ANALYTICS_META_API GST_API_EXPORT +# else +# define GST_ANALYTICS_META_API GST_API_IMPORT +# endif +#endif + +#endif /* __GST_ANALYTICS_META_PRELUDE_H__ */ + diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/analytics.h b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/analytics.h new file mode 100644 index 0000000000..d25cf3ba35 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/analytics.h @@ -0,0 +1,34 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * analytics.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + + +#ifndef __ANALYTICS_H__ +#define __ANALYTICS_H__ + +#include +#include +#include +#include +#include +#include + +#endif /* __ANALYTICS_H__ */ diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsclassificationmtd.c b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsclassificationmtd.c new file mode 100644 index 0000000000..9f5bf1991a --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsclassificationmtd.c @@ -0,0 +1,271 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstanalyticsclassificationmtd.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstanalyticsclassificationmtd.h" + +#define GST_RELATABLE_MTD_CLASSIFICATION_TYPE_NAME "classification" + +static char type[] = GST_RELATABLE_MTD_CLASSIFICATION_TYPE_NAME; + +typedef struct _GstAnalyticsClsConfLvlAndClass GstAnalyticsClsConfLvlAndClass; +typedef struct _GstAnalyticsClsMtdData GstAnalyticsClsMtdData; + +struct _GstAnalyticsClsConfLvlAndClass +{ + GQuark class; + gfloat confidence_levels; +}; + +/* + * GstAnalyticsClsMtd: + * @parent: parent + * @length: classes and confidence levels count + * @class_quarks: (array length=length): Array of quark representing a class + * @confidence_levels: (array length=length): Array of confidence levels for + * each class in @class_quarks. + * + * Information on results of a classification of buffer content. + */ +struct _GstAnalyticsClsMtdData +{ + GstAnalyticsRelatableMtdData parent; + gsize length; + GstAnalyticsClsConfLvlAndClass confidence_levels_and_classes[]; // Must be last +}; + + +static GstAnalyticsClsMtdData * +gst_analytics_cls_mtd_get_data (GstAnalyticsClsMtd * instance) +{ + GstAnalyticsRelatableMtdData *rlt_data = + gst_analytics_relation_meta_get_mtd_data (instance->meta, + instance->id); + g_return_val_if_fail (rlt_data, NULL); + g_return_val_if_fail (rlt_data->analysis_type == + gst_analytics_cls_mtd_get_type_quark (), NULL); + + return (GstAnalyticsClsMtdData *) rlt_data; +} + +/** + * gst_analytics_cls_mtd_get_type_quark: + * Get a quark identifying #GstAnalyticsMtd type. + * + * Returns: Quark of #GstAnalyticsMtd type + * + * Since: 1.24 + */ +GstAnalyticsMtdType +gst_analytics_cls_mtd_get_type_quark (void) +{ + return g_quark_from_static_string (type); +} + +/** + * gst_analytics_cls_mtd_get_type_name: + * Get the static string representing #GstAnalyticsMtd type. + * + * Returns: #GstAnalyticsMtd type name. + * + * Since: 1.24 + */ +const gchar * +gst_analytics_cls_mtd_get_type_name (void) +{ + return GST_RELATABLE_MTD_CLASSIFICATION_TYPE_NAME; +} + +/** + * gst_analytics_cls_mtd_get_level: + * @handle: instance handle + * @index: Object class index + * + * Get confidence level for class at @index + * Returns: confidence level for @index, <0.0 if the call failed. + * + * Since: 1.24 + */ +gfloat +gst_analytics_cls_mtd_get_level (GstAnalyticsClsMtd * handle, gint index) +{ + g_return_val_if_fail (handle, -1.0); + g_return_val_if_fail (index >= 0, -1.0); + g_return_val_if_fail (handle->meta != NULL, -1.0); + GstAnalyticsClsMtdData *cls_mtd_data; + cls_mtd_data = gst_analytics_cls_mtd_get_data (handle); + g_return_val_if_fail (cls_mtd_data != NULL, -1.0); + g_return_val_if_fail (cls_mtd_data->length > index, -1.0); + return cls_mtd_data->confidence_levels_and_classes[index].confidence_levels; +} + +/** + * gst_analytics_cls_mtd_get_index_by_quark: + * @handle: Instance handle + * @quark: Quark of the class + * Get index of class represented by @quark + * Returns: index of the class associated with @quarks ( and label) or + * a negative value on failure. + * + * Since: 1.24 + */ +gint +gst_analytics_cls_mtd_get_index_by_quark (GstAnalyticsClsMtd * handle, + GQuark quark) +{ + g_return_val_if_fail (handle, -1); + + GstAnalyticsClsMtdData *cls_mtd_data; + cls_mtd_data = gst_analytics_cls_mtd_get_data (handle); + g_return_val_if_fail (cls_mtd_data != NULL, -1); + + for (gint i = 0; i < cls_mtd_data->length; i++) { + if (quark == cls_mtd_data->confidence_levels_and_classes[i].class) { + return i; + } + } + return -1; +} + +/** + * gst_analytics_cls_mtd_get_length: + * @handle: Instance handle + * Get number of classes + * Returns: Number of classes in this classification instance + * + * Since: 1.24 + */ +gsize +gst_analytics_cls_mtd_get_length (GstAnalyticsClsMtd * handle) +{ + GstAnalyticsClsMtdData *cls_mtd_data; + cls_mtd_data = gst_analytics_cls_mtd_get_data (handle); + g_return_val_if_fail (cls_mtd_data != NULL, 0); + return cls_mtd_data->length; +} + +/** + * gst_analytics_cls_mtd_get_quark: + * @handle: Instance handle + * @index: index of the class + * Get quark of the class at @index + * Returns: Quark of this class (label) associated with @index + * + * Since: 1.24 + */ +GQuark +gst_analytics_cls_mtd_get_quark (GstAnalyticsClsMtd * handle, gint index) +{ + GstAnalyticsClsMtdData *cls_mtd_data; + g_return_val_if_fail (handle, 0); + cls_mtd_data = gst_analytics_cls_mtd_get_data (handle); + g_return_val_if_fail (cls_mtd_data != NULL, 0); + return cls_mtd_data->confidence_levels_and_classes[index].class; +} + +/** + * gst_analytics_relation_meta_add_cls_mtd: + * @instance: Instance of #GstAnalyticsRelationMeta where to add classification instance + * @length: length of @confidence_levels + * @confidence_levels: (array length=length): confidence levels + * @class_quarks: (array length=length): labels of this + * classification. Order define index, quark, labels relation. This array + * need to exist as long has this classification meta exist. + * @cls_mtd: (out) (not nullable): Handle updated to newly added classification meta. + * + * Add analytic classification metadata to @instance. + * Returns: Added successfully + * + * Since: 1.24 + */ +gboolean +gst_analytics_relation_meta_add_cls_mtd (GstAnalyticsRelationMeta * + instance, gsize length, gfloat * confidence_levels, GQuark * class_quarks, + GstAnalyticsClsMtd * cls_mtd) +{ + GQuark type = gst_analytics_cls_mtd_get_type_quark (); + g_return_val_if_fail (instance, FALSE); + gsize confidence_levels_size = + (sizeof (GstAnalyticsClsConfLvlAndClass) * length); + gsize size = sizeof (GstAnalyticsClsMtdData) + confidence_levels_size; + GstAnalyticsClsConfLvlAndClass *conf_lvls_and_classes; + + GstAnalyticsClsMtdData *cls_mtd_data = (GstAnalyticsClsMtdData *) + gst_analytics_relation_meta_add_mtd (instance, type, size, cls_mtd); + if (cls_mtd_data) { + cls_mtd_data->length = length; + for (gsize i = 0; i < length; i++) { + conf_lvls_and_classes = &(cls_mtd_data->confidence_levels_and_classes[i]); + conf_lvls_and_classes->class = class_quarks[i]; + conf_lvls_and_classes->confidence_levels = confidence_levels[i]; + } + } + return cls_mtd_data != NULL; +} + +/** + * gst_analytics_relation_meta_add_one_cls_mtd: + * @instance: Instance of #GstAnalyticsRelationMeta where to add classification instance + * @confidence_level: confidence levels + * @class_quark: labels of this + * classification. Order define index, quark, labels relation. This array + * need to exist as long has this classification meta exist. + * @cls_mtd: (out) (not nullable): Handle updated to newly added classification meta. + * + * Add analytic classification metadata to @instance. + * Returns: Added successfully + * + * Since: 1.24 + */ +gboolean +gst_analytics_relation_meta_add_one_cls_mtd (GstAnalyticsRelationMeta * + instance, gfloat confidence_level, GQuark class_quark, + GstAnalyticsClsMtd * cls_mtd) +{ + static const gsize len = 1; + + return gst_analytics_relation_meta_add_cls_mtd (instance, len, + &confidence_level, &class_quark, cls_mtd); +} + +/** + * gst_analytics_relation_meta_get_cls_mtd: + * @meta: Instance of #GstAnalyticsRelationMeta + * @an_meta_id: Id of #GstAnalyticsClsMtd instance to retrieve + * @rlt: (out caller-allocates)(not nullable): Will be filled with relatable + * meta + * + * Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, + * otherwise this method return FALSE and @rlt is invalid. + * + * Returns: TRUE if successful. + * + * Since 1.24 + */ +gboolean +gst_analytics_relation_meta_get_cls_mtd (GstAnalyticsRelationMeta * meta, + gint an_meta_id, GstAnalyticsClsMtd * rlt) +{ + return gst_analytics_relation_meta_get_mtd (meta, an_meta_id, + gst_analytics_cls_mtd_get_type_quark (), (GstAnalyticsClsMtd *) rlt); +} diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsclassificationmtd.h b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsclassificationmtd.h new file mode 100644 index 0000000000..753952fa2a --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsclassificationmtd.h @@ -0,0 +1,82 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstanalyticsclassificationmtd.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ANALYTICS_CLASSIFICATION_H__ +#define __GST_ANALYTICS_CLASSIFICATION_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +/** + * GstAnalyticsClsMtd: + * @id: Instance identifier. + * @meta: Instance of #GstAnalyticsRelationMeta where the analysis-metadata + * identified by @id is stored. + * + * Handle containing data required to use gst_analytics_cls_mtd APIs. This type + * is generally expected to be allocated on the stack. + * + * Since: 1.24 + */ +typedef struct _GstAnalyticsMtd GstAnalyticsClsMtd; + +GST_ANALYTICS_META_API +GstAnalyticsMtdType gst_analytics_cls_mtd_get_type_quark (void); + +GST_ANALYTICS_META_API +const gchar *gst_analytics_cls_mtd_get_type_name (void); + +GST_ANALYTICS_META_API +gfloat gst_analytics_cls_mtd_get_level (GstAnalyticsClsMtd * handle, + gint index); + +GST_ANALYTICS_META_API +gint gst_analytics_cls_mtd_get_index_by_quark (GstAnalyticsClsMtd * handle, + GQuark quark); + +GST_ANALYTICS_META_API +gsize gst_analytics_cls_mtd_get_length (GstAnalyticsClsMtd * handle); + +GST_ANALYTICS_META_API +GQuark gst_analytics_cls_mtd_get_quark (GstAnalyticsClsMtd * handle, gint index); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_add_cls_mtd (GstAnalyticsRelationMeta * + instance, gsize length, gfloat * confidence_levels, GQuark * class_quarks, + GstAnalyticsClsMtd * cls_mtd); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_add_one_cls_mtd (GstAnalyticsRelationMeta * + instance, gfloat confidence_level, GQuark class_quark, + GstAnalyticsClsMtd * cls_mtd); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_get_cls_mtd (GstAnalyticsRelationMeta * meta, + gint an_meta_id, GstAnalyticsClsMtd * rlt); + +G_END_DECLS +#endif // __GST_ANALYTICS_CLASSIFICATION_H__ diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsmeta.c b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsmeta.c new file mode 100644 index 0000000000..388d320dad --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsmeta.c @@ -0,0 +1,1038 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstanalyticsmeta.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstanalyticsmeta.h" + +GST_DEBUG_CATEGORY_STATIC (an_relation_meta_debug); +#define GST_CAT_AN_RELATION an_relation_meta_debug + +static char invalid_type_name[] = "_invalid"; + +static gint +gst_analytics_relation_meta_get_next_id (GstAnalyticsRelationMeta * meta); + +/** + * gst_analytics_mtd_get_type_quark: + * @instance: Instance of #GstAnalyticsMtd + * Get analysis result type. + * + * Returns: quark associated with type. + * + * Since: 1.24 + */ +GstAnalyticsMtdType +gst_analytics_mtd_get_type_quark (GstAnalyticsMtd * handle) +{ + GstAnalyticsRelatableMtdData *rlt; + rlt = gst_analytics_relation_meta_get_mtd_data (handle->meta, handle->id); + + g_return_val_if_fail (rlt != NULL, + g_quark_from_static_string (invalid_type_name)); + + return rlt->analysis_type; +} + +/** + * gst_analytics_mtd_get_id: + * @instance: Instance of #GstAnalyticsMtd + * Get instance id + * + * Returns: Id of @instance + * + * Since: 1.24 + */ +guint +gst_analytics_mtd_get_id (GstAnalyticsMtd * handle) +{ + return handle->id; +} + +/** + * gst_analytics_mtd_get_size: + * @instance Instance of #GstAnalyticsRelatableMtd + * Get instance size + * + * Returns: Size (in bytes) of this instance or 0 on failure. + * + * Since: 1.24 + */ +gsize +gst_analytics_mtd_get_size (GstAnalyticsMtd * handle) +{ + GstAnalyticsRelatableMtdData *rlt; + rlt = gst_analytics_relation_meta_get_mtd_data (handle->meta, handle->id); + if (rlt == NULL) { + GST_CAT_ERROR (GST_CAT_AN_RELATION, "Invalid parameter"); + return 0; + } + + return rlt->size; +} + +/* + * GstAnalyticsRelationMeta: + * Meta storing analysis-metadata relation information. + * + */ +typedef struct _GstAnalyticsRelationMeta +{ + GstMeta parent_meta; + + /*< Next id > */ + guint next_id; + + /* Adjacency-matrix */ + guint8 **adj_mat; + + /* Lookup (offset relative to analysis_results) of relatable metadata */ + gsize *mtd_data_lookup; + + /* Relation order */ + gsize rel_order; + + /* Increment used when relation order grow */ + gsize rel_order_increment; + + /* Analysis metadata based on GstAnalyticsRelatableMtdData */ + gint8 *analysis_results; + + /* Current writing location in analysis_results buffer */ + gsize offset; + + /* Size of analysis_results buffer */ + gsize max_size; + + /* Increment of analysis_results */ + gsize max_size_increment; + + /* Number of analytic metadata (GstAnalyticsRelatableMtdData) in + * analysis_results */ + gsize length; + +} GstAnalyticsRelationMeta; + +/** + * gst_analytics_relation_get_length: + * @instance Instance of #GstAnalyticsRelationMeta + * Get number of relatable meta attached to instance + * + * Returns: Number of analysis-meta attached to this + * instance. + * Since: 1.24 + */ +gsize +gst_analytics_relation_get_length (GstAnalyticsRelationMeta * instance) +{ + gsize rv; + g_return_val_if_fail (instance, 0); + + rv = instance->length; + return rv; +} + +/* + * gst_analytics_relation_adj_mat_create: + * @order: Order or the adjacency-matrix to create. + * Create a new adjacency-matrix (array of MxN where M and N are equal + * to order). + * + * Returns: new adjacency-matrix + * + * Since: 1.24 + */ +static guint8 ** +gst_analytics_relation_adj_mat_create (gsize order) +{ + guint8 **adj_mat, *data; + gsize sz = sizeof (guint8 *) * order + sizeof (guint8) * order * order; + adj_mat = (guint8 **) g_malloc0 (sz); + data = (guint8 *) (adj_mat + order); + for (gsize r = 0; r < order; r++) { + adj_mat[r] = (data + order * r); + } + return adj_mat; +} + +/** + * gst_analytics_relation_adj_mat_dup: + * @adj_mat: Adjcency-matrix (array or MxN) + * @order: Order of the existing matrix + * @new_order: Order of the matrix to create + * Duplicate adj_mat to a newly allocated array new_order x new_order dimension + * while keep values of adj_mat at the same indexes in the new array. + * + * Returns: New adjacency matrix with maintained values. + * + * Since: 1.24 + */ +static guint8 ** +gst_analytics_relation_adj_mat_dup (guint8 ** adj_mat, gsize order, + gsize new_order) +{ + guint8 **new_adj_mat = gst_analytics_relation_adj_mat_create (new_order); + for (gsize r = 0; r < order; r++) { + memcpy (new_adj_mat[r], adj_mat[r], sizeof (guint8) * order); + } + return new_adj_mat; +} + +/** + * gst_analytics_relation_meta_api_get_type: + * Returns: GType of GstAnalyticsRelationMeta + * + * Since: 1.24 + */ +GType +gst_analytics_relation_meta_api_get_type (void) +{ + static GType type = 0; + static const gchar *tags[] = { NULL }; + if (g_once_init_enter (&type)) { + GType newType = + gst_meta_api_type_register ("GstAnalyticsRelationMetaAPI", tags); + GST_DEBUG_CATEGORY_INIT (an_relation_meta_debug, "anrelmeta", + GST_DEBUG_FG_BLACK, "Content analysis meta relations meta"); + g_once_init_leave (&type, newType); + } + return type; +} + +static gboolean +gst_analytics_relation_meta_init (GstMeta * meta, gpointer params, + GstBuffer * buffer) +{ + GstAnalyticsRelationMeta *rmeta = (GstAnalyticsRelationMeta *) meta; + GstAnalyticsRelationMetaInitParams *rel_params = params; + rmeta->next_id = 0; + + g_return_val_if_fail (params != NULL, FALSE); + + GST_CAT_TRACE (GST_CAT_AN_RELATION, "Relation order:%" G_GSIZE_FORMAT, + *((gsize *) params)); + + rmeta->rel_order_increment = rel_params->initial_relation_order; + rmeta->rel_order = rmeta->rel_order_increment; + if (rmeta->rel_order > 0) { + rmeta->adj_mat = gst_analytics_relation_adj_mat_create (rmeta->rel_order); + rmeta->mtd_data_lookup = g_malloc0 (sizeof (gpointer) * rmeta->rel_order); + } + rmeta->offset = 0; + rmeta->max_size = rmeta->max_size_increment = rel_params->initial_buf_size; + rmeta->analysis_results = g_malloc (rel_params->initial_buf_size); + rmeta->length = 0; + + if (buffer->pool) + GST_META_FLAG_SET (meta, GST_META_FLAG_POOLED); + + GST_CAT_DEBUG (GST_CAT_AN_RELATION, + "Content analysis meta-relation meta(%p, order=%" G_GSIZE_FORMAT + ") created for buffer(%p)", (gpointer) rmeta, *(gsize *) params, + (gpointer) buffer); + return TRUE; +} + +static void +gst_analytics_relation_meta_free (GstMeta * meta, GstBuffer * buffer) +{ + GstAnalyticsRelationMeta *rmeta = (GstAnalyticsRelationMeta *) meta; + gpointer state = NULL; + GstAnalyticsMtd mtd; + GstAnalyticsRelatableMtdData *data; + GST_CAT_TRACE (GST_CAT_AN_RELATION, + "Content analysis meta-data(%p) freed for buffer(%p)", + (gpointer) rmeta, (gpointer) buffer); + + /* call custom free function if set */ + while (gst_analytics_relation_meta_iterate (rmeta, &state, 0, &mtd)) { + data = gst_analytics_relation_meta_get_mtd_data (mtd.meta, mtd.id); + if (data->free != NULL) { + data->free (data); + } + } + + g_free (rmeta->analysis_results); + g_free (rmeta->adj_mat); + g_free (rmeta->mtd_data_lookup); +} + +static gboolean +gst_analytics_relation_meta_transform (GstBuffer * transbuf, + GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data) +{ + + GST_CAT_TRACE (GST_CAT_AN_RELATION, "meta transform %s", + g_quark_to_string (type)); + + if (GST_META_TRANSFORM_IS_COPY (type)) { + GstAnalyticsRelationMeta *rmeta = (GstAnalyticsRelationMeta *) meta; + GstAnalyticsRelationMeta *new = (GstAnalyticsRelationMeta *) + gst_buffer_get_meta (transbuf, GST_ANALYTICS_RELATION_META_API_TYPE); + + if (new == NULL) { + GstAnalyticsRelationMetaInitParams init_params = { + rmeta->rel_order, rmeta->max_size + }; + + GST_CAT_TRACE (GST_CAT_AN_RELATION, + "meta transform creating new meta rel_order:%" G_GSIZE_FORMAT + " max_size:%" G_GSIZE_FORMAT, + init_params.initial_relation_order, init_params.initial_buf_size); + + new = + gst_buffer_add_analytics_relation_meta_full (transbuf, &init_params); + } + + if (new->offset == 0) { + + if (new->rel_order < rmeta->rel_order) { + g_free (new->adj_mat); + g_free (new->mtd_data_lookup); + new->adj_mat = gst_analytics_relation_adj_mat_create (rmeta->rel_order); + new->mtd_data_lookup = g_malloc0 (sizeof (gpointer) * rmeta->rel_order); + new->rel_order = rmeta->rel_order; + } + + if (new->max_size < rmeta->max_size) { + g_free (new->analysis_results); + new->analysis_results = g_malloc (rmeta->max_size); + new->max_size = rmeta->max_size; + } + + if (rmeta->rel_order == new->rel_order) { + memcpy (new->adj_mat + new->rel_order, rmeta->adj_mat, + rmeta->rel_order * rmeta->rel_order); + } else { + /* When destination adj_mat has a higher order than source we need + * to copy by row to have the correct alignment */ + for (gsize r = 0; r < rmeta->rel_order; r++) { + memcpy (new->adj_mat[r], rmeta->adj_mat[r], rmeta->rel_order); + } + } + memcpy (new->mtd_data_lookup, rmeta->mtd_data_lookup, + sizeof (gpointer) * rmeta->rel_order); + memcpy (new->analysis_results, rmeta->analysis_results, rmeta->offset); + + new->length = rmeta->length; + new->next_id = rmeta->next_id; + new->offset = rmeta->offset; + + return TRUE; + } else { + g_warning ("Trying to copy GstAnalyticsRelationMeta into non-empty meta"); + g_debug ("ofs:%" G_GSIZE_FORMAT, new->offset); + + return FALSE; + } + } else if (GST_META_TRANSFORM_IS_CLEAR (type)) { + GstAnalyticsRelationMeta *rmeta = (GstAnalyticsRelationMeta *) meta; + gsize adj_mat_data_size = + (sizeof (guint8) * rmeta->rel_order * rmeta->rel_order); + + rmeta->next_id = 0; + rmeta->offset = 0; + rmeta->length = 0; + if (adj_mat_data_size) { + /* Only clear data and not lines addresses occupying begining of this + * array. */ + memset (rmeta->adj_mat + rmeta->rel_order, 0, adj_mat_data_size); + } + + return TRUE; + } + + return FALSE; +} + + +const GstMetaInfo * +gst_analytics_relation_meta_get_info (void) +{ + static const GstMetaInfo *info = NULL; + if (g_once_init_enter ((GstMetaInfo **) & info)) { + const GstMetaInfo *meta = + gst_meta_register (GST_ANALYTICS_RELATION_META_API_TYPE, + "GstAnalyticsRelationMeta", + sizeof (GstAnalyticsRelationMeta), + gst_analytics_relation_meta_init, + gst_analytics_relation_meta_free, + gst_analytics_relation_meta_transform); + g_once_init_leave ((GstMetaInfo **) & info, (GstMetaInfo *) meta); + } + return info; +} + +/* + * gst_analytics_relation_meta_bfs: + * @start: start vertex + * @adj_mat: graph's adjacency matrix + * @adj_mat_order: order of the adjacency matrix (number of vertex in the graph) + * @edge_mask: allow to select edge type we are interested by. + * @max_span: Maximum number of edge to traverse from start vertex while + * exploring graph. + * @level: (out)(not nullable)(array length=adj_mat_order): Array that will be + * filled with number of edge to traverse to reach @start from the vertex + * identified by the array index. (Ex: start=1 and level[3]=2, mean we need + * to traverse 2 edges from vertex 2 to vertex 3. A value of -1 in @level + * mean this vertex is un-reachable considering @edge_mask, @max_span and + * @adj_mat. + * @parent: (out)(array length=adj_mat_order): Array that will be filled with + * shortest path information. The shortest path from vertex X and vertex + * @start, where X is the index of @parent array. To find each node on the + * path we need to recursively do ...parent[parent[parent[X]]] until value + * is @start. Value at index Y equal to -1 mean there's no path from + * vertex Y to vertex @start. + * + * This function can be used to find the existence of relation paths that + * emerge from a analysis-metadata (@start). The existence verification can + * also be parametrize by only considering certain types of relation + * (@edge_mask), a maximum intermediates analysis-metadata (@max_span). A + * usage exemple can be found in @gst_analytics_relation_meta_exist. + */ +static void +gst_analytics_relation_meta_bfs (gint start, const guint8 ** adj_mat, + gsize adj_mat_order, guint8 edge_mask, gsize max_span, gint * level, + gint * parent) +{ + GSList *frontier = NULL; + GSList *iter; + GSList *next_frontier; + gsize i = 1; + memset (level, -1, sizeof (gint) * adj_mat_order); + memset (parent, -1, sizeof (gint) * adj_mat_order); + + GST_CAT_TRACE (GST_CAT_AN_RELATION, + "Performing bfs to find relation(%x) starting from %d with less than %" + G_GSIZE_FORMAT " edges from start", edge_mask, start, max_span); + + // vertex that has a relation with itself + if (adj_mat[start][start] & edge_mask) { + level[start] = 0; + } + + frontier = g_slist_prepend (frontier, GINT_TO_POINTER (start)); + + while (frontier && i <= max_span) { + next_frontier = NULL; + for (iter = frontier; iter; iter = iter->next) { + for (gsize j = 0; j < adj_mat_order; j++) { + if (adj_mat[(gsize) GPOINTER_TO_INT (iter->data)][j] & edge_mask) { + if (level[j] == -1) { + level[j] = i; + parent[j] = GPOINTER_TO_INT (iter->data); + GST_CAT_TRACE (GST_CAT_AN_RELATION, "Parent of %" G_GSIZE_FORMAT + " is %d", j, parent[j]); + next_frontier = + g_slist_prepend (next_frontier, GINT_TO_POINTER ((gint) j)); + } + } + } + } + g_slist_free (frontier); + frontier = next_frontier; + i++; + } + g_slist_free (frontier); +} + +/* + * gst_analytics_relation_meta_get_next_id: + * @meta a #GstAnalyticsRelationMeta from which we want to get next id. + * + * Get next id and prepare for future request. + * + * Returns: next id + * + */ +static gint +gst_analytics_relation_meta_get_next_id (GstAnalyticsRelationMeta * meta) +{ + g_return_val_if_fail (meta != NULL, -1); + return g_atomic_int_add (&meta->next_id, 1); +} + +/** + * gst_analytics_relation_meta_get_relation: + * @meta: (transfer none): a #GstAnalyticsRelationMeta + * @an_meta_first_id: Id of first analysis-meta + * @an_meta_second_id: Id of second analysis-meta + * + * Returns: relation description between first and second analysis-meta. + * + * Get relations between first and second analysis-meta. + * Ids (@an_meta_first_id and @an_meta_second_id) must be from a call to + * @gst_analytics_mtd_get_id (handle). + * + * Since: 1.24 + */ +GstAnalyticsRelTypes +gst_analytics_relation_meta_get_relation (GstAnalyticsRelationMeta * meta, + gint an_meta_first, gint an_meta_second) +{ + GstAnalyticsRelTypes types = GST_ANALYTICS_REL_TYPE_NONE; + g_return_val_if_fail (meta, GST_ANALYTICS_REL_TYPE_NONE); + + g_return_val_if_fail (meta->adj_mat != NULL, GST_ANALYTICS_REL_TYPE_NONE); + if (meta->rel_order > an_meta_first && meta->rel_order > an_meta_second) { + types = meta->adj_mat[an_meta_first][an_meta_second]; + } else { + GST_CAT_DEBUG (GST_CAT_AN_RELATION, + "an_meta_first(%i) and an_meta_second(%i) must be inferior to %" + G_GSIZE_FORMAT, an_meta_first, an_meta_second, meta->rel_order); + + if (an_meta_first >= meta->rel_order) { + GST_CAT_ERROR (GST_CAT_AN_RELATION, + "an_meta_first(%i) must be from a call to " + "gst_analytics_mtd_get_id(...)", an_meta_first); + } + + if (an_meta_second >= meta->rel_order) { + GST_CAT_ERROR (GST_CAT_AN_RELATION, + "an_meta_second(%i) must be from a call to " + "gst_analytics_mtd_get_id(...)", an_meta_second); + } + } + return types; +} + +/** + * gst_analytics_relation_meta_set_relation: + * @meta: (transfer none): Parameter to receive new maximum number of + * analysis-meta described by relation. + * @type: a #GstAnalyticsRelTypes defining relation between two analysis-meta + * @an_meta_first_id: first meta id + * @an_meta_second_id: second meta id + * + * Sets the relation (#GstAnalyticsRelTypes) between @an_meta_first and + * @an_meta_second. + * Ids must have been obtained a call to + * @gst_analytics_mtd_get_id(handle). + * + * Returns: TRUE on success and FALSE on failure. + * + * Since: 1.24 + */ +gboolean +gst_analytics_relation_meta_set_relation (GstAnalyticsRelationMeta * meta, + GstAnalyticsRelTypes type, gint an_meta_first_id, gint an_meta_second_id) +{ + g_return_val_if_fail (type < GST_ANALYTICS_REL_TYPE_LAST, FALSE); + g_return_val_if_fail (meta, FALSE); + if (an_meta_first_id >= meta->rel_order + || an_meta_second_id >= meta->rel_order) { + GST_CAT_ERROR (GST_CAT_AN_RELATION, "Invalid parameter"); + return FALSE; + } + meta->adj_mat[an_meta_first_id][an_meta_second_id] = type; + GST_CAT_TRACE (GST_CAT_AN_RELATION, + "Relation %x set between %d and %d", + type, an_meta_first_id, an_meta_second_id); + return TRUE; +} + +/** + * gst_analytics_relation_meta_exist: + * @rmeta: (transfer none): a #GstAnalyticsRelationMeta describing analysis-meta + * relation + * @an_meta_first_id: First analysis-meta + * @an_meta_second_id: Second analysis-meta + * @max_relation_span: Maximum number of relation between @an_meta_first_id and + * @an_meta_second_id. + * A value of 1 mean only only consider direct relation. + * @cond_types: condition on relation types. + * @relations_path: (transfer full)(nullable)(out caller-allocates)(array) + * (element-type gint): + * If not NULL this list will be filled with relation path between + * @an_meta_first_id and + * @an_meta_second_id. List value should be access with GSList API. Use + * GPOINTER_TO_INT(iter->data) where iter is a GSList element to get + * analysis-meta id on the relation path. Free this list with g_slist_free + * (@relations_path) after using. + * + * Verify existence of relation(s) between @an_meta_first_d and + * @an_meta_second_id according to relation condition @cond_types. It optionally + * also return a shortest path of relations ( compliant with @cond_types) + * between @an_meta_first_id and @an_meta_second_id. + * + * Returns: TRUE if a relation between exit between @an_meta_first_id and + * @an_meta_second_id, otherwise FALSE. + * + * Since 1.24 + */ +gboolean +gst_analytics_relation_meta_exist (GstAnalyticsRelationMeta * rmeta, + gint an_meta_first_id, + gint an_meta_second_id, + gint max_relation_span, + GstAnalyticsRelTypes cond_types, GArray ** relations_path) +{ + gboolean rv = FALSE; + guint8 **adj_mat; + gsize adj_mat_order, span; + GArray *path = NULL; + gint *level, i, path_left; + gint *parent; + g_return_val_if_fail (rmeta, FALSE); + + if (!rmeta) { + GST_CAT_ERROR (GST_CAT_AN_RELATION, "Invalid parameter"); + return EINVAL; + } + adj_mat_order = rmeta->rel_order; + + if (adj_mat_order < (an_meta_first_id + 1) + || adj_mat_order < (an_meta_second_id + 1)) { + + GST_CAT_DEBUG (GST_CAT_AN_RELATION, + "Testing relation existence for analysis-meta that have no index in " + "adj-mat."); + + return FALSE; + } + + adj_mat = rmeta->adj_mat; + if (max_relation_span < 0) { + span = G_MAXSIZE; + } + // If we're only considering the direct relation (@max_relation_span <= 1) we can directly read the + // adjacency-matrix, + if (max_relation_span == 0 || max_relation_span == 1) { + rv = (adj_mat[an_meta_first_id][an_meta_second_id] & cond_types) != 0; + if (rv && relations_path) { + path = *relations_path; + + /* re-use array if possible */ + if (path != NULL && (g_array_get_element_size (path) != sizeof (gint) + || path->len < 2)) { + g_array_free (path, TRUE); + path = NULL; + } + + if (path == NULL) { + path = g_array_sized_new (FALSE, FALSE, sizeof (gint), 2); + } + g_array_index (path, gint, 0) = an_meta_first_id; + g_array_index (path, gint, 1) = an_meta_second_id; + path->len = 2; + *relations_path = path; + } + } else { + + level = g_malloc (sizeof (gint) * adj_mat_order); + parent = g_malloc (sizeof (gint) * adj_mat_order); + gst_analytics_relation_meta_bfs (an_meta_first_id, + (const guint8 **) adj_mat, adj_mat_order, cond_types, span, level, + parent); + + GST_CAT_TRACE (GST_CAT_AN_RELATION, "Adj order:%" G_GSIZE_FORMAT, + adj_mat_order); + + rv = level[an_meta_second_id] != -1; + if (rv && relations_path) { + path_left = level[an_meta_second_id] + 1; + i = parent[an_meta_second_id]; + if (i != -1) { + path = *relations_path; + + /* re-use array if possible */ + if (path != NULL && (g_array_get_element_size (path) != sizeof (gint) + || path->len < path_left)) { + g_array_free (path, TRUE); + path = NULL; + } + + if (path == NULL) { + path = g_array_sized_new (FALSE, FALSE, sizeof (gint), path_left); + } + + path->len = path_left; + g_array_index (path, gint, --path_left) = an_meta_second_id; + //path = g_slist_prepend (path, GINT_TO_POINTER (an_meta_second_id)); + while (i != -1 && i != an_meta_second_id) { + GST_CAT_TRACE (GST_CAT_AN_RELATION, "Relation parent of %d", i); + g_array_index (path, gint, --path_left) = i; + //path = g_slist_prepend (path, GINT_TO_POINTER (i)); + i = parent[i]; + } + + while (path_left > 0) { + g_array_index (path, gint, --path_left) = -1; + } + } + *relations_path = path; + } + + g_free (level); + g_free (parent); + } + + GST_CAT_TRACE (GST_CAT_AN_RELATION, + "Relation %x between %d and %d %s", + cond_types, an_meta_first_id, an_meta_second_id, + rv ? "exist" : "does not exist"); + return rv; +} + + +/** + * gst_buffer_add_analytics_relation_meta: + * @buffer: (transfer none): a #GstBuffer + * + * Attach a analysis-results-meta-relation meta (#GstAnalyticsRelationMeta)to @buffer. + * + * A #GstAnalyticsRelationMeta is a metadata describing relation between other + * analysis meta. It's more efficient to use #gst_buffer_add_analytics_relation_meta_full + * and providing the maximum number of analysis meta that will attached to a buffer. + * + * Returns: (transfer none) (nullable) : Newly attached #GstAnalyticsRelationMeta + * + * Since 1.24 + */ +GstAnalyticsRelationMeta * +gst_buffer_add_analytics_relation_meta (GstBuffer * buffer) +{ + GstAnalyticsRelationMetaInitParams init_params = { 5, 1024 }; + return gst_buffer_add_analytics_relation_meta_full (buffer, &init_params); +} + +/** + * gst_buffer_add_analytics_relation_meta_full: + * @buffer: (transfer none): a #GstBuffer + * @init_params: Initialization parameters + * + * Attache a analysis-results relation-meta (#GstAnalyticsRelationMeta) to @buffer. + * + * A #GstAnalyticsRelationMeta is a metadata describing relation between other + * analysis meta. + * + * Returns: (transfer none) (nullable) : Newly attached #GstAnalyticsRelationMeta + * + * Since 1.24 + */ +GstAnalyticsRelationMeta * +gst_buffer_add_analytics_relation_meta_full (GstBuffer * buffer, + GstAnalyticsRelationMetaInitParams * init_params) +{ + GstAnalyticsRelationMeta *meta = NULL; + g_return_val_if_fail (init_params != NULL, NULL); + g_return_val_if_fail (buffer != NULL, NULL); + + // We only want one relation-meta on a buffer, will check if one already + // exist. + meta = (GstAnalyticsRelationMeta *) gst_buffer_get_meta (buffer, + GST_ANALYTICS_RELATION_META_API_TYPE); + + if (!meta) { + meta = + (GstAnalyticsRelationMeta *) gst_buffer_add_meta (buffer, + GST_ANALYTICS_RELATION_META_INFO, init_params); + } + + return meta; +} + +/** + * gst_buffer_get_analytics_relation_meta: + * @buffer: a #GstBuffer + * + * Retrives the meta or %NULL if it doesn't exist + * + * Returns: (transfer none) (nullable) :The #GstAnalyticsRelationMeta if there is one + * Since: 1.24: + */ +GstAnalyticsRelationMeta * +gst_buffer_get_analytics_relation_meta (GstBuffer * buffer) +{ + return (GstAnalyticsRelationMeta *) + gst_buffer_get_meta (buffer, GST_ANALYTICS_RELATION_META_API_TYPE); +} + +/** + * gst_analytics_relation_meta_add_mtd: (skip) + * @meta: Instance + * @type: Type of relatable (#GstAnalyticsRelatableMtd) + * @size: Size required + * @rlt_mtd: Updated handle + * + * Add a relatable metadata to @meta. This method is meant to be used by + * new struct sub-classing GstAnalyticsRelatableMtd. + * + * Returns: New GstAnalyticsRelatableMtdData instance. + * + * Since 1.24 + */ +GstAnalyticsRelatableMtdData * +gst_analytics_relation_meta_add_mtd (GstAnalyticsRelationMeta * meta, + GstAnalyticsMtdType type, gsize size, GstAnalyticsMtd * rlt_mtd) +{ + gsize new_size = size + meta->offset; + GstAnalyticsRelatableMtdData *dest = NULL; + gpointer mem; + guint8 **new_adj_mat; + gsize new_mem_cap, new_rel_order; + GST_CAT_TRACE (GST_CAT_AN_RELATION, "Adding relatable metadata to rmeta %p", + meta); + + if (new_size > meta->max_size) { + + if (new_size > meta->max_size_increment + meta->offset) { + new_mem_cap = new_size; + } else { + new_mem_cap = meta->max_size + meta->max_size_increment; + } + + mem = g_realloc (meta->analysis_results, new_mem_cap); + meta->max_size = new_mem_cap; + meta->analysis_results = mem; + } + + if (meta->length >= meta->rel_order) { + new_rel_order = meta->rel_order + meta->rel_order_increment; + new_adj_mat = gst_analytics_relation_adj_mat_dup (meta->adj_mat, + meta->rel_order, new_rel_order); + g_free (meta->adj_mat); + meta->adj_mat = new_adj_mat; + + mem = g_realloc (meta->mtd_data_lookup, sizeof (gpointer) * new_rel_order); + meta->mtd_data_lookup = mem; + meta->rel_order = new_rel_order; + } + + if (new_size <= meta->max_size && (meta->length < meta->rel_order)) { + dest = + (GstAnalyticsRelatableMtdData *) (meta->analysis_results + + meta->offset); + dest->analysis_type = type; + dest->id = gst_analytics_relation_meta_get_next_id (meta); + dest->size = size; + dest->free = NULL; + meta->mtd_data_lookup[dest->id] = meta->offset; + meta->offset += dest->size; + meta->length++; + rlt_mtd->id = dest->id; + rlt_mtd->meta = meta; + GST_CAT_TRACE (GST_CAT_AN_RELATION, "Add %p relatable type=%s (%" + G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT ").", dest, + g_quark_to_string (type), new_size, meta->max_size); + } else { + GST_CAT_ERROR (GST_CAT_AN_RELATION, + "Failed to add relatable, out-of-space (%" G_GSIZE_FORMAT " / %" + G_GSIZE_FORMAT ").", new_size, meta->max_size); + } + return dest; +} + +/** + * gst_analytics_relation_meta_get_mtd: + * @meta: Instance of GstAnalyticsRelationMeta + * @an_meta_id: Id of GstAnalyticsMtd instance to retrieve + * @type: Filter on a specific type of analysis, use + * %GST_ANALYTICS_MTD_TYPE_ANY to match any type + * @rlt: (out caller-allocates)(not nullable): Will be filled with relatable + * meta + * + * Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, + * otherwise this method return FALSE and @rlt is invalid. + * + * Returns: TRUE if successful. + * + * Since 1.24 + */ +gboolean +gst_analytics_relation_meta_get_mtd (GstAnalyticsRelationMeta * meta, + gint an_meta_id, GstAnalyticsMtdType type, GstAnalyticsMtd * rlt) +{ + GstAnalyticsRelatableMtdData *d; + + g_return_val_if_fail (meta, FALSE); + g_return_val_if_fail (rlt, FALSE); + + rlt->meta = NULL; + + if (an_meta_id < 0 || an_meta_id >= meta->length) { + GST_CAT_ERROR (GST_CAT_AN_RELATION, "Invalid parameter"); + return FALSE; + } + + d = gst_analytics_relation_meta_get_mtd_data (meta, an_meta_id); + if (d == NULL) + return FALSE; + + if (d->analysis_type == 0) { + return FALSE; + } + + if (type != GST_ANALYTICS_MTD_TYPE_ANY && d->analysis_type != type) { + return FALSE; + } + + rlt->meta = meta; + rlt->id = an_meta_id; + + return TRUE; +} + +/** + * gst_analytics_relation_meta_get_mtd_data: (skip) + * @meta: Instance of GstAnalyticsRelationMeta + * @an_meta_id: Id of GstAnalyticsMtd instance to retrieve + * + * Returns:(nullable): Instance of GstAnalyticsRelatableMtdData + * + * Since 1.24 + */ +GstAnalyticsRelatableMtdData * +gst_analytics_relation_meta_get_mtd_data (GstAnalyticsRelationMeta * + meta, gint an_meta_id) +{ + GstAnalyticsRelatableMtdData *rv; + g_return_val_if_fail (meta, NULL); + if (an_meta_id < 0 || an_meta_id >= meta->rel_order) { + GST_CAT_ERROR (GST_CAT_AN_RELATION, "Invalid parameter"); + return NULL; + } + rv = (GstAnalyticsRelatableMtdData *) + (meta->mtd_data_lookup[an_meta_id] + meta->analysis_results); + return rv; +} + + +/** + * gst_analytics_relation_meta_get_direct_related: + * @meta: GstAnalyticsRelationMeta instance where to query for + * GstAnalyticsRelatableMtd. + * @an_meta_id: Id of GstAnalyticsMtd involved in relation to query + * @relation_type: Type of relation to filter on. + * @type: Type of GstAnalyticsMtd to filter on + * @state: (inout) (not nullable): Opaque data to store state of the query. + * If @state point to NULL, the first analytics-metadata directly related + * to @an_meta_id will be set in @rlt_mtd. Doesn't need to be free. + * @rlt_mtd: Handle updated to directly related relatable meta. + * + * Returns: TRUE if @rlt_mtd was updated, other wise FALSE + * + * Since 1.24 + */ +gboolean +gst_analytics_relation_meta_get_direct_related (GstAnalyticsRelationMeta * meta, + gint an_meta_id, GstAnalyticsRelTypes relation_type, + GstAnalyticsMtdType type, gpointer * state, GstAnalyticsMtd * rlt_mtd) +{ + guint8 **adj_mat; + gsize adj_mat_order; + GstAnalyticsRelationMeta *rmeta = (GstAnalyticsRelationMeta *) meta; + GstAnalyticsRelatableMtdData *rlt_mtd_data = NULL; + gsize i; + + GST_CAT_TRACE (GST_CAT_AN_RELATION, + "Looking for %s related to %d by %d", g_quark_to_string (type), + an_meta_id, relation_type); + + g_return_val_if_fail (rmeta != NULL, FALSE); + g_return_val_if_fail (type != 0, FALSE); + + if (state) { + if (*state) { + i = ~G_MINSSIZE & (GPOINTER_TO_SIZE (*state) + 1); + } else { + i = 0; + *state = GSIZE_TO_POINTER (G_MINSSIZE | i); + } + } else { + i = 0; + } + + adj_mat_order = meta->rel_order; + + if (adj_mat_order < (an_meta_id + 1)) { + GST_CAT_DEBUG (GST_CAT_AN_RELATION, + "Testing relation existence for analysis-meta that have no index in " + "adj-mat."); + return FALSE; + } + + rlt_mtd->meta = rmeta; + adj_mat = meta->adj_mat; + for (; i < adj_mat_order; i++) { + if (adj_mat[an_meta_id][i] & relation_type) { + rlt_mtd_data = (GstAnalyticsRelatableMtdData *) + (meta->mtd_data_lookup[i] + meta->analysis_results); + rlt_mtd->id = rlt_mtd_data->id; + if (gst_analytics_mtd_get_type_quark (rlt_mtd) == type) { + if (state) { + *state = GSIZE_TO_POINTER (G_MINSSIZE | i); + } + GST_CAT_TRACE (GST_CAT_AN_RELATION, "Found match at %" G_GSIZE_FORMAT, + i); + break; + } + rlt_mtd_data = NULL; + } + } + + return rlt_mtd_data != NULL; +} + +/** + * gst_analytics_relation_meta_iterate: + * @meta: Instance of GstAnalyticsRelationMeta + * @state: Opaque data to store iteration state, initialize to NULL, no need to + * free it. + * @type: Type of GstAnalyticsMtd to iterate on or use + * %GST_ANALYTICS_MTD_TYPE_ANY for any. + * @rlt_mtd: Handle updated to iterated GstAnalyticsRelatableMtd. + * + * Returns: FALSE if end was reached and iteration is completed. + * + * Since 1.24 + */ +gboolean +gst_analytics_relation_meta_iterate (GstAnalyticsRelationMeta * meta, + gpointer * state, GstAnalyticsMtdType type, GstAnalyticsMtd * rlt_mtd) +{ + gsize index; + gsize len = gst_analytics_relation_get_length (meta); + GstAnalyticsRelatableMtdData *rlt_mtd_data = NULL; + + g_return_val_if_fail (rlt_mtd != NULL, FALSE); + + if (*state) { + index = ~G_MINSSIZE & (GPOINTER_TO_SIZE (*state) + 1); + } else { + index = 0; + *state = GSIZE_TO_POINTER (G_MINSSIZE | index); + } + for (; index < len; index++) { + rlt_mtd_data = (GstAnalyticsRelatableMtdData *) + (meta->mtd_data_lookup[index] + meta->analysis_results); + rlt_mtd->id = rlt_mtd_data->id; + rlt_mtd->meta = meta; + if (type == GST_ANALYTICS_MTD_TYPE_ANY || + gst_analytics_mtd_get_type_quark (rlt_mtd) == type) { + *state = GSIZE_TO_POINTER (G_MINSSIZE | index); + return TRUE; + } + } + + return FALSE; +} diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsmeta.h b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsmeta.h new file mode 100644 index 0000000000..23356dee08 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsmeta.h @@ -0,0 +1,226 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstanalyticsmeta.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __GST_ANALYTICS_META_H__ +#define __GST_ANALYTICS_META_H__ + +#include +#include + +G_BEGIN_DECLS +#define GST_INF_RELATION_SPAN -1 +#define GST_AN_RELATION_META_TAG "GST-ANALYSIS-RELATION-META-TAG" +typedef struct _GstAnalyticsMtd GstAnalyticsMtd; + +/** + * GstAnalyticsMtdType: + * + * Type of analytics meta data + * + * Since: 1.24 + */ +typedef guint32 GstAnalyticsMtdType; + +/** + * GST_ANALYTICS_MTD_TYPE_ANY: + * + * A wildcard matching any type of analysis + * + * Since: 1.24 + */ + +#define GST_ANALYTICS_MTD_TYPE_ANY (0) + +#define GST_ANALYTICS_MTD_CAST(mtd) \ + ((GstAnalyticsMtd *)(mtd)) + +typedef struct _GstAnalyticsRelatableMtdData GstAnalyticsRelatableMtdData; +typedef struct _GstAnalyticsRelationMeta GstAnalyticsRelationMeta; + +/** + * GstAnalyticsMtd: + * @id: Instance identifier. + * @meta: Instance of #GstAnalyticsRelationMeta where the analysis-metadata + * identified by @id is stored. + * + * Handle containing data required to use gst_analytics_mtd API. This type + * is generally expected to be allocated on the stack. + * + * Since: 1.24 + */ +struct _GstAnalyticsMtd +{ + guint id; + GstAnalyticsRelationMeta *meta; +}; + +/** + * GstAnalyticsRelatableMtdData: + * @analysis_type: Identify the type of analysis-metadata + * @id: Instance identifier. + * @size: Size in bytes of the instance + * + * Base structure for analysis-metadata that can be placed in relation. Only + * other analysis-metadata based on GstAnalyticsRelatableMtdData should + * directly use this structure. + * + * Since: 1.24 + */ +struct _GstAnalyticsRelatableMtdData +{ + GstAnalyticsMtdType analysis_type; + guint id; + gsize size; + + void (*free) (GstAnalyticsRelatableMtdData * mtd_data); + + gpointer _gst_reserved[GST_PADDING]; +}; + +GST_ANALYTICS_META_API +GstAnalyticsMtdType gst_analytics_mtd_get_type_quark (GstAnalyticsMtd * instance); + +GST_ANALYTICS_META_API +guint gst_analytics_mtd_get_id (GstAnalyticsMtd * instance); + +GST_ANALYTICS_META_API +gsize gst_analytics_mtd_get_size (GstAnalyticsMtd * instance); + +typedef struct _GstAnalyticsRelationMetaInitParams + GstAnalyticsRelationMetaInitParams; + +#define GST_ANALYTICS_RELATION_META_API_TYPE \ + (gst_analytics_relation_meta_api_get_type()) + +#define GST_ANALYTICS_RELATION_META_INFO \ + (gst_analytics_relation_meta_get_info()) + +/** + * GstAnalyticsRelTypes: + * @GST_ANALYTICS_REL_TYPE_NONE: No relation + * @GST_ANALYTICS_REL_TYPE_IS_PART_OF: First analysis-meta is part of second analysis-meta + * @GST_ANALYTICS_REL_TYPE_CONTAIN: First analysis-meta contain second analysis-meta. + * @GST_ANALYTICS_REL_TYPE_RELATE: First analysis-meta relate to second analysis-meta. + * @GST_ANALYTICS_REL_TYPE_LAST: reserved + * @GST_ANALYTICS_REL_TYPE_ANY: Only use for criteria. + * + * Since: 1.24 + */ +typedef enum +{ + GST_ANALYTICS_REL_TYPE_NONE = 0, + GST_ANALYTICS_REL_TYPE_IS_PART_OF = (1 << 1), + GST_ANALYTICS_REL_TYPE_CONTAIN = (1 << 2), + GST_ANALYTICS_REL_TYPE_RELATE_TO = (1 << 3), + GST_ANALYTICS_REL_TYPE_LAST = (1 << 4), + GST_ANALYTICS_REL_TYPE_ANY = G_MAXINT +} GstAnalyticsRelTypes; + + +/** + * GstAnalyticsRelationMetaInitParams: + * @initial_relation_order: Initial relations order. + * @initial_buf_size: Buffer size in bytes to store relatable metadata + * + * GstAnalyticsRelationMeta initialization parameters. + * + * Since: 1.24 + */ +struct _GstAnalyticsRelationMetaInitParams +{ + gsize initial_relation_order; + gsize initial_buf_size; +}; + +GST_ANALYTICS_META_API GType gst_analytics_relation_meta_api_get_type (void); + +GST_ANALYTICS_META_API +const GstMetaInfo *gst_analytics_relation_meta_get_info (void); + +GST_ANALYTICS_META_API +gsize +gst_analytics_relation_get_length (GstAnalyticsRelationMeta * + instance); + +GST_ANALYTICS_META_API +GstAnalyticsRelTypes +gst_analytics_relation_meta_get_relation (GstAnalyticsRelationMeta * meta, + gint an_meta_first_id, gint an_meta_second_id); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_set_relation (GstAnalyticsRelationMeta + * meta, GstAnalyticsRelTypes type, gint an_meta_first_id, + gint an_meta_second_id); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_exist (GstAnalyticsRelationMeta * + rmeta, gint an_meta_first_id, gint an_meta_second_id, + gint max_relation_span, GstAnalyticsRelTypes cond_types, + GArray ** relations_path); + +GST_ANALYTICS_META_API +GstAnalyticsRelationMeta * +gst_buffer_add_analytics_relation_meta (GstBuffer * buffer); + +GST_ANALYTICS_META_API +GstAnalyticsRelationMeta * +gst_buffer_add_analytics_relation_meta_full (GstBuffer * buffer, + GstAnalyticsRelationMetaInitParams * init_params); + +GST_ANALYTICS_META_API +GstAnalyticsRelationMeta * +gst_buffer_get_analytics_relation_meta (GstBuffer * buffer); + +GST_ANALYTICS_META_API +GstAnalyticsRelatableMtdData * +gst_analytics_relation_meta_add_mtd (GstAnalyticsRelationMeta * meta, + GstAnalyticsMtdType type, gsize size, GstAnalyticsMtd * rlt_mtd); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_get_mtd (GstAnalyticsRelationMeta * + meta, gint an_meta_id, GstAnalyticsMtdType type, GstAnalyticsMtd * rlt); + +GST_ANALYTICS_META_API +GstAnalyticsRelatableMtdData * +gst_analytics_relation_meta_get_mtd_data (GstAnalyticsRelationMeta * meta, + gint an_meta_id); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_iterate (GstAnalyticsRelationMeta * + meta, gpointer * state, GstAnalyticsMtdType type, + GstAnalyticsMtd * rlt_mtd); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_get_direct_related (GstAnalyticsRelationMeta * meta, + gint an_meta_id, GstAnalyticsRelTypes relation_type, + GstAnalyticsMtdType type, gpointer * state, GstAnalyticsMtd * rlt_mtd); + +G_END_DECLS +#endif // __GST_ANALYTICS_META_H__ diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjectdetectionmtd.c b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjectdetectionmtd.c new file mode 100644 index 0000000000..4b7dc34b4f --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjectdetectionmtd.c @@ -0,0 +1,214 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstanalyticsobjectdetectionmtd.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstanalyticsobjectdetectionmtd.h" + +#define GST_RELATABLE_MTD_OD_TYPE_NAME "object-detection" + +static char type[] = GST_RELATABLE_MTD_OD_TYPE_NAME; + +typedef struct _GstAnalyticsODMtdData GstAnalyticsODMtdData; + +/** + * GstAnalyticsODMtd: + * @parent: parent #GstAnalyticsMtd + * @object_type: Type of object + * @x: x component of upper-left corner + * @y: y component of upper-left corner + * @w: bounding box width + * @h: bounding box height + * @location_confidence_lvl: Confidence on object location + * + * Store information on results of object detection + * + * Since: 1.24 + */ +struct _GstAnalyticsODMtdData +{ + GstAnalyticsRelatableMtdData parent; + GQuark object_type; + gint x; + gint y; + gint w; + gint h; + gfloat location_confidence_lvl; +}; + + +/** + * gst_analytics_od_mtd_get_type_quark: + * Get a quark that represent object-detection metadata type + * + * Returns: Quark of #GstAnalyticsMtd type + * + * Since: 1.24 + */ +GstAnalyticsMtdType +gst_analytics_od_mtd_get_type_quark (void) +{ + return g_quark_from_static_string (type); +} + +/** + * gst_analytics_od_mtd_get_type_name: + * Get a text representing object-detection metadata type. + * + * Returns: #GstAnalyticsMtd type name. + * + * Since: 1.24 + */ +const gchar * +gst_analytics_od_mtd_get_type_name (void) +{ + return GST_RELATABLE_MTD_OD_TYPE_NAME; +} + +static GstAnalyticsODMtdData * +gst_analytics_od_mtd_get_data (GstAnalyticsODMtd * instance) +{ + GstAnalyticsRelatableMtdData *rlt_data = + gst_analytics_relation_meta_get_mtd_data (instance->meta, + instance->id); + g_return_val_if_fail (rlt_data, NULL); + g_return_val_if_fail (rlt_data->analysis_type == + gst_analytics_od_mtd_get_type_quark (), NULL); + + return (GstAnalyticsODMtdData *) rlt_data; +} + +/** + * gst_analytics_od_mtd_get_location: + * @instance: instance + * @x: (out): x component of upper-left corner of the object location + * @y: (out): y component of upper-left corner of the object location + * @w: (out): bounding box width of the object location + * @h: (out): bounding box height of the object location + * @loc_conf_lvl: (out)(optional): Confidence on object location + + * + * Retrieve location and location confidence level. + * + * Returns: TRUE on success, otherwise FALSE. + * + * Since: 1.24 + */ +gboolean +gst_analytics_od_mtd_get_location (GstAnalyticsODMtd * instance, + gint * x, gint * y, gint * w, gint * h, gfloat * loc_conf_lvl) +{ + g_return_val_if_fail (instance && x && y && w && h, FALSE); + GstAnalyticsODMtdData *data; + data = gst_analytics_od_mtd_get_data (instance); + g_return_val_if_fail (data != NULL, FALSE); + + if (data) { + *x = data->x; + *y = data->y; + *w = data->w; + *h = data->h; + + if (loc_conf_lvl) { + *loc_conf_lvl = data->location_confidence_lvl; + } + } + + return TRUE; +} + +/** + * gst_analytics_od_mtd_get_type: + * @handle: Instance handle + * Quark of the class of object associated with this location. + * Returns: Quark different from on success and 0 on failure. + * + * Since: 1.24 + */ +GQuark +gst_analytics_od_mtd_get_type (GstAnalyticsODMtd * handle) +{ + GstAnalyticsODMtdData *data; + g_return_val_if_fail (handle != NULL, 0); + data = gst_analytics_od_mtd_get_data (handle); + g_return_val_if_fail (data != NULL, 0); + return data->object_type; +} + +/** + * gst_analytics_relation_meta_add_od_mtd: + * @instance: Instance of #GstAnalyticsRelationMeta where to add classification instance + * @type: Quark of the object type + * @x: x component of bounding box upper-left corner + * @y: y component of bounding box upper-left corner + * @w: bounding box width + * @h: bounding box height + * @loc_conf_lvl: confidence level on the object location + * @od_mtd: (out)(nullable): Handle updated with newly added object detection + * meta. Add an object-detetion metadata to @instance. + * + * Returns: Added successfully + * + * Since: 1.24 + */ +gboolean +gst_analytics_relation_meta_add_od_mtd (GstAnalyticsRelationMeta * + instance, GQuark type, gint x, gint y, gint w, gint h, + gfloat loc_conf_lvl, GstAnalyticsODMtd * od_mtd) +{ + g_return_val_if_fail (instance != NULL, FALSE); + GstAnalyticsMtdType mtd_type = gst_analytics_od_mtd_get_type_quark (); + gsize size = sizeof (GstAnalyticsODMtdData); + GstAnalyticsODMtdData *od_mtd_data = (GstAnalyticsODMtdData *) + gst_analytics_relation_meta_add_mtd (instance, mtd_type, size, od_mtd); + if (od_mtd_data) { + od_mtd_data->x = x; + od_mtd_data->y = y; + od_mtd_data->w = w; + od_mtd_data->h = h; + od_mtd_data->location_confidence_lvl = loc_conf_lvl; + od_mtd_data->object_type = type; + } + return od_mtd_data != NULL; +} + +/** + * gst_analytics_relation_meta_get_od_mtd: + * @meta: Instance of #GstAnalyticsRelationMeta + * @an_meta_id: Id of #GstAnalyticsOdMtd instance to retrieve + * @rlt: (out caller-allocates)(not nullable): Will be filled with relatable + * meta + * + * Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, + * otherwise this method return FALSE and @rlt is invalid. + * + * Returns: TRUE if successful. + * + * Since 1.24 + */ +gboolean +gst_analytics_relation_meta_get_od_mtd (GstAnalyticsRelationMeta * meta, + gint an_meta_id, GstAnalyticsODMtd * rlt) +{ + return gst_analytics_relation_meta_get_mtd (meta, an_meta_id, + gst_analytics_od_mtd_get_type_quark (), (GstAnalyticsODMtd *) rlt); +} diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjectdetectionmtd.h b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjectdetectionmtd.h new file mode 100644 index 0000000000..266fb70fa7 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjectdetectionmtd.h @@ -0,0 +1,73 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstanalyticsobjectdetectionmtd.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __GST_ANALYTICS_OBJECT_DETECTION_MTD__ +#define __GST_ANALYTICS_OBJECT_DETECTION_MTD__ + +#include +#include +#include + +G_BEGIN_DECLS + +/** + * GstAnalyticsODMtd: + * @id: Instance identifier. + * @meta: Instance of #GstAnalyticsRelationMeta where the analysis-metadata + * identified by @id is stored. + * + * Handle containing data required to use gst_analytics_od_mtd APIs. This type + * is generally expected to be allocated on the stack. + * + * Since: 1.24 + */ +typedef struct _GstAnalyticsMtd GstAnalyticsODMtd; + +GST_ANALYTICS_META_API +GstAnalyticsMtdType gst_analytics_od_mtd_get_type_quark (void); + +GST_ANALYTICS_META_API +const gchar *gst_analytics_od_mtd_get_type_name (void); + +GST_ANALYTICS_META_API +gboolean gst_analytics_od_mtd_get_location (GstAnalyticsODMtd * instance, + gint * x, gint * y, gint * w, gint * h, gfloat * loc_conf_lvl); + +GST_ANALYTICS_META_API +GQuark gst_analytics_od_mtd_get_type (GstAnalyticsODMtd * handle); + +GST_ANALYTICS_META_API +gboolean gst_analytics_relation_meta_add_od_mtd ( + GstAnalyticsRelationMeta * instance, GQuark type, gint x, gint y, + gint w, gint h, gfloat loc_conf_lvl, GstAnalyticsODMtd * od_mtd); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_get_od_mtd (GstAnalyticsRelationMeta * xbmeta, + gint an_meta_id, GstAnalyticsODMtd * rlt); + + +G_END_DECLS +#endif // __GST_ANALYTICS_OBJECT_DETECTION_MTD__ diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjecttrackingmtd.c b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjecttrackingmtd.c new file mode 100644 index 0000000000..69a32759ea --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjecttrackingmtd.c @@ -0,0 +1,228 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstanalyticsobjecttrackingmtd.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstanalyticsobjecttrackingmtd.h" + +#define GST_RELATABLE_MTD_TRACKING_TYPE_NAME "object-tracking" + +typedef struct _GstAnalyticsTrackingMtdData GstAnalyticsTrackingMtdData; + +/** + * GstAnalyticsTrackingMtd: + * @parent: parent #GstAnalyticsMtd + * @tracking_id: Tracking identifier + * @tracking_first_seen: Tracking creation time + * @tracking_last_seen: Tracking last observation + * @tracking_lost: Track lost + * + * Store information on results of object tracking + * + * Since: 1.24 + */ +struct _GstAnalyticsTrackingMtdData +{ + GstAnalyticsRelatableMtdData parent; + guint64 tracking_id; + GstClockTime tracking_first_seen; + GstClockTime tracking_last_seen; + gboolean tracking_lost; +}; + + +static char type[] = GST_RELATABLE_MTD_TRACKING_TYPE_NAME; + +static GstAnalyticsTrackingMtdData * +gst_analytics_tracking_mtd_get_data (GstAnalyticsTrackingMtd * instance) +{ + GstAnalyticsRelatableMtdData *rlt_data = + gst_analytics_relation_meta_get_mtd_data (instance->meta, + instance->id); + g_return_val_if_fail (rlt_data, NULL); + + g_return_val_if_fail (rlt_data->analysis_type == + gst_analytics_tracking_mtd_get_type_quark (), NULL); + + return (GstAnalyticsTrackingMtdData *) rlt_data; +} + +/** + * gst_analytics_tracking_mtd_get_type_quark: + * Returns: Quark representing the type of GstAnalyticsRelatableMtd + * + * Get the quark identifying the relatable type + * + * Since: 1.24 + */ +GstAnalyticsMtdType +gst_analytics_tracking_mtd_get_type_quark (void) +{ + return g_quark_from_static_string (type); +} + +/** + * gst_an_od_mtd_get_type_name: + * Returns: #GstAnalyticsMtd type name. + * + * Get the name identifying relatable type name + * + * Since: 1.24 + */ +const gchar * +gst_analytics_tracking_mtd_get_type_name (void) +{ + return GST_RELATABLE_MTD_TRACKING_TYPE_NAME; +} + +/** + * gst_analytics_tracking_mtd_update_last_seen: + * @instance: GstAnalyticsTrackingMtd instance + * @last_seen: Timestamp of last time this object was tracked + * + * Since: 1.24 + */ +gboolean +gst_analytics_tracking_mtd_update_last_seen (GstAnalyticsTrackingMtd * instance, + GstClockTime last_seen) +{ + GstAnalyticsTrackingMtdData *trk_mtd_data; + g_return_val_if_fail (instance, FALSE); + trk_mtd_data = gst_analytics_tracking_mtd_get_data (instance); + g_return_val_if_fail (trk_mtd_data != NULL, FALSE); + + trk_mtd_data->tracking_last_seen = last_seen; + return TRUE; +} + +/** + * gst_analytics_tracking_mtd_set_lost: + * @instance: Instance of GstAnalyticsTrackingMtd. + * Set tracking to lost + * + * Returns: Update successful + * + * Since: 1.24 + */ +gboolean +gst_analytics_tracking_mtd_set_lost (GstAnalyticsTrackingMtd * instance) +{ + GstAnalyticsTrackingMtdData *trk_mtd_data; + g_return_val_if_fail (instance, FALSE); + trk_mtd_data = gst_analytics_tracking_mtd_get_data (instance); + g_return_val_if_fail (trk_mtd_data != NULL, FALSE); + trk_mtd_data->tracking_lost = TRUE; + return TRUE; +} + +/** + * gst_analytics_tracking_mtd_get_info: + * @instance: Instance of tracking metadata + * @tracking_id: (out): Updated tracking id + * @tracking_first_seen: (out): Updated timestamp of the tracking first observation. + * @tracking_last_seen: (out): Updated timestamp of the tracking last observation. + * @tracking_lost: (out): Has the tracking been lost + * + * Retrieve tracking information. + * + * Returns: Successfully retrieved info. + * Since: 1.24 + */ +gboolean +gst_analytics_tracking_mtd_get_info (GstAnalyticsTrackingMtd * instance, + guint64 * tracking_id, GstClockTime * tracking_first_seen, GstClockTime * + tracking_last_seen, gboolean * tracking_lost) +{ + GstAnalyticsTrackingMtdData *trk_mtd_data; + g_return_val_if_fail (instance, FALSE); + trk_mtd_data = gst_analytics_tracking_mtd_get_data (instance); + + g_return_val_if_fail (trk_mtd_data != NULL, FALSE); + + if (tracking_id) + *tracking_id = trk_mtd_data->tracking_id; + if (tracking_first_seen) + *tracking_first_seen = trk_mtd_data->tracking_first_seen; + if (tracking_last_seen) + *tracking_last_seen = trk_mtd_data->tracking_last_seen; + if (tracking_lost) + *tracking_lost = trk_mtd_data->tracking_lost; + + return TRUE; +} + + +/** + * gst_analytics_relation_meta_add_tracking_mtd: + * @instance: Instance of GstAnalyticsRelationMeta where to add tracking mtd + * @tracking_id: Tracking id + * @tracking_first_seen: Timestamp of first time the object was observed. + * @trk_mtd: (out) (not nullable): Handle updated with newly added tracking meta. + * Add an analytic tracking metadata to @instance. + * Returns: Added successfully + * + * Since: 1.24 + */ +gboolean +gst_analytics_relation_meta_add_tracking_mtd (GstAnalyticsRelationMeta * + instance, guint64 tracking_id, GstClockTime tracking_first_seen, + GstAnalyticsTrackingMtd * trk_mtd) +{ + g_return_val_if_fail (instance, FALSE); + + GstAnalyticsMtdType type = gst_analytics_tracking_mtd_get_type_quark (); + gsize size = sizeof (GstAnalyticsTrackingMtdData); + GstAnalyticsTrackingMtdData *trk_mtd_data = (GstAnalyticsTrackingMtdData *) + gst_analytics_relation_meta_add_mtd (instance, + type, size, trk_mtd); + + if (trk_mtd_data) { + trk_mtd_data->tracking_id = tracking_id; + trk_mtd_data->tracking_first_seen = tracking_first_seen; + trk_mtd_data->tracking_last_seen = tracking_first_seen; + trk_mtd_data->tracking_lost = FALSE; + } + return trk_mtd_data != NULL; +} + +/** + * gst_analytics_relation_meta_get_tracking_mtd: + * @meta: Instance of GstAnalyticsRelationMeta + * @an_meta_id: Id of GstAnalyticsMtd instance to retrieve + * @rlt: (out caller-allocates)(not nullable): Will be filled with relatable + * meta + * + * Fill @rlt if a analytics-meta with id == @an_meta_id exist in @meta instance, + * otherwise this method return FALSE and @rlt is invalid. + * + * Returns: TRUE if successful. + * + * Since 1.24 + */ +gboolean +gst_analytics_relation_meta_get_tracking_mtd (GstAnalyticsRelationMeta * meta, + gint an_meta_id, GstAnalyticsTrackingMtd * rlt) +{ + return gst_analytics_relation_meta_get_mtd (meta, an_meta_id, + gst_analytics_tracking_mtd_get_type_quark (), + (GstAnalyticsTrackingMtd *) rlt); +} diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjecttrackingmtd.h b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjecttrackingmtd.h new file mode 100644 index 0000000000..15353cee40 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsobjecttrackingmtd.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) 2023 Collabora Ltd + * + * gstobjecttrackingmtd.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __GST_ANALYTICS_OBJECT_TRACKING_MTD__ +#define __GST_ANALYTICS_OBJECT_TRACKING_MTD__ + +#include +#include +#include + +G_BEGIN_DECLS + +/** + * GstAnalyticsTrackMtd: + * @id: Instance identifier. + * @meta: Instance of #GstAnalyticsRelationMeta where the analysis-metadata + * identified by @id is stored. + * + * Handle containing data required to use gst_analytics_track_mtd APIs. + * This type is generally expected to be allocated on the stack. + * + * Since: 1.24 + */ +typedef struct _GstAnalyticsMtd GstAnalyticsTrackingMtd; + +GST_ANALYTICS_META_API +GQuark gst_analytics_tracking_mtd_get_type_quark (void); + +GST_ANALYTICS_META_API +const gchar *gst_analytics_tracking_mtd_get_type_name (void); + +GST_ANALYTICS_META_API +gboolean gst_analytics_tracking_mtd_update_last_seen (GstAnalyticsTrackingMtd * instance, + GstClockTime last_seen); + +GST_ANALYTICS_META_API +gboolean gst_analytics_tracking_mtd_set_lost (GstAnalyticsTrackingMtd * instance); + +GST_ANALYTICS_META_API +gboolean gst_analytics_tracking_mtd_get_info (GstAnalyticsTrackingMtd * instance, + guint64 * tracking_id, GstClockTime * tracking_first_seen, GstClockTime * + tracking_last_seen, gboolean * tracking_lost); + +GST_ANALYTICS_META_API +gboolean gst_analytics_relation_meta_add_tracking_mtd ( + GstAnalyticsRelationMeta * instance, guint64 tracking_id, + GstClockTime tracking_first_seen, GstAnalyticsTrackingMtd * trk_mtd); + +GST_ANALYTICS_META_API +gboolean +gst_analytics_relation_meta_get_tracking_mtd (GstAnalyticsRelationMeta * xbmeta, + gint an_meta_id, GstAnalyticsTrackingMtd * rlt); + + +G_END_DECLS +#endif // __GST_ANALYTICS_OBJECT_TRACKING_MTD__ diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/analytics/meson.build b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/meson.build new file mode 100644 index 0000000000..55bad96d59 --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/analytics/meson.build @@ -0,0 +1,64 @@ +analytics_sources = files( 'gstanalyticsmeta.c', + 'gstanalyticsclassificationmtd.c', + 'gstanalyticsobjectdetectionmtd.c', + 'gstanalyticsobjecttrackingmtd.c') + +analytics_headers = files( 'analytics.h', + 'gstanalyticsmeta.h', + 'analytics-meta-prelude.h', + 'gstanalyticsclassificationmtd.h', + 'gstanalyticsobjectdetectionmtd.h', + 'gstanalyticsobjecttrackingmtd.h') +install_headers(analytics_headers, subdir : 'gstreamer-1.0/gst/analytics') + +pkg_name = 'gstreamer-analytics-1.0' +gstanalytics = library('gstanalytics-' + api_version, + analytics_sources, + c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API', '-DBUILDING_GST_ANALYTIC_META', '-DG_LOG_DOMAIN="Gstreamer-Analytic"'], + include_directories : [configinc, libsinc], + version : libversion, + soversion : soversion, + darwin_versions : osxversion, + install : true, + dependencies : [gstbase_dep]) + +gst_libraries += [[pkg_name, {'lib': gstanalytics}]] +pkgconfig.generate(gstanalytics, + libraries : [gst_dep, gstbase_dep], + variables : pkgconfig_variables, + subdirs : pkgconfig_subdirs, + name : pkg_name, + description : 'GStreamer analytics metadata library', +) +gen_sources = [] + +if build_gir + gir = { + 'sources' : analytics_sources + analytics_headers, + 'namespace' : 'GstAnalytics', + 'nsversion' : api_version, + 'identifier_prefix' : ['GstAnalytics', 'Gst'], + 'symbol_prefix' : ['gst_analytics', 'gst'], + 'export_packages' : pkg_name, + 'includes' : ['Gst-1.0', 'GstBase-1.0'], + 'install' : true, + 'extra_args' : gir_init_section + ['-DGST_USE_UNSTABLE_API'], + 'dependencies' : [gstbase_dep] + } + library_def += {'gir': [gir]} + if not static_build + analyticsmeta_gir = gnome.generate_gir(gstanalytics, kwargs: gir) + library_def += {'gir_targets': library_def.get('gir_targets', []) + [analyticsmeta_gir]} + gen_sources += analyticsmeta_gir + endif +endif +gst_libraries += [[pkg_name, library_def]] + + + +gstanalytics_dep = declare_dependency(link_with : gstanalytics, + include_directories : [libsinc], + sources: gen_sources, + dependencies : [gstbase_dep]) + +meson.override_dependency(pkg_name, gstanalytics_dep) diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/meson.build b/subprojects/gst-plugins-bad/gst-libs/gst/meson.build index 7772d8c2b1..6207f3381a 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/meson.build +++ b/subprojects/gst-plugins-bad/gst-libs/gst/meson.build @@ -24,3 +24,4 @@ subdir('vulkan') subdir('wayland') subdir('webrtc') subdir('winrt') +subdir('analytics') diff --git a/subprojects/gst-plugins-bad/tests/check/libs/analyticsmeta.c b/subprojects/gst-plugins-bad/tests/check/libs/analyticsmeta.c new file mode 100644 index 0000000000..a6d42d8e36 --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/check/libs/analyticsmeta.c @@ -0,0 +1,978 @@ +/* GStreamer + * Copyright (C) 2022 Collabora Ltd + * + * analyticmeta.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +GST_START_TEST (test_add_classification_meta) +{ + /* Verify we can create a relation metadata + * and attach it classification mtd + */ + GstBuffer *buf; + GstAnalyticsRelationMeta *rmeta; + GstAnalyticsClsMtd cls_mtd; + gfloat conf_lvl[] = { 0.5f, 0.5f }; + GQuark class_quarks[2]; + gboolean ret; + + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + buf = gst_buffer_new (); + rmeta = gst_buffer_add_analytics_relation_meta (buf); + ret = gst_analytics_relation_meta_add_cls_mtd (rmeta, 2, conf_lvl, + class_quarks, &cls_mtd); + fail_unless (ret == TRUE); + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_meta_pooled) +{ + GstBufferPool *pool; + GstStructure *config; + GstBuffer *buf; + GstAnalyticsRelationMeta *rmeta1, *rmeta2; + + pool = gst_buffer_pool_new (); + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, NULL, 1, 1, 1); + gst_buffer_pool_set_config (pool, config); + gst_buffer_pool_set_active (pool, TRUE); + + gst_buffer_pool_acquire_buffer (pool, &buf, NULL); + rmeta1 = gst_buffer_add_analytics_relation_meta (buf); + gst_buffer_unref (buf); + + gst_buffer_pool_acquire_buffer (pool, &buf, NULL); + + rmeta2 = gst_buffer_add_analytics_relation_meta (buf); + + fail_unless (rmeta1 == rmeta2); + gst_buffer_unref (buf); + gst_object_unref (pool); +} + +GST_END_TEST; + +GST_START_TEST (test_classification_meta_classes) +{ + /* Verify we can retrieve classification data + * from the relation metadata + */ + GstBuffer *buf; + GstAnalyticsRelationMeta *rmeta; + gboolean ret; + GQuark class_quarks[2]; + GstAnalyticsClsMtd cls_mtd, cls_mtd2; + + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + buf = gst_buffer_new (); + rmeta = gst_buffer_add_analytics_relation_meta (buf); + gfloat conf_lvl[] = { 0.6f, 0.4f }; + ret = gst_analytics_relation_meta_add_cls_mtd (rmeta, 2, conf_lvl, + class_quarks, &cls_mtd); + fail_unless (ret == TRUE); + fail_unless (gst_analytics_relation_get_length (rmeta) == 1); + + gint dogIndex = gst_analytics_cls_mtd_get_index_by_quark (&cls_mtd, + class_quarks[0]); + fail_unless (dogIndex == 0); + gfloat confLvl = gst_analytics_cls_mtd_get_level (&cls_mtd, dogIndex); + GST_LOG ("dog:%f", confLvl); + assert_equals_float (confLvl, 0.6f); + + gint catIndex = gst_analytics_cls_mtd_get_index_by_quark (&cls_mtd, + g_quark_from_string ("cat")); + confLvl = gst_analytics_cls_mtd_get_level (&cls_mtd, catIndex); + GST_LOG ("Cat:%f", confLvl); + assert_equals_float (confLvl, 0.4f); + assert_equals_int (gst_analytics_mtd_get_id ((GstAnalyticsMtd *) & cls_mtd), + 0); + + conf_lvl[0] = 0.1f; + conf_lvl[1] = 0.9f; + ret = + gst_analytics_relation_meta_add_cls_mtd (rmeta, 2, conf_lvl, + class_quarks, &cls_mtd2); + fail_unless (ret == TRUE); + fail_unless (gst_analytics_relation_get_length (rmeta) == 2); + + dogIndex = gst_analytics_cls_mtd_get_index_by_quark (&cls_mtd2, + class_quarks[0]); + confLvl = gst_analytics_cls_mtd_get_level (&cls_mtd2, dogIndex); + assert_equals_float (confLvl, 0.1f); + + catIndex = gst_analytics_cls_mtd_get_index_by_quark (&cls_mtd2, + class_quarks[0]); + confLvl = gst_analytics_cls_mtd_get_level (&cls_mtd2, catIndex); + assert_equals_float (confLvl, 0.1f); + + /* Verify the relation meta contain the correct number of relatable metadata */ + fail_unless (gst_analytics_relation_get_length (rmeta) == 2); + + /* Verify first relatable metadata has the correct id. */ + assert_equals_int (gst_analytics_mtd_get_id ((GstAnalyticsMtd *) & cls_mtd), + 0); + + /* Verify second relatable metadata has the correct id. */ + assert_equals_int (gst_analytics_mtd_get_id ( + (GstAnalyticsMtd *) & cls_mtd2), 1); + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_add_relation_meta) +{ + /* Verify we set a relation between relatable metadata. */ + + GstBuffer *buf; + GstAnalyticsClsMtd cls_mtd[3]; + GstAnalyticsRelationMetaInitParams init_params = { 5, 150 }; + GstAnalyticsRelationMeta *relations; + GQuark class_quarks[2]; + gboolean ret; + + buf = gst_buffer_new (); + relations = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + gfloat conf_lvl[] = { 0.6f, 0.4f }; + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[0]); + fail_unless (ret == TRUE); + gst_analytics_mtd_get_id ((GstAnalyticsMtd *) + & cls_mtd[0]); + + conf_lvl[0] = 0.6f; + conf_lvl[1] = 0.4f; + + class_quarks[0] = g_quark_from_string ("plant"); + class_quarks[1] = g_quark_from_string ("animal"); + + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[1]); + gst_analytics_mtd_get_id ((GstAnalyticsMtd *) + & cls_mtd[1]); + + fail_unless (gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[0].id, + cls_mtd[1].id) == TRUE); + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_add_relation_inefficiency_reporting_cases) +{ + /* + * Verify inefficiency of relation order is reported. + */ + GstBuffer *buf; + GstAnalyticsClsMtd cls_mtd[3]; + GstAnalyticsRelationMetaInitParams init_params = { 2, 10 }; + GstAnalyticsRelationMeta *relations; + gboolean ret; + GQuark class_quarks[2]; + + + buf = gst_buffer_new (); + relations = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + gfloat conf_lvl[] = { 0.6f, 0.4f }; + + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[0]); + fail_unless (gst_analytics_relation_get_length (relations) == 1); + fail_unless (ret == TRUE); + + conf_lvl[0] = 0.6f; + conf_lvl[1] = 0.4f; + class_quarks[0] = g_quark_from_string ("plant"); + class_quarks[1] = g_quark_from_string ("animal"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[1]); + fail_unless (gst_analytics_relation_get_length (relations) == 2); + fail_unless (ret == TRUE); + + conf_lvl[0] = 0.6f; + conf_lvl[1] = 0.4f; + class_quarks[0] = g_quark_from_string ("male"); + class_quarks[1] = g_quark_from_string ("female"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[2]); + fail_unless (gst_analytics_relation_get_length (relations) == 3); + fail_unless (ret == TRUE); + + fail_unless (gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[0].id, cls_mtd[1].id) + == TRUE); + fail_unless (gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[0].id, cls_mtd[2].id) + == TRUE); + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_query_relation_meta_cases) +{ + /* Verify we can query existence of direct and indirect relation */ + GstBuffer *buf; + GstAnalyticsClsMtd cls_mtd[3]; + GstAnalyticsRelationMetaInitParams init_params = { 2, 150 }; + GstAnalyticsRelationMeta *relations; + gboolean ret; + GQuark class_quarks[2]; + + buf = gst_buffer_new (); + relations = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + gfloat conf_lvl[] = { 0.6f, 0.4f }; + + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[0]); + fail_unless (ret == TRUE); + + conf_lvl[0] = 0.6f; + conf_lvl[1] = 0.4f; + class_quarks[0] = g_quark_from_string ("plant"); + class_quarks[1] = g_quark_from_string ("animal"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[1]); + fail_unless (ret == TRUE); + + conf_lvl[0] = 0.6f; + conf_lvl[1] = 0.4f; + class_quarks[0] = g_quark_from_string ("male"); + class_quarks[1] = g_quark_from_string ("female"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[2]); + fail_unless (ret == TRUE); + + // Pet is part of kingdom + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[0].id, cls_mtd[1].id); + + // Kingdom contain pet + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_CONTAIN, cls_mtd[1].id, cls_mtd[0].id); + + // Pet contain gender + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_CONTAIN, cls_mtd[0].id, cls_mtd[2].id); + + /* Query if pet relate kingdom through a IS_PART relation with a maximum + * relation span of 1. Max relation span of 1 mean they directly related.*/ + gboolean exist = gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[1].id, 1, GST_ANALYTICS_REL_TYPE_IS_PART_OF, NULL); + fail_unless (exist == TRUE); + + /* Query if pet relate to gender through a IS_PART relation. */ + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[2].id, 1, GST_ANALYTICS_REL_TYPE_IS_PART_OF, NULL); + fail_unless (exist == FALSE); + + /* Query if pet relate to kingdom through a CONTAIN relation. */ + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[1].id, 1, GST_ANALYTICS_REL_TYPE_CONTAIN, NULL); + fail_unless (exist == FALSE); + + GstAnalyticsRelTypes cond = + GST_ANALYTICS_REL_TYPE_IS_PART_OF | GST_ANALYTICS_REL_TYPE_CONTAIN | + GST_ANALYTICS_REL_TYPE_RELATE_TO; + + /* Query if pet relate to gender through IS_PART or CONTAIN or + * RELATE_TO relation. */ + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[2].id, 1, cond, NULL); + fail_unless (exist == TRUE); + + /* Query if pet relate to kindom through CONTAIN or RELATE_TO relation */ + cond = GST_ANALYTICS_REL_TYPE_CONTAIN | GST_ANALYTICS_REL_TYPE_RELATE_TO; + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[1].id, 1, cond, NULL); + fail_unless (exist == FALSE); + + /* Query if kingdom relate to gender through a CONTAIN relation with a maximum + * relation span of 1. */ + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[1].id, + cls_mtd[2].id, 1, GST_ANALYTICS_REL_TYPE_CONTAIN, NULL); + + /* We expect this to fail because kingdom relate to gender CONTAIN relations + * but indirectly (via pet) and we set the max relation span to 1*/ + fail_unless (exist == FALSE); + + /* Same has previous check but using INFINIT relation span */ + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[1].id, + cls_mtd[2].id, GST_INF_RELATION_SPAN, GST_ANALYTICS_REL_TYPE_CONTAIN, + NULL); + fail_unless (exist == TRUE); + + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[2].id, + cls_mtd[1].id, GST_INF_RELATION_SPAN, GST_ANALYTICS_REL_TYPE_CONTAIN, + NULL); + fail_unless (exist == FALSE); + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_path_relation_meta) +{ + /* Verify we can retrieve relation path */ + GstBuffer *buf; + GstAnalyticsClsMtd cls_mtd[3]; + GstAnalyticsRelationMetaInitParams init_params = { 2, 150 }; + GstAnalyticsRelationMeta *relations; + gboolean ret; + GQuark class_quarks[2]; + + buf = gst_buffer_new (); + relations = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + gfloat conf_lvl[] = { 0.6f, 0.4f }; + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[0]); + fail_unless (ret == TRUE); + fail_unless (cls_mtd[0].id == 0); + + conf_lvl[0] = 0.6f; + conf_lvl[1] = 0.4f; + class_quarks[0] = g_quark_from_string ("plant"); + class_quarks[1] = g_quark_from_string ("animal"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[1]); + fail_unless (ret == TRUE); + fail_unless (cls_mtd[1].id == 1); + + conf_lvl[0] = 0.6f; + conf_lvl[1] = 0.4f; + class_quarks[0] = g_quark_from_string ("male"); + class_quarks[1] = g_quark_from_string ("female"); + + ret = gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[2]); + fail_unless (ret == TRUE); + fail_unless (cls_mtd[2].id == 2); + + // Pet is part of kingdom + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[0].id, cls_mtd[1].id); + + // Kingdom contain pet + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_CONTAIN, cls_mtd[1].id, cls_mtd[0].id); + + // Pet contain gender + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_CONTAIN, cls_mtd[0].id, cls_mtd[2].id); + + GArray *path = NULL; + GstAnalyticsRelTypes cond = GST_ANALYTICS_REL_TYPE_CONTAIN; + gboolean exist = gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[2].id, GST_INF_RELATION_SPAN, cond, &path); + if (exist) { + fail_unless (path != NULL); + gint i; + gint path_check_ids[] = { 0, 2 }; + fail_unless (path->len == 2); + for (i = 0; i < path->len; i++) { + fail_unless (path_check_ids[i] == g_array_index (path, gint, i)); + } + g_array_free (g_steal_pointer (&path), TRUE); + fail_unless (i == 2); + } + + cond = GST_ANALYTICS_REL_TYPE_CONTAIN; + exist = gst_analytics_relation_meta_exist (relations, cls_mtd[1].id, + cls_mtd[2].id, GST_INF_RELATION_SPAN, cond, &path); + if (exist) { + gint i; + gint path_check_ids[] = { 1, 0, 2 }; + fail_unless (path != NULL); + fail_unless (path->len == 3); + for (i = 0; i < path->len; i++) { + fail_unless (path_check_ids[i] == g_array_index (path, gint, i)); + } + g_array_free (g_steal_pointer (&path), TRUE); + fail_unless (i == 3); + } + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_cyclic_relation_meta) +{ + /* Verify we can discover cycle in relation and not report multiple time + * the same node and get into an infinit exploration */ + + GstBuffer *buf; + GstAnalyticsClsMtd cls_mtd[3]; + GstAnalyticsRelationMetaInitParams init_params = { 2, 150 }; + GstAnalyticsRelationMeta *relations; + gfloat conf_lvl[2]; + GQuark class_quarks[2]; + + class_quarks[0] = g_quark_from_string ("attr1"); + class_quarks[1] = g_quark_from_string ("attr2"); + + buf = gst_buffer_new (); + relations = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + conf_lvl[0] = 0.5f; + conf_lvl[1] = 0.5f; + gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[0]); + + gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[1]); + + gst_analytics_relation_meta_add_cls_mtd (relations, 2, conf_lvl, + class_quarks, &cls_mtd[2]); + + // (0) -> (1) + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[0].id, cls_mtd[1].id); + + // (1)->(2) + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[1].id, cls_mtd[2].id); + + // (2) -> (0) + gst_analytics_relation_meta_set_relation (relations, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd[2].id, cls_mtd[0].id); + + GArray *path = NULL; + GstAnalyticsRelTypes cond = GST_ANALYTICS_REL_TYPE_CONTAIN; + gboolean exist = gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[2].id, GST_INF_RELATION_SPAN, cond, &path); + fail_unless (exist == FALSE); + + cond = GST_ANALYTICS_REL_TYPE_IS_PART_OF; + exist = + gst_analytics_relation_meta_exist (relations, cls_mtd[0].id, + cls_mtd[2].id, GST_INF_RELATION_SPAN, cond, &path); + fail_unless (exist == TRUE); + if (exist) { + gint i; + gint path_ids[] = { 0, 1, 2 }; + fail_unless (path->len == 3); + for (i = 0; i < path->len; i++) { + fail_unless (path_ids[i] == g_array_index (path, gint, i)); + } + g_array_free (g_steal_pointer (&path), TRUE); + fail_unless (i == 3); + } + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_add_od_meta) +{ + /* Verity we can add Object Detection relatable metadata to a relation + * metadata */ + GstBuffer *buf; + GstAnalyticsRelationMetaInitParams init_params = { 5, 150 }; + GstAnalyticsRelationMeta *rmeta; + GstAnalyticsODMtd od_mtd; + gboolean ret; + buf = gst_buffer_new (); + + rmeta = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + GQuark type = g_quark_from_string ("dog"); + gint x = 20; + gint y = 20; + gint w = 10; + gint h = 15; + gfloat loc_conf_lvl = 0.6f; + ret = gst_analytics_relation_meta_add_od_mtd (rmeta, type, x, y, + w, h, loc_conf_lvl, &od_mtd); + fail_unless (ret == TRUE); + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_od_meta_fields) +{ + /* Verify we can readback fields of object detection metadata */ + GstBuffer *buf; + GstAnalyticsRelationMetaInitParams init_params = { 5, 150 }; + GstAnalyticsRelationMeta *rmeta; + GstAnalyticsODMtd od_mtd; + gboolean ret; + buf = gst_buffer_new (); + + rmeta = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + GQuark type = g_quark_from_string ("dog"); + gint x = 21; + gint y = 20; + gint w = 10; + gint h = 15; + gfloat loc_conf_lvl = 0.6f; + ret = gst_analytics_relation_meta_add_od_mtd (rmeta, type, x, y, + w, h, loc_conf_lvl, &od_mtd); + + fail_unless (ret == TRUE); + + gint _x, _y, _w, _h; + gfloat _loc_conf_lvl; + gst_analytics_od_mtd_get_location (&od_mtd, &_x, &_y, &_w, &_h, + &_loc_conf_lvl); + + fail_unless (_x == x); + fail_unless (_y == y); + fail_unless (_w == w); + fail_unless (_h == h); + fail_unless (_loc_conf_lvl == loc_conf_lvl); + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_od_cls_relation) +{ + /* Verify we can add a object detection and classification metadata to + * a relation metadata */ + + GstBuffer *buf; + GstAnalyticsClsMtd cls_mtd; + GstAnalyticsODMtd od_mtd; + /* We intentionally set buffer small than required to verify sanity + * with re-allocation */ + GstAnalyticsRelationMetaInitParams init_params = { 5, 150 }; + GstAnalyticsRelationMeta *rmeta; + gboolean ret; + GArray *path = NULL; + gboolean exist; + gint _x, _y, _w, _h; + gfloat _loc_conf_lvl; + GQuark class_quarks[2]; + + buf = gst_buffer_new (); + rmeta = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + gfloat conf_lvl[] = { 0.7f, 0.3f }; + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + gst_analytics_relation_meta_add_cls_mtd (rmeta, 2, conf_lvl, + class_quarks, &cls_mtd); + + GQuark type = g_quark_from_string ("dog"); + gint x = 21; + gint y = 20; + gint w = 10; + gint h = 15; + gfloat loc_conf_lvl = 0.6f; + gst_analytics_relation_meta_add_od_mtd (rmeta, type, x, y, w, h, + loc_conf_lvl, &od_mtd); + + ret = gst_analytics_relation_meta_set_relation (rmeta, + GST_ANALYTICS_REL_TYPE_CONTAIN, od_mtd.id, cls_mtd.id); + fail_unless (ret == TRUE); + + ret = gst_analytics_relation_meta_set_relation (rmeta, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, cls_mtd.id, od_mtd.id); + fail_unless (ret == TRUE); + + /* Verify OD relate to CLS only through a CONTAIN relation */ + exist = gst_analytics_relation_meta_exist (rmeta, + od_mtd.id, cls_mtd.id, GST_INF_RELATION_SPAN, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, NULL); + fail_unless (exist == FALSE); + + exist = gst_analytics_relation_meta_exist (rmeta, + od_mtd.id, cls_mtd.id, GST_INF_RELATION_SPAN, + GST_ANALYTICS_REL_TYPE_CONTAIN, &path); + fail_unless (exist == TRUE); + + /* Query the relation path and verify it is correct */ + gint ids[2]; + gint i; + fail_unless (path->len == 2); + for (i = 0; i < path->len; i++) { + ids[i] = g_array_index (path, gint, i); + GST_LOG ("id=%u", ids[i]); + } + g_array_free (g_steal_pointer (&path), TRUE); + fail_unless (ids[0] == 1); + fail_unless (ids[1] == 0); + + GstAnalyticsMtd rlt_mtd; + exist = gst_analytics_relation_meta_get_mtd (rmeta, ids[0], 0, &rlt_mtd); + fail_unless (exist == TRUE); + + GQuark mtd_type = gst_analytics_mtd_get_type_quark (&rlt_mtd); + + /* Verify relatable meta with id == 1 is of type Object Detection */ + fail_unless (mtd_type == gst_analytics_od_mtd_get_type_quark ()); + + gst_analytics_od_mtd_get_location ((GstAnalyticsODMtd *) & rlt_mtd, &_x, &_y, + &_w, &_h, &_loc_conf_lvl); + fail_unless (_x == x); + fail_unless (_y == y); + fail_unless (_w == w); + fail_unless (_h == h); + fail_unless (_loc_conf_lvl == loc_conf_lvl); + + GST_LOG ("mtd_type:%s", g_quark_to_string (mtd_type)); + + exist = gst_analytics_relation_meta_get_mtd (rmeta, ids[1], 0, &rlt_mtd); + fail_unless (exist == TRUE); + mtd_type = gst_analytics_mtd_get_type_quark (&rlt_mtd); + + /* Verify relatable meta with id == 0 is of type classification */ + fail_unless (mtd_type == gst_analytics_cls_mtd_get_type_quark ()); + gint index = + gst_analytics_cls_mtd_get_index_by_quark ((GstAnalyticsClsMtd *) & + rlt_mtd, + g_quark_from_string ("dog")); + gfloat lvl = + gst_analytics_cls_mtd_get_level ((GstAnalyticsClsMtd *) & rlt_mtd, index); + GST_LOG ("dog %f [%d, %d %d, %d", lvl, _x, _y, _w, _h); + + fail_unless (lvl == 0.7f); + index = + gst_analytics_cls_mtd_get_index_by_quark ((GstAnalyticsClsMtd *) & + rlt_mtd, g_quark_from_string ("cat")); + lvl = + gst_analytics_cls_mtd_get_level ((GstAnalyticsClsMtd *) & rlt_mtd, index); + fail_unless (lvl == 0.3f); + + GST_LOG ("mtd_type:%s", g_quark_to_string (mtd_type)); + GST_LOG ("cat %f [%d, %d %d, %d", lvl, _x, _y, _w, _h); + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_multi_od_cls_relation) +{ + GstBuffer *buf; + GstAnalyticsClsMtd cls_mtd[3]; + GstAnalyticsODMtd od_mtd[2]; + GstAnalyticsRelationMetaInitParams init_params = { 5, 150 }; + GstAnalyticsRelationMeta *rmeta; + gint cls_id, ids[2], i; + gboolean ret; + const gint dog_cls_index = 0; + const gint cat_cls_index = 1; + gfloat cls_conf_lvl[2], lvl; + GArray *path = NULL; + gfloat _loc_conf_lvl; + gint x, _x, y, _y, w, _w, h, _h; + GQuark mtd_type, cls_type; + GstAnalyticsMtd mtd; + gpointer state = NULL; + GQuark class_quarks[2]; + class_quarks[0] = g_quark_from_string ("dog"); + class_quarks[1] = g_quark_from_string ("cat"); + + + buf = gst_buffer_new (); + rmeta = gst_buffer_add_analytics_relation_meta_full (buf, &init_params); + + /* Define first relation ObjectDetection -contain-> Classification */ + cls_conf_lvl[dog_cls_index] = 0.7f; + cls_conf_lvl[cat_cls_index] = 0.3f; + + gst_analytics_relation_meta_add_cls_mtd (rmeta, 2, cls_conf_lvl, + class_quarks, &cls_mtd[0]); + + cls_type = g_quark_from_string ("dog"); + x = 21; + y = 20; + w = 10; + h = 15; + gfloat loc_conf_lvl = 0.6f; + gst_analytics_relation_meta_add_od_mtd (rmeta, cls_type, x, y, w, + h, loc_conf_lvl, &od_mtd[0]); + + ret = gst_analytics_relation_meta_set_relation (rmeta, + GST_ANALYTICS_REL_TYPE_CONTAIN, od_mtd[0].id, cls_mtd[0].id); + fail_unless (ret == TRUE); + GST_LOG ("Set rel Obj:%d -c-> Cls:%d", od_mtd[0].id, cls_mtd[0].id); + + /* Define second relation ObjectDetection -contain-> Classification */ + cls_conf_lvl[dog_cls_index] = 0.1f; + cls_conf_lvl[cat_cls_index] = 0.9f; + gst_analytics_relation_meta_add_cls_mtd (rmeta, 2, cls_conf_lvl, + class_quarks, &cls_mtd[1]); + + cls_type = g_quark_from_string ("cat"); + x = 50; + y = 21; + w = 11; + h = 16; + loc_conf_lvl = 0.7f; + gst_analytics_relation_meta_add_od_mtd (rmeta, cls_type, x, y, w, + h, loc_conf_lvl, &od_mtd[1]); + + ret = gst_analytics_relation_meta_set_relation (rmeta, + GST_ANALYTICS_REL_TYPE_CONTAIN, od_mtd[1].id, cls_mtd[1].id); + + GST_LOG ("Set rel Obj:%d -c-> Cls:%d", od_mtd[1].id, cls_mtd[1].id); + fail_unless (ret == TRUE); + + /* Query relations */ + + /* Query relation between first object detection and first classification + * and verify they are only related by CONTAIN relation OD relate to + * CLASSIFICATION through a CONTAIN relation. */ + gboolean exist = + gst_analytics_relation_meta_exist (rmeta, od_mtd[0].id, cls_mtd[0].id, + GST_INF_RELATION_SPAN, + GST_ANALYTICS_REL_TYPE_IS_PART_OF, NULL); + fail_unless (exist == FALSE); + + exist = + gst_analytics_relation_meta_exist (rmeta, od_mtd[0].id, cls_mtd[0].id, + GST_INF_RELATION_SPAN, GST_ANALYTICS_REL_TYPE_CONTAIN, NULL); + fail_unless (exist == TRUE); + + + /* Query relation between second object detection and second classification + * and verify they are only related by CONTAIN relation OD relate to + * CLASSIFICATION through a CONTAIN relation. + */ + exist = + gst_analytics_relation_meta_exist (rmeta, od_mtd[1].id, cls_mtd[1].id, + GST_INF_RELATION_SPAN, GST_ANALYTICS_REL_TYPE_CONTAIN, &path); + fail_unless (exist == TRUE); + + /* Verify relation path between OD second (id=3) and Cls second (id=2) + * is correct + */ + fail_unless (path->len == 2); + for (i = 0; i < path->len; i++) { + ids[i] = g_array_index (path, gint, i); + GST_LOG ("id=%u", ids[i]); + } + g_array_free (g_steal_pointer (&path), TRUE); + fail_unless (ids[0] == 3); + fail_unless (ids[1] == 2); + + /* Verify the relatable metadata 3 is of correct type + * (ObjectDetection). Verify it describe the correct + * the correct data. + */ + gst_analytics_relation_meta_get_mtd (rmeta, ids[0], 0, &mtd); + mtd_type = gst_analytics_mtd_get_type_quark (&mtd); + fail_unless (mtd_type == gst_analytics_od_mtd_get_type_quark ()); + + gst_analytics_od_mtd_get_location ((GstAnalyticsODMtd *) & mtd, &_x, &_y, &_w, + &_h, &_loc_conf_lvl); + fail_unless (_x == 50); + fail_unless (_y == 21); + fail_unless (_w == 11); + fail_unless (_h == 16); + fail_unless (_loc_conf_lvl == 0.7f); + + GST_LOG ("mtd_type:%s", g_quark_to_string (mtd_type)); + + /* Verify the relatable metadata 2 is of correct type + * (ObjectDetection). + */ + gst_analytics_relation_meta_get_mtd (rmeta, ids[1], 0, &mtd); + mtd_type = gst_analytics_mtd_get_type_quark (&mtd); + fail_unless (mtd_type == gst_analytics_cls_mtd_get_type_quark ()); + + /* Verify data of the CLASSIFICATION retrieved */ + gint index = + gst_analytics_cls_mtd_get_index_by_quark ((GstAnalyticsClsMtd *) & mtd, + g_quark_from_string ("dog")); + lvl = gst_analytics_cls_mtd_get_level ((GstAnalyticsClsMtd *) & mtd, index); + GST_LOG ("dog %f [%d, %d %d, %d", lvl, _x, _y, _w, _h); + fail_unless (lvl == 0.1f); + + /* Verify data of the CLASSIFICATION retrieved */ + index = + gst_analytics_cls_mtd_get_index_by_quark ((GstAnalyticsClsMtd *) & mtd, + g_quark_from_string ("cat")); + lvl = gst_analytics_cls_mtd_get_level ((GstAnalyticsClsMtd *) & mtd, index); + GST_LOG ("mtd_type:%s", g_quark_to_string (mtd_type)); + GST_LOG ("cat %f [%d, %d %d, %d", lvl, _x, _y, _w, _h); + fail_unless (lvl == 0.9f); + + /* Retrieve relatable metadata related to the first object detection + * through a CONTAIN relation of type CLASSIFICATION + * Verify it's the first classification metadata + */ + gst_analytics_relation_meta_get_direct_related (rmeta, od_mtd[0].id, + GST_ANALYTICS_REL_TYPE_CONTAIN, gst_analytics_cls_mtd_get_type_quark (), + &state, &mtd); + + cls_id = gst_analytics_mtd_get_id (&mtd); + GST_LOG ("Obj:%d -> Cls:%d", od_mtd[0].id, cls_id); + fail_unless (cls_id == cls_mtd[0].id); + + state = NULL; + /* Retrieve relatable metadata related to the second object detection + * through a CONTAIN relation of type CLASSIFICATION + * Verify it's the first classification metadata + */ + gst_analytics_relation_meta_get_direct_related (rmeta, od_mtd[1].id, + GST_ANALYTICS_REL_TYPE_CONTAIN, gst_analytics_cls_mtd_get_type_quark (), + &state, &mtd); + cls_id = gst_analytics_mtd_get_id (&mtd); + + GST_LOG ("Obj:%d -> Cls:%d", od_mtd[1].id, cls_id); + fail_unless (cls_id == cls_mtd[1].id); + + cls_conf_lvl[dog_cls_index] = 0.2f; + cls_conf_lvl[cat_cls_index] = 0.8f; + class_quarks[0] = g_quark_from_string ("canine"); + class_quarks[1] = g_quark_from_string ("feline"); + gst_analytics_relation_meta_add_cls_mtd (rmeta, 2, cls_conf_lvl, + class_quarks, &cls_mtd[2]); + + ret = gst_analytics_relation_meta_set_relation (rmeta, + GST_ANALYTICS_REL_TYPE_CONTAIN, od_mtd[1].id, cls_mtd[2].id); + + state = NULL; + ret = gst_analytics_relation_meta_get_direct_related (rmeta, od_mtd[1].id, + GST_ANALYTICS_REL_TYPE_CONTAIN, gst_analytics_od_mtd_get_type_quark (), + &state, &mtd); + + fail_unless (ret == FALSE); + + gst_buffer_unref (buf); +} + +GST_END_TEST; + +GST_START_TEST (test_add_tracking_meta) +{ + /* Verify we can add tracking relatable meta to relation meta */ + GstBuffer *buf1, *buf2; + GstAnalyticsRelationMetaInitParams init_params = { 5, 150 }; + GstAnalyticsRelationMeta *rmeta; + GstAnalyticsTrackingMtd tracking_mtd; + guint tracking_id; + GstClockTime tracking_observation_time_1; + gboolean ret; + + /* Verify we can add multiple trackings to relation metadata + */ + + buf1 = gst_buffer_new (); + rmeta = gst_buffer_add_analytics_relation_meta_full (buf1, &init_params); + tracking_id = 1; + tracking_observation_time_1 = GST_BUFFER_TIMESTAMP (buf1); + ret = gst_analytics_relation_meta_add_tracking_mtd (rmeta, tracking_id, + tracking_observation_time_1, &tracking_mtd); + fail_unless (ret == TRUE); + + gst_buffer_unref (buf1); + + buf2 = gst_buffer_new (); + rmeta = gst_buffer_add_analytics_relation_meta_full (buf2, &init_params); + tracking_id = 1; + ret = gst_analytics_relation_meta_add_tracking_mtd (rmeta, tracking_id, + tracking_observation_time_1, &tracking_mtd); + fail_unless (ret == TRUE); + + gst_buffer_unref (buf2); +} + +GST_END_TEST; + +static Suite * +analyticmeta_suite (void) +{ + + Suite *s; + TCase *tc_chain_cls; + TCase *tc_chain_relation; + TCase *tc_chain_od; + TCase *tc_chain_od_cls; + TCase *tc_chain_tracking; + + s = suite_create ("Analytic Meta Library"); + + tc_chain_cls = tcase_create ("Classification Mtd"); + suite_add_tcase (s, tc_chain_cls); + tcase_add_test (tc_chain_cls, test_add_classification_meta); + tcase_add_test (tc_chain_cls, test_classification_meta_classes); + + tcase_add_test (tc_chain_cls, test_meta_pooled); + + tc_chain_relation = tcase_create ("Relation Meta"); + suite_add_tcase (s, tc_chain_relation); + tcase_add_test (tc_chain_relation, test_add_relation_meta); + tcase_add_test (tc_chain_relation, + test_add_relation_inefficiency_reporting_cases); + tcase_add_test (tc_chain_relation, test_query_relation_meta_cases); + tcase_add_test (tc_chain_relation, test_path_relation_meta); + tcase_add_test (tc_chain_relation, test_cyclic_relation_meta); + + tc_chain_od = tcase_create ("Object Detection Mtd"); + suite_add_tcase (s, tc_chain_od); + tcase_add_test (tc_chain_od, test_add_od_meta); + tcase_add_test (tc_chain_od, test_od_meta_fields); + + tc_chain_od_cls = tcase_create ("Object Detection <-> Classification Mtd"); + suite_add_tcase (s, tc_chain_od_cls); + tcase_add_test (tc_chain_od_cls, test_od_cls_relation); + tcase_add_test (tc_chain_od_cls, test_multi_od_cls_relation); + + tc_chain_tracking = tcase_create ("Tracking Mtd"); + suite_add_tcase (s, tc_chain_tracking); + tcase_add_test (tc_chain_tracking, test_add_tracking_meta); + return s; +} + +GST_CHECK_MAIN (analyticmeta); diff --git a/subprojects/gst-plugins-bad/tests/check/meson.build b/subprojects/gst-plugins-bad/tests/check/meson.build index e507105430..de602acaba 100644 --- a/subprojects/gst-plugins-bad/tests/check/meson.build +++ b/subprojects/gst-plugins-bad/tests/check/meson.build @@ -96,6 +96,7 @@ base_tests = [ [['libs/h265bitwriter.c'], false, [gstcodecparsers_dep]], [['libs/vkformat.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], [['libs/vkmemory.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], + [['libs/analyticsmeta.c'], false, [gstanalytics_dep]], [['elements/vkcolorconvert.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], [['libs/vkwindow.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], [['libs/vkdevice.c'], not gstvulkan_dep.found(), [gstvulkan_dep]],