]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blob - plugins/indexers/gstfileindex.c
plugins: Make *_get_type() in plugins/* thread safe
[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 G_DEFINE_TYPE (GstFileIndex, gst_file_index, GST_TYPE_INDEX);
151 static void
152 gst_file_index_class_init (GstFileIndexClass * klass)
154   GObjectClass *gobject_class;
155   GstIndexClass *gstindex_class;
157   gobject_class = (GObjectClass *) klass;
158   gstindex_class = (GstIndexClass *) klass;
160   gobject_class->dispose = gst_file_index_dispose;
161   gobject_class->set_property = gst_file_index_set_property;
162   gobject_class->get_property = gst_file_index_get_property;
164   gstindex_class->add_entry = gst_file_index_add_entry;
165   gstindex_class->get_assoc_entry = gst_file_index_get_assoc_entry;
166   gstindex_class->commit = gst_file_index_commit;
167   gstindex_class->get_writer_id = gst_file_index_get_writer_id;
169   g_object_class_install_property (gobject_class, ARG_LOCATION,
170       g_param_spec_string ("location", "File Location",
171           "Location of the index file", NULL,
172           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
175 static void
176 gst_file_index_init (GstFileIndex * index)
178   GST_DEBUG ("created new file index");
180   index->id_index = g_hash_table_new (g_int_hash, g_int_equal);
183 static void
184 _file_index_id_free (GstFileIndexId * index_id, gboolean is_mmapped)
186   if (index_id->id_desc)
187     g_free (index_id->id_desc);
188   if (index_id->format)
189     g_free (index_id->format);
190   if (index_id->array) {
191     if (is_mmapped)
192       munmap (index_id->array->data, ARRAY_TOTAL_SIZE (index_id));
193     g_array_free (index_id->array, !is_mmapped);
194   }
195   g_slice_free (GstFileIndexId, index_id);
198 static gboolean
199 _id_index_free_helper (gpointer _key, GstFileIndexId * index_id,
200     GstFileIndex * index)
202   _file_index_id_free (index_id, index->is_loaded);
203   return TRUE;
206 static void
207 gst_file_index_dispose (GObject * object)
209   GstFileIndex *index = GST_FILE_INDEX (object);
211   if (index->location) {
212     g_free (index->location);
213     index->location = NULL;
214   }
216   {
217     GSList *elem;
219     for (elem = index->unresolved; elem; elem = g_slist_next (elem))
220       _file_index_id_free (elem->data, index->is_loaded);
221     g_slist_free (index->unresolved);
222     index->unresolved = NULL;
223   }
225   g_hash_table_foreach_steal (index->id_index,
226       (GHRFunc) _id_index_free_helper, index);
227   g_hash_table_destroy (index->id_index);
228   index->id_index = NULL;
230   gst_index_entry_free (index->ret_entry);      /* hack */
232   G_OBJECT_CLASS (gst_file_index_parent_class)->dispose (object);
235 struct fi_find_writer_context
237   const gchar *writer_string;
238   GstFileIndexId *ii;
239 };
241 static void
242 _fi_find_writer (gpointer key, gpointer val, gpointer data)
244   struct fi_find_writer_context *cx = data;
245   GstFileIndexId *ii = val;
247   if (strcmp (ii->id_desc, cx->writer_string) == 0)
248     cx->ii = ii;
251 static gboolean
252 gst_file_index_get_writer_id (GstIndex * _index,
253     gint * id, gchar * writer_string)
255   GstFileIndex *index = GST_FILE_INDEX (_index);
256   GSList *pending = index->unresolved;
257   gboolean match = FALSE;
258   GSList *elem;
260   if (!index->is_loaded)
261     return FALSE;
263   g_return_val_if_fail (id, FALSE);
264   g_return_val_if_fail (writer_string, FALSE);
266   index->unresolved = NULL;
268   for (elem = pending; elem; elem = g_slist_next (elem)) {
269     GstFileIndexId *ii = elem->data;
271     if (strcmp (ii->id_desc, writer_string) != 0) {
272       index->unresolved = g_slist_prepend (index->unresolved, ii);
273       continue;
274     }
276     if (match) {
277       GST_WARNING_OBJECT (index, "Duplicate matches for writer '%s'",
278           writer_string);
279       continue;
280     }
282     ii->id = *id = ++index->next_id;
283     g_hash_table_insert (index->id_index, &ii->id, ii);
284     match = TRUE;
285   }
287   g_slist_free (pending);
289   if (!match) {
290     struct fi_find_writer_context cx;
292     cx.writer_string = writer_string;
293     cx.ii = NULL;
294     g_hash_table_foreach (index->id_index, _fi_find_writer, &cx);
296     if (cx.ii) {
297       match = TRUE;
298       GST_DEBUG_OBJECT (index, "Resolved writer '%s' again", writer_string);
299     } else
300       GST_WARNING_OBJECT (index, "Can't resolve writer '%s'", writer_string);
301   }
303   return match;
306 static void
307 _fc_alloc_array (GstFileIndexId * id_index)
309   g_assert (!id_index->array);
310   id_index->array =
311       g_array_sized_new (FALSE, FALSE, ARRAY_ROW_SIZE (id_index), 0);
314 static void
315 gst_file_index_load (GstFileIndex * index)
317   xmlDocPtr doc;
318   xmlNodePtr root, part;
319   xmlChar *val;
321   g_assert (index->location);
322   g_return_if_fail (!index->is_loaded);
324   {
325     gchar *path = g_strdup_printf ("%s/gstindex.xml", index->location);
326     GError *err = NULL;
327     gchar *buf;
328     gsize len;
330     g_file_get_contents (path, &buf, &len, &err);
331     g_free (path);
332     if (err) {
333       GST_ERROR_OBJECT (index, "%s", err->message);
334       return;
335     }
337     doc = xmlParseMemory (buf, len);
338     g_free (buf);
339   }
341   //xmlDocFormatDump (stderr, doc, TRUE);
343   root = doc->xmlRootNode;
344   if (strcmp ((char *) root->name, "gstfileindex") != 0) {
345     GST_ERROR_OBJECT (index, "root node isn't a gstfileindex");
346     return;
347   }
349   val = xmlGetProp (root, (xmlChar *) "version");
350   if (!val || atoi ((char *) val) != 1) {
351     GST_ERROR_OBJECT (index, "version != 1");
352     return;
353   }
354   free (val);
356   for (part = root->children; part; part = part->next) {
357     if (strcmp ((char *) part->name, "writers") == 0) {
358       xmlNodePtr writer;
360       for (writer = part->children; writer; writer = writer->next) {
361         xmlChar *datafile = xmlGetProp (writer, (xmlChar *) "datafile");
362         gchar *path = g_strdup_printf ("%s/%s", index->location, datafile);
363         int fd;
364         GstFileIndexId *id_index;
365         xmlNodePtr wpart;
366         xmlChar *entries_str;
367         gpointer array_data;
369         free (datafile);
371         fd = open (path, O_RDONLY);
372         g_free (path);
373         if (fd < 0) {
374           GST_ERROR_OBJECT (index,
375               "Can't open '%s': %s", path, g_strerror (errno));
376           continue;
377         }
379         id_index = g_slice_new0 (GstFileIndexId);
380         id_index->id_desc = (char *) xmlGetProp (writer, (xmlChar *) "id");
382         for (wpart = writer->children; wpart; wpart = wpart->next) {
383           if (strcmp ((char *) wpart->name, "formats") == 0) {
384             xmlChar *count_str = xmlGetProp (wpart, (xmlChar *) "count");
385             gint fx = 0;
386             xmlNodePtr format;
388             id_index->nformats = atoi ((char *) count_str);
389             free (count_str);
391             id_index->format = g_new (GstFormat, id_index->nformats);
393             for (format = wpart->children; format; format = format->next) {
394               xmlChar *nick = xmlGetProp (format, (xmlChar *) "nick");
395               GstFormat fmt = gst_format_get_by_nick ((gchar *) nick);
397               if (fmt == GST_FORMAT_UNDEFINED)
398                 GST_ERROR_OBJECT (index, "format '%s' undefined", nick);
399               g_assert (fx < id_index->nformats);
400               id_index->format[fx++] = fmt;
401               free (nick);
402             }
403           } else
404             GST_INFO_OBJECT (index, "unknown wpart '%s'", wpart->name);
405         }
407         g_assert (id_index->nformats > 0);
408         _fc_alloc_array (id_index);
409         g_assert (id_index->array->data == NULL);       /* little bit risky */
411         entries_str = xmlGetProp (writer, (xmlChar *) "entries");
412         id_index->array->len = atoi ((char *) entries_str);
413         free (entries_str);
415         array_data =
416             mmap (NULL, ARRAY_TOTAL_SIZE (id_index), PROT_READ, MAP_SHARED, fd,
417             0);
418         close (fd);
419         if (array_data == MAP_FAILED) {
420           GST_ERROR_OBJECT (index,
421               "mmap %s failed: %s", path, g_strerror (errno));
422           continue;
423         }
425         id_index->array->data = array_data;
427         index->unresolved = g_slist_prepend (index->unresolved, id_index);
428       }
429     } else
430       GST_INFO_OBJECT (index, "unknown part '%s'", part->name);
431   }
433   xmlFreeDoc (doc);
435   GST_OBJECT_FLAG_UNSET (index, GST_INDEX_WRITABLE);
436   index->is_loaded = TRUE;
437   GST_LOG_OBJECT (index, "index %s loaded OK", index->location);
440 static void
441 gst_file_index_set_property (GObject * object,
442     guint prop_id, const GValue * value, GParamSpec * pspec)
444   GstFileIndex *index = GST_FILE_INDEX (object);
446   switch (prop_id) {
447     case ARG_LOCATION:
448       if (index->location)
449         g_free (index->location);
450       index->location = g_value_dup_string (value);
452       if (index->location && !g_hash_table_size (index->id_index))
453         gst_file_index_load (index);
454       break;
455   }
458 static void
459 gst_file_index_get_property (GObject * object,
460     guint prop_id, GValue * value, GParamSpec * pspec)
462   GstFileIndex *index = GST_FILE_INDEX (object);
464   switch (prop_id) {
465     case ARG_LOCATION:
466       g_value_set_string (value, index->location);
467       break;
468   }
471 static void
472 _file_index_id_save_xml (gpointer _key, GstFileIndexId * ii, xmlNodePtr writers)
474   const gint bufsize = 16;
475   gchar buf[16];
476   xmlNodePtr writer;
477   xmlNodePtr formats;
478   gint xx;
480   if (!ii->array) {
481     GST_INFO ("Index for %s is empty", ii->id_desc);
482     return;
483   }
485   writer = xmlNewChild (writers, NULL, (xmlChar *) "writer", NULL);
486   xmlSetProp (writer, (xmlChar *) "id", (xmlChar *) ii->id_desc);
487   g_snprintf (buf, bufsize, "%d", ii->array->len);
488   xmlSetProp (writer, (xmlChar *) "entries", (xmlChar *) buf);
489   g_snprintf (buf, bufsize, "%d", ii->id);      /* any unique number is OK */
490   xmlSetProp (writer, (xmlChar *) "datafile", (xmlChar *) buf);
492   formats = xmlNewChild (writer, NULL, (xmlChar *) "formats", NULL);
493   g_snprintf (buf, bufsize, "%d", ii->nformats);
494   xmlSetProp (formats, (xmlChar *) "count", (xmlChar *) buf);
496   for (xx = 0; xx < ii->nformats; xx++) {
497     xmlNodePtr format = xmlNewChild (formats, NULL, (xmlChar *) "format", NULL);
498     const GstFormatDefinition *def = gst_format_get_details (ii->format[xx]);
500     xmlSetProp (format, (xmlChar *) "nick", (xmlChar *) def->nick);
501   }
504 /*
505   We must save the binary data in separate files because
506   mmap wants getpagesize() alignment.  If we append all
507   the data to one file then we don't know the appropriate
508   padding since the page size isn't fixed.
509 */
510 static void
511 _file_index_id_save_entries (gpointer * _key,
512     GstFileIndexId * ii, gchar * prefix)
514   GError *err;
515   gchar *path;
516   GIOChannel *chan;
518   if (!ii->array)
519     return;
521   err = NULL;
522   path = g_strdup_printf ("%s/%d", prefix, ii->id);
523   chan = g_io_channel_new_file (path, "w", &err);
524   g_free (path);
525   if (err)
526     goto fail;
528   g_io_channel_set_encoding (chan, NULL, &err);
529   if (err)
530     goto fail;
532   g_io_channel_write_chars (chan,
533       ii->array->data, ARRAY_TOTAL_SIZE (ii), NULL, &err);
534   if (err)
535     goto fail;
537   g_io_channel_shutdown (chan, TRUE, &err);
538   if (err)
539     goto fail;
541   g_io_channel_unref (chan);
542   return;
544 fail:
545   GST_ERROR ("%s", err->message);
548 /*
549   We have to save the whole set of indexes into a single file
550   so it doesn't make sense to commit only a single writer.
552   i suggest:
554   gst_index_commit (index, -1);
555 */
556 static void
557 gst_file_index_commit (GstIndex * _index, gint _writer_id)
559   GstFileIndex *index = GST_FILE_INDEX (_index);
560   xmlDocPtr doc;
561   xmlNodePtr writers;
562   GError *err = NULL;
563   gchar *path;
564   GIOChannel *tocfile;
566   g_return_if_fail (index->location);
567   g_return_if_fail (!index->is_loaded);
569   GST_OBJECT_FLAG_UNSET (index, GST_INDEX_WRITABLE);
571   doc = xmlNewDoc ((xmlChar *) "1.0");
572   doc->xmlRootNode =
573       xmlNewDocNode (doc, NULL, (xmlChar *) "gstfileindex", NULL);
574   xmlSetProp (doc->xmlRootNode, (xmlChar *) "version", (xmlChar *) "1");
576   writers = xmlNewChild (doc->xmlRootNode, NULL, (xmlChar *) "writers", NULL);
577   g_hash_table_foreach (index->id_index,
578       (GHFunc) _file_index_id_save_xml, writers);
580   if (mkdir (index->location, 0777) && errno != EEXIST) {
581     GST_ERROR_OBJECT (index, "mkdir %s: %s", index->location,
582         g_strerror (errno));
583     return;
584   }
586   path = g_strdup_printf ("%s/gstindex.xml", index->location);
587   tocfile = g_io_channel_new_file (path, "w", &err);
588   g_free (path);
589   if (err) {
590     GST_ERROR_OBJECT (index, "%s", err->message);
591     return;
592   }
594   g_io_channel_set_encoding (tocfile, NULL, &err);
595   if (err) {
596     GST_ERROR_OBJECT (index, "%s", err->message);
597     return;
598   }
600   {
601     xmlChar *xmlmem;
602     int xmlsize;
604     xmlDocDumpMemory (doc, &xmlmem, &xmlsize);
605     g_io_channel_write_chars (tocfile, (gchar *) xmlmem, xmlsize, NULL, &err);
606     if (err) {
607       GST_ERROR_OBJECT (index, "%s", err->message);
608       return;
609     }
610     xmlFreeDoc (doc);
611     free (xmlmem);
612   }
614   g_io_channel_shutdown (tocfile, TRUE, &err);
615   if (err) {
616     GST_ERROR_OBJECT (index, "%s", err->message);
617     return;
618   }
620   g_io_channel_unref (tocfile);
622   g_hash_table_foreach (index->id_index,
623       (GHFunc) _file_index_id_save_entries, index->location);
626 static void
627 gst_file_index_add_id (GstIndex * index, GstIndexEntry * entry)
629   GstFileIndex *fileindex = GST_FILE_INDEX (index);
630   GstFileIndexId *id_index;
632   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
634   if (!id_index) {
635     id_index = g_slice_new0 (GstFileIndexId);
637     id_index->id = entry->id;
638     id_index->id_desc = g_strdup (entry->data.id.description);
640     /* It would be useful to know the GType of the writer so
641        we can try to cope with changes in the id_desc path. */
643     g_hash_table_insert (fileindex->id_index, &id_index->id, id_index);
644   }
647 /* This algorithm differs from libc bsearch in the handling
648    of non-exact matches. */
650 static gboolean
651 _fc_bsearch (GArray * ary,
652     gint stride,
653     gint * ret,
654     GCompareDataFunc compare, gconstpointer sample, gpointer user_data)
656   gint first, last;
657   gint mid;
658   gint midsize;
659   gint cmp;
660   gint tx;
662   g_return_val_if_fail (compare, FALSE);
664   if (!ary->len) {
665     if (ret)
666       *ret = 0;
667     return FALSE;
668   }
670   first = 0;
671   last = ary->len - 1;
673   midsize = last - first;
675   while (midsize > 1) {
676     mid = first + midsize / 2;
678     cmp = (*compare) (sample, ary->data + mid * stride, user_data);
680     if (cmp == 0) {
681       /* if there are multiple matches then scan for the first match */
682       while (mid > 0 &&
683           (*compare) (sample, ary->data + (mid - 1) * stride, user_data) == 0)
684         --mid;
686       if (ret)
687         *ret = mid;
688       return TRUE;
689     }
691     if (cmp < 0)
692       last = mid - 1;
693     else
694       first = mid + 1;
696     midsize = last - first;
697   }
699   for (tx = first; tx <= last; tx++) {
700     cmp = (*compare) (sample, ary->data + tx * stride, user_data);
702     if (cmp < 0) {
703       if (ret)
704         *ret = tx;
705       return FALSE;
706     }
707     if (cmp == 0) {
708       if (ret)
709         *ret = tx;
710       return TRUE;
711     }
712   }
714   if (ret)
715     *ret = last + 1;
716   return FALSE;
719 static gint
720 file_index_compare (gconstpointer sample, gconstpointer row, gpointer user_data)
722   //GstFileIndexId *id_index = user_data;
723   const GstIndexAssociation *ca = sample;
724   gint64 val1 = ca->value;
725   gint64 val2_be = ARRAY_ROW_VALUE (row, ca->format);
726   gint64 val2 = GINT64_FROM_BE (val2_be);
727   gint64 diff = val2 - val1;
729   return (diff == 0 ? 0 : (diff < 0 ? 1 : -1));
732 static void
733 gst_file_index_add_association (GstIndex * index, GstIndexEntry * entry)
735   GstFileIndex *fileindex = GST_FILE_INDEX (index);
736   GstFileIndexId *id_index;
737   gint mx;
738   GstIndexAssociation sample;
739   gboolean exact;
741   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
742   if (!id_index)
743     return;
745   if (!id_index->nformats) {
746     gint fx;
748     id_index->nformats = GST_INDEX_NASSOCS (entry);
749     GST_LOG_OBJECT (fileindex, "creating %d formats for %d",
750         id_index->nformats, entry->id);
751     id_index->format = g_new (GstFormat, id_index->nformats);
752     for (fx = 0; fx < id_index->nformats; fx++)
753       id_index->format[fx] = GST_INDEX_ASSOC_FORMAT (entry, fx);
754     _fc_alloc_array (id_index);
755   } else {
756     /* only sanity checking */
757     if (id_index->nformats != GST_INDEX_NASSOCS (entry))
758       GST_WARNING_OBJECT (fileindex, "arity change %d -> %d",
759           id_index->nformats, GST_INDEX_NASSOCS (entry));
760     else {
761       gint fx;
763       for (fx = 0; fx < id_index->nformats; fx++)
764         if (id_index->format[fx] != GST_INDEX_ASSOC_FORMAT (entry, fx))
765           GST_WARNING_OBJECT (fileindex, "format[%d] changed %d -> %d",
766               fx, id_index->format[fx], GST_INDEX_ASSOC_FORMAT (entry, fx));
767     }
768   }
770   /* this is a hack, we should use a private structure instead */
771   sample.format = 0;
772   sample.value = GST_INDEX_ASSOC_VALUE (entry, 0);
774   exact =
775       _fc_bsearch (id_index->array, ARRAY_ROW_SIZE (id_index),
776       &mx, file_index_compare, &sample, id_index);
778   if (exact) {
779     /* maybe overwrite instead? */
780     GST_DEBUG_OBJECT (index,
781         "Ignoring duplicate index association at %" G_GINT64_FORMAT,
782         GST_INDEX_ASSOC_VALUE (entry, 0));
783     return;
784   }
786   {
787     gchar *row_data = (gchar *) g_malloc (ARRAY_ROW_SIZE (id_index));
788     gint fx;
790     gint32 flags_host = GST_INDEX_ASSOC_FLAGS (entry);
792     ARRAY_ROW_FLAGS (row_data) = GINT32_TO_BE (flags_host);
794     for (fx = 0; fx < id_index->nformats; fx++) {
795       gint64 val_host = GST_INDEX_ASSOC_VALUE (entry, fx);
797       ARRAY_ROW_VALUE (row_data, fx) = GINT64_TO_BE (val_host);
798     }
800     g_array_insert_vals (id_index->array, mx, row_data, 1);
802     g_free (row_data);
803   }
806 /*
807 static void
808 show_entry (GstIndexEntry *entry)
810   switch (entry->type) {
811     case GST_INDEX_ENTRY_ID:
812       g_print ("id %d describes writer %s\n", entry->id,
813                       GST_INDEX_ID_DESCRIPTION (entry));
814       break;
815     case GST_INDEX_ENTRY_FORMAT:
816       g_print ("%d: registered format %d for %s\n", entry->id,
817                       GST_INDEX_FORMAT_FORMAT (entry),
818                       GST_INDEX_FORMAT_KEY (entry));
819       break;
820     case GST_INDEX_ENTRY_ASSOCIATION:
821     {
822       gint i;
824       g_print ("%d: %08x ", entry->id, GST_INDEX_ASSOC_FLAGS (entry));
825       for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
826         g_print ("%d %" G_GINT64_FORMAT, GST_INDEX_ASSOC_FORMAT (entry, i),
827                              GST_INDEX_ASSOC_VALUE (entry, i));
828       }
829       g_print ("\n");
830       break;
831     }
832     default:
833       break;
834   }
836 */
838 static void
839 gst_file_index_add_entry (GstIndex * index, GstIndexEntry * entry)
841   GST_LOG_OBJECT (index, "adding this entry");
843   switch (entry->type) {
844     case GST_INDEX_ENTRY_ID:
845       gst_file_index_add_id (index, entry);
846       break;
847     case GST_INDEX_ENTRY_ASSOCIATION:
848       gst_file_index_add_association (index, entry);
849       break;
850     case GST_INDEX_ENTRY_OBJECT:
851       GST_ERROR_OBJECT (index, "gst_file_index_add_object not implemented");
852       break;
853     case GST_INDEX_ENTRY_FORMAT:
854       /*
855          We infer the formats from the entry itself so this type of
856          GST_INDEX_ENTRY_* can probably go away.
857        */
858       GST_DEBUG_OBJECT (index, "gst_file_index_add_format not implemented");
859       break;
860     default:
861       break;
862   }
865 static GstIndexEntry *
866 gst_file_index_get_assoc_entry (GstIndex * index,
867     gint id,
868     GstIndexLookupMethod method,
869     GstAssocFlags flags,
870     GstFormat format,
871     gint64 value, GCompareDataFunc _ignore_func, gpointer _ignore_user_data)
873   GstFileIndex *fileindex = GST_FILE_INDEX (index);
874   GstFileIndexId *id_index;
875   gint formatx = -1;
876   gint fx;
877   GstIndexAssociation sample;
878   gint mx;
879   gboolean exact;
880   gpointer row_data;
881   GstIndexEntry *entry;
882   gint xx;
884   g_return_val_if_fail (id > 0, NULL);
886   id_index = g_hash_table_lookup (fileindex->id_index, &id);
887   if (!id_index) {
888     GST_WARNING_OBJECT (fileindex, "writer %d unavailable", id);
889     return NULL;
890   }
892   for (fx = 0; fx < id_index->nformats; fx++)
893     if (id_index->format[fx] == format) {
894       formatx = fx;
895       break;
896     }
898   if (formatx == -1) {
899     GST_WARNING_OBJECT (fileindex, "format %d not available", format);
900     return NULL;
901   }
903   /* this is a hack, we should use a private structure instead */
904   sample.format = formatx;
905   sample.value = value;
907   exact = _fc_bsearch (id_index->array, ARRAY_ROW_SIZE (id_index),
908       &mx, file_index_compare, &sample, id_index);
910   if (!exact) {
911     if (method == GST_INDEX_LOOKUP_EXACT)
912       return NULL;
913     else if (method == GST_INDEX_LOOKUP_BEFORE) {
914       if (mx == 0)
915         return NULL;
916       mx -= 1;
917     } else if (method == GST_INDEX_LOOKUP_AFTER) {
918       if (mx == id_index->array->len)
919         return NULL;
920     }
921   }
923   row_data = id_index->array->data + mx * ARRAY_ROW_SIZE (id_index);
925   /* if exact then ignore flags (?) */
926   if (method != GST_INDEX_LOOKUP_EXACT)
927     while ((GINT32_FROM_BE (ARRAY_ROW_FLAGS (row_data)) & flags) != flags) {
928       if (method == GST_INDEX_LOOKUP_BEFORE)
929         mx -= 1;
930       else if (method == GST_INDEX_LOOKUP_AFTER)
931         mx += 1;
932       if (mx < 0 || mx >= id_index->array->len)
933         return NULL;
934       row_data = id_index->array->data + mx * ARRAY_ROW_SIZE (id_index);
935     }
937   /* entry memory management needs improvement FIXME */
938   if (!fileindex->ret_entry)
939     fileindex->ret_entry = g_slice_new0 (GstIndexEntry);
940   entry = fileindex->ret_entry;
941   if (entry->data.assoc.assocs) {
942     g_free (entry->data.assoc.assocs);
943     entry->data.assoc.assocs = NULL;
944   }
946   entry->type = GST_INDEX_ENTRY_ASSOCIATION;
948   GST_INDEX_NASSOCS (entry) = id_index->nformats;
949   entry->data.assoc.assocs = g_new (GstIndexAssociation, id_index->nformats);
951   {
952     gint32 flags_be = ARRAY_ROW_FLAGS (row_data);
954     GST_INDEX_ASSOC_FLAGS (entry) = GINT32_FROM_BE (flags_be);
956     for (xx = 0; xx < id_index->nformats; xx++) {
957       gint64 val_be = ARRAY_ROW_VALUE (row_data, xx);
959       GST_INDEX_ASSOC_FORMAT (entry, xx) = id_index->format[xx];
960       GST_INDEX_ASSOC_VALUE (entry, xx) = GINT64_FROM_BE (val_be);
961     }
962   }
964   return entry;
967 gboolean
968 gst_file_index_plugin_init (GstPlugin * plugin)
970   GstIndexFactory *factory;
972   factory = gst_index_factory_new ("fileindex",
973       "A index that stores entries in file", gst_file_index_get_type ());
975   if (factory == NULL) {
976     return FALSE;
977   }
979   GST_PLUGIN_FEATURE (factory)->plugin_name = g_strdup (plugin->desc.name);
980   GST_PLUGIN_FEATURE (factory)->loaded = TRUE;
982   gst_registry_add_feature (gst_registry_get_default (),
983       GST_PLUGIN_FEATURE (factory));
985   GST_DEBUG_CATEGORY_INIT (DC, "GST_FILEINDEX", 0, NULL);
987   return TRUE;