upgrades to the pixbuf loader:
authorBenjamin Otte <otte@gnome.org>
Mon, 24 Nov 2003 22:18:07 +0000 (22:18 +0000)
committerBenjamin Otte <otte@gnome.org>
Mon, 24 Nov 2003 22:18:07 +0000 (22:18 +0000)
Original commit message from CVS:
upgrades to the pixbuf loader:
- use /proc/self/fd/ to get our own handles to the given files (thanks for the hint David)
- use seeking when we lag too far behind
- try to seek 2 minutes into movies to get the static picture

ext/gdk_pixbuf/Makefile.am
ext/gdk_pixbuf/gst_loader.c
ext/gdk_pixbuf/gstgdkanimation.c
ext/gdk_pixbuf/gstgdkanimation.h

index 82e0d3e..3e0b117 100644 (file)
@@ -22,8 +22,8 @@ gst_loader_headers = gstgdkanimation.h
 
 install-data-hook:
        if test -z "$(DESTDIR)"  ; then \
-         $(mkinstalldirs) $(DESTDIR)$(GDK_PIXBUF_SYSCONFDIR)/gtk-2.0 ; \
-         $(QUERYLOADERS) > $(DESTDIR)$(GDK_PIXBUF_SYSCONFDIR)/gtk-2.0/gdk-pixbuf.loaders ; \
+         $(mkinstalldirs) $(DESTDIR)$(GDK_PIXBUF_CONFDIR) ; \
+         $(QUERYLOADERS) > $(DESTDIR)$(GDK_PIXBUF_CONFDIR)/gdk-pixbuf.loaders ; \
        fi
 
 else
index b8d1c9d..aad4df2 100644 (file)
 #include "gstgdkanimation.h"
 #include <gst/gstinfo.h>
 #include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 
 typedef struct {
   /* stuff gdk throws at us and we're supposed to keep */
@@ -87,6 +91,13 @@ gst_loader_begin_load (GdkPixbufModuleSizeFunc size_func, GdkPixbufModulePrepare
     g_free (context);
     return NULL;
   }
+  context->ani->temp_fd = g_file_open_tmp (NULL, &context->ani->temp_location, error);
+  if (context->ani->temp_fd == 0) {
+    g_object_unref (context->ani);
+    g_free (context);
+    return NULL;
+  }
+
   GST_LOG_OBJECT (context->ani, "begin loading");
   return context;
 }
@@ -137,10 +148,9 @@ gst_loader_stop_load (gpointer context_pointer, GError **error)
 static GdkPixbufAnimation *
 gst_loader_load_animation (FILE *f, GError **error)
 {
-  guchar data[4096];
-  guint size;
-  GdkPixbufAnimationIter *iter;
+  gchar *filename;
   GstGdkAnimation *ani;
+  GdkPixbufAnimationIter *iter = NULL;
   
   if (!gst_loader_init (error))
     return NULL;
@@ -150,23 +160,20 @@ gst_loader_load_animation (FILE *f, GError **error)
   if (!ani)
     return NULL;
   
-  while ((size = fread (data, 1, 4096, f)) > 0) {
-    if (!gst_gdk_animation_add_data (ani, data, size)) {
-      g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
-                  "could not add more data to animation"); /* our errors suck ;) */
-      g_object_unref (ani);
-      GST_WARNING ("load_animation failed");
-      return NULL;
-    }
+  filename = g_strdup_printf ("/proc/self/fd/%d", fileno (f));
+  ani->temp_fd = open (filename, 0);
+  if (ani->temp_fd >= 0) {
+    iter = gdk_pixbuf_animation_get_iter (GDK_PIXBUF_ANIMATION (ani), NULL);
+  } else {
+    GST_DEBUG ("open (\"%s\", 0) failed", filename);
   }
-  gst_gdk_animation_done_adding (ani);
-  iter = gdk_pixbuf_animation_get_iter (GDK_PIXBUF_ANIMATION (ani), NULL);
+  g_free (filename);
   if (iter == NULL) {
-      g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                  "could not create an image");
-      g_object_unref (ani);
-      GST_INFO ("could not create an image");
-      return NULL; 
+    g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                "could not create an image");
+    g_object_unref (ani);
+    GST_INFO ("could not create an image");
+    return NULL; 
   }
   g_object_unref (iter);
   GST_LOG_OBJECT (ani, "load_animation succeeded");
