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