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