imagefreeze: Retry bufferalloc if it was aborted with WRONG_STATE because of a flushi...
[platform/upstream/gstreamer.git] / gst / imagefreeze / gstimagefreeze.c
1 /* GStreamer
2  * Copyright (c) 2005 Edward Hervey <bilboed@bilboed.com>
3  * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:element-imagefreeze
23  *
24  * The imagefreeze element generates a still frame video stream from
25  * the input. It duplicates the first frame with the framerate requested
26  * by downstream, allows seeking and answers queries.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch -v filesrc location=some.png ! decodebin2 ! imagefreeze ! autovideosink
32  * ]| This pipeline shows a still frame stream of a PNG file.
33  * </refsect2>
34  */
35
36 /* This is based on the imagefreeze element from PiTiVi:
37  * http://git.gnome.org/browse/pitivi/tree/pitivi/elements/imagefreeze.py
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include "gstimagefreeze.h"
45
46 static void gst_image_freeze_finalize (GObject * object);
47
48 static void gst_image_freeze_reset (GstImageFreeze * self);
49
50 static GstStateChangeReturn gst_image_freeze_change_state (GstElement * element,
51     GstStateChange transition);
52
53 static GstFlowReturn gst_image_freeze_sink_chain (GstPad * pad,
54     GstBuffer * buffer);
55 static gboolean gst_image_freeze_sink_event (GstPad * pad, GstEvent * event);
56 static gboolean gst_image_freeze_sink_setcaps (GstPad * pad, GstCaps * caps);
57 static GstCaps *gst_image_freeze_sink_getcaps (GstPad * pad);
58 static gboolean gst_image_freeze_sink_query (GstPad * pad, GstQuery * query);
59 static GstFlowReturn gst_image_freeze_sink_bufferalloc (GstPad * pad,
60     guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
61 static void gst_image_freeze_src_loop (GstPad * pad);
62 static gboolean gst_image_freeze_src_event (GstPad * pad, GstEvent * event);
63 static gboolean gst_image_freeze_src_query (GstPad * pad, GstQuery * query);
64 static const GstQueryType *gst_image_freeze_src_query_type (GstPad * pad);
65
66 static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
67     GST_PAD_SINK,
68     GST_PAD_ALWAYS,
69     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; video/x-raw-gray"));
70
71 static GstStaticPadTemplate src_pad_template =
72     GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
73     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; video/x-raw-gray"));
74
75 GST_DEBUG_CATEGORY_STATIC (gst_image_freeze_debug);
76 #define GST_CAT_DEFAULT gst_image_freeze_debug
77
78 GST_BOILERPLATE (GstImageFreeze, gst_image_freeze, GstElement,
79     GST_TYPE_ELEMENT);
80
81 static void
82 gst_image_freeze_base_init (gpointer g_class)
83 {
84   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
85
86   gst_element_class_set_details_simple (gstelement_class,
87       "Still frame stream generator",
88       "Filter/Video",
89       "Generates a still frame stream from an image",
90       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
91
92   gst_element_class_add_pad_template (gstelement_class,
93       gst_static_pad_template_get (&sink_pad_template));
94   gst_element_class_add_pad_template (gstelement_class,
95       gst_static_pad_template_get (&src_pad_template));
96 }
97
98 static void
99 gst_image_freeze_class_init (GstImageFreezeClass * klass)
100 {
101   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
102   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
103
104   gobject_class->finalize = gst_image_freeze_finalize;
105
106   gstelement_class->change_state =
107       GST_DEBUG_FUNCPTR (gst_image_freeze_change_state);
108 }
109
110 static void
111 gst_image_freeze_init (GstImageFreeze * self, GstImageFreezeClass * g_class)
112 {
113   self->sinkpad = gst_pad_new_from_static_template (&sink_pad_template, "sink");
114   gst_pad_set_chain_function (self->sinkpad,
115       GST_DEBUG_FUNCPTR (gst_image_freeze_sink_chain));
116   gst_pad_set_event_function (self->sinkpad,
117       GST_DEBUG_FUNCPTR (gst_image_freeze_sink_event));
118   gst_pad_set_query_function (self->sinkpad,
119       GST_DEBUG_FUNCPTR (gst_image_freeze_sink_query));
120   gst_pad_set_setcaps_function (self->sinkpad,
121       GST_DEBUG_FUNCPTR (gst_image_freeze_sink_setcaps));
122   gst_pad_set_getcaps_function (self->sinkpad,
123       GST_DEBUG_FUNCPTR (gst_image_freeze_sink_getcaps));
124   gst_pad_set_bufferalloc_function (self->sinkpad,
125       GST_DEBUG_FUNCPTR (gst_image_freeze_sink_bufferalloc));
126   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
127
128   self->srcpad = gst_pad_new_from_static_template (&src_pad_template, "src");
129   gst_pad_set_event_function (self->srcpad,
130       GST_DEBUG_FUNCPTR (gst_image_freeze_src_event));
131   gst_pad_set_query_function (self->srcpad,
132       GST_DEBUG_FUNCPTR (gst_image_freeze_src_query));
133   gst_pad_set_query_type_function (self->srcpad,
134       GST_DEBUG_FUNCPTR (gst_image_freeze_src_query_type));
135   gst_pad_use_fixed_caps (self->srcpad);
136   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
137
138   gst_image_freeze_reset (self);
139 }
140
141 static void
142 gst_image_freeze_finalize (GObject * object)
143 {
144   GstImageFreeze *self = GST_IMAGE_FREEZE (object);
145
146   gst_image_freeze_reset (self);
147
148   G_OBJECT_CLASS (parent_class)->finalize (object);
149 }
150
151 static void
152 gst_image_freeze_reset (GstImageFreeze * self)
153 {
154   GST_DEBUG_OBJECT (self, "Resetting internal state");
155
156   GST_OBJECT_LOCK (self);
157   gst_buffer_replace (&self->buffer, NULL);
158
159   gst_segment_init (&self->segment, GST_FORMAT_TIME);
160   self->need_segment = TRUE;
161   gst_event_replace (&self->close_segment, NULL);
162
163   self->fps_n = self->fps_d = 0;
164   self->offset = 0;
165   GST_OBJECT_UNLOCK (self);
166
167   g_atomic_int_set (&self->seeking, 0);
168 }
169
170 static gboolean
171 gst_image_freeze_sink_setcaps (GstPad * pad, GstCaps * caps)
172 {
173   GstImageFreeze *self = GST_IMAGE_FREEZE (gst_pad_get_parent (pad));
174   gboolean ret = FALSE;
175   GstStructure *s;
176   gint fps_n, fps_d;
177   GstCaps *othercaps, *intersection;
178   guint i, n;
179
180   caps = gst_caps_make_writable (gst_caps_ref (caps));
181
182   GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps);
183
184   s = gst_caps_get_structure (caps, 0);
185
186   /* 1. Remove framerate */
187   gst_structure_remove_field (s, "framerate");
188   gst_structure_set (s, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
189       NULL);
190
191   /* 2. Intersect with template caps */
192   othercaps = (GstCaps *) gst_pad_get_pad_template_caps (pad);
193   intersection = gst_caps_intersect (caps, othercaps);
194   GST_DEBUG_OBJECT (pad, "Intersecting: %" GST_PTR_FORMAT, caps);
195   GST_DEBUG_OBJECT (pad, "with: %" GST_PTR_FORMAT, othercaps);
196   GST_DEBUG_OBJECT (pad, "gave: %" GST_PTR_FORMAT, intersection);
197   gst_caps_unref (caps);
198   caps = intersection;
199   intersection = othercaps = NULL;
200
201   /* 3. Intersect with downstream peer caps */
202   othercaps = gst_pad_peer_get_caps (self->srcpad);
203   if (othercaps) {
204     intersection = gst_caps_intersect (caps, othercaps);
205     GST_DEBUG_OBJECT (pad, "Intersecting: %" GST_PTR_FORMAT, caps);
206     GST_DEBUG_OBJECT (pad, "with: %" GST_PTR_FORMAT, othercaps);
207     GST_DEBUG_OBJECT (pad, "gave: %" GST_PTR_FORMAT, intersection);
208     gst_caps_unref (othercaps);
209     gst_caps_unref (caps);
210     caps = intersection;
211     intersection = othercaps = NULL;
212   }
213
214   /* 4. For every candidate check if it's accepted downstream
215    * and fixate framerate to nearest 25/1 */
216   n = gst_caps_get_size (caps);
217   for (i = 0; i < n; i++) {
218     GstCaps *candidate = gst_caps_new_empty ();
219     GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
220
221     gst_caps_append_structure (candidate, s);
222     if (gst_pad_peer_accept_caps (self->srcpad, candidate)) {
223       if (gst_structure_has_field_typed (s, "framerate", GST_TYPE_FRACTION) ||
224           gst_structure_fixate_field_nearest_fraction (s, "framerate", 25, 1)) {
225         gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d);
226         if (fps_d != 0) {
227           GST_OBJECT_LOCK (self);
228           self->fps_n = fps_n;
229           self->fps_d = fps_d;
230           GST_OBJECT_UNLOCK (self);
231           GST_DEBUG_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, candidate);
232           gst_pad_set_caps (self->srcpad, candidate);
233           gst_caps_unref (candidate);
234           ret = TRUE;
235           goto done;
236         } else {
237           GST_WARNING_OBJECT (pad, "Invalid caps with framerate %d/%d", fps_n,
238               fps_d);
239         }
240       }
241     }
242     gst_caps_unref (candidate);
243   }
244
245 done:
246   if (!ret)
247     GST_ERROR_OBJECT (pad, "No usable caps found");
248
249   gst_caps_unref (caps);
250   gst_object_unref (self);
251
252   return ret;
253 }
254
255 static GstCaps *
256 gst_image_freeze_sink_getcaps (GstPad * pad)
257 {
258   GstImageFreeze *self = GST_IMAGE_FREEZE (gst_pad_get_parent (pad));
259   GstCaps *ret, *tmp;
260   guint i, n;
261
262   if (GST_PAD_CAPS (pad)) {
263     ret = gst_caps_copy (GST_PAD_CAPS (pad));
264     goto done;
265   }
266
267   tmp = gst_pad_peer_get_caps (self->srcpad);
268   if (tmp) {
269     ret = gst_caps_intersect (tmp, gst_pad_get_pad_template_caps (pad));
270     gst_caps_unref (tmp);
271   } else {
272     ret = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
273   }
274
275   n = gst_caps_get_size (ret);
276   for (i = 0; i < n; i++) {
277     GstStructure *s = gst_caps_get_structure (ret, i);
278
279     gst_structure_remove_field (s, "framerate");
280     gst_structure_set (s, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT,
281         1, NULL);
282   }
283
284 done:
285   gst_object_unref (self);
286
287   GST_LOG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, ret);
288
289   return ret;
290 }
291
292 static gboolean
293 gst_image_freeze_sink_query (GstPad * pad, GstQuery * query)
294 {
295   GstImageFreeze *self = GST_IMAGE_FREEZE (gst_pad_get_parent (pad));
296   gboolean ret;
297   GstPad *peer = gst_pad_get_peer (self->srcpad);
298
299   GST_LOG_OBJECT (pad, "Handling query of type '%s'",
300       gst_query_type_get_name (GST_QUERY_TYPE (query)));
301
302   if (!peer) {
303     GST_INFO_OBJECT (pad, "No peer yet, dropping query");
304     ret = FALSE;
305   } else {
306     ret = gst_pad_query (peer, query);
307     gst_object_unref (peer);
308   }
309
310   gst_object_unref (self);
311   return ret;
312 }
313
314 static GstFlowReturn
315 gst_image_freeze_sink_bufferalloc (GstPad * pad, guint64 offset, guint size,
316     GstCaps * caps, GstBuffer ** buf)
317 {
318   GstImageFreeze *self = GST_IMAGE_FREEZE (gst_pad_get_parent (pad));
319   GstFlowReturn ret;
320   gboolean do_alloc;
321
322   GST_LOG_OBJECT (pad, "Allocating buffer with offset 0x%" G_GINT64_MODIFIER
323       "x and size %u with caps: %" GST_PTR_FORMAT, offset, size, caps);
324
325   *buf = NULL;
326
327   GST_OBJECT_LOCK (self);
328   do_alloc = self->buffer == NULL;
329   GST_OBJECT_UNLOCK (self);
330
331   if (do_alloc) {
332     do {
333       ret = gst_pad_alloc_buffer (self->srcpad, offset, size, caps, buf);
334     } while (ret == GST_FLOW_WRONG_STATE && g_atomic_int_get (&self->seeking));
335
336     if (G_UNLIKELY (ret != GST_FLOW_OK))
337       GST_ERROR_OBJECT (pad, "Allocating buffer failed: %s",
338           gst_flow_get_name (ret));
339   } else {
340     /* Let upstream go EOS if we already have a buffer */
341     ret = GST_FLOW_UNEXPECTED;
342   }
343
344   gst_object_unref (self);
345
346   return ret;
347 }
348
349 static gboolean
350 gst_image_freeze_convert (GstImageFreeze * self,
351     GstFormat src_format, gint64 src_value,
352     GstFormat * dest_format, gint64 * dest_value)
353 {
354   gboolean ret = FALSE;
355
356   if (src_format == *dest_format) {
357     *dest_value = src_value;
358     return TRUE;
359   }
360
361   if (src_value == -1) {
362     *dest_value = -1;
363     return TRUE;
364   }
365
366   switch (src_format) {
367     case GST_FORMAT_DEFAULT:{
368       switch (*dest_format) {
369         case GST_FORMAT_TIME:
370           GST_OBJECT_LOCK (self);
371           if (self->fps_n == 0)
372             *dest_value = -1;
373           else
374             *dest_value =
375                 gst_util_uint64_scale (src_value, GST_SECOND * self->fps_d,
376                 self->fps_n);
377           GST_OBJECT_UNLOCK (self);
378           ret = TRUE;
379           break;
380         default:
381           break;
382       }
383     }
384     case GST_FORMAT_TIME:{
385       switch (*dest_format) {
386         case GST_FORMAT_DEFAULT:
387           GST_OBJECT_LOCK (self);
388           *dest_value =
389               gst_util_uint64_scale (src_value, self->fps_n,
390               self->fps_d * GST_SECOND);
391           GST_OBJECT_UNLOCK (self);
392           ret = TRUE;
393           break;
394         default:
395           break;
396       }
397
398     }
399     default:
400       break;
401   }
402
403   return ret;
404 }
405
406 static const GstQueryType *
407 gst_image_freeze_src_query_type (GstPad * pad)
408 {
409   static const GstQueryType types[] = {
410     GST_QUERY_POSITION,
411     GST_QUERY_DURATION,
412     GST_QUERY_SEEKING,
413     GST_QUERY_CONVERT,
414     0
415   };
416
417   return types;
418 }
419
420 static gboolean
421 gst_image_freeze_src_query (GstPad * pad, GstQuery * query)
422 {
423   GstImageFreeze *self = GST_IMAGE_FREEZE (gst_pad_get_parent (pad));
424   gboolean ret = FALSE;
425
426   GST_LOG_OBJECT (pad, "Handling query of type '%s'",
427       gst_query_type_get_name (GST_QUERY_TYPE (query)));
428
429   switch (GST_QUERY_TYPE (query)) {
430     case GST_QUERY_CONVERT:{
431       GstFormat src_format, dest_format;
432       gint64 src_value, dest_value;
433
434       gst_query_parse_convert (query, &src_format, &src_value, &dest_format,
435           &dest_value);
436       ret =
437           gst_image_freeze_convert (self, src_format, src_value, &dest_format,
438           &dest_value);
439       if (ret)
440         gst_query_set_convert (query, src_format, src_value, dest_format,
441             dest_value);
442       break;
443     }
444     case GST_QUERY_POSITION:{
445       GstFormat format;
446       gint64 position;
447
448       gst_query_parse_position (query, &format, NULL);
449       switch (format) {
450         case GST_FORMAT_DEFAULT:{
451           GST_OBJECT_LOCK (self);
452           position = self->offset;
453           GST_OBJECT_UNLOCK (self);
454           ret = TRUE;
455         }
456         case GST_FORMAT_TIME:{
457           GST_OBJECT_LOCK (self);
458           position = self->segment.last_stop;
459           GST_OBJECT_UNLOCK (self);
460           ret = TRUE;
461         }
462         default:
463           break;
464       }
465
466       if (ret) {
467         gst_query_set_position (query, format, position);
468         GST_DEBUG_OBJECT (pad,
469             "Returning position %" G_GINT64_FORMAT " in format %s", position,
470             gst_format_get_name (format));
471       } else {
472         GST_DEBUG_OBJECT (pad, "Position query failed");
473       }
474       break;
475     }
476     case GST_QUERY_DURATION:{
477       GstFormat format;
478       gint64 duration;
479
480       gst_query_parse_duration (query, &format, NULL);
481       switch (format) {
482         case GST_FORMAT_TIME:{
483           GST_OBJECT_LOCK (self);
484           duration = self->segment.stop;
485           GST_OBJECT_UNLOCK (self);
486           ret = TRUE;
487         }
488         case GST_FORMAT_DEFAULT:{
489           GST_OBJECT_LOCK (self);
490           duration = self->segment.stop;
491           if (duration != -1)
492             duration =
493                 gst_util_uint64_scale (duration, self->fps_n,
494                 GST_SECOND * self->fps_d);
495           GST_OBJECT_UNLOCK (self);
496           ret = TRUE;
497         }
498         default:
499           break;
500       }
501
502       if (ret) {
503         gst_query_set_duration (query, format, duration);
504         GST_DEBUG_OBJECT (pad,
505             "Returning duration %" G_GINT64_FORMAT " in format %s", duration,
506             gst_format_get_name (format));
507       } else {
508         GST_DEBUG_OBJECT (pad, "Duration query failed");
509       }
510       break;
511     }
512     case GST_QUERY_SEEKING:{
513       GstFormat format;
514       gboolean seekable;
515
516       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
517       seekable = (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT);
518
519       gst_query_set_seeking (query, format, seekable, (seekable ? 0 : -1), -1);
520       ret = TRUE;
521       break;
522     }
523     default:
524       ret = FALSE;
525       break;
526   }
527
528   gst_object_unref (self);
529   return ret;
530 }
531
532
533 static gboolean
534 gst_image_freeze_sink_event (GstPad * pad, GstEvent * event)
535 {
536   GstImageFreeze *self = GST_IMAGE_FREEZE (gst_pad_get_parent (pad));
537   gboolean ret;
538
539   GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
540
541   switch (GST_EVENT_TYPE (event)) {
542     case GST_EVENT_EOS:
543     case GST_EVENT_NEWSEGMENT:
544       GST_DEBUG_OBJECT (pad, "Dropping event");
545       gst_event_unref (event);
546       ret = TRUE;
547       break;
548     case GST_EVENT_FLUSH_START:
549       gst_image_freeze_reset (self);
550       /* fall through */
551     default:
552       ret = gst_pad_push_event (self->srcpad, event);
553       break;
554   }
555
556   gst_object_unref (self);
557   return ret;
558 }
559
560 static gboolean
561 gst_image_freeze_src_event (GstPad * pad, GstEvent * event)
562 {
563   GstImageFreeze *self = GST_IMAGE_FREEZE (gst_pad_get_parent (pad));
564   gboolean ret;
565
566   GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
567
568   switch (GST_EVENT_TYPE (event)) {
569     case GST_EVENT_NAVIGATION:
570     case GST_EVENT_QOS:
571     case GST_EVENT_LATENCY:
572     case GST_EVENT_STEP:
573       GST_DEBUG_OBJECT (pad, "Dropping event");
574       gst_event_unref (event);
575       ret = TRUE;
576       break;
577     case GST_EVENT_SEEK:{
578       gdouble rate;
579       GstFormat format;
580       GstSeekFlags flags;
581       GstSeekType start_type, stop_type;
582       gint64 start, stop;
583       gint64 last_stop;
584       gboolean start_task;
585       gboolean flush;
586
587       gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
588           &stop_type, &stop);
589       gst_event_unref (event);
590
591       flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
592
593       if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
594         GST_ERROR_OBJECT (pad, "Seek in invalid format: %s",
595             gst_format_get_name (format));
596         ret = FALSE;
597         break;
598       }
599
600       if (format == GST_FORMAT_DEFAULT) {
601         format = GST_FORMAT_TIME;
602         if (!gst_image_freeze_convert (self, GST_FORMAT_DEFAULT, start, &format,
603                 &start)
604             || !gst_image_freeze_convert (self, GST_FORMAT_DEFAULT, stop,
605                 &format, &stop)
606             || start == -1 || stop == -1) {
607           GST_ERROR_OBJECT (pad,
608               "Failed to convert seek from DEFAULT format into TIME format");
609           ret = FALSE;
610           break;
611         }
612       }
613
614       if (flush) {
615         GstEvent *e;
616
617         g_atomic_int_set (&self->seeking, 1);
618         e = gst_event_new_flush_start ();
619         gst_pad_push_event (self->srcpad, e);
620       } else {
621         gst_pad_pause_task (self->srcpad);
622       }
623
624       GST_PAD_STREAM_LOCK (self->srcpad);
625
626       GST_OBJECT_LOCK (self);
627       gst_event_replace (&self->close_segment, NULL);
628       if (!flush) {
629         if (!self->need_segment && self->segment.rate >= 0) {
630           self->close_segment =
631               gst_event_new_new_segment_full (TRUE, self->segment.rate,
632               self->segment.applied_rate, self->segment.format,
633               self->segment.start, self->segment.last_stop, self->segment.time);
634         } else if (!self->need_segment) {
635           gint64 stop;
636
637           if ((stop = self->segment.stop) == -1)
638             stop = self->segment.duration;
639
640           self->close_segment =
641               gst_event_new_new_segment_full (TRUE, self->segment.rate,
642               self->segment.applied_rate, self->segment.format,
643               self->segment.last_stop, stop, self->segment.last_stop);
644         }
645       }
646
647       gst_segment_set_seek (&self->segment, rate, format, flags, start_type,
648           start, stop_type, stop, NULL);
649       self->need_segment = TRUE;
650       last_stop = self->segment.last_stop;
651
652       start_task = self->buffer != NULL;
653       GST_OBJECT_UNLOCK (self);
654
655       if (flush) {
656         GstEvent *e;
657
658         e = gst_event_new_flush_stop ();
659         gst_pad_push_event (self->srcpad, e);
660         g_atomic_int_set (&self->seeking, 0);
661       }
662
663       if (flags & GST_SEEK_FLAG_SEGMENT) {
664         GstMessage *m;
665
666         m = gst_message_new_segment_start (GST_OBJECT (self),
667             format, last_stop);
668         gst_element_post_message (GST_ELEMENT (self), m);
669       }
670
671       GST_PAD_STREAM_UNLOCK (self->srcpad);
672
673       GST_DEBUG_OBJECT (pad, "Seek successful");
674
675       if (start_task)
676         gst_pad_start_task (self->srcpad,
677             (GstTaskFunction) gst_image_freeze_src_loop, self->srcpad);
678       ret = TRUE;
679       break;
680     }
681     case GST_EVENT_FLUSH_START:
682       gst_image_freeze_reset (self);
683       /* fall through */
684     default:
685       ret = gst_pad_push_event (self->sinkpad, event);
686       break;
687   }
688
689   gst_object_unref (self);
690   return ret;
691 }
692
693 static GstFlowReturn
694 gst_image_freeze_sink_chain (GstPad * pad, GstBuffer * buffer)
695 {
696   GstImageFreeze *self = GST_IMAGE_FREEZE (GST_PAD_PARENT (pad));
697
698   GST_OBJECT_LOCK (self);
699   if (self->buffer) {
700     GST_DEBUG_OBJECT (pad, "Already have a buffer, dropping");
701     gst_buffer_unref (buffer);
702     GST_OBJECT_UNLOCK (self);
703     return GST_FLOW_UNEXPECTED;
704   }
705
706   self->buffer = buffer;
707   GST_OBJECT_UNLOCK (self);
708
709   gst_pad_start_task (self->srcpad, (GstTaskFunction) gst_image_freeze_src_loop,
710       self->srcpad);
711   return GST_FLOW_OK;
712 }
713
714 static void
715 gst_image_freeze_src_loop (GstPad * pad)
716 {
717   GstImageFreeze *self = GST_IMAGE_FREEZE (GST_PAD_PARENT (pad));
718   GstBuffer *buffer;
719   guint64 offset;
720   GstClockTime timestamp, duration;
721   gint64 cstart, cstop;
722   gboolean in_seg, eos;
723
724   GST_OBJECT_LOCK (self);
725   if (!self->buffer) {
726     GST_ERROR_OBJECT (pad, "Have no buffer yet");
727     GST_OBJECT_UNLOCK (self);
728     gst_pad_pause_task (self->srcpad);
729     return;
730   }
731   buffer = gst_buffer_ref (self->buffer);
732   buffer = gst_buffer_make_metadata_writable (buffer);
733   GST_OBJECT_UNLOCK (self);
734
735   if (self->close_segment) {
736     GST_DEBUG_OBJECT (pad, "Closing previous segment");
737     gst_pad_push_event (self->srcpad, self->close_segment);
738     self->close_segment = NULL;
739   }
740
741   if (self->need_segment) {
742     GstEvent *e;
743
744     GST_DEBUG_OBJECT (pad, "Pushing NEWSEGMENT event: %" GST_SEGMENT_FORMAT,
745         &self->segment);
746     e = gst_event_new_new_segment_full (FALSE, self->segment.rate,
747         self->segment.applied_rate, self->segment.format, self->segment.start,
748         self->segment.stop, self->segment.start);
749
750     GST_OBJECT_LOCK (self);
751     if (self->segment.rate >= 0) {
752       self->offset =
753           gst_util_uint64_scale (self->segment.start, self->fps_n,
754           self->fps_d * GST_SECOND);
755     } else {
756       self->offset =
757           gst_util_uint64_scale (self->segment.stop, self->fps_n,
758           self->fps_d * GST_SECOND);
759     }
760     GST_OBJECT_UNLOCK (self);
761
762     self->need_segment = FALSE;
763
764     gst_pad_push_event (self->srcpad, e);
765   }
766
767   GST_OBJECT_LOCK (self);
768   offset = self->offset;
769
770   if (self->fps_n != 0) {
771     timestamp =
772         gst_util_uint64_scale (offset, self->fps_d * GST_SECOND, self->fps_n);
773     duration = gst_util_uint64_scale_int (GST_SECOND, self->fps_d, self->fps_n);
774   } else {
775     timestamp = self->segment.start;
776     duration = GST_CLOCK_TIME_NONE;
777   }
778   eos = (self->fps_n == 0 && offset > 0) ||
779       (self->segment.rate >= 0 && self->segment.stop != -1
780       && timestamp > self->segment.stop) || (self->segment.rate < 0
781       && offset == 0) || (self->segment.rate < 0
782       && self->segment.start != -1
783       && timestamp + duration < self->segment.start);
784
785   if (self->fps_n == 0 && offset > 0)
786     in_seg = FALSE;
787   else
788     in_seg =
789         gst_segment_clip (&self->segment, GST_FORMAT_TIME, timestamp,
790         timestamp + duration, &cstart, &cstop);
791
792   if (in_seg)
793     gst_segment_set_last_stop (&self->segment, GST_FORMAT_TIME, cstart);
794
795   if (self->segment.rate >= 0)
796     self->offset++;
797   else
798     self->offset--;
799   GST_OBJECT_UNLOCK (self);
800
801   GST_DEBUG_OBJECT (pad, "Handling buffer with timestamp %" GST_TIME_FORMAT,
802       GST_TIME_ARGS (timestamp));
803
804   if (in_seg) {
805     GstFlowReturn ret;
806
807     GST_BUFFER_TIMESTAMP (buffer) = cstart;
808     GST_BUFFER_DURATION (buffer) = cstop - cstart;
809     GST_BUFFER_OFFSET (buffer) = offset;
810     GST_BUFFER_OFFSET_END (buffer) = offset + 1;
811     gst_buffer_set_caps (buffer, GST_PAD_CAPS (self->srcpad));
812     ret = gst_pad_push (self->srcpad, buffer);
813     GST_DEBUG_OBJECT (pad, "Pushing buffer resulted in %s",
814         gst_flow_get_name (ret));
815     if (ret != GST_FLOW_OK)
816       gst_pad_pause_task (self->srcpad);
817   } else {
818     gst_buffer_unref (buffer);
819   }
820
821   if (eos) {
822     if ((self->segment.flags & GST_SEEK_FLAG_SEGMENT)) {
823       GstMessage *m;
824
825       GST_DEBUG_OBJECT (pad, "Sending segment done at end of segment");
826       if (self->segment.rate >= 0)
827         m = gst_message_new_segment_done (GST_OBJECT_CAST (self),
828             GST_FORMAT_TIME, self->segment.stop);
829       else
830         m = gst_message_new_segment_done (GST_OBJECT_CAST (self),
831             GST_FORMAT_TIME, self->segment.start);
832       gst_element_post_message (GST_ELEMENT_CAST (self), m);
833     } else {
834       GST_DEBUG_OBJECT (pad, "Sending EOS at end of segment");
835       gst_pad_push_event (self->srcpad, gst_event_new_eos ());
836     }
837     gst_pad_pause_task (self->srcpad);
838   }
839 }
840
841 static GstStateChangeReturn
842 gst_image_freeze_change_state (GstElement * element, GstStateChange transition)
843 {
844   GstImageFreeze *self = GST_IMAGE_FREEZE (element);
845   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
846
847   switch (transition) {
848     case GST_STATE_CHANGE_READY_TO_PAUSED:
849       gst_image_freeze_reset (self);
850       break;
851     case GST_STATE_CHANGE_PAUSED_TO_READY:
852       gst_pad_stop_task (self->srcpad);
853       gst_image_freeze_reset (self);
854       break;
855     default:
856       break;
857   }
858
859   if (GST_ELEMENT_CLASS (parent_class)->change_state)
860     ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
861
862   switch (transition) {
863     default:
864       break;
865   }
866
867   return ret;
868 }
869
870 static gboolean
871 plugin_init (GstPlugin * plugin)
872 {
873   GST_DEBUG_CATEGORY_INIT (gst_image_freeze_debug, "imagefreeze", 0,
874       "imagefreeze element");
875
876   if (!gst_element_register (plugin, "imagefreeze", GST_RANK_NONE,
877           GST_TYPE_IMAGE_FREEZE))
878     return FALSE;
879
880   return TRUE;
881 }
882
883 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
884     GST_VERSION_MINOR,
885     "imagefreeze",
886     "Still frame stream generator",
887     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)