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