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