add initial version of gdkpixbuf loader for gtk that is capable of loading AVI and...
authorBenjamin Otte <otte@gnome.org>
Wed, 5 Nov 2003 03:24:54 +0000 (03:24 +0000)
committerBenjamin Otte <otte@gnome.org>
Wed, 5 Nov 2003 03:24:54 +0000 (03:24 +0000)
Original commit message from CVS:
add initial version of gdkpixbuf loader for gtk that is capable of loading AVI and mpeg videos as GdkPixbufAnimation. I'm not sure if such a thing would be useful or too much trouble, so I'll throw it at enough testers to figure it out ;) We might want to disable it by defualt though in the future. (Currently there is not even a configure switch implemented to disable it.)
This includes a fix to not use GError in gstgdkpixbuf's typefind function and to only return GST_TYPE_FIND_MINIMUM when doing typefinding via gdk as this breaks quite a bit with the GStreamer loader installed.

configure.ac
ext/gdk_pixbuf/Makefile.am
ext/gdk_pixbuf/gst_loader.c [new file with mode: 0644]
ext/gdk_pixbuf/gstgdkanimation.c [new file with mode: 0644]
ext/gdk_pixbuf/gstgdkanimation.h [new file with mode: 0644]
ext/gdk_pixbuf/gstgdkpixbuf.c

index 30dcfc0..ccdf32d 100644 (file)
@@ -225,6 +225,11 @@ HAVE_GTK=NO
 PKG_CHECK_MODULES(GTK2, gtk+-2.0 >= 2.2.0, HAVE_GTK_22=yes, HAVE_GTK_22=no)
 if test "x$HAVE_GTK_22" = "xyes"; then
   HAVE_GTK=yes
+  AC_PATH_PROG(QUERYLOADERS, gdk-pixbuf-query-loaders, no)
+  GTK_VERSION=`$PKG_CONFIG --variable=gtk_binary_version gtk+-2.0`
+  AC_SUBST(GTK_VERSION)
+  GTK_BASE_DIR=`echo "$QUERYLOADERS" | sed "s/bin\/gdk-pixbuf-query-loaders\$//"`
+  AC_SUBST(GTK_BASE_DIR)
 else
   PKG_CHECK_MODULES(GTK2, gtk+-2.0, HAVE_GTK_20=yes, HAVE_GTK_20=no)
 fi
@@ -236,6 +241,7 @@ GTK_LIBS=$GTK2_LIBS
 AC_SUBST(GTK_LIBS)
 AC_SUBST(GTK_CFLAGS)
 AC_SUBST(HAVE_GTK)
+AM_CONDITIONAL(HAVE_GDK_LOADERS, test "x$HAVE_GTK_22" = "xyes")
 
 
 dnl Check for X11 extensions
index 32e0258..0e2d82c 100644 (file)
@@ -6,5 +6,28 @@ libgstgdkpixbuf_la_CFLAGS = $(GST_CFLAGS) $(GTK_CFLAGS)
 libgstgdkpixbuf_la_LIBADD = $(GTK_LIBS)
 libgstgdkpixbuf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
-noinst_HEADERS = gstgdkpixbuf.h
 
