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