mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
upgrades to the pixbuf loader:
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
This commit is contained in:
parent
8b4dbbc044
commit
2f56fff195
4 changed files with 141 additions and 57 deletions
|
@ -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
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#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
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue