]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blob - plugins/indexers/gstfileindex.c
update plugin initialization restructuring (see email for details
[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 #define GST_TYPE_FILE_INDEX             \
31   (gst_file_index_get_type ())
32 #define GST_FILE_INDEX(obj)             \
33   (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FILE_INDEX, GstFileIndex))
34 #define GST_FILE_INDEX_CLASS(klass)     \
35   (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FILE_INDEX, GstFileIndexClass))
36 #define GST_IS_FILE_INDEX(obj)          \
37   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FILE_INDEX))
38 #define GST_IS_FILE_INDEX_CLASS(obj)    \
39   (GST_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FILE_INDEX))
40         
41 #ifndef __FUNCTION__
42 #define __FUNCTION__ "Unavailable"
43 #endif
45 /*
46  * Object model:
47  *
48  * We build an index to each entry for each id.
49  * 
50  *
51  *  fileindex
52  *    -----------------------------...
53  *    !                  !         
54  *   id1                 id2        
55  *    !
56  *   GArray
57  *
58  * The fileindex creates a FileIndexId object for each writer id, a
59  * Hashtable is kept to map the id to the FileIndexId
60  *
61  * The FileIndexId also keeps all the values in a sorted GArray.
62  *
63  * Finding a value for an id/format requires locating the correct GArray,
64  * then do a binary search to get the required value.
65  *
66  * Unlike gstmemindex:  All formats are assumed to sort to the
67  * same order.  All formats are assumed to be available from
68  * any entry.
69  */
71 /*
72  * Each array element is (32bits flags, nformats * 64bits)
73  */
74 typedef struct {
75   gint           id;
76   gchar         *id_desc;
77   gint           nformats;
78   GstFormat     *format;
79   GArray        *array;
80 } GstFileIndexId;
82 typedef struct _GstFileIndex GstFileIndex;
83 typedef struct _GstFileIndexClass GstFileIndexClass;
85 #define ARRAY_ROW_SIZE(_ii) \
86   (sizeof (gint32) + (_ii)->nformats * sizeof (gint64))
87 #define ARRAY_TOTAL_SIZE(_ii) \
88   (_ii->array->len * ARRAY_ROW_SIZE(_ii))
90 /* don't forget to convert to/from BE byte-order */
91 #define ARRAY_ROW_FLAGS(_row) \
92   (*((gint32*) (_row)))
93 #define ARRAY_ROW_VALUE(_row,_vx) \
94   (*(gint64*) (((gchar*)(_row)) + sizeof (gint32) + (_vx) * sizeof (gint64)))
96 GST_DEBUG_CATEGORY_STATIC(DC);
98 struct _GstFileIndex {
99   GstIndex               parent;
101   gchar                 *location;
102   gboolean               is_loaded;
103   GSList                *unresolved;
104   gint                   next_id;
105   GHashTable            *id_index;
107   GstIndexEntry         *ret_entry;  /* hack to avoid leaking memory */
108 };
110 struct _GstFileIndexClass {
111   GstIndexClass parent_class;
112 };
114 enum {
115   ARG_0,
116   ARG_LOCATION,
117 };
119 static void             gst_file_index_class_init       (GstFileIndexClass *klass);
120 static void             gst_file_index_init             (GstFileIndex *index);
121 static void             gst_file_index_dispose          (GObject *object);
123 static void
124 gst_file_index_set_property (GObject *object,
125                              guint prop_id,
126                              const GValue *value,
127                              GParamSpec *pspec);
128 static void
129 gst_file_index_get_property (GObject *object,
130                              guint prop_id,
131                              GValue *value,
132                              GParamSpec *pspec);
134 static gboolean
135 gst_file_index_get_writer_id  (GstIndex *_index, gint *id, gchar *writer_string);
137 static void             gst_file_index_commit           (GstIndex *index, gint writer_id);
138 static void             gst_file_index_add_entry        (GstIndex *index, GstIndexEntry *entry);
139 static GstIndexEntry*   gst_file_index_get_assoc_entry  (GstIndex *index, gint id,
140                                                          GstIndexLookupMethod method,
141                                                          GstAssocFlags flags,
142                                                          GstFormat format, gint64 value,
143                                                          GCompareDataFunc func,
144                                                          gpointer user_data);
146 #define CLASS(file_index)  GST_FILE_INDEX_CLASS (G_OBJECT_GET_CLASS (file_index))
148 static GstIndex *parent_class = NULL;
150 GType
151 gst_file_index_get_type(void) {
152   static GType file_index_type = 0;
154   if (!file_index_type) {
155     static const GTypeInfo file_index_info = {
156       sizeof(GstFileIndexClass),
157       NULL,
158       NULL,
159       (GClassInitFunc)gst_file_index_class_init,
160       NULL,
161       NULL,
162       sizeof(GstFileIndex),
163       1,
164       (GInstanceInitFunc)gst_file_index_init,
165       NULL
166     };
167     file_index_type = g_type_register_static(GST_TYPE_INDEX, "GstFileIndex", &file_index_info, 0);
168   }
169   return file_index_type;
172 static void
173 gst_file_index_class_init (GstFileIndexClass *klass)
175   GObjectClass *gobject_class;
176   GstIndexClass *gstindex_class;
178   gobject_class = (GObjectClass*)klass;
179   gstindex_class = (GstIndexClass*)klass;
181   parent_class = g_type_class_ref(GST_TYPE_INDEX);
183   gobject_class->dispose        = gst_file_index_dispose;
184   gobject_class->set_property   = gst_file_index_set_property;
185   gobject_class->get_property   = gst_file_index_get_property;
187   gstindex_class->add_entry       = gst_file_index_add_entry;
188   gstindex_class->get_assoc_entry = gst_file_index_get_assoc_entry;
189   gstindex_class->commit          = gst_file_index_commit;
190   gstindex_class->get_writer_id   = gst_file_index_get_writer_id ;
192   g_object_class_install_property (gobject_class, ARG_LOCATION,
193    g_param_spec_string ("location", "File Location",
194                         "Location of the index file",
195                         NULL, G_PARAM_READWRITE));
198 static void
199 gst_file_index_init (GstFileIndex *index)
201   GST_DEBUG ( "created new file index");
203   index->id_index = g_hash_table_new (g_int_hash, g_int_equal);
206 static void
207 _file_index_id_free (GstFileIndexId *index_id, gboolean is_mmapped)
209   if (index_id->id_desc)
210     g_free (index_id->id_desc);
211   if (index_id->format)
212     g_free (index_id->format);
213   if (index_id->array) {
214     if (is_mmapped)
215       munmap (index_id->array->data,  ARRAY_TOTAL_SIZE (index_id));
216     g_array_free (index_id->array, !is_mmapped);
217   }
218   g_free (index_id);
221 static gboolean
222 _id_index_free_helper (gpointer _key, GstFileIndexId *index_id,
223                        GstFileIndex *index)
225   _file_index_id_free (index_id, index->is_loaded);
226   return TRUE;
229 static void
230 gst_file_index_dispose (GObject *object)
232   GstFileIndex *index = GST_FILE_INDEX (object);
234   if (index->location) {
235     g_free (index->location);
236     index->location = NULL;
237   }
239   {
240     GSList *elem;
241     for (elem = index->unresolved; elem; elem = g_slist_next (elem))
242       _file_index_id_free (elem->data, index->is_loaded);
243     g_slist_free (index->unresolved);
244     index->unresolved = NULL;
245   }
246   
247   g_hash_table_foreach_steal (index->id_index,
248                               (GHRFunc) _id_index_free_helper, index);
249   g_hash_table_destroy (index->id_index);
250   index->id_index = NULL;
252   gst_index_entry_free (index->ret_entry);  /* hack */
254   G_OBJECT_CLASS (parent_class)->dispose (object);
257 struct fi_find_writer_context {
258   const gchar *writer_string;
259   GstFileIndexId *ii;
260 };
262 void
263 _fi_find_writer (gpointer key, gpointer val, gpointer data)
265   struct fi_find_writer_context *cx = data;
266   GstFileIndexId *ii = val;
267   if (strcmp (ii->id_desc, cx->writer_string) == 0)
268     cx->ii = ii;
271 static gboolean
272 gst_file_index_get_writer_id  (GstIndex *_index, 
273                                gint *id, gchar *writer_string)
275   GstFileIndex *index = GST_FILE_INDEX (_index);
276   GSList *pending = index->unresolved;
277   gboolean match = FALSE;
278   GSList *elem;
280   if (!index->is_loaded)
281     return FALSE;
283   g_return_val_if_fail (id, FALSE);
284   g_return_val_if_fail (writer_string, FALSE);
286   index->unresolved = NULL;
288   for (elem = pending; elem; elem = g_slist_next (elem)) {
289     GstFileIndexId *ii = elem->data;
290     if (strcmp (ii->id_desc, writer_string) != 0) {
291       index->unresolved = g_slist_prepend (index->unresolved, ii);
292       continue;
293     }
294     
295     if (match) {
296       GST_CAT_WARNING_OBJECT (DC, index, "Duplicate matches for writer '%s'",
297                               writer_string);
298       continue;
299     }
301     ii->id = *id = ++index->next_id;
302     g_hash_table_insert (index->id_index, &ii->id, ii);
303     match = TRUE;
304   }
306   g_slist_free (pending);
308   if (!match) {
309     struct fi_find_writer_context cx;
310     cx.writer_string = writer_string;
311     cx.ii = NULL;
312     g_hash_table_foreach (index->id_index, _fi_find_writer, &cx);
314     if (cx.ii) {
315       match = TRUE;
316       GST_CAT_DEBUG_OBJECT (DC, index, "Resolved writer '%s' again",
317                             writer_string);
318     }
319     else
320       GST_CAT_WARNING_OBJECT (DC, index, "Can't resolve writer '%s'",
321                               writer_string);
322   }
324   return match;
327 static void
328 _fc_alloc_array (GstFileIndexId *id_index)
330   g_assert (!id_index->array);
331   id_index->array =
332     g_array_sized_new (FALSE, FALSE, ARRAY_ROW_SIZE (id_index), 0);
335 static void
336 gst_file_index_load (GstFileIndex *index)
338   xmlDocPtr doc;
339   xmlNodePtr root, part;
340   xmlChar *val;
342   g_assert (index->location);
343   g_return_if_fail (!index->is_loaded);
345   {
346     gchar *path = g_strdup_printf ("%s/gstindex.xml", index->location);
347     GError *err = NULL;
348     gchar *buf;
349     gsize len;
350     g_file_get_contents (path, &buf, &len, &err);
351     g_free (path);
352     if (err) {
353       GST_CAT_ERROR_OBJECT (DC, index, "%s", err->message);
354       return;
355     }
357     doc = xmlParseMemory (buf, len);
358     g_free (buf);
359   }
361   //xmlDocFormatDump (stderr, doc, TRUE);
363   root = doc->xmlRootNode;
364   if (strcmp (root->name, "gstfileindex") != 0) {
365     GST_CAT_ERROR_OBJECT (DC, index, "root node isn't a gstfileindex");
366     return;
367   }
368   
369   val = xmlGetProp (root, "version");
370   if (!val || atoi (val) != 1) {
371     GST_CAT_ERROR_OBJECT (DC, index, "version != 1");
372     return;
373   }
374   free (val);
376   for (part = root->children; part; part = part->next) {
377     if (strcmp (part->name, "writers") == 0) {
378       xmlNodePtr writer;
379       for (writer = part->children; writer; writer = writer->next) {
380         xmlChar *datafile = xmlGetProp (writer, "datafile");
381         gchar *path = g_strdup_printf ("%s/%s", index->location, datafile);
382         int fd;
383         GstFileIndexId *id_index;
384         xmlNodePtr wpart;
385         xmlChar *entries_str;
386         gpointer array_data;
388         free (datafile);
390         fd = open (path, O_RDONLY);
391         g_free (path);
392         if (fd < 0) {
393           GST_CAT_ERROR_OBJECT (DC, index,
394                                 "Can't open '%s': %s", path, strerror (errno));
395           continue;
396         }
398         id_index = g_new0 (GstFileIndexId, 1);
399         id_index->id_desc = xmlGetProp (writer, "id");
401         for (wpart = writer->children; wpart; wpart = wpart->next) {
402           if (strcmp (wpart->name, "formats") == 0) {
403             xmlChar *count_str = xmlGetProp (wpart, "count");
404             gint fx=0;
405             xmlNodePtr format;
407             id_index->nformats = atoi (count_str);
408             free (count_str);
410             id_index->format = g_new (GstFormat, id_index->nformats);
412             for (format = wpart->children;
413                  format; format = format->next) {
414               xmlChar *nick = xmlGetProp (format, "nick");
415               GstFormat fmt = gst_format_get_by_nick (nick);
416               if (fmt == GST_FORMAT_UNDEFINED)
417                 GST_CAT_ERROR_OBJECT (DC, index,
418                                       "format '%s' undefined", nick);
419               g_assert (fx < id_index->nformats);
420               id_index->format[fx++] = fmt;
421               free (nick);
422             }
423           } else
424             GST_CAT_INFO_OBJECT (DC, index, "unknown wpart '%s'", wpart->name);
425         }
427         g_assert (id_index->nformats > 0);
428         _fc_alloc_array (id_index);
429         g_assert (id_index->array->data == NULL);  /* little bit risky */
431         entries_str = xmlGetProp (writer, "entries");
432         id_index->array->len = atoi (entries_str);
433         free (entries_str);
435         array_data =
436           mmap (NULL, ARRAY_TOTAL_SIZE (id_index), PROT_READ, MAP_SHARED, fd, 0);
437         close (fd);
438         if (array_data == MAP_FAILED) {
439           GST_CAT_ERROR_OBJECT (DC, index,
440                                 "mmap %s failed: %s", path, strerror (errno));
441           continue;
442         }
444         id_index->array->data = array_data;
446         index->unresolved = g_slist_prepend (index->unresolved, id_index);
447       }
448     } else
449       GST_CAT_INFO_OBJECT (DC, index, "unknown part '%s'", part->name);
450   }
452   xmlFreeDoc (doc);
454   GST_FLAG_UNSET (index, GST_INDEX_WRITABLE);
455   index->is_loaded = TRUE;
456   GST_CAT_LOG_OBJECT (DC, index, "index %s loaded OK", index->location);
459 static void
460 gst_file_index_set_property (GObject *object,
461                              guint prop_id,
462                              const GValue *value,
463                              GParamSpec *pspec)
465   GstFileIndex *index = GST_FILE_INDEX (object);
467   switch (prop_id) {
468   case ARG_LOCATION:
469     if (index->location)
470       g_free (index->location);
471     index->location = g_value_dup_string (value);
473     if (index->location && !g_hash_table_size (index->id_index))
474       gst_file_index_load (index);
475     break;
476   }
479 static void
480 gst_file_index_get_property (GObject *object,
481                              guint prop_id,
482                              GValue *value,
483                              GParamSpec *pspec)
485   GstFileIndex *index = GST_FILE_INDEX (object);
486   
487   switch (prop_id) {
488   case ARG_LOCATION:
489     g_value_set_string (value, index->location);
490     break;
491   }
494 static void
495 _file_index_id_save_xml (gpointer _key, GstFileIndexId *ii, xmlNodePtr writers)
497   const gint bufsize = 16;
498   gchar buf[bufsize];
499   xmlNodePtr writer;
500   xmlNodePtr formats;
501   gint xx;
502   
503   if (!ii->array) {
504     GST_INFO ("Index for %s is empty", ii->id_desc);
505     return;
506   }
508   writer = xmlNewChild (writers, NULL, "writer", NULL);
509   xmlSetProp (writer, "id", ii->id_desc);
510   g_snprintf (buf, bufsize, "%d", ii->array->len);
511   xmlSetProp (writer, "entries", buf);
512   g_snprintf (buf, bufsize, "%d", ii->id); /* any unique number is OK */
513   xmlSetProp (writer, "datafile", buf);
515   formats = xmlNewChild (writer, NULL, "formats", NULL);
516   g_snprintf (buf, bufsize, "%d", ii->nformats);
517   xmlSetProp (formats, "count", buf);
519   for (xx=0; xx < ii->nformats; xx++) {
520     xmlNodePtr format = xmlNewChild (formats, NULL, "format", NULL);
521     const GstFormatDefinition* def =
522       gst_format_get_details (ii->format[xx]);
523     xmlSetProp (format, "nick", def->nick);
524   }
527 /*
528   We must save the binary data in separate files because
529   mmap wants getpagesize() alignment.  If we append all
530   the data to one file then we don't know the appropriate
531   padding since the page size isn't fixed.
532 */
533 static void
534 _file_index_id_save_entries (gpointer *_key,
535                              GstFileIndexId *ii,
536                              gchar *prefix)
538   GError *err;
539   gchar *path;
540   GIOChannel *chan;
542   if (!ii->array)
543     return;
545   err = NULL;
546   path = g_strdup_printf ("%s/%d", prefix, ii->id);
547   chan = g_io_channel_new_file (path, "w", &err);
548   g_free (path);
549   if (err) goto fail;
550   
551   g_io_channel_set_encoding (chan, NULL, &err);
552   if (err) goto fail;
554   g_io_channel_write_chars (chan,
555                             ii->array->data,
556                             ARRAY_TOTAL_SIZE (ii),
557                             NULL,
558                             &err);
559   if (err) goto fail;
561   g_io_channel_shutdown (chan, TRUE, &err);
562   if (err) goto fail;
564   g_io_channel_unref (chan);
565   return;
567  fail:
568   GST_CAT_ERROR (DC, "%s", err->message);
571 /*
572   We have to save the whole set of indexes into a single file
573   so it doesn't make sense to commit only a single writer.
575   i suggest:
577   gst_index_commit (index, -1);
578 */
579 static void
580 gst_file_index_commit (GstIndex *_index, gint _writer_id)
582   GstFileIndex *index = GST_FILE_INDEX (_index);
583   xmlDocPtr doc;
584   xmlNodePtr writers;
585   GError *err = NULL;
586   gchar *path;
587   GIOChannel *tocfile;
589   g_return_if_fail (index->location);
590   g_return_if_fail (!index->is_loaded);
592   GST_FLAG_UNSET (index, GST_INDEX_WRITABLE);
594   doc = xmlNewDoc ("1.0");
595   doc->xmlRootNode = xmlNewDocNode (doc, NULL, "gstfileindex", NULL);
596   xmlSetProp (doc->xmlRootNode, "version", "1");
598   writers = xmlNewChild (doc->xmlRootNode, NULL, "writers", NULL);
599   g_hash_table_foreach (index->id_index,
600                         (GHFunc) _file_index_id_save_xml, writers);
602   if (mkdir (index->location, 0777) &&
603       errno != EEXIST) {
604     GST_CAT_ERROR_OBJECT (DC, index,
605                           "mkdir %s: %s", index->location, strerror (errno));
606     return;
607   }
609   path = g_strdup_printf ("%s/gstindex.xml", index->location);
610   tocfile =
611     g_io_channel_new_file (path, "w", &err);
612   g_free (path);
613   if (err) {
614     GST_CAT_ERROR_OBJECT (DC, index, "%s", err->message);
615     return;
616   }
618   g_io_channel_set_encoding (tocfile, NULL, &err);
619   if (err) {
620     GST_CAT_ERROR_OBJECT (DC, index, "%s", err->message);
621     return;
622   }
624   {
625     xmlChar *xmlmem;
626     int xmlsize;
627     xmlDocDumpMemory (doc, &xmlmem, &xmlsize);
628     g_io_channel_write_chars (tocfile, xmlmem, xmlsize, NULL, &err);
629     if (err) {
630       GST_CAT_ERROR_OBJECT (DC, index, "%s", err->message);
631       return;
632     }
633     xmlFreeDoc (doc);
634     free (xmlmem);
635   }
637   g_io_channel_shutdown (tocfile, TRUE, &err);
638   if (err) {
639     GST_CAT_ERROR_OBJECT (DC, index, "%s", err->message);
640     return;
641   }
643   g_io_channel_unref (tocfile);
645   g_hash_table_foreach (index->id_index,
646                         (GHFunc) _file_index_id_save_entries,
647                         index->location);
650 static void
651 gst_file_index_add_id (GstIndex *index, GstIndexEntry *entry)
653   GstFileIndex *fileindex = GST_FILE_INDEX (index);
654   GstFileIndexId *id_index;
656   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
658   if (!id_index) {
659     id_index = g_new0 (GstFileIndexId, 1);
661     id_index->id = entry->id;
662     id_index->id_desc = g_strdup (entry->data.id.description);
664     /* It would be useful to know the GType of the writer so
665        we can try to cope with changes in the id_desc path. */
667     g_hash_table_insert (fileindex->id_index, &id_index->id, id_index);
668   }
671 /* This algorithm differs from libc bsearch in the handling
672    of non-exact matches. */
674 static gboolean
675 _fc_bsearch (GArray *          ary,
676              gint              stride,
677              gint *            ret,
678              GCompareDataFunc  compare,
679              gconstpointer     sample,
680              gpointer          user_data)
682   gint first, last;
683   gint mid;
684   gint midsize;
685   gint cmp;
686   gint tx;
688   g_return_val_if_fail (compare, FALSE);
690   if (!ary->len)
691     {
692       if (ret) *ret = 0;
693       return FALSE;
694     }
696   first = 0;
697   last = ary->len - 1;
699   midsize = last - first;
700   
701   while (midsize > 1) {
702     mid = first + midsize / 2;
703     
704     cmp = (*compare) (sample, ary->data + mid*stride, user_data);
705     
706     if (cmp == 0)
707       {
708         /* if there are multiple matches then scan for the first match */
709         while (mid > 0 &&
710                (*compare) (sample,
711                            ary->data + (mid - 1) * stride,
712                            user_data) == 0)
713           --mid;
715         if (ret) *ret = mid;
716         return TRUE;
717       }
718     
719     if (cmp < 0)
720       last = mid-1;
721     else
722       first = mid+1;
723     
724     midsize = last - first;
725   }
727   for (tx = first; tx <= last; tx++)
728     {
729       cmp = (*compare) (sample, ary->data + tx*stride, user_data);
731       if (cmp < 0)
732         {
733           if (ret) *ret = tx;
734           return FALSE;
735         }
736       if (cmp == 0)
737         {
738           if (ret) *ret = tx;
739           return TRUE;
740         }
741     }
743   if (ret) *ret = last+1;
744   return FALSE;
747 static gint
748 file_index_compare (gconstpointer sample,
749                     gconstpointer row,
750                     gpointer user_data)
752   //GstFileIndexId *id_index = user_data;
753   const GstIndexAssociation *ca = sample;
754   gint64 val1 = ca->value;
755   gint64 val2_be = ARRAY_ROW_VALUE (row, ca->format);
756   gint64 val2 = GINT64_FROM_BE (val2_be);
757   gint64 diff = val2 - val1;
758   return (diff == 0 ? 0 : (diff < 0 ? 1 : -1));
761 static void
762 gst_file_index_add_association (GstIndex *index, GstIndexEntry *entry)
764   GstFileIndex *fileindex = GST_FILE_INDEX (index);
765   GstFileIndexId *id_index;
766   gint mx;
767   GstIndexAssociation sample;
768   gboolean exact;
770   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
771   if (!id_index)
772     return;
774   if (!id_index->nformats) {
775     gint fx;
776     id_index->nformats = GST_INDEX_NASSOCS (entry);
777     GST_CAT_LOG_OBJECT (DC, fileindex, "creating %d formats for %d",
778                         id_index->nformats, entry->id);
779     id_index->format = g_new (GstFormat, id_index->nformats);
780     for (fx=0; fx < id_index->nformats; fx++)
781       id_index->format[fx] = GST_INDEX_ASSOC_FORMAT (entry, fx);
782     _fc_alloc_array (id_index);
783   } else {
784     /* only sanity checking */
785     if (id_index->nformats != GST_INDEX_NASSOCS (entry))
786       GST_CAT_WARNING_OBJECT (DC, fileindex, "arity change %d -> %d",
787                               id_index->nformats, GST_INDEX_NASSOCS (entry));
788     else {
789       gint fx;
790       for (fx=0; fx < id_index->nformats; fx++)
791         if (id_index->format[fx] != GST_INDEX_ASSOC_FORMAT (entry, fx))
792           GST_CAT_WARNING_OBJECT (DC, fileindex, "format[%d] changed %d -> %d",
793                                   fx, id_index->format[fx],
794                                   GST_INDEX_ASSOC_FORMAT (entry, fx));
795     }
796   }
798   /* this is a hack, we should use a private structure instead */
799   sample.format = 0;
800   sample.value = GST_INDEX_ASSOC_VALUE (entry, 0);
802   exact =
803     _fc_bsearch (id_index->array, ARRAY_ROW_SIZE (id_index),
804                  &mx, file_index_compare,
805                  &sample, id_index);
807   if (exact) {
808     /* maybe overwrite instead? */
809     GST_CAT_DEBUG_OBJECT (DC, index,
810                           "Ignoring duplicate index association at %lld",
811                           GST_INDEX_ASSOC_VALUE (entry, 0));
812     return;
813   }
815   {
816     gchar row_data[ARRAY_ROW_SIZE (id_index)];
817     gint fx;
819     gint32 flags_host = GST_INDEX_ASSOC_FLAGS (entry);
820     ARRAY_ROW_FLAGS (row_data) = GINT32_TO_BE (flags_host);
822     for (fx = 0; fx < id_index->nformats; fx++) {
823       gint64 val_host = GST_INDEX_ASSOC_VALUE (entry, fx);
824       ARRAY_ROW_VALUE (row_data, fx) = GINT64_TO_BE (val_host);
825     }
827     g_array_insert_vals (id_index->array, mx, row_data, 1);
828   }
831 /*
832 static void
833 show_entry (GstIndexEntry *entry)
835   switch (entry->type) {
836     case GST_INDEX_ENTRY_ID:
837       g_print ("id %d describes writer %s\n", entry->id, 
838                       GST_INDEX_ID_DESCRIPTION (entry));
839       break;
840     case GST_INDEX_ENTRY_FORMAT:
841       g_print ("%d: registered format %d for %s\n", entry->id, 
842                       GST_INDEX_FORMAT_FORMAT (entry),
843                       GST_INDEX_FORMAT_KEY (entry));
844       break;
845     case GST_INDEX_ENTRY_ASSOCIATION:
846     {
847       gint i;
849       g_print ("%d: %08x ", entry->id, GST_INDEX_ASSOC_FLAGS (entry));
850       for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
851         g_print ("%d %lld ", GST_INDEX_ASSOC_FORMAT (entry, i), 
852                              GST_INDEX_ASSOC_VALUE (entry, i));
853       }
854       g_print ("\n");
855       break;
856     }
857     default:
858       break;
859   }
861 */
863 static void
864 gst_file_index_add_entry (GstIndex *index, GstIndexEntry *entry)
866   GST_LOG_OBJECT (index, "adding this entry");
868   switch (entry->type){
869      case GST_INDEX_ENTRY_ID:
870        gst_file_index_add_id (index, entry);
871        break;
872      case GST_INDEX_ENTRY_ASSOCIATION:
873        gst_file_index_add_association (index, entry);
874        break;
875      case GST_INDEX_ENTRY_OBJECT:
876        GST_CAT_ERROR_OBJECT (DC, index,
877                              "gst_file_index_add_object not implemented");
878        break;
879      case GST_INDEX_ENTRY_FORMAT:
880        /*
881          We infer the formats from the entry itself so this type of
882          GST_INDEX_ENTRY_* can probably go away.
883        */
884        GST_CAT_DEBUG_OBJECT (DC, index,
885                              "gst_file_index_add_format not implemented");
886        break;
887      default:
888        break;
889   }
892 static GstIndexEntry*
893 gst_file_index_get_assoc_entry (GstIndex *index,
894                                 gint id,
895                                 GstIndexLookupMethod method,
896                                 GstAssocFlags flags,
897                                 GstFormat format,
898                                 gint64 value,
899                                 GCompareDataFunc _ignore_func,
900                                 gpointer _ignore_user_data)
902   GstFileIndex *fileindex = GST_FILE_INDEX (index);
903   GstFileIndexId *id_index;
904   gint formatx = -1;
905   gint fx;
906   GstIndexAssociation sample;
907   gint mx;
908   gboolean exact;
909   gpointer row_data;
910   GstIndexEntry *entry;
911   gint xx;
913   g_return_val_if_fail (id > 0, NULL);
915   id_index = g_hash_table_lookup (fileindex->id_index, &id);
916   if (!id_index) {
917     GST_CAT_WARNING_OBJECT (DC, fileindex, "writer %d unavailable", id);
918     return NULL;
919   }
921   for (fx=0; fx < id_index->nformats; fx++)
922     if (id_index->format[fx] == format)
923       { formatx = fx; break; }
925   if (formatx == -1) {
926     GST_CAT_WARNING_OBJECT (DC, fileindex,
927                           "%s, format %d not available", __FUNCTION__, format);
928     return NULL;
929   }
931   /* this is a hack, we should use a private structure instead */
932   sample.format = formatx;
933   sample.value = value;
935   exact = _fc_bsearch (id_index->array, ARRAY_ROW_SIZE (id_index),
936                        &mx, file_index_compare, &sample, id_index);
938   if (!exact) {
939     if (method == GST_INDEX_LOOKUP_EXACT)
940       return NULL;
941     else if (method == GST_INDEX_LOOKUP_BEFORE) {
942       if (mx == 0)
943         return NULL;
944       mx -= 1;
945     } else if (method == GST_INDEX_LOOKUP_AFTER) {
946       if (mx == id_index->array->len)
947         return NULL;
948     }
949   }
951   row_data = id_index->array->data + mx * ARRAY_ROW_SIZE (id_index);
953   /* if exact then ignore flags (?) */
954   if (method != GST_INDEX_LOOKUP_EXACT)
955     while ((GINT32_FROM_BE (ARRAY_ROW_FLAGS (row_data)) & flags) != flags) {
956       if (method == GST_INDEX_LOOKUP_BEFORE)
957         mx -= 1;
958       else if (method == GST_INDEX_LOOKUP_AFTER)
959         mx += 1;
960       if (mx < 0 || mx >= id_index->array->len)
961         return NULL;
962       row_data = id_index->array->data + mx * ARRAY_ROW_SIZE (id_index);
963     }
965   /* entry memory management needs improvement FIXME */
966   if (!fileindex->ret_entry)
967     fileindex->ret_entry = g_new0 (GstIndexEntry, 1);
968   entry = fileindex->ret_entry;
969   if (entry->data.assoc.assocs) {
970     g_free (entry->data.assoc.assocs);
971     entry->data.assoc.assocs = NULL;
972   }
974   entry->type = GST_INDEX_ENTRY_ASSOCIATION;
976   GST_INDEX_NASSOCS (entry) = id_index->nformats;
977   entry->data.assoc.assocs =
978     g_new (GstIndexAssociation, id_index->nformats);
980   {
981     gint32 flags_be = ARRAY_ROW_FLAGS (row_data);
982     GST_INDEX_ASSOC_FLAGS (entry) = GINT32_FROM_BE (flags_be);
984     for (xx=0; xx < id_index->nformats; xx++) 
985       {
986         gint64 val_be = ARRAY_ROW_VALUE (row_data, xx);
987         GST_INDEX_ASSOC_FORMAT (entry, xx) = id_index->format[xx];
988         GST_INDEX_ASSOC_VALUE (entry, xx) = GINT64_FROM_BE (val_be);
989       }
990   }
992   return entry;
995 gboolean
996 gst_file_index_plugin_init (GstPlugin *plugin)
998   GstIndexFactory *factory;
1000   GST_DEBUG_CATEGORY_INIT(DC, "GST_FILEINDEX", 0, NULL);
1002   factory = gst_index_factory_new ("fileindex",
1003                                    "A index that stores entries in file",
1004                                    gst_file_index_get_type());
1006   if (factory != NULL) {
1007     gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
1008   }
1009   else {
1010     GST_CAT_ERROR (DC, "could not register fileindex");
1011   }
1012   return TRUE;