summaryrefslogtreecommitdiffstats
path: root/ext/gdk_pixbuf/gstgdkanimation.c
diff options
context:
space:
mode:
authorBenjamin Otte <otte@gnome.org>2003-11-05 03:24:54 +0000
committerBenjamin Otte <otte@gnome.org>2003-11-05 03:24:54 +0000
commit8468a02e24fa79adc1b396b56e661a3887dfcb52 (patch)
treeb392b760891b093f8d6d9ce74eba551bd93de653 /ext/gdk_pixbuf/gstgdkanimation.c
parentbbd6c00598bb1e533011c85ba59cb8f9e0413ab9 (diff)
add initial version of gdkpixbuf loader for gtk that is capable of loading AVI and mpeg videos as GdkPixbufAnimation....
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.
Diffstat (limited to 'ext/gdk_pixbuf/gstgdkanimation.c')
-rw-r--r--ext/gdk_pixbuf/gstgdkanimation.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/ext/gdk_pixbuf/gstgdkanimation.c b/ext/gdk_pixbuf/gstgdkanimation.c
new file mode 100644
index 00000000..6519cc1b
--- /dev/null
+++ b/ext/gdk_pixbuf/gstgdkanimation.c
@@ -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;
+}