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