docs: gst: more gobject introspection annotations
[platform/upstream/gstreamer.git] / gst / gstindex.c
1 /* GStreamer
2  * Copyright (C) 2001 RidgeRun (http://www.ridgerun.com/)
3  * Written by Erik Walthinsen <omega@ridgerun.com>
4  *
5  * gstindex.c: Index for mappings and other data
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /**
24  * SECTION:gstindex
25  * @short_description: Generate indexes on objects
26  * @see_also: #GstIndexFactory
27  *
28  * GstIndex is used to generate a stream index of one or more elements
29  * in a pipeline.
30  */
31
32 /* FIXME: complete gobject annotations */
33
34 #include "gst_private.h"
35
36 #include "gstinfo.h"
37 #include "gstindex.h"
38 #include "gstindexfactory.h"
39 #include "gstmarshal.h"
40 #include "gstregistry.h"
41 /* for constructing an entry name */
42 #include "gstelement.h"
43 #include "gstpad.h"
44 #include "gstinfo.h"
45
46 /* Index signals and args */
47 enum
48 {
49   ENTRY_ADDED,
50   LAST_SIGNAL
51 };
52
53 enum
54 {
55   ARG_0,
56   ARG_RESOLVER
57       /* FILL ME */
58 };
59
60 GST_DEBUG_CATEGORY_STATIC (index_debug);
61 #define GST_CAT_DEFAULT index_debug
62
63 static void gst_index_finalize (GObject * object);
64
65 static void gst_index_set_property (GObject * object, guint prop_id,
66     const GValue * value, GParamSpec * pspec);
67 static void gst_index_get_property (GObject * object, guint prop_id,
68     GValue * value, GParamSpec * pspec);
69
70 static GstIndexGroup *gst_index_group_new (guint groupnum);
71 static void gst_index_group_free (GstIndexGroup * group);
72
73 static gboolean gst_index_path_resolver (GstIndex * index, GstObject * writer,
74     gchar ** writer_string, gpointer data);
75 static gboolean gst_index_gtype_resolver (GstIndex * index, GstObject * writer,
76     gchar ** writer_string, gpointer data);
77 static void gst_index_add_entry (GstIndex * index, GstIndexEntry * entry);
78
79 static GstObject *parent_class = NULL;
80 static guint gst_index_signals[LAST_SIGNAL] = { 0 };
81
82 typedef struct
83 {
84   GstIndexResolverMethod method;
85   GstIndexResolver resolver;
86   gpointer user_data;
87 }
88 ResolverEntry;
89
90 static const ResolverEntry resolvers[] = {
91   {GST_INDEX_RESOLVER_CUSTOM, NULL, NULL},
92   {GST_INDEX_RESOLVER_GTYPE, gst_index_gtype_resolver, NULL},
93   {GST_INDEX_RESOLVER_PATH, gst_index_path_resolver, NULL},
94 };
95
96 #define GST_TYPE_INDEX_RESOLVER (gst_index_resolver_get_type())
97 static GType
98 gst_index_resolver_get_type (void)
99 {
100   static GType index_resolver_type = 0;
101   static const GEnumValue index_resolver[] = {
102     {GST_INDEX_RESOLVER_CUSTOM, "GST_INDEX_RESOLVER_CUSTOM", "custom"},
103     {GST_INDEX_RESOLVER_GTYPE, "GST_INDEX_RESOLVER_GTYPE", "gtype"},
104     {GST_INDEX_RESOLVER_PATH, "GST_INDEX_RESOLVER_PATH", "path"},
105     {0, NULL, NULL},
106   };
107
108   if (!index_resolver_type) {
109     index_resolver_type =
110         g_enum_register_static ("GstIndexResolver", index_resolver);
111   }
112   return index_resolver_type;
113 }
114
115 GType
116 gst_index_entry_get_type (void)
117 {
118   static GType index_entry_type = 0;
119
120   if (!index_entry_type) {
121     index_entry_type = g_boxed_type_register_static ("GstIndexEntry",
122         (GBoxedCopyFunc) gst_index_entry_copy,
123         (GBoxedFreeFunc) gst_index_entry_free);
124   }
125   return index_entry_type;
126 }
127
128 #define _do_init \
129 { \
130   GST_DEBUG_CATEGORY_INIT (index_debug, "GST_INDEX", GST_DEBUG_BOLD, \
131       "Generic indexing support"); \
132 }
133
134 G_DEFINE_TYPE_WITH_CODE (GstIndex, gst_index, GST_TYPE_OBJECT, _do_init);
135
136 static void
137 gst_index_class_init (GstIndexClass * klass)
138 {
139   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
140
141   parent_class = g_type_class_peek_parent (klass);
142
143   /**
144    * GstIndex::entry-added
145    * @gstindex: the object which received the signal.
146    * @arg1: The entry added to the index.
147    *
148    * Is emitted when a new entry is added to the index.
149    */
150   gst_index_signals[ENTRY_ADDED] =
151       g_signal_new ("entry-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
152       G_STRUCT_OFFSET (GstIndexClass, entry_added), NULL, NULL,
153       gst_marshal_VOID__BOXED, G_TYPE_NONE, 1, GST_TYPE_INDEX_ENTRY);
154
155   gobject_class->set_property = gst_index_set_property;
156   gobject_class->get_property = gst_index_get_property;
157   gobject_class->finalize = gst_index_finalize;
158
159   g_object_class_install_property (gobject_class, ARG_RESOLVER,
160       g_param_spec_enum ("resolver", "Resolver",
161           "Select a predefined object to string mapper",
162           GST_TYPE_INDEX_RESOLVER, GST_INDEX_RESOLVER_PATH,
163           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
164 }
165
166 static void
167 gst_index_init (GstIndex * index)
168 {
169   index->curgroup = gst_index_group_new (0);
170   index->maxgroup = 0;
171   index->groups = g_list_prepend (NULL, index->curgroup);
172
173   index->writers = g_hash_table_new (NULL, NULL);
174   index->last_id = 0;
175
176   index->method = GST_INDEX_RESOLVER_PATH;
177   index->resolver = resolvers[index->method].resolver;
178   index->resolver_user_data = resolvers[index->method].user_data;
179
180   GST_OBJECT_FLAG_SET (index, GST_INDEX_WRITABLE);
181   GST_OBJECT_FLAG_SET (index, GST_INDEX_READABLE);
182
183   GST_DEBUG ("created new index");
184 }
185
186 static void
187 gst_index_free_writer (gpointer key, gpointer value, gpointer user_data)
188 {
189   GstIndexEntry *entry = (GstIndexEntry *) value;
190
191   if (entry) {
192     gst_index_entry_free (entry);
193   }
194 }
195
196 static void
197 gst_index_finalize (GObject * object)
198 {
199   GstIndex *index = GST_INDEX (object);
200
201   if (index->groups) {
202     g_list_foreach (index->groups, (GFunc) gst_index_group_free, NULL);
203     g_list_free (index->groups);
204     index->groups = NULL;
205   }
206
207   if (index->writers) {
208     g_hash_table_foreach (index->writers, gst_index_free_writer, NULL);
209     g_hash_table_destroy (index->writers);
210     index->writers = NULL;
211   }
212
213   if (index->filter_user_data && index->filter_user_data_destroy)
214     index->filter_user_data_destroy (index->filter_user_data);
215
216   if (index->resolver_user_data && index->resolver_user_data_destroy)
217     index->resolver_user_data_destroy (index->resolver_user_data);
218
219   G_OBJECT_CLASS (parent_class)->finalize (object);
220 }
221
222 static void
223 gst_index_set_property (GObject * object, guint prop_id,
224     const GValue * value, GParamSpec * pspec)
225 {
226   GstIndex *index;
227
228   index = GST_INDEX (object);
229
230   switch (prop_id) {
231     case ARG_RESOLVER:
232       index->method = g_value_get_enum (value);
233       index->resolver = resolvers[index->method].resolver;
234       index->resolver_user_data = resolvers[index->method].user_data;
235       break;
236     default:
237       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
238       break;
239   }
240 }
241
242 static void
243 gst_index_get_property (GObject * object, guint prop_id,
244     GValue * value, GParamSpec * pspec)
245 {
246   GstIndex *index;
247
248   index = GST_INDEX (object);
249
250   switch (prop_id) {
251     case ARG_RESOLVER:
252       g_value_set_enum (value, index->method);
253       break;
254     default:
255       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
256       break;
257   }
258 }
259
260 static GstIndexGroup *
261 gst_index_group_new (guint groupnum)
262 {
263   GstIndexGroup *indexgroup = g_slice_new (GstIndexGroup);
264
265   indexgroup->groupnum = groupnum;
266   indexgroup->entries = NULL;
267   indexgroup->certainty = GST_INDEX_UNKNOWN;
268   indexgroup->peergroup = -1;
269
270   GST_DEBUG ("created new index group %d", groupnum);
271
272   return indexgroup;
273 }
274
275 static void
276 gst_index_group_free (GstIndexGroup * group)
277 {
278   g_slice_free (GstIndexGroup, group);
279 }
280
281 /**
282  * gst_index_new:
283  *
284  * Create a new tileindex object
285  *
286  * Returns: (transfer full): a new index object
287  */
288 GstIndex *
289 gst_index_new (void)
290 {
291   GstIndex *index;
292
293   index = g_object_newv (gst_index_get_type (), 0, NULL);
294
295   return index;
296 }
297
298 /**
299  * gst_index_commit:
300  * @index: the index to commit
301  * @id: the writer that commited the index
302  *
303  * Tell the index that the writer with the given id is done
304  * with this index and is not going to write any more entries
305  * to it.
306  */
307 void
308 gst_index_commit (GstIndex * index, gint id)
309 {
310   GstIndexClass *iclass;
311
312   iclass = GST_INDEX_GET_CLASS (index);
313
314   if (iclass->commit)
315     iclass->commit (index, id);
316 }
317
318
319 /**
320  * gst_index_get_group:
321  * @index: the index to get the current group from
322  *
323  * Get the id of the current group.
324  *
325  * Returns: the id of the current group.
326  */
327 gint
328 gst_index_get_group (GstIndex * index)
329 {
330   return index->curgroup->groupnum;
331 }
332
333 /**
334  * gst_index_new_group:
335  * @index: the index to create the new group in
336  *
337  * Create a new group for the given index. It will be
338  * set as the current group.
339  *
340  * Returns: the id of the newly created group.
341  */
342 gint
343 gst_index_new_group (GstIndex * index)
344 {
345   index->curgroup = gst_index_group_new (++index->maxgroup);
346   index->groups = g_list_append (index->groups, index->curgroup);
347   GST_DEBUG ("created new group %d in index", index->maxgroup);
348   return index->maxgroup;
349 }
350
351 /**
352  * gst_index_set_group:
353  * @index: the index to set the new group in
354  * @groupnum: the groupnumber to set
355  *
356  * Set the current groupnumber to the given argument.
357  *
358  * Returns: TRUE if the operation succeeded, FALSE if the group
359  * did not exist.
360  */
361 gboolean
362 gst_index_set_group (GstIndex * index, gint groupnum)
363 {
364   GList *list;
365   GstIndexGroup *indexgroup;
366
367   /* first check for null change */
368   if (groupnum == index->curgroup->groupnum)
369     return TRUE;
370
371   /* else search for the proper group */
372   list = index->groups;
373   while (list) {
374     indexgroup = (GstIndexGroup *) (list->data);
375     list = g_list_next (list);
376     if (indexgroup->groupnum == groupnum) {
377       index->curgroup = indexgroup;
378       GST_DEBUG ("switched to index group %d", indexgroup->groupnum);
379       return TRUE;
380     }
381   }
382
383   /* couldn't find the group in question */
384   GST_DEBUG ("couldn't find index group %d", groupnum);
385   return FALSE;
386 }
387
388 /**
389  * gst_index_set_certainty:
390  * @index: the index to set the certainty on
391  * @certainty: the certainty to set
392  *
393  * Set the certainty of the given index.
394  */
395 void
396 gst_index_set_certainty (GstIndex * index, GstIndexCertainty certainty)
397 {
398   index->curgroup->certainty = certainty;
399 }
400
401 /**
402  * gst_index_get_certainty:
403  * @index: the index to get the certainty of
404  *
405  * Get the certainty of the given index.
406  *
407  * Returns: the certainty of the index.
408  */
409 GstIndexCertainty
410 gst_index_get_certainty (GstIndex * index)
411 {
412   return index->curgroup->certainty;
413 }
414
415 /**
416  * gst_index_set_filter:
417  * @index: the index to register the filter on
418  * @filter: the filter to register
419  * @user_data: data passed to the filter function
420  *
421  * Lets the app register a custom filter function so that
422  * it can select what entries should be stored in the index.
423  */
424 void
425 gst_index_set_filter (GstIndex * index,
426     GstIndexFilter filter, gpointer user_data)
427 {
428   g_return_if_fail (GST_IS_INDEX (index));
429
430   gst_index_set_filter_full (index, filter, user_data, NULL);
431 }
432
433 /**
434  * gst_index_set_filter_full:
435  * @index: the index to register the filter on
436  * @filter: the filter to register
437  * @user_data: data passed to the filter function
438  * @user_data_destroy: function to call when @user_data is unset
439  *
440  * Lets the app register a custom filter function so that
441  * it can select what entries should be stored in the index.
442  */
443 void
444 gst_index_set_filter_full (GstIndex * index,
445     GstIndexFilter filter, gpointer user_data, GDestroyNotify user_data_destroy)
446 {
447   g_return_if_fail (GST_IS_INDEX (index));
448
449   if (index->filter_user_data && index->filter_user_data_destroy)
450     index->filter_user_data_destroy (index->filter_user_data);
451
452   index->filter = filter;
453   index->filter_user_data = user_data;
454   index->filter_user_data_destroy = user_data_destroy;
455 }
456
457 /**
458  * gst_index_set_resolver:
459  * @index: the index to register the resolver on
460  * @resolver: the resolver to register
461  * @user_data: data passed to the resolver function
462  *
463  * Lets the app register a custom function to map index
464  * ids to writer descriptions.
465  */
466 void
467 gst_index_set_resolver (GstIndex * index,
468     GstIndexResolver resolver, gpointer user_data)
469 {
470   gst_index_set_resolver_full (index, resolver, user_data, NULL);
471 }
472
473 /**
474  * gst_index_set_resolver_full:
475  * @index: the index to register the resolver on
476  * @resolver: the resolver to register
477  * @user_data: data passed to the resolver function
478  * @user_data_destroy: destroy function for @user_data
479  *
480  * Lets the app register a custom function to map index
481  * ids to writer descriptions.
482  *
483  * Since: 0.10.18
484  */
485 void
486 gst_index_set_resolver_full (GstIndex * index, GstIndexResolver resolver,
487     gpointer user_data, GDestroyNotify user_data_destroy)
488 {
489   g_return_if_fail (GST_IS_INDEX (index));
490
491   if (index->resolver_user_data && index->resolver_user_data_destroy)
492     index->resolver_user_data_destroy (index->resolver_user_data);
493
494   index->resolver = resolver;
495   index->resolver_user_data = user_data;
496   index->resolver_user_data_destroy = user_data_destroy;
497   index->method = GST_INDEX_RESOLVER_CUSTOM;
498 }
499
500 /**
501  * gst_index_entry_copy:
502  * @entry: the entry to copy
503  *
504  * Copies an entry and returns the result.
505  *
506  * Free-function: gst_index_entry_free
507  *
508  * Returns: (transfer full): a newly allocated #GstIndexEntry.
509  */
510 GstIndexEntry *
511 gst_index_entry_copy (GstIndexEntry * entry)
512 {
513   GstIndexEntry *new_entry = g_slice_new (GstIndexEntry);
514
515   memcpy (new_entry, entry, sizeof (GstIndexEntry));
516   return new_entry;
517 }
518
519 /**
520  * gst_index_entry_free:
521  * @entry: (transfer full): the entry to free
522  *
523  * Free the memory used by the given entry.
524  */
525 void
526 gst_index_entry_free (GstIndexEntry * entry)
527 {
528   switch (entry->type) {
529     case GST_INDEX_ENTRY_ID:
530       if (entry->data.id.description) {
531         g_free (entry->data.id.description);
532         entry->data.id.description = NULL;
533       }
534       break;
535     case GST_INDEX_ENTRY_ASSOCIATION:
536       if (entry->data.assoc.assocs) {
537         g_free (entry->data.assoc.assocs);
538         entry->data.assoc.assocs = NULL;
539       }
540       break;
541     case GST_INDEX_ENTRY_OBJECT:
542       break;
543     case GST_INDEX_ENTRY_FORMAT:
544       break;
545   }
546
547   g_slice_free (GstIndexEntry, entry);
548 }
549
550 /**
551  * gst_index_add_format:
552  * @index: the index to add the entry to
553  * @id: the id of the index writer
554  * @format: the format to add to the index
555  *
556  * Adds a format entry into the index. This function is
557  * used to map dynamic GstFormat ids to their original
558  * format key.
559  *
560  * Free-function: gst_index_entry_free
561  *
562  * Returns: (transfer full): a pointer to the newly added entry in the index.
563  */
564 GstIndexEntry *
565 gst_index_add_format (GstIndex * index, gint id, GstFormat format)
566 {
567   GstIndexEntry *entry;
568   const GstFormatDefinition *def;
569
570   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
571   g_return_val_if_fail (format != 0, NULL);
572
573   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
574     return NULL;
575
576   entry = g_slice_new (GstIndexEntry);
577   entry->type = GST_INDEX_ENTRY_FORMAT;
578   entry->id = id;
579   entry->data.format.format = format;
580
581   def = gst_format_get_details (format);
582   entry->data.format.key = def->nick;
583
584   gst_index_add_entry (index, entry);
585
586   return entry;
587 }
588
589 /**
590  * gst_index_add_id:
591  * @index: the index to add the entry to
592  * @id: the id of the index writer
593  * @description: the description of the index writer
594  *
595  * Add an id entry into the index.
596  *
597  * Returns: a pointer to the newly added entry in the index.
598  */
599 GstIndexEntry *
600 gst_index_add_id (GstIndex * index, gint id, gchar * description)
601 {
602   GstIndexEntry *entry;
603
604   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
605   g_return_val_if_fail (description != NULL, NULL);
606
607   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
608     return NULL;
609
610   entry = g_slice_new (GstIndexEntry);
611   entry->type = GST_INDEX_ENTRY_ID;
612   entry->id = id;
613   entry->data.id.description = description;
614
615   gst_index_add_entry (index, entry);
616
617   return entry;
618 }
619
620 static gboolean
621 gst_index_path_resolver (GstIndex * index, GstObject * writer,
622     gchar ** writer_string, gpointer data)
623 {
624   *writer_string = gst_object_get_path_string (writer);
625
626   return TRUE;
627 }
628
629 static gboolean
630 gst_index_gtype_resolver (GstIndex * index, GstObject * writer,
631     gchar ** writer_string, gpointer data)
632 {
633   g_return_val_if_fail (writer != NULL, FALSE);
634
635   if (GST_IS_PAD (writer)) {
636     GstElement *element =
637         (GstElement *) gst_object_get_parent (GST_OBJECT (writer));
638     gchar *name;
639
640     name = gst_object_get_name (writer);
641     *writer_string = g_strdup_printf ("%s.%s",
642         g_type_name (G_OBJECT_TYPE (element)), name);
643
644     gst_object_unref (element);
645     g_free (name);
646
647   } else {
648     *writer_string =
649         g_strdup_printf ("%s", g_type_name (G_OBJECT_TYPE (writer)));
650   }
651
652   return TRUE;
653 }
654
655 /**
656  * gst_index_get_writer_id:
657  * @index: the index to get a unique write id for
658  * @writer: the GstObject to allocate an id for
659  * @id: a pointer to a gint to hold the id
660  *
661  * Before entries can be added to the index, a writer
662  * should obtain a unique id. The methods to add new entries
663  * to the index require this id as an argument.
664  *
665  * The application can implement a custom function to map the writer object
666  * to a string. That string will be used to register or look up an id
667  * in the index.
668  *
669  * Returns: TRUE if the writer would be mapped to an id.
670  */
671 gboolean
672 gst_index_get_writer_id (GstIndex * index, GstObject * writer, gint * id)
673 {
674   gchar *writer_string = NULL;
675   GstIndexEntry *entry;
676   GstIndexClass *iclass;
677   gboolean success = FALSE;
678
679   g_return_val_if_fail (GST_IS_INDEX (index), FALSE);
680   g_return_val_if_fail (GST_IS_OBJECT (writer), FALSE);
681   g_return_val_if_fail (id, FALSE);
682
683   *id = -1;
684
685   /* first try to get a previously cached id */
686   entry = g_hash_table_lookup (index->writers, writer);
687   if (entry == NULL) {
688
689     iclass = GST_INDEX_GET_CLASS (index);
690
691     /* let the app make a string */
692     if (index->resolver) {
693       gboolean res;
694
695       res =
696           index->resolver (index, writer, &writer_string,
697           index->resolver_user_data);
698       if (!res)
699         return FALSE;
700     } else {
701       g_warning ("no resolver found");
702       return FALSE;
703     }
704
705     /* if the index has a resolver, make it map this string to an id */
706     if (iclass->get_writer_id) {
707       success = iclass->get_writer_id (index, id, writer_string);
708     }
709     /* if the index could not resolve, we allocate one ourselves */
710     if (!success) {
711       *id = ++index->last_id;
712     }
713
714     entry = gst_index_add_id (index, *id, writer_string);
715     if (!entry) {
716       /* index is probably not writable, make an entry anyway
717        * to keep it in our cache */
718       entry = g_slice_new (GstIndexEntry);
719       entry->type = GST_INDEX_ENTRY_ID;
720       entry->id = *id;
721       entry->data.id.description = writer_string;
722     }
723     g_hash_table_insert (index->writers, writer, entry);
724   } else {
725     *id = entry->id;
726   }
727
728   return TRUE;
729 }
730
731 static void
732 gst_index_add_entry (GstIndex * index, GstIndexEntry * entry)
733 {
734   GstIndexClass *iclass;
735
736   iclass = GST_INDEX_GET_CLASS (index);
737
738   if (iclass->add_entry) {
739     iclass->add_entry (index, entry);
740   }
741
742   g_signal_emit (index, gst_index_signals[ENTRY_ADDED], 0, entry);
743 }
744
745 /**
746  * gst_index_add_associationv:
747  * @index: the index to add the entry to
748  * @id: the id of the index writer
749  * @flags: optinal flags for this entry
750  * @n: number of associations
751  * @list: list of associations
752  *
753  * Associate given format/value pairs with each other.
754  *
755  * Returns: a pointer to the newly added entry in the index.
756  */
757 GstIndexEntry *
758 gst_index_add_associationv (GstIndex * index, gint id, GstAssocFlags flags,
759     gint n, const GstIndexAssociation * list)
760 {
761   GstIndexEntry *entry;
762
763   g_return_val_if_fail (n > 0, NULL);
764   g_return_val_if_fail (list != NULL, NULL);
765   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
766
767   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
768     return NULL;
769
770   entry = g_slice_new (GstIndexEntry);
771
772   entry->type = GST_INDEX_ENTRY_ASSOCIATION;
773   entry->id = id;
774   entry->data.assoc.flags = flags;
775   entry->data.assoc.assocs = g_memdup (list, sizeof (GstIndexAssociation) * n);
776   entry->data.assoc.nassocs = n;
777
778   gst_index_add_entry (index, entry);
779
780   return entry;
781 }
782
783 /**
784  * gst_index_add_association:
785  * @index: the index to add the entry to
786  * @id: the id of the index writer
787  * @flags: optinal flags for this entry
788  * @format: the format of the value
789  * @value: the value
790  * @...: other format/value pairs or 0 to end the list
791  *
792  * Associate given format/value pairs with each other.
793  * Be sure to pass gint64 values to this functions varargs,
794  * you might want to use a gint64 cast to be sure.
795  *
796  * Returns: a pointer to the newly added entry in the index.
797  */
798 GstIndexEntry *
799 gst_index_add_association (GstIndex * index, gint id, GstAssocFlags flags,
800     GstFormat format, gint64 value, ...)
801 {
802   va_list args;
803   GstIndexEntry *entry;
804   GstIndexAssociation *list;
805   gint n_assocs = 0;
806   GstFormat cur_format;
807   GArray *array;
808
809   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
810   g_return_val_if_fail (format != 0, NULL);
811
812   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
813     return NULL;
814
815   array = g_array_new (FALSE, FALSE, sizeof (GstIndexAssociation));
816
817   {
818     GstIndexAssociation a;
819
820     a.format = format;
821     a.value = value;
822     n_assocs = 1;
823     g_array_append_val (array, a);
824   }
825
826   va_start (args, value);
827
828   while ((cur_format = va_arg (args, GstFormat))) {
829     GstIndexAssociation a;
830
831     a.format = cur_format;
832     a.value = va_arg (args, gint64);
833     n_assocs++;
834     g_array_append_val (array, a);
835   }
836
837   va_end (args);
838
839   list = (GstIndexAssociation *) g_array_free (array, FALSE);
840
841   entry = gst_index_add_associationv (index, id, flags, n_assocs, list);
842   g_free (list);
843
844   return entry;
845 }
846
847 /**
848  * gst_index_add_object:
849  * @index: the index to add the object to
850  * @id: the id of the index writer
851  * @key: a key for the object
852  * @type: the GType of the object
853  * @object: a pointer to the object to add
854  *
855  * Add the given object to the index with the given key.
856  *
857  * This function is not yet implemented.
858  *
859  * Returns: a pointer to the newly added entry in the index.
860  */
861 GstIndexEntry *
862 gst_index_add_object (GstIndex * index, gint id, gchar * key,
863     GType type, gpointer object)
864 {
865   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
866     return NULL;
867
868   return NULL;
869 }
870
871 static gint
872 gst_index_compare_func (gconstpointer a, gconstpointer b, gpointer user_data)
873 {
874   if (a < b)
875     return -1;
876   if (a > b)
877     return 1;
878   return 0;
879 }
880
881 /**
882  * gst_index_get_assoc_entry:
883  * @index: the index to search
884  * @id: the id of the index writer
885  * @method: The lookup method to use
886  * @flags: Flags for the entry
887  * @format: the format of the value
888  * @value: the value to find
889  *
890  * Finds the given format/value in the index
891  *
892  * Returns: the entry associated with the value or NULL if the
893  *   value was not found.
894  */
895 GstIndexEntry *
896 gst_index_get_assoc_entry (GstIndex * index, gint id,
897     GstIndexLookupMethod method, GstAssocFlags flags,
898     GstFormat format, gint64 value)
899 {
900   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
901
902   if (id == -1)
903     return NULL;
904
905   return gst_index_get_assoc_entry_full (index, id, method, flags, format,
906       value, gst_index_compare_func, NULL);
907 }
908
909 /**
910  * gst_index_get_assoc_entry_full:
911  * @index: the index to search
912  * @id: the id of the index writer
913  * @method: The lookup method to use
914  * @flags: Flags for the entry
915  * @format: the format of the value
916  * @value: the value to find
917  * @func: the function used to compare entries
918  * @user_data: user data passed to the compare function
919  *
920  * Finds the given format/value in the index with the given
921  * compare function and user_data.
922  *
923  * Returns: the entry associated with the value or NULL if the
924  *   value was not found.
925  */
926 GstIndexEntry *
927 gst_index_get_assoc_entry_full (GstIndex * index, gint id,
928     GstIndexLookupMethod method, GstAssocFlags flags,
929     GstFormat format, gint64 value, GCompareDataFunc func, gpointer user_data)
930 {
931   GstIndexClass *iclass;
932
933   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
934
935   if (id == -1)
936     return NULL;
937
938   iclass = GST_INDEX_GET_CLASS (index);
939
940   if (iclass->get_assoc_entry)
941     return iclass->get_assoc_entry (index, id, method, flags, format, value,
942         func, user_data);
943
944   return NULL;
945 }
946
947 /**
948  * gst_index_entry_assoc_map:
949  * @entry: the index to search
950  * @format: the format of the value the find
951  * @value: a pointer to store the value
952  *
953  * Gets alternative formats associated with the indexentry.
954  *
955  * Returns: TRUE if there was a value associated with the given
956  * format.
957  */
958 gboolean
959 gst_index_entry_assoc_map (GstIndexEntry * entry,
960     GstFormat format, gint64 * value)
961 {
962   gint i;
963
964   g_return_val_if_fail (entry != NULL, FALSE);
965   g_return_val_if_fail (value != NULL, FALSE);
966
967   for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
968     if (GST_INDEX_ASSOC_FORMAT (entry, i) == format) {
969       *value = GST_INDEX_ASSOC_VALUE (entry, i);
970       return TRUE;
971     }
972   }
973   return FALSE;
974 }