@@ -184,13 +191,22 @@ void
 fill_info (GdkPixbufFormat *info)
 {
   static GdkPixbufModulePattern signature[] = {
+    /* AVI */
     { "RIFF    AVI ", "    xxxx    ", 100 },
+    /* MPEG 1 */
     { "xx\001\272", "zz  ", 100 },
+    /* Quicktime */
+    { "    wide", "xxxx    ", 80 },
+    { "    moov", "xxxx    ", 80 },
+    { "    mdat", "xxxx    ", 80 },
+    { "    pnot", "xxxx    ", 80 },
+    { "    PICT", "xxxx    ", 80 },
+    { "    free", "xxxx    ", 80 },
     { NULL, NULL, 0 }
   };
   
   static gchar *mime_types[] = {
-    "video/avi",
+    "video/avi", "video/x-avi",
     "video/mpeg",
     NULL
   };
@@ -198,6 +214,7 @@ fill_info (GdkPixbufFormat *info)
   static gchar *extensions[] = {
     "avi",
     "mpeg", "mpe", "mpg",
+    "mov",
     NULL
   };
   
index 9e1aa16..d8e5cfa 100644 (file)
@@ -105,12 +105,6 @@ gst_gdk_animation_new (GError **error)
 {
   GstGdkAnimation *ani = GST_GDK_ANIMATION (g_object_new (GST_TYPE_GDK_ANIMATION, NULL));
 
-  ani->temp_fd = g_file_open_tmp (NULL, &ani->temp_location, error);
-  if (ani->temp_fd == 0) {
-    g_object_unref (ani);
-    return NULL;
-  }
-
   return ani;
 }
 gboolean
@@ -134,7 +128,38 @@ static GdkPixbuf*
 gst_gdk_animation_get_static_image (GdkPixbufAnimation *animation)
 {
   GstGdkAnimation *ani = GST_GDK_ANIMATION (animation);
+  GTimeVal tv;
 
+  if (!ani->pixbuf) {
+    GST_LOG_OBJECT (ani, "trying to create pixbuf");
+    g_get_current_time (&tv);
+    GdkPixbufAnimationIter *iter = gdk_pixbuf_animation_get_iter (animation, &tv);
+    if (iter) {
+      guint64 length;
+      GstFormat time = GST_FORMAT_TIME;
+
+      if (!gst_element_query (gst_bin_get_by_name (GST_BIN (
+                       GST_GDK_ANIMATION_ITER (iter)->pipeline), "sink"),
+                       GST_QUERY_TOTAL, &time, &length)) {
+       length = 0;
+      }
+      if (length > 120 * GST_SECOND) {
+       length = 120 * GST_SECOND;
+      } else if (length < 120 * GST_SECOND && length >= 10 * GST_SECOND) {
+       length = length / 2;
+      }
+      g_assert (time == GST_FORMAT_TIME);
+      if (length > 0)
+       g_time_val_add (&tv, length * 1000 / GST_SECOND);
+      GST_LOG_OBJECT (ani, "using time offset %"G_GUINT64_FORMAT" for creating static image",
+                     length);
+      gdk_pixbuf_animation_iter_advance (GDK_PIXBUF_ANIMATION_ITER (iter), &tv);
+      ani->pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (iter);
+      g_object_ref (ani->pixbuf);
+    } else {
+      GST_DEBUG_OBJECT (ani, "Could not get an iterator. No pixbuf available");
+    }
+  }
   return ani->pixbuf;
 }
 
@@ -213,6 +238,8 @@ gst_gdk_animation_iter_init (GTypeInstance *instance, gpointer g_class)
 
   iter->buffers = g_queue_new ();
   iter->eos = FALSE;
+  /* workaround for loads of demuxers that don't handle seeks before initializing... */
+  iter->just_seeked = TRUE;
 }
 static void
 gst_gdk_animation_iter_finalize (GObject *object)
@@ -238,16 +265,16 @@ gst_gdk_animation_iter_finalize (GObject *object)
   G_OBJECT_CLASS (iter_parent_class)->finalize (object);
 }
 static void
-got_handoff (GstElement *fakesink, GstBuffer *buffer, GstGdkAnimationIter *iter)
+got_handoff (GstElement *fakesink, GstBuffer *buffer, GstPad *pad, GstGdkAnimationIter *iter)
 {
-  GST_LOG_OBJECT (iter, "enqueing buffer %p", buffer);
+  GST_LOG_OBJECT (iter, "enqueing buffer %p (timestamp %"G_GUINT64_FORMAT")", 
+         buffer, GST_BUFFER_TIMESTAMP (buffer));
   gst_data_ref (GST_DATA (buffer));
   g_queue_push_tail (iter->buffers, buffer);
 }