+if HAVE_GDK_LOADERS
+loaderdir = $(GTK_BASE_DIR)/lib/gtk-2.0/$(GTK_VERSION)/loaders
+loader_LTLIBRARIES = gst_loader.la
+
+gst_loader_la_CFLAGS = $(GST_CFLAGS) $(GTK_CFLAGS) -DGDK_PIXBUF_ENABLE_BACKEND
+gst_loader_la_SOURCES =        \
+       gstgdkanimation.c       \
+       gst_loader.c
+gst_loader_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) --avoid-version --module
+gst_loader_la_LIBADD = $(GTK_LIBS) $(GST_LIBS)
+
+gst_loader_headers = gstgdkanimation.h
+
+install-data-hook:
+       $(mkinstalldirs) $(GTK_BASE_DIR)/etc/gtk-2.0 ; \
+       $(QUERYLOADERS) > $(GTK_BASE_DIR)/etc/gtk-2.0/gdk-pixbuf.loaders ;
+
+else
+gst_loader_headers =
+endif
+
+
+noinst_HEADERS = gstgdkpixbuf.h \
+       $(gst_loader_headers)
diff --git a/ext/gdk_pixbuf/gst_loader.c b/ext/gdk_pixbuf/gst_loader.c
new file mode 100644 (file)
index 0000000..3eb859e
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gst_loader.c: Load GStreamer videos as gdkpixbufs
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstgdkanimation.h"
+#include <stdio.h>
+
+static GdkPixbufAnimation *
+gst_loader_load_animation (FILE *f, GError **error)
+{
+  return gst_gdk_animation_new_from_file (f, error);
+}
+void
+fill_vtable (GdkPixbufModule *module)
+{
+  if (gst_init_check (0, NULL)) {
+    module->load_animation = gst_loader_load_animation;
+  }
+}
+void
+fill_info (GdkPixbufFormat *info)
+{
+  static GdkPixbufModulePattern signature[] = {
+    { "RIFF    AVI ", "    xxxx    ", 100 },
+    { "\000\000\001\272", "", 100 },
+    { NULL, NULL, 0 }
+  };
+  
+  static gchar *mime_types[] = {
+    "video/avi",
+    "video/mpeg",
+    NULL
+  };
+
+  static gchar *extensions[] = {
+    "avi",
+    "mpeg", "mpe", "mpg",
+    NULL
+  };
+  
+  info->name        = "GStreamer";
+  info->signature   = signature;
+  info->description = "GStreamer supported video";
+  info->mime_types  = mime_types;
+  info->extensions  = extensions;
+  info->flags       = 0;
+}
diff --git a/ext/gdk_pixbuf/gstgdkanimation.c b/ext/gdk_pixbuf/gstgdkanimation.c
new file mode 100644 (file)
index 0000000..6519cc1
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gstgdkanimation.h"
+#include <unistd.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_gdk_animation_debug);
+#define GST_CAT_DEFAULT gst_gdk_animation_debug
+
+static void            gst_gdk_animation_class_init            (gpointer               g_class,
+                                                                gpointer               class_data);
+static void            gst_gdk_animation_finalize              (GObject *              object);
+
+static gboolean         gst_gdk_animation_is_static_image      (GdkPixbufAnimation *   animation);
+static GdkPixbuf*       gst_gdk_animation_get_static_image     (GdkPixbufAnimation *   animation);
+static void             gst_gdk_animation_get_size             (GdkPixbufAnimation *   anim,
+                                                                gint *                 width,
+                                                                gint *                 height);
+static GdkPixbufAnimationIter* gst_gdk_animation_get_iter      (GdkPixbufAnimation *   anim,
+                                                                const GTimeVal *       start_time);
+
+
+static gpointer parent_class;
+
+GType
+gst_gdk_animation_get_type (void)
+{
+  static GType object_type = 0;
+
+  if (!object_type) {
+    static const GTypeInfo object_info = {
+      sizeof (GstGdkAnimationClass),
+      NULL,
+      NULL,
+      gst_gdk_animation_class_init,
+      NULL,           /* class_finalize */
+      NULL,           /* class_data */
+      sizeof (GstGdkAnimation),
+      0,              /* n_preallocs */
+      NULL,
+    };
+                
+    object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION,
+           "GstGdkAnimation", &object_info, 0);
+
+    GST_DEBUG_CATEGORY_INIT (gst_gdk_animation_debug, "gstloader_animation", 0, "GStreamer GdkPixbuf loader - GdkAnimation class");
+  }
+        
+  return object_type;
+}
+static void
+gst_gdk_animation_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (g_class);
+  GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (g_class);
+        
+  parent_class = g_type_class_peek_parent (g_class);
+        
+  object_class->finalize = gst_gdk_animation_finalize;
+
+  anim_class->is_static_image = gst_gdk_animation_is_static_image;
+  anim_class->get_static_image = gst_gdk_animation_get_static_image;
+  anim_class->get_size = gst_gdk_animation_get_size;
+  anim_class->get_iter = gst_gdk_animation_get_iter;
+}
+static void
+gst_gdk_animation_finalize (GObject *object)
+{
+  GstGdkAnimation *ani = GST_GDK_ANIMATION (object);
+
+  if (ani->temp_fd) {
+    close (ani->temp_fd);
+  }
+  if (ani->temp_location) {
+    remove (ani->temp_location);
+    g_free (ani->temp_location);
+  }
+        
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+GdkPixbufAnimation *
+gst_gdk_animation_new_from_file (FILE *f, GError **error)
+{
+  GdkPixbufAnimationIter *iter;
+  GTimeVal tv;
+  int fd2;  
+  guint8 data[4096];
+  guint size;
+  int fd = fileno (f);
+  GstGdkAnimation *ani = GST_GDK_ANIMATION (g_object_new (GST_TYPE_GDK_ANIMATION, NULL));
+
+  fd2 = g_file_open_tmp (NULL, &ani->temp_location, error);
+  if (fd2 == -1)
+    return NULL;
+  while ((size = read (fd, data, 4096)) > 0) {
+    if (write (fd2, data, size) != size)
+      break;
+  }
+  close (fd2);
+  
+  /* we need some info that is only provided by iters */
+  g_get_current_time (&tv);
+  iter = gst_gdk_animation_get_iter (GDK_PIXBUF_ANIMATION (ani), &tv);
+  g_object_unref (iter);
+  
+  return GDK_PIXBUF_ANIMATION (ani);
+}
+static gboolean
+gst_gdk_animation_is_static_image (GdkPixbufAnimation *animation)
+{
+  return FALSE;
+}
+
+static GdkPixbuf*
+gst_gdk_animation_get_static_image (GdkPixbufAnimation *animation)
+{
+  return NULL;
+}
+
+static void
+gst_gdk_animation_get_size (GdkPixbufAnimation *anim, gint *width, int *height)
+{
+  GstGdkAnimation *ani = GST_GDK_ANIMATION (anim);
+
+  GST_LOG_OBJECT (ani, "get_size called (%p, %p) %d x %d", width, height, ani->width, ani->height);
+  if (width)
+    *width = ani->width;
+
+  if (height)
+    *height = ani->height;
+}
+
+
+static void            gst_gdk_animation_iter_class_init               (gpointer                       g_class,
+                                                                        gpointer                       class_data);
+static void            gst_gdk_animation_iter_init                     (GTypeInstance *                instance, 
+                                                                        gpointer                       g_class);
+static void            gst_gdk_animation_iter_finalize                 (GObject *                      object);
+
+static gint            gst_gdk_animation_iter_get_delay_time           (GdkPixbufAnimationIter *       iter);
+static GdkPixbuf *     gst_gdk_animation_iter_get_pixbuf               (GdkPixbufAnimationIter *       iter);
+static gboolean                gst_gdk_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *     iter);
+static gboolean                gst_gdk_animation_iter_advance                  (GdkPixbufAnimationIter *       iter,
+                                                                        const GTimeVal *               current_time);
+
+static gpointer iter_parent_class;
+
+GType
+gst_gdk_animation_iter_get_type (void)
+{
+  static GType object_type = 0;
+
+  if (!object_type) {
+    static const GTypeInfo object_info = {
+      sizeof (GstGdkAnimationIterClass),
+      NULL,
+      NULL,
+      gst_gdk_animation_iter_class_init,
+      NULL,           /* class_finalize */
+      NULL,           /* class_data */
+      sizeof (GstGdkAnimationIter),
+      0,              /* n_preallocs */
+      gst_gdk_animation_iter_init,
+    };
+    
+    object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER,
+           "GdkPixbufAniAnimIter", &object_info, 0);
+  }
+  
+  return object_type;
+}
+
+static void
+gst_gdk_animation_iter_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (g_class);
+  GdkPixbufAnimationIterClass *anim_iter_class = GDK_PIXBUF_ANIMATION_ITER_CLASS (g_class);
+  
+  iter_parent_class = g_type_class_peek_parent (g_class);
+  
+  object_class->finalize = gst_gdk_animation_iter_finalize;
+       
+  anim_iter_class->get_delay_time = gst_gdk_animation_iter_get_delay_time;
+  anim_iter_class->get_pixbuf = gst_gdk_animation_iter_get_pixbuf;
+  anim_iter_class->on_currently_loading_frame = gst_gdk_animation_iter_on_currently_loading_frame;
+  anim_iter_class->advance = gst_gdk_animation_iter_advance;
+}
+static void
+gst_gdk_animation_iter_init (GTypeInstance *instance, gpointer g_class)
+{
+  GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (instance);
+
+  iter->buffers = g_queue_new ();
+  iter->eos = FALSE;
+}
+static void
+gst_gdk_animation_iter_finalize (GObject *object)
+{
+  GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (object);
+  
+  g_object_unref (iter->ani);
+  
+  if (iter->pipeline)
+    g_object_unref (iter->pipeline);
+  if (iter->pixbuf)
+    g_object_unref (iter->pixbuf);
+  while (iter->buffers) {
+    GstBuffer *buffer = GST_BUFFER (g_queue_pop_head (iter->buffers));
+    if (buffer) {
+      GST_LOG_OBJECT (iter, "unreffing buffer %p on finalize", buffer);
+      gst_data_unref (GST_DATA (buffer));
+    } else {
+      g_queue_free (iter->buffers);
+      iter->buffers = NULL;
+    }
+  }
+  G_OBJECT_CLASS (iter_parent_class)->finalize (object);
+}
+static void
+got_handoff (GstElement *fakesink, GstBuffer *buffer, GstGdkAnimationIter *iter)
+{
+  GST_LOG_OBJECT (iter, "enqueing buffer %p", buffer);
+  gst_data_ref (GST_DATA (buffer));
+  g_queue_push_tail (iter->buffers, buffer);
+}
+static GstElement *
+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", 
+                                 "endianness", GST_PROPS_INT (G_BIG_ENDIAN),
+                                 "bpp",        GST_PROPS_LIST (
+                                                       GST_PROPS_INT (32),
+                                                       GST_PROPS_INT (24)
+                                               ),
+                                 "red_mask",   GST_PROPS_INT (0x000000FF),
+                                 "green_mask", GST_PROPS_INT (0x0000FF00),
+                                 "blue_mask",  GST_PROPS_INT (0x00FF0000)
+                               );
+
+  ret = gst_element_factory_make ("pipeline", "main_pipeline");
+  if (!ret) return NULL;
+
+  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);
+
+  if (!(autoplugger = gst_element_factory_make ("spider", "autoplugger")))
+    goto error;
+  gst_bin_add (GST_BIN (ret), autoplugger);
+  gst_element_link (src, autoplugger);
+  
+  if (!(colorspace = gst_element_factory_make ("colorspace", "colorspace")))
+    goto error;
+  gst_bin_add (GST_BIN (ret), colorspace);
+  gst_element_link (autoplugger, colorspace);
+  
+  if (!(sink = gst_element_factory_make ("fakesink", "sink")))
+    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_element_link_filtered (colorspace, sink, caps);
+  gst_element_set_state (ret, GST_STATE_PLAYING);
+  
+  return ret;
+error:
+  g_object_unref (ret);
+  return NULL;
+}
+static gboolean
+gst_gdk_animation_iter_may_advance (GstGdkAnimationIter *iter)
+{
+  GstFormat bytes = GST_FORMAT_BYTES;
+  gint64 offset;
+  gint64 data_amount;
+  
+  if (iter->ani->temp_fd == 0)
+    return TRUE;
+
+  data_amount = lseek (iter->ani->temp_fd, 0, SEEK_CUR);
+  g_assert (data_amount >= 0);
+  g_assert (gst_element_query (gst_bin_get_by_name (GST_BIN (iter->pipeline), "source"),
+                        GST_QUERY_POSITION, &bytes, &offset));
+  if (data_amount - offset > GST_GDK_BUFFER_SIZE) /* random number */
+    return TRUE;
+
+  return FALSE;
+}
+static gboolean
+gst_gdk_animation_get_more_buffers (GstGdkAnimationIter *iter)
+{
+  GstBuffer *last = g_queue_peek_tail (iter->buffers);
+  
+  do {
+    GST_LOG_OBJECT (iter, "iterating...");
+    if (!gst_gdk_animation_iter_may_advance (iter))
+      return FALSE;
+    if (!gst_bin_iterate (GST_BIN (iter->pipeline))) {
+      GST_LOG_OBJECT (iter, "iterating done, setting EOS");
+      iter->eos = TRUE;
+      break;
+    }
+  } while (last == g_queue_peek_tail (iter->buffers));
+  return TRUE;
+}
+static void
+pixbuf_destroy_notify (guchar *pixels, gpointer data)
+{
+  GST_LOG ("unreffing buffer %p because pixbuf was destroyed", data);
+  gst_data_unref (GST_DATA (data));
+}
+static void
+gst_gdk_animation_iter_create_pixbuf (GstGdkAnimationIter *iter)
+{
+  GstBuffer *buf;
+  GstGdkAnimation *ani = iter->ani;
+  
+  buf = g_queue_pop_head (iter->buffers);
+  g_assert (buf);
+  if (iter->pixbuf) {
+    GST_LOG_OBJECT (iter, "unreffing pixbuf %p", iter->pixbuf);
+    g_object_unref (iter->pixbuf);
+  }
+  if (ani->width == 0) {
+    GstPad *pad;
+    GstCaps *caps;
+    GstElement *fakesink = gst_bin_get_by_name (GST_BIN (iter->pipeline), "sink");
+    g_assert (fakesink);
+    pad = gst_element_get_pad (fakesink, "sink");
+    g_assert (pad);
+    caps = gst_pad_get_caps (pad);
+    g_assert (caps);
+    g_assert (GST_CAPS_IS_FIXED (caps));
+    g_assert (gst_caps_has_fixed_property (caps, "bpp") &&
+             gst_caps_has_fixed_property (caps, "width") &&
+             gst_caps_has_fixed_property (caps, "height"));
+    gst_caps_get_int (caps, "width", &ani->width);
+    gst_caps_get_int (caps, "height", &ani->height);
+    gst_caps_get_int (caps, "bpp", &ani->bpp);
+    GST_DEBUG_OBJECT (ani, "found format (width %d, height %d, bpp %d)", ani->width, ani->height, ani->bpp);
+  }
+  g_assert (GST_BUFFER_SIZE (buf) == ani->width * ani->height * ani->bpp / 8);
+  if (ani->bpp == 32) {
+    gint i;
+    guint32 *data = (guint32 *) GST_BUFFER_DATA (buf);
+    /* ensure opacity */
+    for (i = 0; i < ani->width * ani->height; i++) {
+      data[i] |= 0xFF000000;
+    }
+  }
+  iter->pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buf),
+         GDK_COLORSPACE_RGB, ani->bpp == 32, 8, ani->width, ani->height, ani->width * ani->bpp / 8, pixbuf_destroy_notify, buf);
+  GST_LOG_OBJECT (iter, "created pixbuf %p from buffer %p (refcount %d)", iter->pixbuf, buf, GST_DATA_REFCOUNT_VALUE (buf));
+}
+static GdkPixbufAnimationIter*
+gst_gdk_animation_get_iter (GdkPixbufAnimation *anim, const GTimeVal *start_time)
+{
+  GstGdkAnimationIter *iter;
+
+  iter = g_object_new (GST_TYPE_GDK_ANIMATION_ITER, NULL);
+
+  iter->start = *start_time;
+  
+  iter->ani = GST_GDK_ANIMATION (anim);
+  iter->pipeline = gst_gdk_animation_iter_create_pipeline (iter);
+  if (iter->pipeline == NULL) {
+    g_object_unref (iter);
+    return NULL;
+  }
+    
+  g_object_ref (iter->ani);
+        
+  gst_gdk_animation_get_more_buffers (iter);
+  gst_gdk_animation_iter_create_pixbuf (iter);
+        
+  return GDK_PIXBUF_ANIMATION_ITER (iter);
+}
+static gboolean
+gst_gdk_animation_iter_advance (GdkPixbufAnimationIter *anim_iter, const GTimeVal *current_time)
+{
+  GstClockTime offset;
+  GstBuffer *buffer = NULL;
+  GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
+
+  /* compute timestamp that next buffer must match */
+  GST_DEBUG_OBJECT (iter, "advancing to %ld:%ld (started at %ld:%ld)", 
+                   current_time->tv_sec, current_time->tv_usec, iter->start.tv_sec, iter->start.tv_usec);
+  offset = ((GstClockTime) current_time->tv_sec - iter->start.tv_sec) * GST_SECOND;
+  if (iter->start.tv_usec > current_time->tv_usec) {
+    offset -= ((GstClockTime) iter->start.tv_usec - current_time->tv_usec) * GST_SECOND / G_USEC_PER_SEC;
+  } else {
+    offset += ((GstClockTime) current_time->tv_usec - iter->start.tv_usec) * GST_SECOND / G_USEC_PER_SEC;
+  }
+  
+  while (TRUE) {
+    if (g_queue_is_empty (iter->buffers)) {
+      if (iter->eos)
+       return FALSE;
+      if (gst_gdk_animation_get_more_buffers (iter))
+       continue;
+      break;
+    }
+    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")",
+                     buffer, GST_BUFFER_TIMESTAMP (buffer), offset);
+      gst_data_unref (GST_DATA (buffer));
+    }
+    buffer = GST_BUFFER (g_queue_pop_head (iter->buffers));
+  }
+  if (!buffer)
+    return FALSE;
+  iter->last_timestamp = GST_BUFFER_TIMESTAMP (buffer);
+  g_queue_push_head (iter->buffers, buffer);
+  gst_gdk_animation_iter_create_pixbuf (iter);
+  return TRUE;
+}
+static gint
+gst_gdk_animation_iter_get_delay_time (GdkPixbufAnimationIter *anim_iter)
+{
+  gint delay;
+  GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
+  
+  while (g_queue_is_empty (iter->buffers)) {
+    if (iter->eos) {
+      GST_LOG_OBJECT (iter, "returning delay of infinite, we're EOS");
+      return -1;
+    }
+    if (!gst_gdk_animation_get_more_buffers (iter))
+      return -1; /* FIXME? */
+  }
+
+  delay = (GST_BUFFER_TIMESTAMP (g_queue_peek_head (iter->buffers)) - iter->last_timestamp) *
+          1000 / GST_SECOND;
+  GST_LOG_OBJECT (iter, "returning delay of %d ms", delay);
+  return delay;
+}
+GdkPixbuf*
+gst_gdk_animation_iter_get_pixbuf (GdkPixbufAnimationIter *anim_iter)
+{
+  GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
+  
+  GST_LOG_OBJECT (iter, "returning pixbuf %p", iter->pixbuf);
+  return iter->pixbuf;
+}
+static gboolean
+gst_gdk_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *anim_iter)
+{
+  GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
+  
+  /* EOS - last frame */
+  if (iter->eos && g_queue_is_empty (iter->buffers))
+    return TRUE;
+
+  /* can't load more frames */
+  if (!gst_gdk_animation_iter_may_advance (iter))
+    return FALSE;
+  
+  return TRUE;
+}
diff --git a/ext/gdk_pixbuf/gstgdkanimation.h b/ext/gdk_pixbuf/gstgdkanimation.h
new file mode 100644 (file)
index 0000000..de5ce05
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_LOADER_H__
+#define __GST_LOADER_H__
+
+#include <gst/gst.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf/gdk-pixbuf-animation.h>
+#include <gdk-pixbuf/gdk-pixbuf-io.h>
+#include <stdio.h>
+
+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_TYPE_GDK_ANIMATION                 (gst_gdk_animation_get_type())
+#define GST_GDK_ANIMATION(obj)                 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GDK_ANIMATION,GstGdkAnimation))
+#define GST_GDK_ANIMATION_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GDK_ANIMATION,GstGdkAnimation))
+#define GST_IS_GDK_ANIMATION(obj)              (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GDK_ANIMATION))
+#define GST_IS_GDK_ANIMATION_CLASS(obj)                (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GDK_ANIMATION))
+
+typedef struct _GstGdkAnimation      GstGdkAnimation;
+typedef struct _GstGdkAnimationClass GstGdkAnimationClass;
+
+typedef struct _GstGdkAnimationIter GstGdkAnimationIter;
+typedef struct _GstGdkAnimationIterClass GstGdkAnimationIterClass;
+
+struct _GstGdkAnimation
+{
+  GdkPixbufAnimation           parent;
+
+  /* name of temporary buffer file */
+  gchar *                      temp_location;
+  /* file descriptor to temporary file or 0 if we're done writing */
+  int                          temp_fd;
+  /* functions to notify the loader */
+
+
+  /* size of image */
+  gint                         width;
+  gint                         height;
+  gint                         bpp;
+};
+
+struct _GstGdkAnimationClass 
+{
+  GdkPixbufAnimationClass      parent_class;
+};
+
+GType gst_gdk_animation_get_type (void);
+
+GdkPixbufAnimation *gst_gdk_animation_new_from_file (FILE *f, GError **error);
+
+
+#define GST_TYPE_GDK_ANIMATION_ITER            (gst_gdk_animation_iter_get_type ())
+#define GST_GDK_ANIMATION_ITER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GST_TYPE_GDK_ANIMATION_ITER, GstGdkAnimationIter))
+#define GST_IS_GDK_ANIMATION_ITER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GST_TYPE_GDK_ANIMATION_ITER))
+
+#define GST_GDK_ANIMATION_ITER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GDK_ANIMATION_ITER, GstGdkAnimationIterClass))
+#define GST_IS_GDK_ANIMATION_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GDK_ANIMATION_ITER))
+#define GST_GDK_ANIMATION_ITER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_GDK_ANIMATION_ITER, GstGdkAnimationIterClass))
+
+struct _GstGdkAnimationIter {
+  GdkPixbufAnimationIter       parent;
+        
+  /* our animation */
+  GstGdkAnimation *            ani;
+  /* start timeval */
+  GTimeVal                     start;
+  /* timestamp of last buffer */
+  GstClockTime                 last_timestamp;
+  
+  /* pipeline we're using */
+  GstElement *                 pipeline;
+  gboolean                     eos;
+  
+  /* current image and the buffers containing the data */
+  GdkPixbuf *                  pixbuf;
+  GQueue *                     buffers;
+};
+
+struct _GstGdkAnimationIterClass {
+  GdkPixbufAnimationIterClass  parent_class;
+};
+
+GType gst_gdk_animation_iter_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GST_GDK_ANIMATION_H__ */
index c5b5903..e3c07ff 100644 (file)
@@ -382,7 +382,6 @@ gst_gdk_pixbuf_type_find (GstTypeFind *tf, gpointer ignore)
   guint8 *data;
   GdkPixbufLoader *pixbuf_loader;
   GdkPixbufFormat *format;
-  GError *error = NULL;
 
   data = gst_type_find_peek (tf, 0, GST_GDK_PIXBUF_TYPE_FIND_SIZE);
   if (data == NULL) return;
@@ -392,18 +391,18 @@ gst_gdk_pixbuf_type_find (GstTypeFind *tf, gpointer ignore)
   pixbuf_loader = gdk_pixbuf_loader_new();
   
   gdk_pixbuf_loader_write (pixbuf_loader, data, GST_GDK_PIXBUF_TYPE_FIND_SIZE,
-      &error);
+      NULL);
   
   format = gdk_pixbuf_loader_get_format (pixbuf_loader);
 
   if (format != NULL) {
     gchar **mlist = gdk_pixbuf_format_get_mime_types(format);
 
-    gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM,
+    gst_type_find_suggest (tf, GST_TYPE_FIND_MINIMUM,
         gst_caps_new ("gdk_pixbuf_type_find", mlist[0], NULL));
   }
 
-  gdk_pixbuf_loader_close (pixbuf_loader, &error);
+  gdk_pixbuf_loader_close (pixbuf_loader, NULL);
   g_object_unref (G_OBJECT (pixbuf_loader));
 }