Documentation updates
[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 #include "gstlog.h"
24 #include "gst_private.h"
25 #include "gstregistry.h"
26
27 #include "gstindex.h"
28
29 /* Index signals and args */
30 enum {
31   ENTRY_ADDED,
32   LAST_SIGNAL
33 };
34
35 enum {
36   ARG_0,
37   /* FILL ME */
38 };
39
40 static void             gst_index_class_init    (GstIndexClass *klass);
41 static void             gst_index_init          (GstIndex *index);
42
43 #define CLASS(index)  GST_INDEX_CLASS (G_OBJECT_GET_CLASS (index))
44
45 static GstObject *parent_class = NULL;
46 static guint gst_index_signals[LAST_SIGNAL] = { 0 };
47
48 GType
49 gst_index_get_type(void) {
50   static GType index_type = 0;
51
52   if (!index_type) {
53     static const GTypeInfo index_info = {
54       sizeof(GstIndexClass),
55       NULL,
56       NULL,
57       (GClassInitFunc)gst_index_class_init,
58       NULL,
59       NULL,
60       sizeof(GstIndex),
61       1,
62       (GInstanceInitFunc)gst_index_init,
63       NULL
64     };
65     index_type = g_type_register_static(GST_TYPE_OBJECT, "GstIndex", &index_info, 0);
66   }
67   return index_type;
68 }
69
70 static void
71 gst_index_class_init (GstIndexClass *klass)
72 {
73   GObjectClass *gobject_class;
74   GstElementClass *gstelement_class;
75
76   gobject_class = (GObjectClass*)klass;
77   gstelement_class = (GstElementClass*)klass;
78
79   parent_class = g_type_class_ref(GST_TYPE_OBJECT);
80
81   gst_index_signals[ENTRY_ADDED] =
82     g_signal_new ("entry_added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
83                   G_STRUCT_OFFSET (GstIndexClass, entry_added), NULL, NULL,
84                   gst_marshal_VOID__POINTER, G_TYPE_NONE, 1,
85                   G_TYPE_POINTER);
86 }
87
88 static GstIndexGroup *
89 gst_index_group_new(guint groupnum)
90 {
91   GstIndexGroup *indexgroup = g_new(GstIndexGroup,1);
92
93   indexgroup->groupnum = groupnum;
94   indexgroup->entries = NULL;
95   indexgroup->certainty = GST_INDEX_UNKNOWN;
96   indexgroup->peergroup = -1;
97
98   GST_DEBUG(0, "created new index group %d",groupnum);
99
100   return indexgroup;
101 }
102
103 static void
104 gst_index_init (GstIndex *index)
105 {
106   index->curgroup = gst_index_group_new(0);
107   index->maxgroup = 0;
108   index->groups = g_list_prepend(NULL, index->curgroup);
109
110   index->writers = g_hash_table_new (NULL, NULL);
111   index->last_id = 0;
112   
113   GST_DEBUG(0, "created new index");
114 }
115
116 /**
117  * gst_index_new:
118  *
119  * Create a new tileindex object
120  *
121  * Returns: a new index object
122  */
123 GstIndex *
124 gst_index_new()
125 {
126   GstIndex *index;
127
128   index = g_object_new (gst_index_get_type (), NULL);
129
130   return index;
131 }
132
133 /**
134  * gst_index_get_group:
135  * @index: the index to get the current group from
136  *
137  * Get the id of the current group.
138  *
139  * Returns: the id of the current group.
140  */
141 gint
142 gst_index_get_group(GstIndex *index)
143 {
144   return index->curgroup->groupnum;
145 }
146
147 /**
148  * gst_index_new_group:
149  * @index: the index to create the new group in
150  *
151  * Create a new group for the given index. It will be
152  * set as the current group.
153  *
154  * Returns: the id of the newly created group.
155  */
156 gint
157 gst_index_new_group(GstIndex *index)
158 {
159   index->curgroup = gst_index_group_new(++index->maxgroup);
160   index->groups = g_list_append(index->groups,index->curgroup);
161   GST_DEBUG(0, "created new group %d in index",index->maxgroup);
162   return index->maxgroup;
163 }
164
165 /**
166  * gst_index_set_group:
167  * @index: the index to set the new group in
168  * @groupnum: the groupnumber to set
169  *
170  * Set the current groupnumber to the given argument.
171  *
172  * Returns: TRUE if the operation succeeded, FALSE if the group
173  * did not exist.
174  */
175 gboolean
176 gst_index_set_group(GstIndex *index, gint groupnum)
177 {
178   GList *list;
179   GstIndexGroup *indexgroup;
180
181   /* first check for null change */
182   if (groupnum == index->curgroup->groupnum)
183     return TRUE;
184
185   /* else search for the proper group */
186   list = index->groups;
187   while (list) {
188     indexgroup = (GstIndexGroup *)(list->data);
189     list = g_list_next(list);
190     if (indexgroup->groupnum == groupnum) {
191       index->curgroup = indexgroup;
192       GST_DEBUG(0, "switched to index group %d", indexgroup->groupnum);
193       return TRUE;
194     }
195   }
196
197   /* couldn't find the group in question */
198   GST_DEBUG(0, "couldn't find index group %d",groupnum);
199   return FALSE;
200 }
201
202 /**
203  * gst_index_set_certainty:
204  * @index: the index to set the certainty on
205  * @certainty: the certainty to set
206  *
207  * Set the certainty of the given index.
208  */
209 void
210 gst_index_set_certainty(GstIndex *index, GstIndexCertainty certainty)
211 {
212   index->curgroup->certainty = certainty;
213 }
214
215 /**
216  * gst_index_get_certainty:
217  * @index: the index to get the certainty of
218  *
219  * Get the certainty of the given index.
220  *
221  * Returns: the certainty of the index.
222  */
223 GstIndexCertainty
224 gst_index_get_certainty(GstIndex *index)
225 {
226   return index->curgroup->certainty;
227 }
228
229 /**
230  * gst_index_set_filter:
231  * @index: the index to register the filter on
232  * @filter: the filter to register
233  * @user_data: data passed to the filter function
234  *
235  * Lets the app register a custom filter function so that
236  * it can select what entries should be stored in the index.
237  */
238 void
239 gst_index_set_filter (GstIndex *index, 
240                       GstIndexFilter filter, gpointer user_data)
241 {
242   g_return_if_fail (GST_IS_INDEX (index));
243
244   index->filter = filter;
245   index->filter_user_data = user_data;
246 }
247
248 /**
249  * gst_index_set_resolver:
250  * @index: the index to register the resolver on
251  * @resolver: the resolver to register
252  * @user_data: data passed to the resolver function
253  *
254  * Lets the app register a custom function to map index
255  * ids to writer descriptions.
256  */
257 void
258 gst_index_set_resolver (GstIndex *index, 
259                         GstIndexResolver resolver, gpointer user_data)
260 {
261   g_return_if_fail (GST_IS_INDEX (index));
262
263   index->resolver = resolver;
264   index->resolver_user_data = user_data;
265 }
266
267 /**
268  * gst_index_entry_free:
269  * @entry: the entry to free
270  *
271  * Free the memory used by the given entry.
272  */
273 void
274 gst_index_entry_free (GstIndexEntry *entry)
275 {
276   g_free (entry);
277 }
278
279 /**
280  * gst_index_add_format:
281  * @index: the index to add the entry to
282  * @id: the id of the index writer
283  * @format: the format to add to the index
284  *
285  * Adds a format entry into the index. This function is
286  * used to map dynamic GstFormat ids to their original
287  * format key.
288  *
289  * Returns: a pointer to the newly added entry in the index.
290  */
291 GstIndexEntry*
292 gst_index_add_format (GstIndex *index, gint id, GstFormat format)
293 {
294   GstIndexEntry *entry;
295   const GstFormatDefinition* def;
296
297   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
298   g_return_val_if_fail (format != 0, NULL);
299   
300   entry = g_new0 (GstIndexEntry, 1);
301   entry->type = GST_INDEX_ENTRY_FORMAT;
302   entry->id = id;
303   entry->data.format.format = format;
304   def = gst_format_get_details (format);
305   entry->data.format.key = def->nick;
306   
307   if (CLASS (index)->add_entry)
308     CLASS (index)->add_entry (index, entry);
309
310   g_signal_emit (G_OBJECT (index), gst_index_signals[ENTRY_ADDED], 0, entry);
311
312   return entry;
313 }
314
315 /**
316  * gst_index_add_id:
317  * @index: the index to add the entry to
318  * @id: the id of the index writer
319  * @description: the description of the index writer
320  *
321  * Add an id entry into the index.
322  *
323  * Returns: a pointer to the newly added entry in the index.
324  */
325 GstIndexEntry*
326 gst_index_add_id (GstIndex *index, gint id, gchar *description)
327 {
328   GstIndexEntry *entry;
329
330   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
331   g_return_val_if_fail (description != NULL, NULL);
332   
333   entry = g_new0 (GstIndexEntry, 1);
334   entry->type = GST_INDEX_ENTRY_ID;
335   entry->id = id;
336   entry->data.id.description = description;
337
338   if (CLASS (index)->add_entry)
339     CLASS (index)->add_entry (index, entry);
340   
341   g_signal_emit (G_OBJECT (index), gst_index_signals[ENTRY_ADDED], 0, entry);
342
343   return entry;
344 }
345
346 /**
347  * gst_index_get_writer_id:
348  * @index: the index to get a unique write id for
349  * @writer: the GstObject to allocate an id for
350  * @id: a pointer to a gint to hold the id
351  *
352  * Before entries can be added to the index, a writer
353  * should obtain a unique id. The methods to add new entries
354  * to the index require this id as an argument. 
355  *
356  * The application or a GstIndex subclass can implement
357  * custom functions to map the writer object to an id.
358  *
359  * Returns: TRUE if the writer would be mapped to an id.
360  */
361 gboolean 
362 gst_index_get_writer_id (GstIndex *index, GstObject *writer, gint *id)
363 {
364   gchar *writer_string = NULL;
365   gboolean success = FALSE;
366   GstIndexEntry *entry;
367
368   g_return_val_if_fail (GST_IS_INDEX (index), FALSE);
369   g_return_val_if_fail (GST_IS_OBJECT (writer), FALSE);
370   g_return_val_if_fail (id, FALSE);
371
372   *id = -1;
373
374   entry = g_hash_table_lookup (index->writers, writer);
375   if (entry == NULL) { 
376     *id = index->last_id;
377
378     writer_string = gst_object_get_path_string (writer);
379     
380     gst_index_add_id (index, *id, writer_string);
381     index->last_id++;
382     g_hash_table_insert (index->writers, writer, entry);
383   }
384
385   if (CLASS (index)->resolve_writer) {
386     success = CLASS (index)->resolve_writer (index, writer, id, &writer_string);
387   }
388
389   if (index->resolver) {
390     success = index->resolver (index, writer, id, &writer_string, index->resolver_user_data);
391   }
392
393   return success;
394 }
395
396 /**
397  * gst_index_add_association:
398  * @index: the index to add the entry to
399  * @id: the id of the index writer
400  * @flags: optinal flags for this entry
401  * @format: the format of the value
402  * @value: the value 
403  * @...: other format/value pairs or 0 to end the list
404  *
405  * Associate given format/value pairs with eachother.
406  * Be sure to pass gint64 values to this functions varargs,
407  * you might want to use a gint64 cast to be sure.
408  *
409  * Returns: a pointer to the newly added entry in the index.
410  */
411 GstIndexEntry*
412 gst_index_add_association (GstIndex *index, gint id, GstAssocFlags flags, 
413                            GstFormat format, gint64 value, ...)
414 {
415   va_list args;
416   GstIndexAssociation *assoc;
417   GstIndexEntry *entry;
418   gulong size;
419   gint nassocs = 0;
420   GstFormat cur_format;
421   volatile gint64 dummy;
422
423   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
424   g_return_val_if_fail (format != 0, NULL);
425   
426   va_start (args, value);
427
428   cur_format = format;
429
430   while (cur_format) {
431     nassocs++;
432     cur_format = va_arg (args, GstFormat);
433     if (cur_format)
434       dummy = va_arg (args, gint64);
435   }
436   va_end (args);
437
438   /* make room for two assoc */
439   size = sizeof (GstIndexEntry) + (sizeof (GstIndexAssociation) * nassocs);
440
441   entry = g_malloc (size);
442
443   entry->type = GST_INDEX_ENTRY_ASSOCIATION;
444   entry->id = id;
445   entry->data.assoc.flags = flags;
446   assoc = (GstIndexAssociation *) (((guint8 *) entry) + sizeof (GstIndexEntry));
447   entry->data.assoc.assocs = assoc;
448   entry->data.assoc.nassocs = nassocs;
449
450   va_start (args, value);
451   while (format) {
452     assoc->format = format;
453     assoc->value = value;
454
455     assoc++;
456
457     format = va_arg (args, GstFormat);
458     if (format)
459       value = va_arg (args, gint64);
460   }
461   va_end (args);
462
463   if (CLASS (index)->add_entry)
464     CLASS (index)->add_entry (index, entry);
465
466   g_signal_emit (G_OBJECT (index), gst_index_signals[ENTRY_ADDED], 0, entry);
467
468   return entry;
469 }
470
471 /**
472  * gst_index_add_object:
473  * @index: the index to add the object to
474  * @id: the id of the index writer
475  * @key: a key for the object
476  * @type: the GType of the object
477  * @object: a pointer to the object to add
478  *
479  * Add the given object to the index with the given key.
480  * 
481  * Returns: a pointer to the newly added entry in the index.
482  */
483 GstIndexEntry*
484 gst_index_add_object (GstIndex *index, gint id, gchar *key,
485                       GType type, gpointer object)
486 {
487   return NULL;
488 }
489
490 static gint
491 gst_index_compare_func (gconstpointer a,
492                         gconstpointer b,
493                         gpointer user_data)
494 {
495   return a - b;  
496 }
497
498 /**
499  * gst_index_get_assoc_entry:
500  * @index: the index to search
501  * @id: the id of the index writer
502  * @method: The lookup method to use
503  * @format: the format of the value
504  * @value: the value to find
505  *
506  * Finds the given format/value in the index
507  *
508  * Returns: the entry associated with the value or NULL if the
509  *   value was not found.
510  */
511 GstIndexEntry*
512 gst_index_get_assoc_entry (GstIndex *index, gint id,
513                            GstIndexLookupMethod method,
514                            GstFormat format, gint64 value)
515 {
516   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
517
518   return gst_index_get_assoc_entry_full (index, id, method, format, value, 
519                                   gst_index_compare_func, NULL);
520 }
521
522 /**
523  * gst_index_get_assoc_entry_full:
524  * @index: the index to search
525  * @id: the id of the index writer
526  * @method: The lookup method to use
527  * @format: the format of the value
528  * @value: the value to find
529  * @func: the function used to compare entries
530  * @user_data: user data passed to the compare function
531  *
532  * Finds the given format/value in the index with the given
533  * compare function and user_data.
534  *
535  * Returns: the entry associated with the value or NULL if the
536  *   value was not found.
537  */
538 GstIndexEntry*
539 gst_index_get_assoc_entry_full (GstIndex *index, gint id,
540                                 GstIndexLookupMethod method,
541                                 GstFormat format, gint64 value,
542                                 GCompareDataFunc func,
543                                 gpointer user_data)
544 {
545   g_return_val_if_fail (GST_IS_INDEX (index), NULL);
546
547   if (CLASS(index)->get_assoc_entry)
548     return CLASS (index)->get_assoc_entry (index, id, method, format, value, func, user_data);
549   
550   return NULL;
551 }
552
553 /**
554  * gst_index_entry_assoc_map:
555  * @entry: the index to search
556  * @format: the format of the value the find
557  * @value: a pointer to store the value
558  *
559  * Gets alternative formats associated with the indexentry.
560  *
561  * Returns: TRUE if there was a value associated with the given 
562  * format.
563  */
564 gboolean
565 gst_index_entry_assoc_map (GstIndexEntry *entry,
566                            GstFormat format, gint64 *value)
567 {
568   gint i;
569
570   g_return_val_if_fail (entry != NULL, FALSE);
571   g_return_val_if_fail (value != NULL, FALSE);
572
573   for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
574      if (GST_INDEX_ASSOC_FORMAT (entry, i) == format) {
575        *value = GST_INDEX_ASSOC_VALUE (entry, i);
576        return TRUE;
577      }
578   }
579   return FALSE;
580 }
581
582
583 static void             gst_index_factory_class_init            (GstIndexFactoryClass *klass);
584 static void             gst_index_factory_init          (GstIndexFactory *factory);
585
586 static GstPluginFeatureClass *factory_parent_class = NULL;
587 /* static guint gst_index_factory_signals[LAST_SIGNAL] = { 0 }; */
588
589 GType 
590 gst_index_factory_get_type (void) 
591 {
592   static GType indexfactory_type = 0;
593
594   if (!indexfactory_type) {
595     static const GTypeInfo indexfactory_info = {
596       sizeof (GstIndexFactoryClass),
597       NULL,
598       NULL,
599       (GClassInitFunc) gst_index_factory_class_init,
600       NULL,
601       NULL,
602       sizeof(GstIndexFactory),
603       0,
604       (GInstanceInitFunc) gst_index_factory_init,
605       NULL
606     };
607     indexfactory_type = g_type_register_static (GST_TYPE_PLUGIN_FEATURE, 
608                                           "GstIndexFactory", &indexfactory_info, 0);
609   }
610   return indexfactory_type;
611 }
612
613 static void
614 gst_index_factory_class_init (GstIndexFactoryClass *klass)
615 {
616   GObjectClass *gobject_class;
617   GstObjectClass *gstobject_class;
618   GstPluginFeatureClass *gstpluginfeature_class;
619
620   gobject_class = (GObjectClass*)klass;
621   gstobject_class = (GstObjectClass*)klass;
622   gstpluginfeature_class = (GstPluginFeatureClass*) klass;
623
624   factory_parent_class = g_type_class_ref (GST_TYPE_PLUGIN_FEATURE);
625 }
626
627 static void
628 gst_index_factory_init (GstIndexFactory *factory)
629 {
630 }
631
632 /**
633  * gst_index_factory_new:
634  * @name: name of indexfactory to create
635  * @longdesc: long description of indexfactory to create
636  * @type: the GType of the GstIndex element of this factory
637  *
638  * Create a new indexfactory with the given parameters
639  *
640  * Returns: a new #GstIndexFactory.
641  */
642 GstIndexFactory*
643 gst_index_factory_new (const gchar *name, const gchar *longdesc, GType type)
644 {
645   GstIndexFactory *factory;
646
647   g_return_val_if_fail(name != NULL, NULL);
648   factory = gst_index_factory_find (name);
649   if (!factory) {
650     factory = GST_INDEX_FACTORY (g_object_new (GST_TYPE_INDEX_FACTORY, NULL));
651   }
652
653   GST_PLUGIN_FEATURE_NAME (factory) = g_strdup (name);
654   if (factory->longdesc)
655     g_free (factory->longdesc);
656   factory->longdesc = g_strdup (longdesc);
657   factory->type = type;
658
659   return factory;
660 }
661
662 /**
663  * gst_index_factory_destroy:
664  * @factory: factory to destroy
665  *
666  * Removes the index from the global list.
667  */
668 void
669 gst_index_factory_destroy (GstIndexFactory *factory)
670 {
671   g_return_if_fail (factory != NULL);
672
673   /* we don't free the struct bacause someone might  have a handle to it.. */
674 }
675
676 /**
677  * gst_index_factory_find:
678  * @name: name of indexfactory to find
679  *
680  * Search for an indexfactory of the given name.
681  *
682  * Returns: #GstIndexFactory if found, NULL otherwise
683  */
684 GstIndexFactory*
685 gst_index_factory_find (const gchar *name)
686 {
687   GstPluginFeature *feature;
688
689   g_return_val_if_fail (name != NULL, NULL);
690
691   GST_DEBUG (0,"gstindex: find \"%s\"", name);
692
693   feature = gst_registry_pool_find_feature (name, GST_TYPE_INDEX_FACTORY);
694   if (feature)
695     return GST_INDEX_FACTORY (feature);
696
697   return NULL;
698 }
699
700 /**
701  * gst_index_factory_create:
702  * @factory: the factory used to create the instance
703  *
704  * Create a new #GstIndex instance from the 
705  * given indexfactory.
706  *
707  * Returns: A new #GstIndex instance.
708  */
709 GstIndex*
710 gst_index_factory_create (GstIndexFactory *factory)
711 {
712   GstIndex *new = NULL;
713
714   g_return_val_if_fail (factory != NULL, NULL);
715
716   if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) {
717     g_return_val_if_fail (factory->type != 0, NULL);
718
719     new = GST_INDEX (g_object_new(factory->type,NULL));
720   }
721
722   return new;
723 }
724
725 /**
726  * gst_index_factory_make:
727  * @name: the name of the factory used to create the instance
728  *
729  * Create a new #GstIndex instance from the 
730  * indexfactory with the given name.
731  *
732  * Returns: A new #GstIndex instance.
733  */
734 GstIndex*
735 gst_index_factory_make (const gchar *name)
736 {
737   GstIndexFactory *factory;
738
739   g_return_val_if_fail (name != NULL, NULL);
740
741   factory = gst_index_factory_find (name);
742
743   if (factory == NULL)
744     return NULL;
745
746   return gst_index_factory_create (factory);
747 }
748