gstreamer/subprojects/gst-plugins-bad/gst-libs/gst/analytics/gstanalyticsmeta.c
Daniel Morin d23a90cb16 analytics: base class for analytics meta
- GstAnalyticRelationMeta is a base class for analytics
  meta. It's able to store analytics results (GstAnalyticRelatableMtd)
  and describe the relation between each analysis results.
- GstAnalysisRelationMeta also contain an algorithm able to explore
  analysis results relation using a bfs.
- Relation(edge) between analysis results (vertice) are stored in an adjacency-matrix
  that allow to quickly identify if two analysis results are related and by
  which relation they related. It also work for indirect relation
  and can provide the path of analysis results by which two
  analysis results are related.
- One allocation per buffer to store analysis results. Here we rely on
  the application to guess how much space will be required to store all
  analysis results. This is something that could be improved
  significantly but it's a starting point.
- Define common analysis results, classification, object-detection,
  tracking that are subclass of GstAnalyticRelatableMtd. The also
  provide exemple of how to extend GstAnalyticRelatableMtd to have them
  benefit for the mechanim to express relation with other analysis
  results.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4962>
2023-11-21 18:04:53 +00:00

1038 lines
32 KiB
C

/* 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;
}