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