-static GstElement *
+static gboolean
 gst_gdk_animation_iter_create_pipeline (GstGdkAnimationIter *iter)
 {
-  GstElement *ret;
   GstElement *src, *autoplugger, *sink, *colorspace;
   GstCaps *caps = GST_CAPS_NEW ("pixbuf_filter",
                                "video/x-raw-rgb", 
@@ -256,28 +283,45 @@ gst_gdk_animation_iter_create_pipeline (GstGdkAnimationIter *iter)
                                                        GST_PROPS_INT (32),
                                                        GST_PROPS_INT (24)
                                                ),
-                                 "red_mask",   GST_PROPS_INT (0x000000FF),
+                                 "red_mask",   GST_PROPS_INT (0x00FF0000),
                                  "green_mask", GST_PROPS_INT (0x0000FF00),
-                                 "blue_mask",  GST_PROPS_INT (0x00FF0000)
+                                 "blue_mask",  GST_PROPS_INT (0x000000FF)
                                );
 
-  ret = gst_element_factory_make ("pipeline", "main_pipeline");
-  if (!ret) return NULL;
+  iter->pipeline = gst_element_factory_make ("pipeline", "main_pipeline");
+  if (iter->pipeline == NULL) return FALSE;
 
   if (!(src = gst_element_factory_make ("filesrc", "source")))
     goto error;
-  gst_bin_add (GST_BIN (ret), src);
-  g_object_set (src, "location", iter->ani->temp_location, NULL);
+  gst_bin_add (GST_BIN (iter->pipeline), src);
+  if (iter->ani->temp_location) {
+    g_object_set (src, "location", iter->ani->temp_location, NULL);
+    GST_INFO_OBJECT (iter, "using file '%s'", iter->ani->temp_location);
+  } else {
+    gchar *filename = g_strdup_printf ("/proc/self/fd/%d", iter->ani->temp_fd);
+    g_object_set (src, "location", filename, NULL);
+    GST_INFO_OBJECT (iter, "using file '%s'", filename);
+    g_free (filename);
+  }
 
   if (!(autoplugger = gst_element_factory_make ("spider", "autoplugger")))
     goto error;
-  gst_bin_add (GST_BIN (ret), autoplugger);
+  gst_bin_add (GST_BIN (iter->pipeline), autoplugger);
   if (!gst_element_link (src, autoplugger))
     goto error;
+
+  /* add ffcolorspace if available so we get svq1, too */
+  if ((colorspace = gst_element_factory_make ("ffcolorspace", "ffcolorspace"))) {
+    gst_bin_add (GST_BIN (iter->pipeline), colorspace);
+    if (!gst_element_link (autoplugger, colorspace))
+      goto error;
+    autoplugger = colorspace;
+  }
+
   
   if (!(colorspace = gst_element_factory_make ("colorspace", "colorspace")))
     goto error;
-  gst_bin_add (GST_BIN (ret), colorspace);
+  gst_bin_add (GST_BIN (iter->pipeline), colorspace);
   if (!gst_element_link (autoplugger, colorspace))
     goto error;
   
@@ -285,16 +329,17 @@ gst_gdk_animation_iter_create_pipeline (GstGdkAnimationIter *iter)
     goto error;
   g_object_set (sink, "signal-handoffs", TRUE, NULL);
   g_signal_connect (sink, "handoff", (GCallback) got_handoff, iter);
-  gst_bin_add (GST_BIN (ret), sink);
+  gst_bin_add (GST_BIN (iter->pipeline), sink);
   if (!gst_element_link_filtered (colorspace, sink, caps))
     goto error;
-  if (gst_element_set_state (ret, GST_STATE_PLAYING) != GST_STATE_SUCCESS)
+  if (gst_element_set_state (iter->pipeline, GST_STATE_PLAYING) != GST_STATE_SUCCESS)
     goto error;
   
-  return ret;
+  return TRUE;
 error:
-  g_object_unref (ret);
-  return NULL;
+  g_object_unref (iter->pipeline);
+  iter->pipeline = NULL;
+  return FALSE;
 }
 static gboolean
 gst_gdk_animation_iter_may_advance (GstGdkAnimationIter *iter)
@@ -303,7 +348,7 @@ gst_gdk_animation_iter_may_advance (GstGdkAnimationIter *iter)
   gint64 offset;
   gint64 data_amount;
   
-  if (iter->ani->temp_fd == 0)
+  if (iter->ani->temp_fd == 0 || iter->ani->temp_location == NULL)
     return TRUE;
 
   data_amount = lseek (iter->ani->temp_fd, 0, SEEK_CUR);
