gststructure: early out when we know a value cannot be a subset
[glsdk/gstreamer0-10.git] / plugins / indexers / gstfileindex.c
1 /* GStreamer
2  * Copyright (C) 2003 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2003 Joshua N Pritikin <jpritikin@pobox.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
21 #include <gst/gst.h>
23 #include <unistd.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <string.h>
30 #ifdef GST_DISABLE_DEPRECATED
31 #include <libxml/parser.h>
32 #endif
34 #include "gstindexers.h"
36 #define GST_TYPE_FILE_INDEX             \
37   (gst_file_index_get_type ())
38 #define GST_FILE_INDEX(obj)             \
39   (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FILE_INDEX, GstFileIndex))
40 #define GST_FILE_INDEX_CLASS(klass)     \
41   (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FILE_INDEX, GstFileIndexClass))
42 #define GST_IS_FILE_INDEX(obj)          \
43   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FILE_INDEX))
44 #define GST_IS_FILE_INDEX_CLASS(klass)    \
45   (GST_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FILE_INDEX))
47 /*
48  * Object model:
49  *
50  * We build an index to each entry for each id.
51  *
52  *
53  *  fileindex
54  *    -----------------------------...
55  *    !                  !
56  *   id1                 id2
57  *    !
58  *   GArray
59  *
60  * The fileindex creates a FileIndexId object for each writer id, a
61  * Hashtable is kept to map the id to the FileIndexId
62  *
63  * The FileIndexId also keeps all the values in a sorted GArray.
64  *
65  * Finding a value for an id/format requires locating the correct GArray,
66  * then do a binary search to get the required value.
67  *
68  * Unlike gstmemindex:  All formats are assumed to sort to the
69  * same order.  All formats are assumed to be available from
70  * any entry.
71  */
73 /*
74  * Each array element is (32bits flags, nformats * 64bits)
75  */
76 typedef struct
77 {
78   gint id;
79   gchar *id_desc;
80   gint nformats;
81   GstFormat *format;
82   GArray *array;
83 }
84 GstFileIndexId;
86 typedef struct _GstFileIndex GstFileIndex;
87 typedef struct _GstFileIndexClass GstFileIndexClass;
89 #define ARRAY_ROW_SIZE(_ii) \
90   (sizeof (gint32) + (_ii)->nformats * sizeof (gint64))
91 #define ARRAY_TOTAL_SIZE(_ii) \
92   (_ii->array->len * ARRAY_ROW_SIZE(_ii))
94 /* don't forget to convert to/from BE byte-order */
95 #define ARRAY_ROW_FLAGS(_row) \
96   (*((gint32*) (_row)))
97 #define ARRAY_ROW_VALUE(_row,_vx) \
98   (*(gint64*) (((gchar*)(_row)) + sizeof (gint32) + (_vx) * sizeof (gint64)))
100 GST_DEBUG_CATEGORY_STATIC (DC);
101 #define GST_CAT_DEFAULT DC
103 struct _GstFileIndex
105   GstIndex parent;
107   gchar *location;
108   gboolean is_loaded;
109   GSList *unresolved;
110   gint next_id;
111   GHashTable *id_index;
113   GstIndexEntry *ret_entry;     /* hack to avoid leaking memory */
114 };
116 struct _GstFileIndexClass
118   GstIndexClass parent_class;
119 };
121 enum
123   ARG_0,
124   ARG_LOCATION,
125 };
127 static void gst_file_index_dispose (GObject * object);
129 static void
130 gst_file_index_set_property (GObject * object,
131     guint prop_id, const GValue * value, GParamSpec * pspec);
132 static void
133 gst_file_index_get_property (GObject * object,
134     guint prop_id, GValue * value, GParamSpec * pspec);
136 static gboolean
137 gst_file_index_get_writer_id (GstIndex * _index, gint * id,
138     gchar * writer_string);
140 static void gst_file_index_commit (GstIndex * index, gint writer_id);
141 static void gst_file_index_add_entry (GstIndex * index, GstIndexEntry * entry);
142 static GstIndexEntry *gst_file_index_get_assoc_entry (GstIndex * index, gint id,
143     GstIndexLookupMethod method,
144     GstAssocFlags flags,
145     GstFormat format, gint64 value, GCompareDataFunc func, gpointer user_data);
147 #define CLASS(file_index)  GST_FILE_INDEX_CLASS (G_OBJECT_GET_CLASS (file_index))
149 GType gst_file_index_get_type (void);
151 G_DEFINE_TYPE (GstFileIndex, gst_file_index, GST_TYPE_INDEX);
153 static void
154 gst_file_index_class_init (GstFileIndexClass * klass)
156   GObjectClass *gobject_class;
157   GstIndexClass *gstindex_class;
159   gobject_class = (GObjectClass *) klass;
160   gstindex_class = (GstIndexClass *) klass;
162   gobject_class->dispose = gst_file_index_dispose;
163   gobject_class->set_property = gst_file_index_set_property;
164   gobject_class->get_property = gst_file_index_get_property;
166   gstindex_class->add_entry = gst_file_index_add_entry;
167   gstindex_class->get_assoc_entry = gst_file_index_get_assoc_entry;
168   gstindex_class->commit = gst_file_index_commit;
169   gstindex_class->get_writer_id = gst_file_index_get_writer_id;
171   g_object_class_install_property (gobject_class, ARG_LOCATION,
172       g_param_spec_string ("location", "File Location",
173           "Location of the index file", NULL,
174           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
177 static void
178 gst_file_index_init (GstFileIndex * index)
180   GST_DEBUG ("created new file index");
182   index->id_index = g_hash_table_new (g_int_hash, g_int_equal);
185 static void
186 _file_index_id_free (GstFileIndexId * index_id, gboolean is_mmapped)
188   if (index_id->id_desc)
189     g_free (index_id->id_desc);
190   if (index_id->format)
191     g_free (index_id->format);
192   if (index_id->array) {
193     if (is_mmapped)
194       munmap (index_id->array->data, ARRAY_TOTAL_SIZE (index_id));
195     g_array_free (index_id->array, !is_mmapped);
196   }
197   g_slice_free (GstFileIndexId, index_id);
200 static gboolean
201 _id_index_free_helper (gpointer _key, GstFileIndexId * index_id,
202     GstFileIndex * index)
204   _file_index_id_free (index_id, index->is_loaded);
205   return TRUE;
208 static void
209 gst_file_index_dispose (GObject * object)
211   GstFileIndex *index = GST_FILE_INDEX (object);
213   if (index->location) {
214     g_free (index->location);
215     index->location = NULL;
216   }
218   {
219     GSList *elem;
221     for (elem = index->unresolved; elem; elem = g_slist_next (elem))
222       _file_index_id_free (elem->data, index->is_loaded);
223     g_slist_free (index->unresolved);
224     index->unresolved = NULL;
225   }
227   g_hash_table_foreach_steal (index->id_index,
228       (GHRFunc) _id_index_free_helper, index);
229   g_hash_table_destroy (index->id_index);
230   index->id_index = NULL;
232   gst_index_entry_free (index->ret_entry);      /* hack */
234   G_OBJECT_CLASS (gst_file_index_parent_class)->dispose (object);
237 struct fi_find_writer_context
239   const gchar *writer_string;
240   GstFileIndexId *ii;
241 };
243 static void
244 _fi_find_writer (gpointer key, gpointer val, gpointer data)
246   struct fi_find_writer_context *cx = data;
247   GstFileIndexId *ii = val;
249   if (strcmp (ii->id_desc, cx->writer_string) == 0)
250     cx->ii = ii;
253 static gboolean
254 gst_file_index_get_writer_id (GstIndex * _index,
255     gint * id, gchar * writer_string)
257   GstFileIndex *index = GST_FILE_INDEX (_index);
258   GSList *pending = index->unresolved;
259   gboolean match = FALSE;
260   GSList *elem;
262   if (!index->is_loaded)
263     return FALSE;
265   g_return_val_if_fail (id, FALSE);
266   g_return_val_if_fail (writer_string, FALSE);
268   index->unresolved = NULL;
270   for (elem = pending; elem; elem = g_slist_next (elem)) {
271     GstFileIndexId *ii = elem->data;
273     if (strcmp (ii->id_desc, writer_string) != 0) {
274       index->unresolved = g_slist_prepend (index->unresolved, ii);
275       continue;
276     }
278     if (match) {
279       GST_WARNING_OBJECT (index, "Duplicate matches for writer '%s'",
280           writer_string);
281       continue;
282     }
284     ii->id = *id = ++index->next_id;
285     g_hash_table_insert (index->id_index, &ii->id, ii);
286     match = TRUE;
287   }
289   g_slist_free (pending);
291   if (!match) {
292     struct fi_find_writer_context cx;
294     cx.writer_string = writer_string;
295     cx.ii = NULL;
296     g_hash_table_foreach (index->id_index, _fi_find_writer, &cx);
298     if (cx.ii) {
299       match = TRUE;
300       GST_DEBUG_OBJECT (index, "Resolved writer '%s' again", writer_string);
301     } else
302       GST_WARNING_OBJECT (index, "Can't resolve writer '%s'", writer_string);
303   }
305   return match;
308 static void
309 _fc_alloc_array (GstFileIndexId * id_index)
311   g_assert (!id_index->array);
312   id_index->array =
313       g_array_sized_new (FALSE, FALSE, ARRAY_ROW_SIZE (id_index), 0);
316 static void
317 gst_file_index_load (GstFileIndex * index)
319   xmlDocPtr doc;
320   xmlNodePtr root, part;
321   xmlChar *val;
323   g_assert (index->location);
324   g_return_if_fail (!index->is_loaded);
326   {
327     gchar *path = g_strdup_printf ("%s/gstindex.xml", index->location);
328     GError *err = NULL;
329     gchar *buf;
330     gsize len;
332     g_file_get_contents (path, &buf, &len, &err);
333     g_free (path);
334     if (err) {
335       GST_ERROR_OBJECT (index, "%s", err->message);
336       return;
337     }
339     doc = xmlParseMemory (buf, len);
340     g_free (buf);
341   }
343   //xmlDocFormatDump (stderr, doc, TRUE);
345   root = doc->xmlRootNode;
346   if (strcmp ((char *) root->name, "gstfileindex") != 0) {
347     GST_ERROR_OBJECT (index, "root node isn't a gstfileindex");
348     return;
349   }
351   val = xmlGetProp (root, (xmlChar *) "version");
352   if (!val || atoi ((char *) val) != 1) {
353     GST_ERROR_OBJECT (index, "version != 1");
354     return;
355   }
356   free (val);
358   for (part = root->children; part; part = part->next) {
359     if (strcmp ((char *) part->name, "writers") == 0) {
360       xmlNodePtr writer;
362       for (writer = part->children; writer; writer = writer->next) {
363         xmlChar *datafile = xmlGetProp (writer, (xmlChar *) "datafile");
364         gchar *path = g_strdup_printf ("%s/%s", index->location, datafile);
365         int fd;
366         GstFileIndexId *id_index;
367         xmlNodePtr wpart;
368         xmlChar *entries_str;
369         gpointer array_data;
371         free (datafile);
373         fd = open (path, O_RDONLY);
374         g_free (path);
375         if (fd < 0) {
376           GST_ERROR_OBJECT (index,
377               "Can't open '%s': %s", path, g_strerror (errno));
378           continue;
379         }
381         id_index = g_slice_new0 (GstFileIndexId);
382         id_index->id_desc = (char *) xmlGetProp (writer, (xmlChar *) "id");
384         for (wpart = writer->children; wpart; wpart = wpart->next) {
385           if (strcmp ((char *) wpart->name, "formats") == 0) {
386             xmlChar *count_str = xmlGetProp (wpart, (xmlChar *) "count");
387             gint fx = 0;
388             xmlNodePtr format;
390             id_index->nformats = atoi ((char *) count_str);
391             free (count_str);
393             id_index->format = g_new (GstFormat, id_index->nformats);
395             for (format = wpart->children; format; format = format->next) {
396               xmlChar *nick = xmlGetProp (format, (xmlChar *) "nick");
397               GstFormat fmt = gst_format_get_by_nick ((gchar *) nick);
399               if (fmt == GST_FORMAT_UNDEFINED)
400                 GST_ERROR_OBJECT (index, "format '%s' undefined", nick);
401               g_assert (fx < id_index->nformats);
402               id_index->format[fx++] = fmt;
403               free (nick);
404             }
405           } else
406             GST_INFO_OBJECT (index, "unknown wpart '%s'", wpart->name);
407         }
409         g_assert (id_index->nformats > 0);
410         _fc_alloc_array (id_index);
411         g_assert (id_index->array->data == NULL);       /* little bit risky */
413         entries_str = xmlGetProp (writer, (xmlChar *) "entries");
414         id_index->array->len = atoi ((char *) entries_str);
415         free (entries_str);
417         array_data =
418             mmap (NULL, ARRAY_TOTAL_SIZE (id_index), PROT_READ, MAP_SHARED, fd,
419             0);
420         close (fd);
421         if (array_data == MAP_FAILED) {
422           GST_ERROR_OBJECT (index,
423               "mmap %s failed: %s", path, g_strerror (errno));
424           continue;
425         }
427         id_index->array->data = array_data;
429         index->unresolved = g_slist_prepend (index->unresolved, id_index);
430       }
431     } else
432       GST_INFO_OBJECT (index, "unknown part '%s'", part->name);
433   }
435   xmlFreeDoc (doc);
437   GST_OBJECT_FLAG_UNSET (index, GST_INDEX_WRITABLE);
438   index->is_loaded = TRUE;
439   GST_LOG_OBJECT (index, "index %s loaded OK", index->location);
442 static void
443 gst_file_index_set_property (GObject * object,
444     guint prop_id, const GValue * value, GParamSpec * pspec)
446   GstFileIndex *index = GST_FILE_INDEX (object);
448   switch (prop_id) {
449     case ARG_LOCATION:
450       if (index->location)
451         g_free (index->location);
452       index->location = g_value_dup_string (value);
454       if (index->location && !g_hash_table_size (index->id_index))
455         gst_file_index_load (index);
456       break;
457   }
460 static void
461 gst_file_index_get_property (GObject * object,
462     guint prop_id, GValue * value, GParamSpec * pspec)
464   GstFileIndex *index = GST_FILE_INDEX (object);
466   switch (prop_id) {
467     case ARG_LOCATION:
468       g_value_set_string (value, index->location);
469       break;
470   }
473 static void
474 _file_index_id_save_xml (gpointer _key, GstFileIndexId * ii, xmlNodePtr writers)
476   const gint bufsize = 16;
477   gchar buf[16];
478   xmlNodePtr writer;
479   xmlNodePtr formats;
480   gint xx;
482   if (!ii->array) {
483     GST_INFO ("Index for %s is empty", ii->id_desc);
484     return;
485   }
487   writer = xmlNewChild (writers, NULL, (xmlChar *) "writer", NULL);
488   xmlSetProp (writer, (xmlChar *) "id", (xmlChar *) ii->id_desc);
489   g_snprintf (buf, bufsize, "%d", ii->array->len);
490   xmlSetProp (writer, (xmlChar *) "entries", (xmlChar *) buf);
491   g_snprintf (buf, bufsize, "%d", ii->id);      /* any unique number is OK */
492   xmlSetProp (writer, (xmlChar *) "datafile", (xmlChar *) buf);
494   formats = xmlNewChild (writer, NULL, (xmlChar *) "formats", NULL);
495   g_snprintf (buf, bufsize, "%d", ii->nformats);
496   xmlSetProp (formats, (xmlChar *) "count", (xmlChar *) buf);
498   for (xx = 0; xx < ii->nformats; xx++) {
499     xmlNodePtr format = xmlNewChild (formats, NULL, (xmlChar *) "format", NULL);
500     const GstFormatDefinition *def = gst_format_get_details (ii->format[xx]);
502     xmlSetProp (format, (xmlChar *) "nick", (xmlChar *) def->nick);
503   }
506 /*
507   We must save the binary data in separate files because
508   mmap wants getpagesize() alignment.  If we append all
509   the data to one file then we don't know the appropriate
510   padding since the page size isn't fixed.
511 */
512 static void
513 _file_index_id_save_entries (gpointer * _key,
514     GstFileIndexId * ii, gchar * prefix)
516   GError *err;
517   gchar *path;
518   GIOChannel *chan;
520   if (!ii->array)
521     return;
523   err = NULL;
524   path = g_strdup_printf ("%s/%d", prefix, ii->id);
525   chan = g_io_channel_new_file (path, "w", &err);
526   g_free (path);
527   if (err)
528     goto fail;
530   g_io_channel_set_encoding (chan, NULL, &err);
531   if (err)
532     goto fail;
534   g_io_channel_write_chars (chan,
535       ii->array->data, ARRAY_TOTAL_SIZE (ii), NULL, &err);
536   if (err)
537     goto fail;
539   g_io_channel_shutdown (chan, TRUE, &err);
540   if (err)
541     goto fail;
543   g_io_channel_unref (chan);
544   return;
546 fail:
547   GST_ERROR ("%s", err->message);
550 /*
551   We have to save the whole set of indexes into a single file
552   so it doesn't make sense to commit only a single writer.
554   i suggest:
556   gst_index_commit (index, -1);
557 */
558 static void
559 gst_file_index_commit (GstIndex * _index, gint _writer_id)
561   GstFileIndex *index = GST_FILE_INDEX (_index);
562   xmlDocPtr doc;
563   xmlNodePtr writers;
564   GError *err = NULL;
565   gchar *path;
566   GIOChannel *tocfile;
568   g_return_if_fail (index->location);
569   g_return_if_fail (!index->is_loaded);
571   GST_OBJECT_FLAG_UNSET (index, GST_INDEX_WRITABLE);
573   doc = xmlNewDoc ((xmlChar *) "1.0");
574   doc->xmlRootNode =
575       xmlNewDocNode (doc, NULL, (xmlChar *) "gstfileindex", NULL);
576   xmlSetProp (doc->xmlRootNode, (xmlChar *) "version", (xmlChar *) "1");
578   writers = xmlNewChild (doc->xmlRootNode, NULL, (xmlChar *) "writers", NULL);
579   g_hash_table_foreach (index->id_index,
580       (GHFunc) _file_index_id_save_xml, writers);
582   if (mkdir (index->location, 0777) && errno != EEXIST) {
583     GST_ERROR_OBJECT (index, "mkdir %s: %s", index->location,
584         g_strerror (errno));
585     return;
586   }
588   path = g_strdup_printf ("%s/gstindex.xml", index->location);
589   tocfile = g_io_channel_new_file (path, "w", &err);
590   g_free (path);
591   if (err) {
592     GST_ERROR_OBJECT (index, "%s", err->message);
593     return;
594   }
596   g_io_channel_set_encoding (tocfile, NULL, &err);
597   if (err) {
598     GST_ERROR_OBJECT (index, "%s", err->message);
599     return;
600   }
602   {
603     xmlChar *xmlmem;
604     int xmlsize;
606     xmlDocDumpMemory (doc, &xmlmem, &xmlsize);
607     g_io_channel_write_chars (tocfile, (gchar *) xmlmem, xmlsize, NULL, &err);
608     if (err) {
609       GST_ERROR_OBJECT (index, "%s", err->message);
610       return;
611     }
612     xmlFreeDoc (doc);
613     free (xmlmem);
614   }
616   g_io_channel_shutdown (tocfile, TRUE, &err);
617   if (err) {
618     GST_ERROR_OBJECT (index, "%s", err->message);
619     return;
620   }
622   g_io_channel_unref (tocfile);
624   g_hash_table_foreach (index->id_index,
625       (GHFunc) _file_index_id_save_entries, index->location);
628 static void
629 gst_file_index_add_id (GstIndex * index, GstIndexEntry * entry)
631   GstFileIndex *fileindex = GST_FILE_INDEX (index);
632   GstFileIndexId *id_index;
634   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
636   if (!id_index) {
637     id_index = g_slice_new0 (GstFileIndexId);
639     id_index->id = entry->id;
640     id_index->id_desc = g_strdup (entry->data.id.description);
642     /* It would be useful to know the GType of the writer so
643        we can try to cope with changes in the id_desc path. */
645     g_hash_table_insert (fileindex->id_index, &id_index->id, id_index);
646   }
649 /* This algorithm differs from libc bsearch in the handling
650    of non-exact matches. */
652 static gboolean
653 _fc_bsearch (GArray * ary,
654     gint stride,
655     gint * ret,
656     GCompareDataFunc compare, gconstpointer sample, gpointer user_data)
658   gint first, last;
659   gint mid;
660   gint midsize;
661   gint cmp;
662   gint tx;
664   g_return_val_if_fail (compare, FALSE);
666   if (!ary->len) {
667     if (ret)
668       *ret = 0;
669     return FALSE;
670   }
672   first = 0;
673   last = ary->len - 1;
675   midsize = last - first;
677   while (midsize > 1) {
678     mid = first + midsize / 2;
680     cmp = (*compare) (sample, ary->data + mid * stride, user_data);
682     if (cmp == 0) {
683       /* if there are multiple matches then scan for the first match */
684       while (mid > 0 &&
685           (*compare) (sample, ary->data + (mid - 1) * stride, user_data) == 0)
686         --mid;
688       if (ret)
689         *ret = mid;
690       return TRUE;
691     }
693     if (cmp < 0)
694       last = mid - 1;
695     else
696       first = mid + 1;
698     midsize = last - first;
699   }
701   for (tx = first; tx <= last; tx++) {
702     cmp = (*compare) (sample, ary->data + tx * stride, user_data);
704     if (cmp < 0) {
705       if (ret)
706         *ret = tx;
707       return FALSE;
708     }
709     if (cmp == 0) {
710       if (ret)
711         *ret = tx;
712       return TRUE;
713     }
714   }
716   if (ret)
717     *ret = last + 1;
718   return FALSE;
721 static gint
722 file_index_compare (gconstpointer sample, gconstpointer row, gpointer user_data)
724   //GstFileIndexId *id_index = user_data;
725   const GstIndexAssociation *ca = sample;
726   gint64 val1 = ca->value;
727   gint64 val2_be = ARRAY_ROW_VALUE (row, ca->format);
728   gint64 val2 = GINT64_FROM_BE (val2_be);
729   gint64 diff = val2 - val1;
731   return (diff == 0 ? 0 : (diff < 0 ? 1 : -1));
734 static void
735 gst_file_index_add_association (GstIndex * index, GstIndexEntry * entry)
737   GstFileIndex *fileindex = GST_FILE_INDEX (index);
738   GstFileIndexId *id_index;
739   gint mx;
740   GstIndexAssociation sample;
741   gboolean exact;
743   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
744   if (!id_index)
745     return;
747   if (!id_index->nformats) {
748     gint fx;
750     id_index->nformats = GST_INDEX_NASSOCS (entry);
751     GST_LOG_OBJECT (fileindex, "creating %d formats for %d",
752         id_index->nformats, entry->id);
753     id_index->format = g_new (GstFormat, id_index->nformats);
754     for (fx = 0; fx < id_index->nformats; fx++)
755       id_index->format[fx] = GST_INDEX_ASSOC_FORMAT (entry, fx);
756     _fc_alloc_array (id_index);
757   } else {
758     /* only sanity checking */
759     if (id_index->nformats != GST_INDEX_NASSOCS (entry))
760       GST_WARNING_OBJECT (fileindex, "arity change %d -> %d",
761           id_index->nformats, GST_INDEX_NASSOCS (entry));
762     else {
763       gint fx;
765       for (fx = 0; fx < id_index->nformats; fx++)
766         if (id_index->format[fx] != GST_INDEX_ASSOC_FORMAT (entry, fx))
767           GST_WARNING_OBJECT (fileindex, "format[%d] changed %d -> %d",
768               fx, id_index->format[fx], GST_INDEX_ASSOC_FORMAT (entry, fx));
769     }
770   }
772   /* this is a hack, we should use a private structure instead */
773   sample.format = GST_FORMAT_UNDEFINED;
774   sample.value = GST_INDEX_ASSOC_VALUE (entry, 0);
776   exact =
777       _fc_bsearch (id_index->array, ARRAY_ROW_SIZE (id_index),
778       &mx, file_index_compare, &sample, id_index);
780   if (exact) {
781     /* maybe overwrite instead? */
782     GST_DEBUG_OBJECT (index,
783         "Ignoring duplicate index association at %" G_GINT64_FORMAT,
784         GST_INDEX_ASSOC_VALUE (entry, 0));
785     return;
786   }
788   {
789     gchar *row_data = (gchar *) g_malloc (ARRAY_ROW_SIZE (id_index));
790     gint fx;
792     gint32 flags_host = GST_INDEX_ASSOC_FLAGS (entry);
794     ARRAY_ROW_FLAGS (row_data) = GINT32_TO_BE (flags_host);
796     for (fx = 0; fx < id_index->nformats; fx++) {
797       gint64 val_host = GST_INDEX_ASSOC_VALUE (entry, fx);
799       ARRAY_ROW_VALUE (row_data, fx) = GINT64_TO_BE (val_host);
800     }
802     g_array_insert_vals (id_index->array, mx, row_data, 1);
804     g_free (row_data);
805   }
808 /*
809 static void
810 show_entry (GstIndexEntry *entry)
812   switch (entry->type) {
813     case GST_INDEX_ENTRY_ID:
814       g_print ("id %d describes writer %s\n", entry->id,
815                       GST_INDEX_ID_DESCRIPTION (entry));
816       break;
817     case GST_INDEX_ENTRY_FORMAT:
818       g_print ("%d: registered format %d for %s\n", entry->id,
819                       GST_INDEX_FORMAT_FORMAT (entry),
820                       GST_INDEX_FORMAT_KEY (entry));
821       break;
822     case GST_INDEX_ENTRY_ASSOCIATION:
823     {
824       gint i;
826       g_print ("%d: %08x ", entry->id, GST_INDEX_ASSOC_FLAGS (entry));
827       for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
828         g_print ("%d %" G_GINT64_FORMAT, GST_INDEX_ASSOC_FORMAT (entry, i),
829                              GST_INDEX_ASSOC_VALUE (entry, i));
830       }
831       g_print ("\n");
832       break;
833     }
834     default:
835       break;
836   }
838 */
840 static void
841 gst_file_index_add_entry (GstIndex * index, GstIndexEntry * entry)
843   GST_LOG_OBJECT (index, "adding this entry");
845   switch (entry->type) {
846     case GST_INDEX_ENTRY_ID:
847       gst_file_index_add_id (index, entry);
848       break;
849     case GST_INDEX_ENTRY_ASSOCIATION:
850       gst_file_index_add_association (index, entry);
851       break;
852     case GST_INDEX_ENTRY_OBJECT:
853       GST_ERROR_OBJECT (index, "gst_file_index_add_object not implemented");
854       break;
855     case GST_INDEX_ENTRY_FORMAT:
856       /*
857          We infer the formats from the entry itself so this type of
858          GST_INDEX_ENTRY_* can probably go away.
859        */
860       GST_DEBUG_OBJECT (index, "gst_file_index_add_format not implemented");
861       break;
862     default:
863       break;
864   }
867 static GstIndexEntry *
868 gst_file_index_get_assoc_entry (GstIndex * index,
869     gint id,
870     GstIndexLookupMethod method,
871     GstAssocFlags flags,
872     GstFormat format,
873     gint64 value, GCompareDataFunc _ignore_func, gpointer _ignore_user_data)
875   GstFileIndex *fileindex = GST_FILE_INDEX (index);
876   GstFileIndexId *id_index;
877   gint formatx = -1;
878   gint fx;
879   GstIndexAssociation sample;
880   gint mx;
881   gboolean exact;
882   gpointer row_data;
883   GstIndexEntry *entry;
884   gint xx;
886   g_return_val_if_fail (id > 0, NULL);
888   id_index = g_hash_table_lookup (fileindex->id_index, &id);
889   if (!id_index) {
890     GST_WARNING_OBJECT (fileindex, "writer %d unavailable", id);
891     return NULL;
892   }
894   for (fx = 0; fx < id_index->nformats; fx++)
895     if (id_index->format[fx] == format) {
896       formatx = fx;
897       break;
898     }
900   if (formatx == -1) {
901     GST_WARNING_OBJECT (fileindex, "format %d not available", format);
902     return NULL;
903   }
905   /* this is a hack, we should use a private structure instead */
906   sample.format = (GstFormat) formatx;
907   sample.value = value;
909   exact = _fc_bsearch (id_index->array, ARRAY_ROW_SIZE (id_index),
910       &mx, file_index_compare, &sample, id_index);
912   if (!exact) {
913     if (method == GST_INDEX_LOOKUP_EXACT)
914       return NULL;
915     else if (method == GST_INDEX_LOOKUP_BEFORE) {
916       if (mx == 0)
917         return NULL;
918       mx -= 1;
919     } else if (method == GST_INDEX_LOOKUP_AFTER) {
920       if (mx == id_index->array->len)
921         return NULL;
922     }
923   }
925   row_data = id_index->array->data + mx * ARRAY_ROW_SIZE (id_index);
927   /* if exact then ignore flags (?) */
928   if (method != GST_INDEX_LOOKUP_EXACT)
929     while ((GINT32_FROM_BE (ARRAY_ROW_FLAGS (row_data)) & flags) != flags) {
930       if (method == GST_INDEX_LOOKUP_BEFORE)
931         mx -= 1;
932       else if (method == GST_INDEX_LOOKUP_AFTER)
933         mx += 1;
934       if (mx < 0 || mx >= id_index->array->len)
935         return NULL;
936       row_data = id_index->array->data + mx * ARRAY_ROW_SIZE (id_index);
937     }
939   /* entry memory management needs improvement FIXME */
940   if (!fileindex->ret_entry)
941     fileindex->ret_entry = g_slice_new0 (GstIndexEntry);
942   entry = fileindex->ret_entry;
943   if (entry->data.assoc.assocs) {
944     g_free (entry->data.assoc.assocs);
945     entry->data.assoc.assocs = NULL;
946   }
948   entry->type = GST_INDEX_ENTRY_ASSOCIATION;
950   GST_INDEX_NASSOCS (entry) = id_index->nformats;
951   entry->data.assoc.assocs = g_new (GstIndexAssociation, id_index->nformats);
953   {
954     gint32 flags_be = ARRAY_ROW_FLAGS (row_data);
956     GST_INDEX_ASSOC_FLAGS (entry) = (GstAssocFlags) GINT32_FROM_BE (flags_be);
958     for (xx = 0; xx < id_index->nformats; xx++) {
959       gint64 val_be = ARRAY_ROW_VALUE (row_data, xx);
961       GST_INDEX_ASSOC_FORMAT (entry, xx) = id_index->format[xx];
962       GST_INDEX_ASSOC_VALUE (entry, xx) = GINT64_FROM_BE (val_be);
963     }
964   }
966   return entry;
969 gboolean
970 gst_file_index_plugin_init (GstPlugin * plugin)
972   GstIndexFactory *factory;
974   factory = gst_index_factory_new ("fileindex",
975       "A index that stores entries in file", gst_file_index_get_type ());
977   if (factory == NULL) {
978     return FALSE;
979   }
981   GST_PLUGIN_FEATURE (factory)->plugin_name = plugin->desc.name;
982   GST_PLUGIN_FEATURE (factory)->loaded = TRUE;
984   gst_registry_add_feature (gst_registry_get_default (),
985       GST_PLUGIN_FEATURE (factory));
987   GST_DEBUG_CATEGORY_INIT (DC, "GST_FILEINDEX", 0, NULL);
989   return TRUE;