diff --git a/ext/gdk_pixbuf/Makefile.am b/ext/gdk_pixbuf/Makefile.am index 82e0d3efd1..3e0b117959 100644 --- a/ext/gdk_pixbuf/Makefile.am +++ b/ext/gdk_pixbuf/Makefile.am @@ -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 diff --git a/ext/gdk_pixbuf/gst_loader.c b/ext/gdk_pixbuf/gst_loader.c index b8d1c9d724..aad4df2afd 100644 --- a/ext/gdk_pixbuf/gst_loader.c +++ b/ext/gdk_pixbuf/gst_loader.c @@ -26,6 +26,10 @@ #include "gstgdkanimation.h" #include #include +#include +#include +#include + 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 }; diff --git a/ext/gdk_pixbuf/gstgdkanimation.c b/ext/gdk_pixbuf/gstgdkanimation.c index 9e1aa16fd5..d8e5cfa77d 100644 --- a/ext/gdk_pixbuf/gstgdkanimation.c +++ b/ext/gdk_pixbuf/gstgdkanimation.c @@ -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); diff --git a/ext/gdk_pixbuf/gstgdkanimation.h b/ext/gdk_pixbuf/gstgdkanimation.h index 1ac48675f9..183d335e19 100644 --- a/ext/gdk_pixbuf/gstgdkanimation.h +++ b/ext/gdk_pixbuf/gstgdkanimation.h @@ -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;