diff options
author | Benjamin Otte <otte@gnome.org> | 2003-11-05 03:24:54 +0000 |
---|---|---|
committer | Benjamin Otte <otte@gnome.org> | 2003-11-05 03:24:54 +0000 |
commit | 8468a02e24fa79adc1b396b56e661a3887dfcb52 (patch) | |
tree | b392b760891b093f8d6d9ce74eba551bd93de653 /ext/gdk_pixbuf/gstgdkanimation.c | |
parent | bbd6c00598bb1e533011c85ba59cb8f9e0413ab9 (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.c | 483 |
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; +} |