1 /*
2 * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
3 *
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.
8 *
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.
13 *
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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstgdkanimation.h"
25 #include <unistd.h>
26
27 GST_DEBUG_CATEGORY_STATIC (gst_gdk_animation_debug);
28 #define GST_CAT_DEFAULT gst_gdk_animation_debug
29
30 static void gst_gdk_animation_class_init (gpointer g_class,
31 gpointer class_data);
32 static void gst_gdk_animation_finalize (GObject * object);
33
34 static gboolean gst_gdk_animation_is_static_image (GdkPixbufAnimation *
35 animation);
36 static GdkPixbuf *gst_gdk_animation_get_static_image (GdkPixbufAnimation *
37 animation);
38 static void gst_gdk_animation_get_size (GdkPixbufAnimation * anim, gint * width,
39 gint * height);
40 static GdkPixbufAnimationIter *gst_gdk_animation_get_iter (GdkPixbufAnimation *
41 anim, const GTimeVal * start_time);
42
43
44 static gpointer parent_class;
45
46 GType
gst_gdk_animation_get_type(void)47 gst_gdk_animation_get_type (void)
48 {
49 static GType object_type = 0;
50
51 if (!object_type) {
52 static const GTypeInfo object_info = {
53 sizeof (GstGdkAnimationClass),
54 NULL,
55 NULL,
56 gst_gdk_animation_class_init,
57 NULL, /* class_finalize */
58 NULL, /* class_data */
59 sizeof (GstGdkAnimation),
60 0, /* n_preallocs */
61 NULL,
62 };
63
64 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION,
65 "GstGdkAnimation", &object_info, 0);
66
67 GST_DEBUG_CATEGORY_INIT (gst_gdk_animation_debug, "gstloader_animation", 0,
68 "GStreamer GdkPixbuf loader - GdkAnimation class");
69 }
70
71 return object_type;
72 }
73
74 static void
gst_gdk_animation_class_init(gpointer g_class,gpointer class_data)75 gst_gdk_animation_class_init (gpointer g_class, gpointer class_data)
76 {
77 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
78 GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (g_class);
79
80 parent_class = g_type_class_peek_parent (g_class);
81
82 object_class->finalize = gst_gdk_animation_finalize;
83
84 anim_class->is_static_image = gst_gdk_animation_is_static_image;
85 anim_class->get_static_image = gst_gdk_animation_get_static_image;
86 anim_class->get_size = gst_gdk_animation_get_size;
87 anim_class->get_iter = gst_gdk_animation_get_iter;
88 }
89
90 static void
gst_gdk_animation_finalize(GObject * object)91 gst_gdk_animation_finalize (GObject * object)
92 {
93 GstGdkAnimation *ani = GST_GDK_ANIMATION (object);
94
95 if (ani->temp_fd) {
96 close (ani->temp_fd);
97 }
98 if (ani->temp_location) {
99 remove (ani->temp_location);
100 g_free (ani->temp_location);
101 }
102 if (ani->pixbuf) {
103 g_object_unref (ani->pixbuf);
104 ani->pixbuf = NULL;
105 }
106
107 G_OBJECT_CLASS (parent_class)->finalize (object);
108 }
109
110 GstGdkAnimation *
gst_gdk_animation_new(GError ** error)111 gst_gdk_animation_new (GError ** error)
112 {
113 GstGdkAnimation *ani =
114 GST_GDK_ANIMATION (g_object_new (GST_TYPE_GDK_ANIMATION, NULL));
115
116 return ani;
117 }
118
119 gboolean
gst_gdk_animation_add_data(GstGdkAnimation * ani,const guint8 * data,guint size)120 gst_gdk_animation_add_data (GstGdkAnimation * ani, const guint8 * data,
121 guint size)
122 {
123 return (write (ani->temp_fd, data, size) == size);
124 }
125
126 void
gst_gdk_animation_done_adding(GstGdkAnimation * ani)127 gst_gdk_animation_done_adding (GstGdkAnimation * ani)
128 {
129 close (ani->temp_fd);
130 ani->temp_fd = 0;
131 }
132
133 static gboolean
gst_gdk_animation_is_static_image(GdkPixbufAnimation * animation)134 gst_gdk_animation_is_static_image (GdkPixbufAnimation * animation)
135 {
136 return FALSE;
137 }
138
139 static void
gst_gdk_animation_get_size(GdkPixbufAnimation * anim,gint * width,int * height)140 gst_gdk_animation_get_size (GdkPixbufAnimation * anim, gint * width,
141 int *height)
142 {
143 GstGdkAnimation *ani = GST_GDK_ANIMATION (anim);
144
145 GST_LOG_OBJECT (ani, "get_size called (%p, %p) %d x %d", width, height,
146 ani->width, ani->height);
147 if (width)
148 *width = ani->width;
149
150 if (height)
151 *height = ani->height;
152 }
153
154
155 static void gst_gdk_animation_iter_class_init (gpointer g_class,
156 gpointer class_data);
157 static void gst_gdk_animation_iter_init (GTypeInstance * instance,
158 gpointer g_class);
159 static void gst_gdk_animation_iter_finalize (GObject * object);
160
161 static gint gst_gdk_animation_iter_get_delay_time (GdkPixbufAnimationIter *
162 iter);
163 static GdkPixbuf *gst_gdk_animation_iter_get_pixbuf (GdkPixbufAnimationIter *
164 iter);
165 static gboolean
166 gst_gdk_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *
167 iter);
168 static gboolean gst_gdk_animation_iter_advance (GdkPixbufAnimationIter * iter,
169 const GTimeVal * current_time);
170
171 static gpointer iter_parent_class;
172
173 GType
gst_gdk_animation_iter_get_type(void)174 gst_gdk_animation_iter_get_type (void)
175 {
176 static GType object_type = 0;
177
178 if (!object_type) {
179 static const GTypeInfo object_info = {
180 sizeof (GstGdkAnimationIterClass),
181 NULL,
182 NULL,
183 gst_gdk_animation_iter_class_init,
184 NULL, /* class_finalize */
185 NULL, /* class_data */
186 sizeof (GstGdkAnimationIter),
187 0, /* n_preallocs */
188 gst_gdk_animation_iter_init,
189 };
190
191 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER,
192 "GdkPixbufAniAnimIter", &object_info, 0);
193 }
194
195 return object_type;
196 }
197
198 static void
gst_gdk_animation_iter_class_init(gpointer g_class,gpointer class_data)199 gst_gdk_animation_iter_class_init (gpointer g_class, gpointer class_data)
200 {
201 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
202 GdkPixbufAnimationIterClass *anim_iter_class =
203 GDK_PIXBUF_ANIMATION_ITER_CLASS (g_class);
204
205 iter_parent_class = g_type_class_peek_parent (g_class);
206
207 object_class->finalize = gst_gdk_animation_iter_finalize;
208
209 anim_iter_class->get_delay_time = gst_gdk_animation_iter_get_delay_time;
210 anim_iter_class->get_pixbuf = gst_gdk_animation_iter_get_pixbuf;
211 anim_iter_class->on_currently_loading_frame =
212 gst_gdk_animation_iter_on_currently_loading_frame;
213 anim_iter_class->advance = gst_gdk_animation_iter_advance;
214 }
215
216 static void
gst_gdk_animation_iter_init(GTypeInstance * instance,gpointer g_class)217 gst_gdk_animation_iter_init (GTypeInstance * instance, gpointer g_class)
218 {
219 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (instance);
220
221 iter->buffers = g_queue_new ();
222 iter->eos = FALSE;
223 }
224
225 static void
gst_gdk_animation_iter_finalize(GObject * object)226 gst_gdk_animation_iter_finalize (GObject * object)
227 {
228 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (object);
229
230 g_object_unref (iter->ani);
231
232 if (iter->pipeline)
233 g_object_unref (iter->pipeline);
234 if (iter->pixbuf)
235 g_object_unref (iter->pixbuf);
236 while (iter->buffers) {
237 GstBuffer *buffer = GST_BUFFER (g_queue_pop_head (iter->buffers));
238
239 if (buffer) {
240 GST_LOG_OBJECT (iter, "unreffing buffer %p on finalize", buffer);
241 gst_data_unref (GST_DATA (buffer));
242 } else {
243 g_queue_free (iter->buffers);
244 iter->buffers = NULL;
245 }
246 }
247 G_OBJECT_CLASS (iter_parent_class)->finalize (object);
248 }
249
250 static void
got_handoff(GstElement * fakesink,GstBuffer * buffer,GstPad * pad,GstGdkAnimationIter * iter)251 got_handoff (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
252 GstGdkAnimationIter * iter)
253 {
254 GST_LOG_OBJECT (iter, "enqueing buffer %p (timestamp %" G_GUINT64_FORMAT ")",
255 buffer, GST_BUFFER_TIMESTAMP (buffer));
256 gst_data_ref (GST_DATA (buffer));
257 g_queue_push_tail (iter->buffers, buffer);
258 }
259
260 static gboolean
gst_gdk_animation_iter_create_pipeline(GstGdkAnimationIter * iter)261 gst_gdk_animation_iter_create_pipeline (GstGdkAnimationIter * iter)
262 {
263 GstElement *src, *typefind, *autoplugger, *sink, *colorspace;
264 GstCaps *caps = GST_CAPS_NEW ("pixbuf_filter32",
265 "video/x-raw-rgb",
266 "endianness", GST_PROPS_INT (G_BIG_ENDIAN),
267 "bpp", GST_PROPS_INT (32),
268 "red_mask", GST_PROPS_INT (0xFF000000),
269 "green_mask", GST_PROPS_INT (0x00FF0000),
270 "blue_mask", GST_PROPS_INT (0x0000FF00)
271 );
272
273 gst_caps_append (caps, GST_CAPS_NEW ("pixbuf_filter24",
274 "video/x-raw-rgb",
275 "endianness", GST_PROPS_INT (G_BIG_ENDIAN),
276 "bpp", GST_PROPS_INT (24),
277 "red_mask", GST_PROPS_INT (0xFF0000),
278 "green_mask", GST_PROPS_INT (0x00FF00),
279 "blue_mask", GST_PROPS_INT (0x0000FF)
280 ));
281
282 iter->pipeline = gst_element_factory_make ("pipeline", "main_pipeline");
283 if (iter->pipeline == NULL)
284 return FALSE;
285
286 if (!(src = gst_element_factory_make ("filesrc", "source")))
287 goto error;
288 gst_bin_add (GST_BIN (iter->pipeline), src);
289 if (iter->ani->temp_location) {
290 g_object_set (src, "location", iter->ani->temp_location, NULL);
291 GST_INFO_OBJECT (iter, "using file '%s'", iter->ani->temp_location);
292 } else {
293 gchar *filename = g_strdup_printf ("/proc/self/fd/%d", iter->ani->temp_fd);
294
295 g_object_set (src, "location", filename, NULL);
296 GST_INFO_OBJECT (iter, "using file '%s'", filename);
297 g_free (filename);
298 }
299
300 /* add typefind for correct typefinding */
301 if ((typefind = gst_element_factory_make ("typefind", "typefind"))) {
302 gst_bin_add (GST_BIN (iter->pipeline), typefind);
303 if (!gst_element_link (src, typefind))
304 goto error;
305 }
306
307 if (!(autoplugger = gst_element_factory_make ("spider", "autoplugger")))
308 goto error;
309 gst_bin_add (GST_BIN (iter->pipeline), autoplugger);
310 if (!gst_element_link (typefind, autoplugger))
311 goto error;
312
313 /* try ffcolorspace if available so we get svq1, too */
314 colorspace = gst_element_factory_make ("ffcolorspace", "ffcolorspace");
315 if (!colorspace)
316 colorspace = gst_element_factory_make ("colorspace", "colorspace");
317 if (!colorspace)
318 goto error;
319 gst_bin_add (GST_BIN (iter->pipeline), colorspace);
320 if (!gst_element_link (autoplugger, colorspace))
321 goto error;
322
323 if (!(sink = gst_element_factory_make ("fakesink", "sink")))
324 goto error;
325 g_object_set (sink, "signal-handoffs", TRUE, NULL);
326 g_signal_connect (sink, "handoff", (GCallback) got_handoff, iter);
327 gst_bin_add (GST_BIN (iter->pipeline), sink);
328 if (!gst_element_link_filtered (colorspace, sink, caps))
329 goto error;
330 if (gst_element_set_state (iter->pipeline,
331 GST_STATE_PLAYING) != GST_STATE_CHANGE_SUCCESS)
332 goto error;
333
334 return TRUE;
335 error:
336 g_object_unref (iter->pipeline);
337 iter->pipeline = NULL;
338 return FALSE;
339 }
340
341 static gboolean
gst_gdk_animation_iter_may_advance(GstGdkAnimationIter * iter)342 gst_gdk_animation_iter_may_advance (GstGdkAnimationIter * iter)
343 {
344 GstFormat bytes = GST_FORMAT_BYTES;
345 gint64 offset;
346 gint64 data_amount;
347
348 if (iter->ani->temp_fd == 0 || iter->ani->temp_location == NULL)
349 return TRUE;
350
351 data_amount = lseek (iter->ani->temp_fd, 0, SEEK_CUR);
352 g_assert (data_amount >= 0);
353 if (!gst_element_query (gst_bin_get_by_name (GST_BIN (iter->pipeline),
354 "source"), GST_QUERY_POSITION, &bytes, &offset))
355 g_assert_not_reached ();
356 if (data_amount - offset > GST_GDK_BUFFER_SIZE)
357 return TRUE;
358
359 return FALSE;
360 }
361
362 static gboolean
gst_gdk_animation_get_more_buffers(GstGdkAnimationIter * iter)363 gst_gdk_animation_get_more_buffers (GstGdkAnimationIter * iter)
364 {
365 GstBuffer *last = g_queue_peek_tail (iter->buffers);
366
367 do {
368 GST_LOG_OBJECT (iter, "iterating...");
369 if (!gst_gdk_animation_iter_may_advance (iter)) {
370 GST_LOG_OBJECT (iter, "no more data available");
371 break;
372 }
373 if (!gst_bin_iterate (GST_BIN (iter->pipeline))) {
374 GST_LOG_OBJECT (iter, "iterating done, setting EOS");
375 iter->eos = TRUE;
376 break;
377 }
378 } while (last == g_queue_peek_tail (iter->buffers));
379 return last != g_queue_peek_tail (iter->buffers);
380 }
381
382 static void
pixbuf_destroy_notify(guchar * pixels,gpointer data)383 pixbuf_destroy_notify (guchar * pixels, gpointer data)
384 {
385 GST_LOG ("unreffing buffer %p because pixbuf was destroyed", data);
386 gst_data_unref (GST_DATA (data));
387 }
388
389 static void
gst_gdk_animation_iter_create_pixbuf(GstGdkAnimationIter * iter)390 gst_gdk_animation_iter_create_pixbuf (GstGdkAnimationIter * iter)
391 {
392 GstBuffer *buf;
393 GstGdkAnimation *ani = iter->ani;
394
395 buf = g_queue_pop_head (iter->buffers);
396 g_assert (buf);
397 if (iter->pixbuf) {
398 GST_LOG_OBJECT (iter, "unreffing pixbuf %p", iter->pixbuf);
399 g_object_unref (iter->pixbuf);
400 }
401 if (ani->width == 0) {
402 GstPad *pad;
403 GstCaps *caps;
404 GstElement *fakesink =
405 gst_bin_get_by_name (GST_BIN (iter->pipeline), "sink");
406 g_assert (fakesink);
407 pad = gst_element_get_pad (fakesink, "sink");
408 g_assert (pad);
409 caps = gst_pad_get_negotiated_caps (pad);
410 g_assert (caps);
411 g_assert (GST_CAPS_IS_FIXED (caps));
412 g_assert (gst_caps_has_fixed_property (caps, "bpp") &&
413 gst_caps_has_fixed_property (caps, "width") &&
414 gst_caps_has_fixed_property (caps, "height"));
415 gst_caps_get_int (caps, "width", &ani->width);
416 gst_caps_get_int (caps, "height", &ani->height);
417 gst_caps_get_int (caps, "bpp", &ani->bpp);
418 GST_DEBUG_OBJECT (ani, "found format (width %d, height %d, bpp %d)",
419 ani->width, ani->height, ani->bpp);
420 }
421 g_assert (GST_BUFFER_SIZE (buf) == ani->width * ani->height * ani->bpp / 8);
422 if (ani->bpp == 32) {
423 gint i;
424 guint32 *data = (guint32 *) GST_BUFFER_DATA (buf);
425
426 /* ensure opacity */
427 for (i = 0; i < ani->width * ani->height; i++) {
428 data[i] |= 0xFF000000;
429 }
430 }
431 iter->pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buf),
432 GDK_COLORSPACE_RGB, ani->bpp == 32, 8, ani->width, ani->height,
433 ani->width * ani->bpp / 8, pixbuf_destroy_notify, buf);
434 GST_LOG_OBJECT (iter, "created pixbuf %p from buffer %p (refcount %d)",
435 iter->pixbuf, buf, GST_DATA_REFCOUNT_VALUE (buf));
436 }
437
438 static GdkPixbufAnimationIter *
gst_gdk_animation_get_iter(GdkPixbufAnimation * anim,const GTimeVal * start_time)439 gst_gdk_animation_get_iter (GdkPixbufAnimation * anim,
440 const GTimeVal * start_time)
441 {
442 GstGdkAnimation *ani = GST_GDK_ANIMATION (anim);
443 GstGdkAnimationIter *iter;
444
445 if (ani->temp_fd != 0 && ani->temp_location != NULL &&
446 lseek (ani->temp_fd, 0, SEEK_CUR) < GST_GDK_BUFFER_SIZE) {
447 GST_DEBUG_OBJECT (ani, "Not enough data to create iterator.");
448 return NULL;
449 }
450
451 iter = g_object_new (GST_TYPE_GDK_ANIMATION_ITER, NULL);
452
453 iter->start = *start_time;
454
455 iter->ani = ani;
456 g_object_ref (ani);
457 if (!gst_gdk_animation_iter_create_pipeline (iter))
458 goto error;
459
460 if (!gst_gdk_animation_get_more_buffers (iter))
461 goto error;
462
463 gst_gdk_animation_iter_create_pixbuf (iter);
464
465 return GDK_PIXBUF_ANIMATION_ITER (iter);
466
467 error:
468 g_object_unref (iter);
469 return NULL;
470 }
471
472 static gboolean
gst_gdk_animation_iter_advance(GdkPixbufAnimationIter * anim_iter,const GTimeVal * current_time)473 gst_gdk_animation_iter_advance (GdkPixbufAnimationIter * anim_iter,
474 const GTimeVal * current_time)
475 {
476 GstClockTime offset;
477 GstBuffer *buffer = NULL;
478 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
479
480 /* compute timestamp that next buffer must match */
481 offset =
482 ((GstClockTime) current_time->tv_sec - iter->start.tv_sec) * GST_SECOND;
483 if (iter->start.tv_usec > current_time->tv_usec) {
484 offset -=
485 ((GstClockTime) iter->start.tv_usec -
486 current_time->tv_usec) * GST_SECOND / G_USEC_PER_SEC;
487 } else {
488 offset +=
489 ((GstClockTime) current_time->tv_usec -
490 iter->start.tv_usec) * GST_SECOND / G_USEC_PER_SEC;
491 }
492 GST_DEBUG_OBJECT (iter,
493 "advancing to %ld:%ld (started at %ld:%ld) need offset %"
494 G_GUINT64_FORMAT, current_time->tv_sec, current_time->tv_usec,
495 iter->start.tv_sec, iter->start.tv_usec, offset);
496 if (!iter->just_seeked
497 && offset - iter->last_timestamp > GST_GDK_MAX_DELAY_TO_SEEK) {
498 GST_INFO_OBJECT (iter,
499 "current pipeline timestamp is too old (%" G_GUINT64_FORMAT " vs %"
500 G_GUINT64_FORMAT "), seeking there", iter->last_timestamp, offset);
501 if (gst_element_send_event (gst_bin_get_by_name (GST_BIN (iter->pipeline),
502 "sink"),
503 gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET |
504 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, offset))) {
505 iter->last_timestamp = offset;
506 iter->just_seeked = TRUE;
507 } else {
508 GST_WARNING_OBJECT (iter,
509 "seek to %" G_GUINT64_FORMAT " didn't work. Iterating there...",
510 offset);
511 }
512 } else if (iter->just_seeked) {
513 iter->just_seeked = FALSE;
514 }
515
516 while (TRUE) {
517 if (g_queue_is_empty (iter->buffers)) {
518 if (iter->eos)
519 return FALSE;
520 if (gst_gdk_animation_get_more_buffers (iter))
521 continue;
522 break;
523 }
524 if (GST_BUFFER_TIMESTAMP (g_queue_peek_head (iter->buffers)) > offset)
525 break;
526 if (buffer) {
527 GST_LOG_OBJECT (iter, "unreffing buffer %p, because timestamp too low (%"
528 G_GUINT64_FORMAT " vs %" G_GUINT64_FORMAT ")",
529 buffer, GST_BUFFER_TIMESTAMP (buffer), offset);
530 gst_data_unref (GST_DATA (buffer));
531 }
532 buffer = GST_BUFFER (g_queue_pop_head (iter->buffers));
533 }
534 if (!buffer)
535 return FALSE;
536 if (GST_BUFFER_TIMESTAMP (buffer) < iter->last_timestamp) {
537 gst_data_unref (GST_DATA (buffer));
538 iter->last_timestamp = offset;
539 return FALSE;
540 }
541 iter->last_timestamp = GST_BUFFER_TIMESTAMP (buffer);
542 g_queue_push_head (iter->buffers, buffer);
543 gst_gdk_animation_iter_create_pixbuf (iter);
544 return TRUE;
545 }
546
547 static gint
gst_gdk_animation_iter_get_delay_time(GdkPixbufAnimationIter * anim_iter)548 gst_gdk_animation_iter_get_delay_time (GdkPixbufAnimationIter * anim_iter)
549 {
550 gint delay;
551 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
552
553 while (g_queue_is_empty (iter->buffers)) {
554 if (iter->eos) {
555 GST_LOG_OBJECT (iter, "returning delay of infinite, we're EOS");
556 return -1;
557 }
558 if (!gst_gdk_animation_get_more_buffers (iter))
559 return -1; /* FIXME? */
560 }
561
562 delay =
563 (GST_BUFFER_TIMESTAMP (g_queue_peek_head (iter->buffers)) -
564 iter->last_timestamp) * 1000 / GST_SECOND;
565 GST_LOG_OBJECT (iter, "returning delay of %d ms", delay);
566 return delay;
567 }
568
569 GdkPixbuf *
gst_gdk_animation_iter_get_pixbuf(GdkPixbufAnimationIter * anim_iter)570 gst_gdk_animation_iter_get_pixbuf (GdkPixbufAnimationIter * anim_iter)
571 {
572 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
573
574 GST_LOG_OBJECT (iter, "returning pixbuf %p", iter->pixbuf);
575 return iter->pixbuf;
576 }
577
578 static gboolean
gst_gdk_animation_iter_on_currently_loading_frame(GdkPixbufAnimationIter * anim_iter)579 gst_gdk_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *
580 anim_iter)
581 {
582 GstGdkAnimationIter *iter = GST_GDK_ANIMATION_ITER (anim_iter);
583
584 /* EOS - last frame */
585 if (iter->eos && g_queue_is_empty (iter->buffers))
586 return TRUE;
587
588 /* can't load more frames */
589 if (!gst_gdk_animation_iter_may_advance (iter))
590 return FALSE;
591
592 return TRUE;
593 }
594
595 static GdkPixbuf *
gst_gdk_animation_get_static_image(GdkPixbufAnimation * animation)596 gst_gdk_animation_get_static_image (GdkPixbufAnimation * animation)
597 {
598 GstGdkAnimation *ani = GST_GDK_ANIMATION (animation);
599 GTimeVal tv;
600 GstGdkAnimationIter *iter;
601
602 if (!ani->pixbuf) {
603 GST_LOG_OBJECT (ani, "trying to create pixbuf");
604 g_get_current_time (&tv);
605 iter =
606 GST_GDK_ANIMATION_ITER (gdk_pixbuf_animation_get_iter (animation, &tv));
607 if (iter) {
608 guint64 offset;
609 GstBuffer *buf;
610 GstFormat time = GST_FORMAT_TIME;
611
612 if (!gst_element_query (gst_bin_get_by_name (GST_BIN (iter->pipeline),
613 "sink"), GST_QUERY_TOTAL, &time, &offset)) {
614 offset = 0;
615 }
616 if (offset > 120 * GST_SECOND) {
617 offset = 120 * GST_SECOND;
618 } else if (offset < 120 * GST_SECOND && offset >= 10 * GST_SECOND) {
619 offset = offset / 2;
620 }
621 g_assert (time == GST_FORMAT_TIME);
622 GST_LOG_OBJECT (ani,
623 "using time offset %" G_GUINT64_FORMAT " for creating static image",
624 offset);
625 while ((buf = g_queue_pop_head (iter->buffers)) != NULL) {
626 gst_data_unref (GST_DATA (buf));
627 }
628 /* now we do evil stuff, be sure to get rid of the iterator afterwards */
629 if (!gst_element_send_event (gst_bin_get_by_name (GST_BIN
630 (iter->pipeline), "sink"),
631 gst_event_new_seek (GST_FORMAT_TIME | GST_SEEK_METHOD_SET |
632 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, offset))) {
633 GST_INFO_OBJECT (ani, "seeking didn't work. Using next image");
634 }
635
636 do {
637 if (g_queue_is_empty (iter->buffers)) {
638 if (iter->eos)
639 return FALSE;
640 if (gst_gdk_animation_get_more_buffers (iter))
641 continue;
642 }
643 } while (FALSE);
644 if (!g_queue_is_empty (iter->buffers)) {
645 gst_gdk_animation_iter_create_pixbuf (iter);
646 ani->pixbuf =
647 gst_gdk_animation_iter_get_pixbuf (GDK_PIXBUF_ANIMATION_ITER
648 (iter));
649 g_object_ref (ani->pixbuf);
650 } else {
651 g_assert (ani->pixbuf == NULL);
652 }
653 /* DiE iterator, DiE */
654 g_object_unref (iter);
655 } else {
656 GST_DEBUG_OBJECT (ani, "Could not get an iterator. No pixbuf available");
657 }
658 }
659 GST_LOG_OBJECT (ani, "Returning pixbuf %p\n", ani->pixbuf);
660 return ani->pixbuf;
661 }
662