2 * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include "gstgdkanimation.h"
27 GST_DEBUG_CATEGORY_STATIC (gst_gdk_animation_debug);
28 #define GST_CAT_DEFAULT gst_gdk_animation_debug
30 static void gst_gdk_animation_class_init (gpointer g_class,
32 static void gst_gdk_animation_finalize (GObject * object);
34 static gboolean gst_gdk_animation_is_static_image (GdkPixbufAnimation *
36 static GdkPixbuf *gst_gdk_animation_get_static_image (GdkPixbufAnimation *
38 static void gst_gdk_animation_get_size (GdkPixbufAnimation * anim, gint * width,
40 static GdkPixbufAnimationIter *gst_gdk_animation_get_iter (GdkPixbufAnimation *
41 anim, const GTimeVal * start_time);
44 static gpointer parent_class;
47 gst_gdk_animation_get_type (void)
49 static GType object_type = 0;
52 static const GTypeInfo object_info = {
53 sizeof (GstGdkAnimationClass),
56 gst_gdk_animation_class_init,
57 NULL, /* class_finalize */
58 NULL, /* class_data */
59 sizeof (GstGdkAnimation),
64 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION,
65 "GstGdkAnimation", &object_info, 0);
67 GST_DEBUG_CATEGORY_INIT (gst_gdk_animation_debug, "gstloader_animation", 0,
68 "GStreamer GdkPixbuf loader - GdkAnimation class");
74 gst_gdk_animation_class_init (gpointer g_class, gpointer class_data)
76 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
77 GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (g_class);
79 parent_class = g_type_class_peek_parent (g_class);
81 object_class->finalize = gst_gdk_animation_finalize;
83 anim_class->is_static_image = gst_gdk_animation_is_static_image;
84 anim_class->get_static_image = gst_gdk_animation_get_static_image;
85 anim_class->get_size = gst_gdk_animation_get_size;
86 anim_class->get_iter = gst_gdk_animation_get_iter;
89 gst_gdk_animation_finalize (GObject * object)
91 GstGdkAnimation *ani = GST_GDK_ANIMATION (object);
96 if (ani->temp_location) {
97 remove (ani->temp_location);
98 g_free (ani->temp_location);
101 g_object_unref (ani->pixbuf);
105 G_OBJECT_CLASS (parent_class)->finalize (object);
109 gst_gdk_animation_new (GError ** error)
111 GstGdkAnimation *ani =
112 GST_GDK_ANIMATION (g_object_new (GST_TYPE_GDK_ANIMATION, NULL));
118 gst_gdk_animation_add_data (GstGdkAnimation * ani, const guint8 * data,
121 return (write (ani->temp_fd, data, size) == size);
125 gst_gdk_animation_done_adding (GstGdkAnimation * ani)
127 close (ani->temp_fd);
132 gst_gdk_animation_is_static_image (GdkPixbufAnimation * animation)
138 gst_gdk_animation_get_size (GdkPixbufAnimation * anim, gint * width,
141 GstGdkAnimation *ani = GST_GDK_ANIMATION (anim);
143 GST_LOG_OBJECT (ani, "get_size called (%p, %p) %d x %d", width, height,
144 ani->width, ani->height);
149 *height = ani->height;
153 static void gst_gdk_animation_iter_class_init (gpointer g_class,
154 gpointer class_data);
155 static void gst_gdk_animation_iter_init (GTypeInstance * instance,
157 static void gst_gdk_animation_iter_finalize (GObject * object);
159 static gint gst_gdk_animation_iter_get_delay_time (GdkPixbufAnimationIter *
161 static GdkPixbuf *gst_gdk_animation_iter_get_pixbuf (GdkPixbufAnimationIter *
164 gst_gdk_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *
166 static gboolean gst_gdk_animation_iter_advance (GdkPixbufAnimationIter * iter,
167 const GTimeVal * current_time);
169 static gpointer iter_parent_class;
172 gst_gdk_animation_iter_get_type (void)
174 static GType object_type = 0;
177 static const GTypeInfo object_info = {
178 sizeof (GstGdkAnimationIterClass),
181 gst_gdk_animation_iter_class_init,
182 NULL, /* class_finalize */
183 NULL, /* class_data */
184 sizeof (GstGdkAnimationIter),
186 gst_gdk_animation_iter_init,
189 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER,
190 "GdkPixbufAniAnimIter", &object_info, 0);
197 gst_gdk_animation_iter_class_init (gpointer g_class, gpointer class_data)
199 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
200 GdkPixbufAnimationIterClass *anim_iter_class =
201 GDK_PIXBUF_ANIMATION_ITER_CLASS (g_class);
203 iter_parent_class = g_type_class_peek_parent (g_class);
205 object_class->finalize = gst_gdk_animation_iter_finalize;
207 anim_iter_class->get_delay_time = gst_gdk_animation_iter_get_delay_time;
208 anim_iter_class->get_pixbuf = gst_gdk_animation_iter_get_pixbuf;
209 anim_iter_class->on_currently_loading_frame =
210 gst_gdk_animation_iter_on_currently_loading_frame;
211 anim_iter_class->advance = gst_gdk_animation_iter_advance;
214 gst_gdk_animation_iter_init (GTypeInstance * instance, gpointer g_class)
216 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (instance);
218 iter->buffers = g_queue_new ();
222 gst_gdk_animation_iter_finalize (GObject * object)
224 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (object);
226 g_object_unref (iter->ani);
229 g_object_unref (iter->pipeline);
231 g_object_unref (iter->pixbuf);
232 while (iter->buffers) {
233 GstBuffer *buffer = GST_BUFFER (g_queue_pop_head (iter->buffers));
236 GST_LOG_OBJECT (iter, "unreffing buffer %p on finalize", buffer);
237 gst_data_unref (GST_DATA (buffer));
239 g_queue_free (iter->buffers);
240 iter->buffers = NULL;
243 G_OBJECT_CLASS (iter_parent_class)->finalize (object);
246 got_handoff (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
247 GstGdkAnimationIter * iter)
249 GST_LOG_OBJECT (iter, "enqueing buffer %p (timestamp %" G_GUINT64_FORMAT ")",
250 buffer, GST_BUFFER_TIMESTAMP (buffer));
251 gst_data_ref (GST_DATA (buffer));
252 g_queue_push_tail (iter->buffers, buffer);
256 gst_gdk_animation_iter_create_pipeline (GstGdkAnimationIter * iter)
258 GstElement *src, *typefind, *autoplugger, *sink, *colorspace;
259 GstCaps *caps = GST_CAPS_NEW ("pixbuf_filter32",
261 "endianness", GST_PROPS_INT (G_BIG_ENDIAN),
262 "bpp", GST_PROPS_INT (32),
263 "red_mask", GST_PROPS_INT (0xFF000000),
264 "green_mask", GST_PROPS_INT (0x00FF0000),
265 "blue_mask", GST_PROPS_INT (0x0000FF00)
268 gst_caps_append (caps, GST_CAPS_NEW ("pixbuf_filter24",
270 "endianness", GST_PROPS_INT (G_BIG_ENDIAN),
271 "bpp", GST_PROPS_INT (24),
272 "red_mask", GST_PROPS_INT (0xFF0000),
273 "green_mask", GST_PROPS_INT (0x00FF00),
274 "blue_mask", GST_PROPS_INT (0x0000FF)
277 iter->pipeline = gst_element_factory_make ("pipeline", "main_pipeline");
278 if (iter->pipeline == NULL)
281 if (!(src = gst_element_factory_make ("filesrc", "source")))
283 gst_bin_add (GST_BIN (iter->pipeline), src);
284 if (iter->ani->temp_location) {
285 g_object_set (src, "location", iter->ani->temp_location, NULL);
286 GST_INFO_OBJECT (iter, "using file '%s'", iter->ani->temp_location);
288 gchar *filename = g_strdup_printf ("/proc/self/fd/%d", iter->ani->temp_fd);
290 g_object_set (src, "location", filename, NULL);
291 GST_INFO_OBJECT (iter, "using file '%s'", filename);
295 /* add typefind for correct typefinding */
296 if ((typefind = gst_element_factory_make ("typefind", "typefind"))) {
297 gst_bin_add (GST_BIN (iter->pipeline), typefind);
298 if (!gst_element_link (src, typefind))
302 if (!(autoplugger = gst_element_factory_make ("spider", "autoplugger")))
304 gst_bin_add (GST_BIN (iter->pipeline), autoplugger);
305 if (!gst_element_link (typefind, autoplugger))
308 /* try ffcolorspace if available so we get svq1, too */
309 colorspace = gst_element_factory_make ("ffcolorspace", "ffcolorspace");
311 colorspace = gst_element_factory_make ("colorspace", "colorspace");
314 gst_bin_add (GST_BIN (iter->pipeline), colorspace);
315 if (!gst_element_link (autoplugger, colorspace))
318 if (!(sink = gst_element_factory_make ("fakesink", "sink")))
320 g_object_set (sink, "signal-handoffs", TRUE, NULL);
321 g_signal_connect (sink, "handoff", (GCallback) got_handoff, iter);
322 gst_bin_add (GST_BIN (iter->pipeline), sink);
323 if (!gst_element_link_filtered (colorspace, sink, caps))
325 if (gst_element_set_state (iter->pipeline,
326 GST_STATE_PLAYING) != GST_STATE_CHANGE_SUCCESS)
331 g_object_unref (iter->pipeline);
332 iter->pipeline = NULL;
337 gst_gdk_animation_iter_may_advance (GstGdkAnimationIter * iter)
339 GstFormat bytes = GST_FORMAT_BYTES;
343 if (iter->ani->temp_fd == 0 || iter->ani->temp_location == NULL)
346 data_amount = lseek (iter->ani->temp_fd, 0, SEEK_CUR);
347 g_assert (data_amount >= 0);
348 if (!gst_element_query (gst_bin_get_by_name (GST_BIN (iter->pipeline),
349 "source"), GST_QUERY_POSITION, &bytes, &offset))
350 g_assert_not_reached ();
351 if (data_amount - offset > GST_GDK_BUFFER_SIZE)
358 gst_gdk_animation_get_more_buffers (GstGdkAnimationIter * iter)
360 GstBuffer *last = g_queue_peek_tail (iter->buffers);
363 GST_LOG_OBJECT (iter, "iterating...");
364 if (!gst_gdk_animation_iter_may_advance (iter)) {
365 GST_LOG_OBJECT (iter, "no more data available");
368 if (!gst_bin_iterate (GST_BIN (iter->pipeline))) {
369 GST_LOG_OBJECT (iter, "iterating done, setting EOS");
373 } while (last == g_queue_peek_tail (iter->buffers));
374 return last != g_queue_peek_tail (iter->buffers);
377 pixbuf_destroy_notify (guchar * pixels, gpointer data)
379 GST_LOG ("unreffing buffer %p because pixbuf was destroyed", data);
380 gst_data_unref (GST_DATA (data));
383 gst_gdk_animation_iter_create_pixbuf (GstGdkAnimationIter * iter)
386 GstGdkAnimation *ani = iter->ani;
388 buf = g_queue_pop_head (iter->buffers);
391 GST_LOG_OBJECT (iter, "unreffing pixbuf %p", iter->pixbuf);
392 g_object_unref (iter->pixbuf);
394 if (ani->width == 0) {
397 GstElement *fakesink =
398 gst_bin_get_by_name (GST_BIN (iter->pipeline), "sink");
400 pad = gst_element_get_pad (fakesink, "sink");
402 caps = gst_pad_get_negotiated_caps (pad);
404 g_assert (GST_CAPS_IS_FIXED (caps));
405 g_assert (gst_caps_has_fixed_property (caps, "bpp") &&
406 gst_caps_has_fixed_property (caps, "width") &&
407 gst_caps_has_fixed_property (caps, "height"));
408 gst_caps_get_int (caps, "width", &ani->width);
409 gst_caps_get_int (caps, "height", &ani->height);
410 gst_caps_get_int (caps, "bpp", &ani->bpp);
411 GST_DEBUG_OBJECT (ani, "found format (width %d, height %d, bpp %d)",
412 ani->width, ani->height, ani->bpp);
414 g_assert (GST_BUFFER_SIZE (buf) == ani->width * ani->height * ani->bpp / 8);
415 if (ani->bpp == 32) {
417 guint32 *data = (guint32 *) GST_BUFFER_DATA (buf);
420 for (i = 0; i < ani->width * ani->height; i++) {
421 data[i] |= 0xFF000000;
424 iter->pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buf),
425 GDK_COLORSPACE_RGB, ani->bpp == 32, 8, ani->width, ani->height,
426 ani->width * ani->bpp / 8, pixbuf_destroy_notify, buf);
427 GST_LOG_OBJECT (iter, "created pixbuf %p from buffer %p (refcount %d)",
428 iter->pixbuf, buf, GST_DATA_REFCOUNT_VALUE (buf));
430 static GdkPixbufAnimationIter *
431 gst_gdk_animation_get_iter (GdkPixbufAnimation * anim,
432 const GTimeVal * start_time)
434 GstGdkAnimation *ani = GST_GDK_ANIMATION (anim);
435 GstGdkAnimationIter *iter;
437 if (ani->temp_fd != 0 && ani->temp_location != NULL &&
438 lseek (ani->temp_fd, 0, SEEK_CUR) < GST_GDK_BUFFER_SIZE) {
439 GST_DEBUG_OBJECT (ani, "Not enough data to create iterator.");
443 iter = g_object_new (GST_TYPE_GDK_ANIMATION_ITER, NULL);
445 iter->start = *start_time;
449 if (!gst_gdk_animation_iter_create_pipeline (iter))
452 if (!gst_gdk_animation_get_more_buffers (iter))
455 gst_gdk_animation_iter_create_pixbuf (iter);
457 return GDK_PIXBUF_ANIMATION_ITER (iter);
460 g_object_unref (iter);
465 gst_gdk_animation_iter_advance (GdkPixbufAnimationIter * anim_iter,
466 const GTimeVal * current_time)
469 GstBuffer *buffer = NULL;
470 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
472 /* compute timestamp that next buffer must match */
474 ((GstClockTime) current_time->tv_sec - iter->start.tv_sec) * GST_SECOND;
475 if (iter->start.tv_usec > current_time->tv_usec) {
477 ((GstClockTime) iter->start.tv_usec -
478 current_time->tv_usec) * GST_SECOND / G_USEC_PER_SEC;
481 ((GstClockTime) current_time->tv_usec -
482 iter->start.tv_usec) * GST_SECOND / G_USEC_PER_SEC;
484 GST_DEBUG_OBJECT (iter,
485 "advancing to %ld:%ld (started at %ld:%ld) need offset %"
486 G_GUINT64_FORMAT, current_time->tv_sec, current_time->tv_usec,
487 iter->start.tv_sec, iter->start.tv_usec, offset);
488 if (!iter->just_seeked
489 && offset - iter->last_timestamp > GST_GDK_MAX_DELAY_TO_SEEK) {
490 GST_INFO_OBJECT (iter,
491 "current pipeline timestamp is too old (%" G_GUINT64_FORMAT " vs %"
492 G_GUINT64_FORMAT "), seeking there", iter->last_timestamp, offset);
493 if (gst_element_send_event (gst_bin_get_by_name (GST_BIN (iter->pipeline),
495 gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET |
496 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, offset))) {
497 iter->last_timestamp = offset;
498 iter->just_seeked = TRUE;
500 GST_WARNING_OBJECT (iter,
501 "seek to %" G_GUINT64_FORMAT " didn't work. Iterating there...",
504 } else if (iter->just_seeked) {
505 iter->just_seeked = FALSE;
509 if (g_queue_is_empty (iter->buffers)) {
512 if (gst_gdk_animation_get_more_buffers (iter))
516 if (GST_BUFFER_TIMESTAMP (g_queue_peek_head (iter->buffers)) > offset)
519 GST_LOG_OBJECT (iter, "unreffing buffer %p, because timestamp too low (%"
520 G_GUINT64_FORMAT " vs %" G_GUINT64_FORMAT ")",
521 buffer, GST_BUFFER_TIMESTAMP (buffer), offset);
522 gst_data_unref (GST_DATA (buffer));
524 buffer = GST_BUFFER (g_queue_pop_head (iter->buffers));
528 if (GST_BUFFER_TIMESTAMP (buffer) < iter->last_timestamp) {
529 gst_data_unref (GST_DATA (buffer));
530 iter->last_timestamp = offset;
533 iter->last_timestamp = GST_BUFFER_TIMESTAMP (buffer);
534 g_queue_push_head (iter->buffers, buffer);
535 gst_gdk_animation_iter_create_pixbuf (iter);
540 gst_gdk_animation_iter_get_delay_time (GdkPixbufAnimationIter * anim_iter)
543 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
545 while (g_queue_is_empty (iter->buffers)) {
547 GST_LOG_OBJECT (iter, "returning delay of infinite, we're EOS");
550 if (!gst_gdk_animation_get_more_buffers (iter))
551 return -1; /* FIXME? */
555 (GST_BUFFER_TIMESTAMP (g_queue_peek_head (iter->buffers)) -
556 iter->last_timestamp) * 1000 / GST_SECOND;
557 GST_LOG_OBJECT (iter, "returning delay of %d ms", delay);
562 gst_gdk_animation_iter_get_pixbuf (GdkPixbufAnimationIter * anim_iter)
564 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
566 GST_LOG_OBJECT (iter, "returning pixbuf %p", iter->pixbuf);
571 gst_gdk_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *
574 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
576 /* EOS - last frame */
577 if (iter->eos && g_queue_is_empty (iter->buffers))
580 /* can't load more frames */
581 if (!gst_gdk_animation_iter_may_advance (iter))
587 gst_gdk_animation_get_static_image (GdkPixbufAnimation * animation)
589 GstGdkAnimation *ani = GST_GDK_ANIMATION (animation);
591 GstGdkAnimationIter *iter;
594 GST_LOG_OBJECT (ani, "trying to create pixbuf");
595 g_get_current_time (&tv);
597 GST_GDK_ANIMATION_ITER (gdk_pixbuf_animation_get_iter (animation, &tv));
601 GstFormat time = GST_FORMAT_TIME;
603 if (!gst_element_query (gst_bin_get_by_name (GST_BIN (iter->pipeline),
604 "sink"), GST_QUERY_TOTAL, &time, &offset)) {
607 if (offset > 120 * GST_SECOND) {
608 offset = 120 * GST_SECOND;
609 } else if (offset < 120 * GST_SECOND && offset >= 10 * GST_SECOND) {
612 g_assert (time == GST_FORMAT_TIME);
614 "using time offset %" G_GUINT64_FORMAT " for creating static image",
616 while ((buf = g_queue_pop_head (iter->buffers)) != NULL) {
617 gst_data_unref (GST_DATA (buf));
619 /* now we do evil stuff, be sure to get rid of the iterator afterwards */
620 if (!gst_element_send_event (gst_bin_get_by_name (GST_BIN (iter->
622 gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET |
623 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, offset))) {
624 GST_INFO_OBJECT (ani, "seeking didn't work. Using next image");
628 if (g_queue_is_empty (iter->buffers)) {
631 if (gst_gdk_animation_get_more_buffers (iter))
635 if (!g_queue_is_empty (iter->buffers)) {
636 gst_gdk_animation_iter_create_pixbuf (iter);
638 gst_gdk_animation_iter_get_pixbuf (GDK_PIXBUF_ANIMATION_ITER
640 g_object_ref (ani->pixbuf);
642 g_assert (ani->pixbuf == NULL);
644 /* DiE iterator, DiE */
645 g_object_unref (iter);
647 GST_DEBUG_OBJECT (ani, "Could not get an iterator. No pixbuf available");
650 GST_LOG_OBJECT (ani, "Returning pixbuf %p\n", ani->pixbuf);