@@ -389,29 +434,25 @@ gst_gdk_animation_get_iter (GdkPixbufAnimation *anim, const GTimeVal *start_time
   GstGdkAnimation *ani = GST_GDK_ANIMATION (anim);
   GstGdkAnimationIter *iter;
 
-  if (ani->temp_fd != 0 && lseek (ani->temp_fd, 0, SEEK_CUR) < GST_GDK_BUFFER_SIZE)
+  if (ani->temp_fd != 0 && ani->temp_location != NULL && 
+      lseek (ani->temp_fd, 0, SEEK_CUR) < GST_GDK_BUFFER_SIZE) {
+    GST_DEBUG_OBJECT (ani, "Not enough data to create iterator.");
     return NULL;
-
+  }
+  
   iter = g_object_new (GST_TYPE_GDK_ANIMATION_ITER, NULL);
 
   iter->start = *start_time;
   
   iter->ani = ani;
   g_object_ref (ani);
-  iter->pipeline = gst_gdk_animation_iter_create_pipeline (iter);
-  if (iter->pipeline == NULL) 
+  if (!gst_gdk_animation_iter_create_pipeline (iter))
     goto error;
-    
         
   if (!gst_gdk_animation_get_more_buffers (iter))
     goto error;
   
   gst_gdk_animation_iter_create_pixbuf (iter);
-  if (!ani->pixbuf) {
-    /* set our static image */
-    g_object_ref (iter->pixbuf);
-    ani->pixbuf = iter->pixbuf;
-  }
         
   return GDK_PIXBUF_ANIMATION_ITER (iter);
 
@@ -435,6 +476,23 @@ gst_gdk_animation_iter_advance (GdkPixbufAnimationIter *anim_iter, const GTimeVa
   } else {
     offset += ((GstClockTime) current_time->tv_usec - iter->start.tv_usec) * GST_SECOND / G_USEC_PER_SEC;
   }
+  if (!iter->just_seeked &&
+      offset - iter->last_timestamp > GST_GDK_MAX_DELAY_TO_SEEK) {
+    GST_INFO_OBJECT (iter, "current pipeline timestamp is too old (%"G_GUINT64_FORMAT
+                    " vs %"G_GUINT64_FORMAT"), seeking", iter->last_timestamp, offset);
+    if (gst_element_send_event (gst_bin_get_by_name (GST_BIN (iter->pipeline), "sink"),
+               gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET |
+                                   GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, 
+                       offset - GST_SECOND / 10))) {
+       iter->last_timestamp = offset - GST_SECOND / 10;
+       iter->just_seeked = TRUE;
+      } else {
+       GST_WARNING_OBJECT (iter, "seek to %"G_GUINT64_FORMAT" didn't work. Iterating there...", 
+                           offset);
+      }
+  } else if (iter->just_seeked) {
+    iter->just_seeked = FALSE;
+  }
   
   while (TRUE) {
     if (g_queue_is_empty (iter->buffers)) {
@@ -447,7 +505,8 @@ gst_gdk_animation_iter_advance (GdkPixbufAnimationIter *anim_iter, const GTimeVa
     if (GST_BUFFER_TIMESTAMP (g_queue_peek_head (iter->buffers)) > offset)
       break;
     if (buffer) {
-      GST_LOG_OBJECT (iter, "unreffing buffer %p, because timestamp too low (%"G_GUINT64_FORMAT" vs %"G_GUINT64_FORMAT")",
+      GST_LOG_OBJECT (iter, "unreffing buffer %p, because timestamp too low (%"
+                     G_GUINT64_FORMAT" vs %"G_GUINT64_FORMAT")",
                      buffer, GST_BUFFER_TIMESTAMP (buffer), offset);
       gst_data_unref (GST_DATA (buffer));
     }
@@ -455,6 +514,11 @@ gst_gdk_animation_iter_advance (GdkPixbufAnimationIter *anim_iter, const GTimeVa
   }
   if (!buffer)
     return FALSE;
+  if (GST_BUFFER_TIMESTAMP (buffer) < iter->last_timestamp) {
+    gst_data_unref (GST_DATA (buffer));
+    iter->last_timestamp = offset;
+    return FALSE;
+  }
   iter->last_timestamp = GST_BUFFER_TIMESTAMP (buffer);
   g_queue_push_head (iter->buffers, buffer);
   gst_gdk_animation_iter_create_pixbuf (iter);
index 1ac4867..183d335 100644 (file)
@@ -29,7 +29,9 @@
 G_BEGIN_DECLS
 
 /* how many bytes we need to have available before we dare to start a new iteration */
-#define GST_GDK_BUFFER_SIZE 102400
+#define GST_GDK_BUFFER_SIZE (102400)
+/* how far behind we need to be before we attempt to seek */
+#define GST_GDK_MAX_DELAY_TO_SEEK (GST_SECOND / 4)
 
 
 #define GST_TYPE_GDK_ANIMATION                 (gst_gdk_animation_get_type())
@@ -97,6 +99,7 @@ struct _GstGdkAnimationIter {
   /* pipeline we're using */
   GstElement *                 pipeline;
   gboolean                     eos;
+  gboolean                     just_seeked;
   
   /* current image and the buffers containing the data */
   GdkPixbuf *                  pixbuf;