2 * Copyright (C) 2001 RidgeRun (http://www.ridgerun.com/)
3 * Written by Erik Walthinsen <omega@ridgerun.com>
5 * gstindex.c: Index for mappings and other data
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
24 #include "gst_private.h"
25 #include "gstregistrypool.h"
30 /* Index signals and args */
42 static void gst_index_class_init (GstIndexClass *klass);
43 static void gst_index_init (GstIndex *index);
45 static void gst_index_set_property (GObject *object, guint prop_id,
46 const GValue *value, GParamSpec *pspec);
47 static void gst_index_get_property (GObject *object, guint prop_id,
48 GValue *value, GParamSpec *pspec);
50 static GstIndexGroup* gst_index_group_new (guint groupnum);
52 static gboolean gst_index_path_resolver (GstIndex *index, GstObject *writer,
53 gchar **writer_string, gpointer data);
54 static gboolean gst_index_gtype_resolver (GstIndex *index, GstObject *writer,
55 gchar **writer_string, gpointer data);
57 static GstObject *parent_class = NULL;
58 static guint gst_index_signals[LAST_SIGNAL] = { 0 };
62 GstIndexResolverMethod method;
63 GstIndexResolver resolver;
67 static const ResolverEntry resolvers[] =
69 { GST_INDEX_RESOLVER_CUSTOM, NULL, NULL },
70 { GST_INDEX_RESOLVER_GTYPE, gst_index_gtype_resolver, NULL },
71 { GST_INDEX_RESOLVER_PATH, gst_index_path_resolver, NULL },
74 #define GST_TYPE_INDEX_RESOLVER (gst_index_resolver_get_type())
76 gst_index_resolver_get_type (void)
78 static GType index_resolver_type = 0;
79 static GEnumValue index_resolver[] = {
80 { GST_INDEX_RESOLVER_CUSTOM, "GST_INDEX_RESOLVER_CUSTOM", "Use a custom resolver"},
81 { GST_INDEX_RESOLVER_GTYPE, "GST_INDEX_RESOLVER_GTYPE", "Resolve an object to its GType[.padname]"},
82 { GST_INDEX_RESOLVER_PATH, "GST_INDEX_RESOLVER_PATH", "Resolve an object to its path in the pipeline"},
85 if (!index_resolver_type) {
86 index_resolver_type = g_enum_register_static ("GstIndexResolver", index_resolver);
88 return index_resolver_type;
92 gst_index_get_type (void) {
93 static GType index_type = 0;
96 static const GTypeInfo index_info = {
97 sizeof(GstIndexClass),
100 (GClassInitFunc)gst_index_class_init,
105 (GInstanceInitFunc)gst_index_init,
108 index_type = g_type_register_static(GST_TYPE_OBJECT, "GstIndex", &index_info, 0);
114 gst_index_class_init (GstIndexClass *klass)
116 GObjectClass *gobject_class;
117 GstElementClass *gstelement_class;
119 gobject_class = (GObjectClass*)klass;
120 gstelement_class = (GstElementClass*)klass;
122 parent_class = g_type_class_ref(GST_TYPE_OBJECT);
124 gst_index_signals[ENTRY_ADDED] =
125 g_signal_new ("entry_added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
126 G_STRUCT_OFFSET (GstIndexClass, entry_added), NULL, NULL,
127 gst_marshal_VOID__POINTER, G_TYPE_NONE, 1,
130 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_index_set_property);
131 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_index_get_property);
133 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RESOLVER,
134 g_param_spec_enum ("resolver", "Resolver", "Select a predefined object to string mapper",
135 GST_TYPE_INDEX_RESOLVER, GST_INDEX_RESOLVER_PATH, G_PARAM_READWRITE));
139 gst_index_init (GstIndex *index)
141 index->curgroup = gst_index_group_new(0);
143 index->groups = g_list_prepend(NULL, index->curgroup);
145 index->writers = g_hash_table_new (NULL, NULL);
148 index->method = GST_INDEX_RESOLVER_PATH;
149 index->resolver = resolvers[index->method].resolver;
150 index->resolver_user_data = resolvers[index->method].user_data;
152 GST_FLAG_SET (index, GST_INDEX_WRITABLE);
153 GST_FLAG_SET (index, GST_INDEX_READABLE);
155 GST_DEBUG(0, "created new index");
159 gst_index_set_property (GObject *object, guint prop_id,
160 const GValue *value, GParamSpec *pspec)
164 index = GST_INDEX (object);
168 index->method = g_value_get_enum (value);
169 index->resolver = resolvers[index->method].resolver;
170 index->resolver_user_data = resolvers[index->method].user_data;
173 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179 gst_index_get_property (GObject *object, guint prop_id,
180 GValue *value, GParamSpec *pspec)
184 index = GST_INDEX (object);
188 g_value_set_enum (value, index->method);
191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196 static GstIndexGroup *
197 gst_index_group_new(guint groupnum)
199 GstIndexGroup *indexgroup = g_new(GstIndexGroup,1);
201 indexgroup->groupnum = groupnum;
202 indexgroup->entries = NULL;
203 indexgroup->certainty = GST_INDEX_UNKNOWN;
204 indexgroup->peergroup = -1;
206 GST_DEBUG(0, "created new index group %d",groupnum);
214 * Create a new tileindex object
216 * Returns: a new index object
223 index = g_object_new (gst_index_get_type (), NULL);
230 * @index: the index to commit
231 * @id: the writer that commited the index
233 * Tell the index that the writer with the given id is done
234 * with this index and is not going to write any more entries
238 gst_index_commit (GstIndex *index, gint id)
240 GstIndexClass *iclass;
242 iclass = GST_INDEX_GET_CLASS (index);
245 iclass->commit (index, id);
250 * gst_index_get_group:
251 * @index: the index to get the current group from
253 * Get the id of the current group.
255 * Returns: the id of the current group.
258 gst_index_get_group(GstIndex *index)
260 return index->curgroup->groupnum;
264 * gst_index_new_group:
265 * @index: the index to create the new group in
267 * Create a new group for the given index. It will be
268 * set as the current group.
270 * Returns: the id of the newly created group.
273 gst_index_new_group(GstIndex *index)
275 index->curgroup = gst_index_group_new(++index->maxgroup);
276 index->groups = g_list_append(index->groups,index->curgroup);
277 GST_DEBUG(0, "created new group %d in index",index->maxgroup);
278 return index->maxgroup;
282 * gst_index_set_group:
283 * @index: the index to set the new group in
284 * @groupnum: the groupnumber to set
286 * Set the current groupnumber to the given argument.
288 * Returns: TRUE if the operation succeeded, FALSE if the group
292 gst_index_set_group(GstIndex *index, gint groupnum)
295 GstIndexGroup *indexgroup;
297 /* first check for null change */
298 if (groupnum == index->curgroup->groupnum)
301 /* else search for the proper group */
302 list = index->groups;
304 indexgroup = (GstIndexGroup *)(list->data);
305 list = g_list_next(list);
306 if (indexgroup->groupnum == groupnum) {
307 index->curgroup = indexgroup;
308 GST_DEBUG(0, "switched to index group %d", indexgroup->groupnum);
313 /* couldn't find the group in question */
314 GST_DEBUG(0, "couldn't find index group %d",groupnum);
319 * gst_index_set_certainty:
320 * @index: the index to set the certainty on
321 * @certainty: the certainty to set
323 * Set the certainty of the given index.
326 gst_index_set_certainty(GstIndex *index, GstIndexCertainty certainty)
328 index->curgroup->certainty = certainty;
332 * gst_index_get_certainty:
333 * @index: the index to get the certainty of
335 * Get the certainty of the given index.
337 * Returns: the certainty of the index.
340 gst_index_get_certainty(GstIndex *index)
342 return index->curgroup->certainty;
346 * gst_index_set_filter:
347 * @index: the index to register the filter on
348 * @filter: the filter to register
349 * @user_data: data passed to the filter function
351 * Lets the app register a custom filter function so that
352 * it can select what entries should be stored in the index.
355 gst_index_set_filter (GstIndex *index,
356 GstIndexFilter filter, gpointer user_data)
358 g_return_if_fail (GST_IS_INDEX (index));
360 index->filter = filter;
361 index->filter_user_data = user_data;
365 * gst_index_set_resolver:
366 * @index: the index to register the resolver on
367 * @resolver: the resolver to register
368 * @user_data: data passed to the resolver function
370 * Lets the app register a custom function to map index
371 * ids to writer descriptions.
374 gst_index_set_resolver (GstIndex *index,
375 GstIndexResolver resolver, gpointer user_data)
377 g_return_if_fail (GST_IS_INDEX (index));
379 index->resolver = resolver;
380 index->resolver_user_data = user_data;
381 index->method = GST_INDEX_RESOLVER_CUSTOM;
385 * gst_index_entry_free:
386 * @entry: the entry to free
388 * Free the memory used by the given entry.
391 gst_index_entry_free (GstIndexEntry *entry)
397 * gst_index_add_format:
398 * @index: the index to add the entry to
399 * @id: the id of the index writer
400 * @format: the format to add to the index
402 * Adds a format entry into the index. This function is
403 * used to map dynamic GstFormat ids to their original
406 * Returns: a pointer to the newly added entry in the index.
409 gst_index_add_format (GstIndex *index, gint id, GstFormat format)
411 GstIndexEntry *entry;
412 const GstFormatDefinition* def;
413 GstIndexClass *iclass;
415 g_return_val_if_fail (GST_IS_INDEX (index), NULL);
416 g_return_val_if_fail (format != 0, NULL);
418 if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
421 entry = g_new0 (GstIndexEntry, 1);
422 entry->type = GST_INDEX_ENTRY_FORMAT;
424 entry->data.format.format = format;
426 def = gst_format_get_details (format);
427 entry->data.format.key = def->nick;
429 iclass = GST_INDEX_GET_CLASS (index);
431 if (iclass->add_entry)
432 iclass->add_entry (index, entry);
434 g_signal_emit (G_OBJECT (index), gst_index_signals[ENTRY_ADDED], 0, entry);
441 * @index: the index to add the entry to
442 * @id: the id of the index writer
443 * @description: the description of the index writer
445 * Add an id entry into the index.
447 * Returns: a pointer to the newly added entry in the index.
450 gst_index_add_id (GstIndex *index, gint id, gchar *description)
452 GstIndexEntry *entry;
453 GstIndexClass *iclass;
455 g_return_val_if_fail (GST_IS_INDEX (index), NULL);
456 g_return_val_if_fail (description != NULL, NULL);
458 if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
461 entry = g_new0 (GstIndexEntry, 1);
462 entry->type = GST_INDEX_ENTRY_ID;
464 entry->data.id.description = description;
466 iclass = GST_INDEX_GET_CLASS (index);
468 if (iclass->add_entry)
469 iclass->add_entry (index, entry);
471 g_signal_emit (G_OBJECT (index), gst_index_signals[ENTRY_ADDED], 0, entry);
477 gst_index_path_resolver (GstIndex *index, GstObject *writer,
478 gchar **writer_string, gpointer data)
480 *writer_string = gst_object_get_path_string (writer);
486 gst_index_gtype_resolver (GstIndex *index, GstObject *writer,
487 gchar **writer_string, gpointer data)
489 if (GST_IS_PAD (writer)) {
490 GstElement *element = gst_pad_get_parent (GST_PAD (writer));
492 *writer_string = g_strdup_printf ("%s.%s",
493 g_type_name (G_OBJECT_TYPE (element)),
494 gst_object_get_name (writer));
497 *writer_string = g_strdup_printf ("%s", g_type_name (G_OBJECT_TYPE (writer)));
504 * gst_index_get_writer_id:
505 * @index: the index to get a unique write id for
506 * @writer: the GstObject to allocate an id for
507 * @id: a pointer to a gint to hold the id
509 * Before entries can be added to the index, a writer
510 * should obtain a unique id. The methods to add new entries
511 * to the index require this id as an argument.
513 * The application can implement a custom function to map the writer object
514 * to a string. That string will be used to register or look up an id
517 * Returns: TRUE if the writer would be mapped to an id.
520 gst_index_get_writer_id (GstIndex *index, GstObject *writer, gint *id)
522 gchar *writer_string = NULL;
523 GstIndexEntry *entry;
524 GstIndexClass *iclass;
525 gboolean success = FALSE;
527 g_return_val_if_fail (GST_IS_INDEX (index), FALSE);
528 g_return_val_if_fail (GST_IS_OBJECT (writer), FALSE);
529 g_return_val_if_fail (id, FALSE);
533 /* first try to get a previously cached id */
534 entry = g_hash_table_lookup (index->writers, writer);
537 iclass = GST_INDEX_GET_CLASS (index);
539 /* let the app make a string */
540 if (index->resolver) {
543 res = index->resolver (index, writer, &writer_string, index->resolver_user_data);
548 g_warning ("no resolver found");
552 /* if the index has a resolver, make it map this string to an id */
553 if (iclass->get_writer_id) {
554 success = iclass->get_writer_id (index, id, writer_string);
556 /* if the index could not resolve, we allocate one ourselves */
558 *id = index->last_id++;
561 entry = gst_index_add_id (index, *id, writer_string);
563 /* index is probably not writable, make an entry anyway
564 * to keep it in our cache */
565 entry = g_new0 (GstIndexEntry, 1);
566 entry->type = GST_INDEX_ENTRY_ID;
568 entry->data.id.description = writer_string;
570 g_hash_table_insert (index->writers, writer, entry);
580 * gst_index_add_association:
581 * @index: the index to add the entry to
582 * @id: the id of the index writer
583 * @flags: optinal flags for this entry
584 * @format: the format of the value
586 * @...: other format/value pairs or 0 to end the list
588 * Associate given format/value pairs with eachother.
589 * Be sure to pass gint64 values to this functions varargs,
590 * you might want to use a gint64 cast to be sure.
592 * Returns: a pointer to the newly added entry in the index.
595 gst_index_add_association (GstIndex *index, gint id, GstAssocFlags flags,
596 GstFormat format, gint64 value, ...)
599 GstIndexAssociation *assoc;
600 GstIndexEntry *entry;
603 GstFormat cur_format;
604 volatile gint64 dummy;
605 GstIndexClass *iclass;
607 g_return_val_if_fail (GST_IS_INDEX (index), NULL);
608 g_return_val_if_fail (format != 0, NULL);
610 if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
613 va_start (args, value);
619 cur_format = va_arg (args, GstFormat);
621 dummy = va_arg (args, gint64);
625 /* make room for two assoc */
626 size = sizeof (GstIndexEntry) + (sizeof (GstIndexAssociation) * nassocs);
628 entry = g_malloc (size);
630 entry->type = GST_INDEX_ENTRY_ASSOCIATION;
632 entry->data.assoc.flags = flags;
633 assoc = (GstIndexAssociation *) (((guint8 *) entry) + sizeof (GstIndexEntry));
634 entry->data.assoc.assocs = assoc;
635 entry->data.assoc.nassocs = nassocs;
637 va_start (args, value);
639 assoc->format = format;
640 assoc->value = value;
644 format = va_arg (args, GstFormat);
646 value = va_arg (args, gint64);
650 iclass = GST_INDEX_GET_CLASS (index);
652 if (iclass->add_entry)
653 iclass->add_entry (index, entry);
655 g_signal_emit (G_OBJECT (index), gst_index_signals[ENTRY_ADDED], 0, entry);
661 * gst_index_add_object:
662 * @index: the index to add the object to
663 * @id: the id of the index writer
664 * @key: a key for the object
665 * @type: the GType of the object
666 * @object: a pointer to the object to add
668 * Add the given object to the index with the given key.
670 * Returns: a pointer to the newly added entry in the index.
673 gst_index_add_object (GstIndex *index, gint id, gchar *key,
674 GType type, gpointer object)
676 if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
683 gst_index_compare_func (gconstpointer a,
691 * gst_index_get_assoc_entry:
692 * @index: the index to search
693 * @id: the id of the index writer
694 * @method: The lookup method to use
695 * @flags: Flags for the entry
696 * @format: the format of the value
697 * @value: the value to find
699 * Finds the given format/value in the index
701 * Returns: the entry associated with the value or NULL if the
702 * value was not found.
705 gst_index_get_assoc_entry (GstIndex *index, gint id,
706 GstIndexLookupMethod method, GstAssocFlags flags,
707 GstFormat format, gint64 value)
709 g_return_val_if_fail (GST_IS_INDEX (index), NULL);
714 return gst_index_get_assoc_entry_full (index, id, method, flags, format, value,
715 gst_index_compare_func, NULL);
719 * gst_index_get_assoc_entry_full:
720 * @index: the index to search
721 * @id: the id of the index writer
722 * @method: The lookup method to use
723 * @flags: Flags for the entry
724 * @format: the format of the value
725 * @value: the value to find
726 * @func: the function used to compare entries
727 * @user_data: user data passed to the compare function
729 * Finds the given format/value in the index with the given
730 * compare function and user_data.
732 * Returns: the entry associated with the value or NULL if the
733 * value was not found.
736 gst_index_get_assoc_entry_full (GstIndex *index, gint id,
737 GstIndexLookupMethod method, GstAssocFlags flags,
738 GstFormat format, gint64 value,
739 GCompareDataFunc func,
742 GstIndexClass *iclass;
744 g_return_val_if_fail (GST_IS_INDEX (index), NULL);
749 iclass = GST_INDEX_GET_CLASS (index);
751 if (iclass->get_assoc_entry)
752 return iclass->get_assoc_entry (index, id, method, flags, format, value, func, user_data);
758 * gst_index_entry_assoc_map:
759 * @entry: the index to search
760 * @format: the format of the value the find
761 * @value: a pointer to store the value
763 * Gets alternative formats associated with the indexentry.
765 * Returns: TRUE if there was a value associated with the given
769 gst_index_entry_assoc_map (GstIndexEntry *entry,
770 GstFormat format, gint64 *value)
774 g_return_val_if_fail (entry != NULL, FALSE);
775 g_return_val_if_fail (value != NULL, FALSE);
777 for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
778 if (GST_INDEX_ASSOC_FORMAT (entry, i) == format) {
779 *value = GST_INDEX_ASSOC_VALUE (entry, i);
787 static void gst_index_factory_class_init (GstIndexFactoryClass *klass);
788 static void gst_index_factory_init (GstIndexFactory *factory);
790 static GstPluginFeatureClass *factory_parent_class = NULL;
791 /* static guint gst_index_factory_signals[LAST_SIGNAL] = { 0 }; */
794 gst_index_factory_get_type (void)
796 static GType indexfactory_type = 0;
798 if (!indexfactory_type) {
799 static const GTypeInfo indexfactory_info = {
800 sizeof (GstIndexFactoryClass),
803 (GClassInitFunc) gst_index_factory_class_init,
806 sizeof(GstIndexFactory),
808 (GInstanceInitFunc) gst_index_factory_init,
811 indexfactory_type = g_type_register_static (GST_TYPE_PLUGIN_FEATURE,
812 "GstIndexFactory", &indexfactory_info, 0);
814 return indexfactory_type;
818 gst_index_factory_class_init (GstIndexFactoryClass *klass)
820 GObjectClass *gobject_class;
821 GstObjectClass *gstobject_class;
822 GstPluginFeatureClass *gstpluginfeature_class;
824 gobject_class = (GObjectClass*)klass;
825 gstobject_class = (GstObjectClass*)klass;
826 gstpluginfeature_class = (GstPluginFeatureClass*) klass;
828 factory_parent_class = g_type_class_ref (GST_TYPE_PLUGIN_FEATURE);
832 gst_index_factory_init (GstIndexFactory *factory)
837 * gst_index_factory_new:
838 * @name: name of indexfactory to create
839 * @longdesc: long description of indexfactory to create
840 * @type: the GType of the GstIndex element of this factory
842 * Create a new indexfactory with the given parameters
844 * Returns: a new #GstIndexFactory.
847 gst_index_factory_new (const gchar *name, const gchar *longdesc, GType type)
849 GstIndexFactory *factory;
851 g_return_val_if_fail(name != NULL, NULL);
852 factory = gst_index_factory_find (name);
854 factory = GST_INDEX_FACTORY (g_object_new (GST_TYPE_INDEX_FACTORY, NULL));
857 GST_PLUGIN_FEATURE_NAME (factory) = g_strdup (name);
858 if (factory->longdesc)
859 g_free (factory->longdesc);
860 factory->longdesc = g_strdup (longdesc);
861 factory->type = type;
867 * gst_index_factory_destroy:
868 * @factory: factory to destroy
870 * Removes the index from the global list.
873 gst_index_factory_destroy (GstIndexFactory *factory)
875 g_return_if_fail (factory != NULL);
877 /* we don't free the struct bacause someone might have a handle to it.. */
881 * gst_index_factory_find:
882 * @name: name of indexfactory to find
884 * Search for an indexfactory of the given name.
886 * Returns: #GstIndexFactory if found, NULL otherwise
889 gst_index_factory_find (const gchar *name)
891 GstPluginFeature *feature;
893 g_return_val_if_fail (name != NULL, NULL);
895 GST_DEBUG (0,"gstindex: find \"%s\"", name);
897 feature = gst_registry_pool_find_feature (name, GST_TYPE_INDEX_FACTORY);
899 return GST_INDEX_FACTORY (feature);
905 * gst_index_factory_create:
906 * @factory: the factory used to create the instance
908 * Create a new #GstIndex instance from the
909 * given indexfactory.
911 * Returns: A new #GstIndex instance.
914 gst_index_factory_create (GstIndexFactory *factory)
916 GstIndex *new = NULL;
918 g_return_val_if_fail (factory != NULL, NULL);
920 if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) {
921 g_return_val_if_fail (factory->type != 0, NULL);
923 new = GST_INDEX (g_object_new(factory->type,NULL));
930 * gst_index_factory_make:
931 * @name: the name of the factory used to create the instance
933 * Create a new #GstIndex instance from the
934 * indexfactory with the given name.
936 * Returns: A new #GstIndex instance.
939 gst_index_factory_make (const gchar *name)
941 GstIndexFactory *factory;
943 g_return_val_if_fail (name != NULL, NULL);
945 factory = gst_index_factory_find (name);
950 return gst_index_factory_create (factory);