1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /**
21  * SECTION:element-videorate
22  * @title: videorate
23  *
24  * This element takes an incoming stream of timestamped video frames.
25  * It will produce a perfect stream that matches the source pad's framerate.
26  *
27  * The correction is performed by dropping and duplicating frames, no fancy
28  * algorithm is used to interpolate frames (yet).
29  *
30  * By default the element will simply negotiate the same framerate on its
31  * source and sink pad.
32  *
33  * This operation is useful to link to elements that require a perfect stream.
34  * Typical examples are formats that do not store timestamps for video frames,
35  * but only store a framerate, like Ogg and AVI.
36  *
37  * A conversion to a specific framerate can be forced by using filtered caps on
38  * the source pad.
39  *
40  * The properties #GstVideoRate:in, #GstVideoRate:out, #GstVideoRate:duplicate
41  * and #GstVideoRate:drop can be read to obtain information about number of
42  * input frames, output frames, dropped frames (i.e. the number of unused input
43  * frames) and duplicated frames (i.e. the number of times an input frame was
44  * duplicated, beside being used normally).
45  *
46  * An input stream that needs no adjustments will thus never have dropped or
47  * duplicated frames.
48  *
49  * When the #GstVideoRate:silent property is set to FALSE, a GObject property
50  * notification will be emitted whenever one of the #GstVideoRate:duplicate or
51  * #GstVideoRate:drop values changes.
52  * This can potentially cause performance degradation.
53  * Note that property notification will happen from the streaming thread, so
54  * applications should be prepared for this.
55  *
56  * The property #GstVideoRate:rate allows the modification of video speed by a
57  * certain factor. It must not be confused with framerate. Think of rate as
58  * speed and framerate as flow.
59  *
60  * ## Example pipelines
61  * |[
62  * gst-launch-1.0 -v uridecodebin uri=file:///path/to/video.ogg ! videoconvert ! videoscale ! videorate ! video/x-raw,framerate=15/1 ! autovideosink
63  * ]|
64  *  Decode a video file and adjust the framerate to 15 fps before playing.
65  * To create a test Ogg/Theora file refer to the documentation of theoraenc.
66  * |[
67  * gst-launch-1.0 -v v4l2src ! videorate ! video/x-raw,framerate=25/2 ! theoraenc ! oggmux ! filesink location=recording.ogg
68  * ]|
69  *  Capture video from a V4L device, and adjust the stream to 12.5 fps before
70  * encoding to Ogg/Theora.
71  * |[
72  * gst-launch-1.0 -v uridecodebin uri=file:///path/to/video.ogg ! videoconvert ! videoscale ! videorate ! video/x-raw,framerate=1/5 ! jpegenc ! multifilesink location=snapshot-%05d.jpg
73  * ]|
74  *  Decode a video file and save a snapshot every 5 seconds as consecutively numbered jpeg file.
75  *
76  */
77 
78 #ifdef HAVE_CONFIG_H
79 #include "config.h"
80 #endif
81 
82 #include "gstvideorate.h"
83 #include <gst/video/video.h>
84 
85 GST_DEBUG_CATEGORY_STATIC (video_rate_debug);
86 #define GST_CAT_DEFAULT video_rate_debug
87 
88 /* GstVideoRate signals and args */
89 enum
90 {
91   /* FILL ME */
92   LAST_SIGNAL
93 };
94 
95 #define DEFAULT_SILENT          TRUE
96 #define DEFAULT_NEW_PREF        1.0
97 #define DEFAULT_SKIP_TO_FIRST   FALSE
98 #define DEFAULT_DROP_ONLY       FALSE
99 #define DEFAULT_AVERAGE_PERIOD  0
100 #define DEFAULT_MAX_RATE        G_MAXINT
101 #define DEFAULT_RATE            1.0
102 #define DEFAULT_MAX_DUPLICATION_TIME      0
103 
104 enum
105 {
106   PROP_0,
107   PROP_IN,
108   PROP_OUT,
109   PROP_DUP,
110   PROP_DROP,
111   PROP_SILENT,
112   PROP_NEW_PREF,
113   PROP_SKIP_TO_FIRST,
114   PROP_DROP_ONLY,
115   PROP_AVERAGE_PERIOD,
116   PROP_MAX_RATE,
117   PROP_RATE,
118   PROP_MAX_DUPLICATION_TIME
119 };
120 
121 static GstStaticPadTemplate gst_video_rate_src_template =
122     GST_STATIC_PAD_TEMPLATE ("src",
123     GST_PAD_SRC,
124     GST_PAD_ALWAYS,
125     GST_STATIC_CAPS ("video/x-raw(ANY);" "video/x-bayer(ANY);"
126         "image/jpeg(ANY);" "image/png(ANY)")
127     );
128 
129 static GstStaticPadTemplate gst_video_rate_sink_template =
130     GST_STATIC_PAD_TEMPLATE ("sink",
131     GST_PAD_SINK,
132     GST_PAD_ALWAYS,
133     GST_STATIC_CAPS ("video/x-raw(ANY);" "video/x-bayer(ANY);"
134         "image/jpeg(ANY);" "image/png(ANY)")
135     );
136 
137 static void gst_video_rate_swap_prev (GstVideoRate * videorate,
138     GstBuffer * buffer, gint64 time);
139 static gboolean gst_video_rate_sink_event (GstBaseTransform * trans,
140     GstEvent * event);
141 static gboolean gst_video_rate_src_event (GstBaseTransform * trans,
142     GstEvent * event);
143 static gboolean gst_video_rate_query (GstBaseTransform * trans,
144     GstPadDirection direction, GstQuery * query);
145 
146 static gboolean gst_video_rate_setcaps (GstBaseTransform * trans,
147     GstCaps * in_caps, GstCaps * out_caps);
148 
149 static GstCaps *gst_video_rate_transform_caps (GstBaseTransform * trans,
150     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
151 
152 static GstCaps *gst_video_rate_fixate_caps (GstBaseTransform * trans,
153     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
154 
155 static GstFlowReturn gst_video_rate_transform_ip (GstBaseTransform * trans,
156     GstBuffer * buf);
157 
158 static gboolean gst_video_rate_propose_allocation (GstBaseTransform * trans,
159     GstQuery * decide_query, GstQuery * query);
160 
161 static gboolean gst_video_rate_start (GstBaseTransform * trans);
162 static gboolean gst_video_rate_stop (GstBaseTransform * trans);
163 
164 
165 static void gst_video_rate_set_property (GObject * object,
166     guint prop_id, const GValue * value, GParamSpec * pspec);
167 static void gst_video_rate_get_property (GObject * object,
168     guint prop_id, GValue * value, GParamSpec * pspec);
169 
170 static GParamSpec *pspec_drop = NULL;
171 static GParamSpec *pspec_duplicate = NULL;
172 
173 #define gst_video_rate_parent_class parent_class
174 G_DEFINE_TYPE (GstVideoRate, gst_video_rate, GST_TYPE_BASE_TRANSFORM);
175 
176 static void
gst_video_rate_class_init(GstVideoRateClass * klass)177 gst_video_rate_class_init (GstVideoRateClass * klass)
178 {
179   GObjectClass *object_class = G_OBJECT_CLASS (klass);
180   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
181   GstBaseTransformClass *base_class = GST_BASE_TRANSFORM_CLASS (klass);
182 
183   object_class->set_property = gst_video_rate_set_property;
184   object_class->get_property = gst_video_rate_get_property;
185 
186   base_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_rate_setcaps);
187   base_class->transform_caps =
188       GST_DEBUG_FUNCPTR (gst_video_rate_transform_caps);
189   base_class->transform_ip = GST_DEBUG_FUNCPTR (gst_video_rate_transform_ip);
190   base_class->sink_event = GST_DEBUG_FUNCPTR (gst_video_rate_sink_event);
191   base_class->src_event = GST_DEBUG_FUNCPTR (gst_video_rate_src_event);
192   base_class->start = GST_DEBUG_FUNCPTR (gst_video_rate_start);
193   base_class->stop = GST_DEBUG_FUNCPTR (gst_video_rate_stop);
194   base_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_rate_fixate_caps);
195   base_class->query = GST_DEBUG_FUNCPTR (gst_video_rate_query);
196   base_class->propose_allocation =
197       GST_DEBUG_FUNCPTR (gst_video_rate_propose_allocation);
198 
199   g_object_class_install_property (object_class, PROP_IN,
200       g_param_spec_uint64 ("in", "In",
201           "Number of input frames", 0, G_MAXUINT64, 0,
202           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
203   g_object_class_install_property (object_class, PROP_OUT,
204       g_param_spec_uint64 ("out", "Out", "Number of output frames", 0,
205           G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
206   pspec_duplicate = g_param_spec_uint64 ("duplicate", "Duplicate",
207       "Number of duplicated frames", 0, G_MAXUINT64, 0,
208       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
209   g_object_class_install_property (object_class, PROP_DUP, pspec_duplicate);
210   pspec_drop = g_param_spec_uint64 ("drop", "Drop", "Number of dropped frames",
211       0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
212   g_object_class_install_property (object_class, PROP_DROP, pspec_drop);
213   g_object_class_install_property (object_class, PROP_SILENT,
214       g_param_spec_boolean ("silent", "silent",
215           "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT,
216           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
217   g_object_class_install_property (object_class, PROP_NEW_PREF,
218       g_param_spec_double ("new-pref", "New Pref",
219           "Value indicating how much to prefer new frames (unused)", 0.0, 1.0,
220           DEFAULT_NEW_PREF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221 
222   /**
223    * GstVideoRate:skip-to-first:
224    *
225    * Don't produce buffers before the first one we receive.
226    */
227   g_object_class_install_property (object_class, PROP_SKIP_TO_FIRST,
228       g_param_spec_boolean ("skip-to-first", "Skip to first buffer",
229           "Don't produce buffers before the first one we receive",
230           DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
231 
232   /**
233    * GstVideoRate:drop-only:
234    *
235    * Only drop frames, no duplicates are produced.
236    */
237   g_object_class_install_property (object_class, PROP_DROP_ONLY,
238       g_param_spec_boolean ("drop-only", "Only Drop",
239           "Only drop frames, no duplicates are produced",
240           DEFAULT_DROP_ONLY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
241 
242   /**
243    * GstVideoRate:average-period:
244    *
245    * Arrange for maximum framerate by dropping frames beyond a certain framerate,
246    * where the framerate is calculated using a moving average over the
247    * configured.
248    */
249   g_object_class_install_property (object_class, PROP_AVERAGE_PERIOD,
250       g_param_spec_uint64 ("average-period", "Period over which to average",
251           "Period over which to average the framerate (in ns) (0 = disabled)",
252           0, G_MAXINT64, DEFAULT_AVERAGE_PERIOD,
253           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
254 
255   /**
256    * GstVideoRate:max-rate:
257    *
258    * maximum framerate to pass through
259    */
260   g_object_class_install_property (object_class, PROP_MAX_RATE,
261       g_param_spec_int ("max-rate", "maximum framerate",
262           "Maximum framerate allowed to pass through "
263           "(in frames per second, implies drop-only)",
264           1, G_MAXINT, DEFAULT_MAX_RATE,
265           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
266 
267   /**
268    * GstVideoRate:rate:
269    *
270    * Factor of speed for frame displaying
271    *
272    * Since: 1.12
273    */
274   g_object_class_install_property (object_class, PROP_RATE,
275       g_param_spec_double ("rate", "Rate",
276           "Factor of speed for frame displaying", 0.0, G_MAXDOUBLE,
277           DEFAULT_RATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
278           GST_PARAM_MUTABLE_READY));
279 
280   /**
281    * GstVideoRate:max-duplication-time:
282    *
283    * Duplicate frames only if the gap between two consecutive frames does not
284    * exceed this duration.
285    *
286    * Since: 1.16
287    */
288   g_object_class_install_property (object_class, PROP_MAX_DUPLICATION_TIME,
289       g_param_spec_uint64 ("max-duplication-time",
290           "Maximum time to duplicate a frame",
291           "Do not duplicate frames if the gap exceeds this period "
292           "(in ns) (0 = disabled)",
293           0, G_MAXUINT64, DEFAULT_MAX_DUPLICATION_TIME,
294           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
295 
296   gst_element_class_set_static_metadata (element_class,
297       "Video rate adjuster", "Filter/Effect/Video",
298       "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
299       "Wim Taymans <wim@fluendo.com>");
300 
301   gst_element_class_add_static_pad_template (element_class,
302       &gst_video_rate_sink_template);
303   gst_element_class_add_static_pad_template (element_class,
304       &gst_video_rate_src_template);
305 }
306 
307 static void
gst_value_fraction_get_extremes(const GValue * v,gint * min_num,gint * min_denom,gint * max_num,gint * max_denom)308 gst_value_fraction_get_extremes (const GValue * v,
309     gint * min_num, gint * min_denom, gint * max_num, gint * max_denom)
310 {
311   if (GST_VALUE_HOLDS_FRACTION (v)) {
312     *min_num = *max_num = gst_value_get_fraction_numerator (v);
313     *min_denom = *max_denom = gst_value_get_fraction_denominator (v);
314   } else if (GST_VALUE_HOLDS_FRACTION_RANGE (v)) {
315     const GValue *min, *max;
316 
317     min = gst_value_get_fraction_range_min (v);
318     *min_num = gst_value_get_fraction_numerator (min);
319     *min_denom = gst_value_get_fraction_denominator (min);
320 
321     max = gst_value_get_fraction_range_max (v);
322     *max_num = gst_value_get_fraction_numerator (max);
323     *max_denom = gst_value_get_fraction_denominator (max);
324   } else if (GST_VALUE_HOLDS_LIST (v)) {
325     gint min_n = G_MAXINT, min_d = 1, max_n = 0, max_d = 1;
326     int i, n;
327 
328     *min_num = G_MAXINT;
329     *min_denom = 1;
330     *max_num = 0;
331     *max_denom = 1;
332 
333     n = gst_value_list_get_size (v);
334 
335     g_assert (n > 0);
336 
337     for (i = 0; i < n; i++) {
338       const GValue *t = gst_value_list_get_value (v, i);
339 
340       gst_value_fraction_get_extremes (t, &min_n, &min_d, &max_n, &max_d);
341       if (gst_util_fraction_compare (min_n, min_d, *min_num, *min_denom) < 0) {
342         *min_num = min_n;
343         *min_denom = min_d;
344       }
345 
346       if (gst_util_fraction_compare (max_n, max_d, *max_num, *max_denom) > 0) {
347         *max_num = max_n;
348         *max_denom = max_d;
349       }
350     }
351   } else {
352     g_warning ("Unknown type for framerate");
353     *min_num = 0;
354     *min_denom = 1;
355     *max_num = G_MAXINT;
356     *max_denom = 1;
357   }
358 }
359 
360 /* Clamp the framerate in a caps structure to be a smaller range then
361  * [1...max_rate], otherwise return false */
362 static gboolean
gst_video_max_rate_clamp_structure(GstStructure * s,gint maxrate,gint * min_num,gint * min_denom,gint * max_num,gint * max_denom)363 gst_video_max_rate_clamp_structure (GstStructure * s, gint maxrate,
364     gint * min_num, gint * min_denom, gint * max_num, gint * max_denom)
365 {
366   gboolean ret = FALSE;
367 
368   if (!gst_structure_has_field (s, "framerate")) {
369     /* No framerate field implies any framerate, clamping would result in
370      * [1..max_rate] so not a real subset */
371     goto out;
372   } else {
373     const GValue *v;
374     GValue intersection = { 0, };
375     GValue clamp = { 0, };
376     gint tmp_num, tmp_denom;
377 
378     g_value_init (&clamp, GST_TYPE_FRACTION_RANGE);
379     gst_value_set_fraction_range_full (&clamp, 0, 1, maxrate, 1);
380 
381     v = gst_structure_get_value (s, "framerate");
382     ret = gst_value_intersect (&intersection, v, &clamp);
383     g_value_unset (&clamp);
384 
385     if (!ret)
386       goto out;
387 
388     gst_value_fraction_get_extremes (&intersection,
389         min_num, min_denom, max_num, max_denom);
390 
391     gst_value_fraction_get_extremes (v,
392         &tmp_num, &tmp_denom, max_num, max_denom);
393 
394     if (gst_util_fraction_compare (*max_num, *max_denom, maxrate, 1) > 0) {
395       *max_num = maxrate;
396       *max_denom = 1;
397     }
398 
399     gst_structure_take_value (s, "framerate", &intersection);
400   }
401 
402 out:
403   return ret;
404 }
405 
406 static GstCaps *
gst_video_rate_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)407 gst_video_rate_transform_caps (GstBaseTransform * trans,
408     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
409 {
410   GstVideoRate *videorate = GST_VIDEO_RATE (trans);
411   GstCaps *ret;
412   GstStructure *s, *s1, *s2, *s3 = NULL;
413   int maxrate = g_atomic_int_get (&videorate->max_rate);
414   gint i;
415 
416   ret = gst_caps_new_empty ();
417 
418   for (i = 0; i < gst_caps_get_size (caps); i++) {
419     s = gst_caps_get_structure (caps, i);
420 
421     s1 = gst_structure_copy (s);
422 
423     if (videorate->updating_caps && direction == GST_PAD_SINK) {
424       GST_INFO_OBJECT (trans,
425           "Only updating caps %" GST_PTR_FORMAT " with framerate" " %d/%d",
426           caps, videorate->to_rate_numerator, videorate->to_rate_denominator);
427 
428       gst_structure_set (s1, "framerate", GST_TYPE_FRACTION,
429           videorate->to_rate_numerator, videorate->to_rate_denominator, NULL);
430       ret = gst_caps_merge_structure (ret, s1);
431 
432       continue;
433     }
434 
435     s2 = gst_structure_copy (s);
436     s3 = NULL;
437 
438     if (videorate->drop_only) {
439       gint min_num = 0, min_denom = 1;
440       gint max_num = G_MAXINT, max_denom = 1;
441 
442       /* Clamp the caps to our maximum rate as the first caps if possible */
443       if (!gst_video_max_rate_clamp_structure (s1, maxrate,
444               &min_num, &min_denom, &max_num, &max_denom)) {
445         min_num = 0;
446         min_denom = 1;
447         max_num = maxrate;
448         max_denom = 1;
449 
450         /* clamp wouldn't be a real subset of 1..maxrate, in this case the sink
451          * caps should become [1..maxrate], [1..maxint] and the src caps just
452          * [1..maxrate].  In case there was a caps incompatibility things will
453          * explode later as appropriate :)
454          *
455          * In case [X..maxrate] == [X..maxint], skip as we'll set it later
456          */
457         if (direction == GST_PAD_SRC && maxrate != G_MAXINT)
458           gst_structure_set (s1, "framerate", GST_TYPE_FRACTION_RANGE,
459               min_num, min_denom, maxrate, 1, NULL);
460         else {
461           gst_structure_free (s1);
462           s1 = NULL;
463         }
464       }
465 
466       if (direction == GST_PAD_SRC) {
467         /* We can accept anything as long as it's at least the minimal framerate
468          * the the sink needs */
469         gst_structure_set (s2, "framerate", GST_TYPE_FRACTION_RANGE,
470             min_num, min_denom, G_MAXINT, 1, NULL);
471 
472         /* Also allow unknown framerate, if it isn't already */
473         if (min_num != 0 || min_denom != 1) {
474           s3 = gst_structure_copy (s);
475           gst_structure_set (s3, "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
476         }
477       } else if (max_num != 0 || max_denom != 1) {
478         /* We can provide everything upto the maximum framerate at the src */
479         gst_structure_set (s2, "framerate", GST_TYPE_FRACTION_RANGE,
480             0, 1, max_num, max_denom, NULL);
481       }
482     } else if (direction == GST_PAD_SINK) {
483       gint min_num = 0, min_denom = 1;
484       gint max_num = G_MAXINT, max_denom = 1;
485 
486       if (!gst_video_max_rate_clamp_structure (s1, maxrate,
487               &min_num, &min_denom, &max_num, &max_denom)) {
488         gst_structure_free (s1);
489         s1 = NULL;
490       }
491       gst_structure_set (s2, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
492           maxrate, 1, NULL);
493     } else {
494       /* set the framerate as a range */
495       gst_structure_set (s2, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
496           G_MAXINT, 1, NULL);
497     }
498     if (s1 != NULL)
499       ret = gst_caps_merge_structure_full (ret, s1,
500           gst_caps_features_copy (gst_caps_get_features (caps, i)));
501     ret = gst_caps_merge_structure_full (ret, s2,
502         gst_caps_features_copy (gst_caps_get_features (caps, i)));
503     if (s3 != NULL)
504       ret = gst_caps_merge_structure_full (ret, s3,
505           gst_caps_features_copy (gst_caps_get_features (caps, i)));
506   }
507   if (filter) {
508     GstCaps *intersection;
509 
510     intersection =
511         gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
512     gst_caps_unref (ret);
513     ret = intersection;
514   }
515   return ret;
516 }
517 
518 static GstCaps *
gst_video_rate_fixate_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)519 gst_video_rate_fixate_caps (GstBaseTransform * trans,
520     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
521 {
522   GstStructure *s;
523   gint num, denom;
524   const GValue *par;
525 
526   s = gst_caps_get_structure (caps, 0);
527   if (G_UNLIKELY (!gst_structure_get_fraction (s, "framerate", &num, &denom)))
528     return othercaps;
529 
530   othercaps = gst_caps_truncate (othercaps);
531   othercaps = gst_caps_make_writable (othercaps);
532   s = gst_caps_get_structure (othercaps, 0);
533   gst_structure_fixate_field_nearest_fraction (s, "framerate", num, denom);
534 
535   if ((par = gst_structure_get_value (s, "pixel-aspect-ratio")))
536     gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
537 
538   return othercaps;
539 }
540 
541 static gboolean
gst_video_rate_setcaps(GstBaseTransform * trans,GstCaps * in_caps,GstCaps * out_caps)542 gst_video_rate_setcaps (GstBaseTransform * trans, GstCaps * in_caps,
543     GstCaps * out_caps)
544 {
545   GstVideoRate *videorate = GST_VIDEO_RATE (trans);
546   GstStructure *structure;
547   gboolean ret = TRUE;
548   gint rate_numerator, rate_denominator;
549 
550   GST_DEBUG_OBJECT (trans, "setcaps called in: %" GST_PTR_FORMAT
551       " out: %" GST_PTR_FORMAT, in_caps, out_caps);
552 
553   structure = gst_caps_get_structure (in_caps, 0);
554   if (!gst_structure_get_fraction (structure, "framerate",
555           &rate_numerator, &rate_denominator))
556     goto no_framerate;
557 
558   videorate->from_rate_numerator = rate_numerator;
559   videorate->from_rate_denominator = rate_denominator;
560 
561   structure = gst_caps_get_structure (out_caps, 0);
562   if (!gst_structure_get_fraction (structure, "framerate",
563           &rate_numerator, &rate_denominator))
564     goto no_framerate;
565 
566   /* out_frame_count is scaled by the frame rate caps when calculating next_ts.
567    * when the frame rate caps change, we must update base_ts and reset
568    * out_frame_count */
569   if (videorate->to_rate_numerator) {
570     videorate->base_ts +=
571         gst_util_uint64_scale (videorate->out_frame_count +
572         (videorate->segment.rate < 0.0 ? 1 : 0),
573         videorate->to_rate_denominator * GST_SECOND,
574         videorate->to_rate_numerator);
575   }
576   videorate->out_frame_count = 0;
577   videorate->to_rate_numerator = rate_numerator;
578   videorate->to_rate_denominator = rate_denominator;
579 
580   if (rate_numerator)
581     videorate->wanted_diff = gst_util_uint64_scale_int (GST_SECOND,
582         rate_denominator, rate_numerator);
583   else
584     videorate->wanted_diff = 0;
585 
586 done:
587   /* After a setcaps, our caps may have changed. In that case, we can't use
588    * the old buffer, if there was one (it might have different dimensions) */
589   GST_DEBUG_OBJECT (videorate, "swapping old buffers");
590   gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE);
591   videorate->last_ts = GST_CLOCK_TIME_NONE;
592   videorate->average = 0;
593 
594   return ret;
595 
596 no_framerate:
597   {
598     GST_DEBUG_OBJECT (videorate, "no framerate specified");
599     ret = FALSE;
600     goto done;
601   }
602 }
603 
604 static void
gst_video_rate_reset(GstVideoRate * videorate)605 gst_video_rate_reset (GstVideoRate * videorate)
606 {
607   GST_DEBUG_OBJECT (videorate, "resetting internal variables");
608 
609   videorate->in = 0;
610   videorate->out = 0;
611   videorate->base_ts = 0;
612   videorate->out_frame_count = 0;
613   videorate->drop = 0;
614   videorate->dup = 0;
615   videorate->next_ts = GST_CLOCK_TIME_NONE;
616   videorate->last_ts = GST_CLOCK_TIME_NONE;
617   videorate->discont = TRUE;
618   videorate->average = 0;
619   videorate->force_variable_rate = FALSE;
620   gst_video_rate_swap_prev (videorate, NULL, 0);
621 
622   gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
623 }
624 
625 static void
gst_video_rate_init(GstVideoRate * videorate)626 gst_video_rate_init (GstVideoRate * videorate)
627 {
628   gst_video_rate_reset (videorate);
629   videorate->silent = DEFAULT_SILENT;
630   videorate->new_pref = DEFAULT_NEW_PREF;
631   videorate->drop_only = DEFAULT_DROP_ONLY;
632   videorate->average_period = DEFAULT_AVERAGE_PERIOD;
633   videorate->average_period_set = DEFAULT_AVERAGE_PERIOD;
634   videorate->max_rate = DEFAULT_MAX_RATE;
635   videorate->rate = DEFAULT_RATE;
636   videorate->max_duplication_time = DEFAULT_MAX_DUPLICATION_TIME;
637 
638   videorate->from_rate_numerator = 0;
639   videorate->from_rate_denominator = 0;
640   videorate->to_rate_numerator = 0;
641   videorate->to_rate_denominator = 0;
642 
643   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (videorate), TRUE);
644 }
645 
646 /* @outbuf: (transfer full) needs to be writable */
647 static GstFlowReturn
gst_video_rate_push_buffer(GstVideoRate * videorate,GstBuffer * outbuf,gboolean duplicate,GstClockTime next_intime)648 gst_video_rate_push_buffer (GstVideoRate * videorate, GstBuffer * outbuf,
649     gboolean duplicate, GstClockTime next_intime)
650 {
651   GstFlowReturn res;
652   GstClockTime push_ts;
653 
654   GST_BUFFER_OFFSET (outbuf) = videorate->out;
655   GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
656 
657   if (videorate->discont) {
658     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
659     videorate->discont = FALSE;
660   } else
661     GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
662 
663   if (duplicate)
664     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
665   else
666     GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP);
667 
668   /* this is the timestamp we put on the buffer */
669   push_ts = videorate->next_ts;
670 
671   videorate->out++;
672   videorate->out_frame_count++;
673   if (videorate->segment.rate < 0.0) {
674     if (videorate->to_rate_numerator) {
675       /* interpolate next expected timestamp in the segment */
676       videorate->next_ts =
677           videorate->segment.base + videorate->segment.stop -
678           videorate->base_ts -
679           gst_util_uint64_scale (videorate->out_frame_count + 1,
680           videorate->to_rate_denominator * GST_SECOND,
681           videorate->to_rate_numerator);
682 
683       GST_BUFFER_DURATION (outbuf) = push_ts - videorate->next_ts;
684     } else if (next_intime != GST_CLOCK_TIME_NONE) {
685       videorate->next_ts = next_intime;
686     } else {
687       GST_FIXME_OBJECT (videorate, "No next intime for reverse playback");
688     }
689   } else {
690     if (videorate->to_rate_numerator) {
691       /* interpolate next expected timestamp in the segment */
692       videorate->next_ts =
693           videorate->segment.base + videorate->segment.start +
694           videorate->base_ts +
695           gst_util_uint64_scale (videorate->out_frame_count,
696           videorate->to_rate_denominator * GST_SECOND,
697           videorate->to_rate_numerator);
698       GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
699     } else if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (outbuf))) {
700       videorate->next_ts =
701           GST_BUFFER_PTS (outbuf) + GST_BUFFER_DURATION (outbuf);
702     } else {
703       /* There must always be a valid duration on prevbuf if rate > 0,
704        * it is ensured in the transform_ip function */
705       GST_FIXME_OBJECT (videorate, "No buffer duration known");
706     }
707   }
708 
709   /* We do not need to update time in VFR (variable frame rate) mode */
710   if (!videorate->drop_only) {
711     /* adapt for looping, bring back to time in current segment. */
712     GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.base;
713   }
714 
715   GST_LOG_OBJECT (videorate,
716       "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
717       GST_TIME_ARGS (push_ts));
718 
719   res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (videorate), outbuf);
720 
721   return res;
722 }
723 
724 /* flush the oldest buffer */
725 static GstFlowReturn
gst_video_rate_flush_prev(GstVideoRate * videorate,gboolean duplicate,GstClockTime next_intime)726 gst_video_rate_flush_prev (GstVideoRate * videorate, gboolean duplicate,
727     GstClockTime next_intime)
728 {
729   GstBuffer *outbuf;
730 
731   if (!videorate->prevbuf)
732     goto eos_before_buffers;
733 
734   outbuf = gst_buffer_ref (videorate->prevbuf);
735   /* make sure we can write to the metadata */
736   outbuf = gst_buffer_make_writable (outbuf);
737 
738   return gst_video_rate_push_buffer (videorate, outbuf, duplicate, next_intime);
739 
740   /* WARNINGS */
741 eos_before_buffers:
742   {
743     GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
744     return GST_FLOW_OK;
745   }
746 }
747 
748 static void
gst_video_rate_swap_prev(GstVideoRate * videorate,GstBuffer * buffer,gint64 time)749 gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
750     gint64 time)
751 {
752   GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer);
753   if (videorate->prevbuf)
754     gst_buffer_unref (videorate->prevbuf);
755   videorate->prevbuf = buffer != NULL ? gst_buffer_ref (buffer) : NULL;
756   videorate->prev_ts = time;
757 }
758 
759 static void
gst_video_rate_notify_drop(GstVideoRate * videorate)760 gst_video_rate_notify_drop (GstVideoRate * videorate)
761 {
762   g_object_notify_by_pspec ((GObject *) videorate, pspec_drop);
763 }
764 
765 static void
gst_video_rate_notify_duplicate(GstVideoRate * videorate)766 gst_video_rate_notify_duplicate (GstVideoRate * videorate)
767 {
768   g_object_notify_by_pspec ((GObject *) videorate, pspec_duplicate);
769 }
770 
771 #define MAGIC_LIMIT  25
772 static gboolean
gst_video_rate_sink_event(GstBaseTransform * trans,GstEvent * event)773 gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
774 {
775   GstVideoRate *videorate;
776 
777   videorate = GST_VIDEO_RATE (trans);
778 
779   switch (GST_EVENT_TYPE (event)) {
780     case GST_EVENT_SEGMENT:
781     {
782       GstSegment segment;
783       gint seqnum;
784 
785       gst_event_copy_segment (event, &segment);
786       if (segment.format != GST_FORMAT_TIME)
787         goto format_error;
788 
789       GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
790 
791       /* close up the previous segment, if appropriate */
792       if (videorate->prevbuf) {
793         gint count = 0;
794         GstFlowReturn res;
795 
796         res = GST_FLOW_OK;
797         /* fill up to the end of current segment,
798          * or only send out the stored buffer if there is no specific stop.
799          * regardless, prevent going loopy in strange cases */
800         while (res == GST_FLOW_OK && count <= MAGIC_LIMIT
801             && !videorate->drop_only
802             && ((videorate->segment.rate > 0.0
803                     && GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)
804                     && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
805                     && videorate->next_ts - videorate->segment.base <
806                     videorate->segment.stop) || (videorate->segment.rate < 0.0
807                     && GST_CLOCK_TIME_IS_VALID (videorate->segment.start)
808                     && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
809                     && videorate->next_ts - videorate->segment.base >=
810                     videorate->segment.start)
811                 || count < 1)) {
812           res =
813               gst_video_rate_flush_prev (videorate, count > 0,
814               GST_CLOCK_TIME_NONE);
815           count++;
816         }
817         if (count > 1) {
818           videorate->dup += count - 1;
819           if (!videorate->silent)
820             gst_video_rate_notify_duplicate (videorate);
821         }
822         /* clean up for the new one; _chain will resume from the new start */
823         gst_video_rate_swap_prev (videorate, NULL, 0);
824       }
825 
826       videorate->base_ts = 0;
827       videorate->out_frame_count = 0;
828       videorate->next_ts = GST_CLOCK_TIME_NONE;
829 
830       /* We just want to update the accumulated stream_time  */
831 
832       segment.start = (gint64) (segment.start / videorate->rate);
833       segment.position = (gint64) (segment.position / videorate->rate);
834       if (GST_CLOCK_TIME_IS_VALID (segment.stop))
835         segment.stop = (gint64) (segment.stop / videorate->rate);
836       segment.time = (gint64) (segment.time / videorate->rate);
837 
838       gst_segment_copy_into (&segment, &videorate->segment);
839       GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
840           &videorate->segment);
841 
842 
843       seqnum = gst_event_get_seqnum (event);
844       gst_event_unref (event);
845       event = gst_event_new_segment (&segment);
846       gst_event_set_seqnum (event, seqnum);
847 
848       break;
849     }
850     case GST_EVENT_SEGMENT_DONE:
851     case GST_EVENT_EOS:{
852       gint count = 0;
853       GstFlowReturn res = GST_FLOW_OK;
854 
855       GST_DEBUG_OBJECT (videorate, "Got %s",
856           gst_event_type_get_name (GST_EVENT_TYPE (event)));
857 
858       /* If the segment has a stop position, fill the segment */
859       if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) {
860         /* fill up to the end of current segment,
861          * or only send out the stored buffer if there is no specific stop.
862          * regardless, prevent going loopy in strange cases */
863         while (res == GST_FLOW_OK && count <= MAGIC_LIMIT
864             && !videorate->drop_only
865             && ((videorate->segment.rate > 0.0
866                     && GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)
867                     && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
868                     && videorate->next_ts - videorate->segment.base <
869                     videorate->segment.stop) || (videorate->segment.rate < 0.0
870                     && GST_CLOCK_TIME_IS_VALID (videorate->segment.start)
871                     && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
872                     && videorate->next_ts - videorate->segment.base >=
873                     videorate->segment.start)
874                 || count < 1)) {
875           res =
876               gst_video_rate_flush_prev (videorate, count > 0,
877               GST_CLOCK_TIME_NONE);
878           count++;
879         }
880       } else if (!videorate->drop_only && videorate->prevbuf) {
881         /* Output at least one frame but if the buffer duration is valid, output
882          * enough frames to use the complete buffer duration */
883         if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) {
884           GstClockTime end_ts =
885               videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf);
886 
887           while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
888               ((videorate->segment.rate > 0.0
889                       && GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)
890                       && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
891                       && videorate->next_ts - videorate->segment.base < end_ts)
892                   || count < 1)) {
893             res =
894                 gst_video_rate_flush_prev (videorate, count > 0,
895                 GST_CLOCK_TIME_NONE);
896             count++;
897           }
898         } else {
899           res =
900               gst_video_rate_flush_prev (videorate, FALSE, GST_CLOCK_TIME_NONE);
901           count = 1;
902         }
903       }
904 
905       if (count > 1) {
906         videorate->dup += count - 1;
907         if (!videorate->silent)
908           gst_video_rate_notify_duplicate (videorate);
909       } else if (count == 0) {
910         videorate->drop++;
911         if (!videorate->silent)
912           gst_video_rate_notify_drop (videorate);
913       }
914 
915       break;
916     }
917     case GST_EVENT_FLUSH_STOP:
918       /* also resets the segment */
919       GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP");
920       gst_video_rate_reset (videorate);
921       break;
922     case GST_EVENT_GAP:
923       /* no gaps after videorate, ignore the event */
924       gst_event_unref (event);
925       return TRUE;
926     default:
927       break;
928   }
929 
930   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
931 
932   /* ERRORS */
933 format_error:
934   {
935     GST_WARNING_OBJECT (videorate,
936         "Got segment but doesn't have GST_FORMAT_TIME value");
937     return FALSE;
938   }
939 }
940 
941 static gboolean
gst_video_rate_src_event(GstBaseTransform * trans,GstEvent * event)942 gst_video_rate_src_event (GstBaseTransform * trans, GstEvent * event)
943 {
944   GstVideoRate *videorate;
945   GstPad *sinkpad;
946   gboolean res = FALSE;
947 
948   videorate = GST_VIDEO_RATE (trans);
949   sinkpad = GST_BASE_TRANSFORM_SINK_PAD (trans);
950   switch (GST_EVENT_TYPE (event)) {
951     case GST_EVENT_SEEK:
952     {
953       gdouble srate;
954       GstSeekFlags flags;
955       GstSeekType start_type, stop_type;
956       gint64 start, stop;
957       gint seqnum = gst_event_get_seqnum (event);
958 
959       gst_event_parse_seek (event, &srate, NULL, &flags, &start_type, &start,
960           &stop_type, &stop);
961 
962       start = (gint64) (start * videorate->rate);
963       if (GST_CLOCK_TIME_IS_VALID (stop)) {
964         stop = (gint64) (stop * videorate->rate);
965       }
966 
967       gst_event_unref (event);
968       event = gst_event_new_seek (srate, GST_FORMAT_TIME,
969           flags, start_type, start, stop_type, stop);
970       gst_event_set_seqnum (event, seqnum);
971 
972       res = gst_pad_push_event (sinkpad, event);
973       break;
974     }
975     default:
976       res = gst_pad_push_event (sinkpad, event);
977       break;
978   }
979   return res;
980 }
981 
982 static gboolean
gst_video_rate_query(GstBaseTransform * trans,GstPadDirection direction,GstQuery * query)983 gst_video_rate_query (GstBaseTransform * trans, GstPadDirection direction,
984     GstQuery * query)
985 {
986   GstVideoRate *videorate = GST_VIDEO_RATE (trans);
987   gboolean res = FALSE;
988   GstPad *otherpad;
989 
990   otherpad = (direction == GST_PAD_SRC) ?
991       GST_BASE_TRANSFORM_SINK_PAD (trans) : GST_BASE_TRANSFORM_SRC_PAD (trans);
992 
993   switch (GST_QUERY_TYPE (query)) {
994     case GST_QUERY_LATENCY:
995     {
996       GstClockTime min, max;
997       gboolean live;
998       guint64 latency;
999       guint64 avg_period;
1000       gboolean drop_only;
1001       GstPad *peer;
1002 
1003       GST_OBJECT_LOCK (videorate);
1004       avg_period = videorate->average_period_set;
1005       drop_only = videorate->drop_only;
1006       GST_OBJECT_UNLOCK (videorate);
1007 
1008       if (avg_period == 0 && (peer = gst_pad_get_peer (otherpad))) {
1009         if ((res = gst_pad_query (peer, query))) {
1010           gst_query_parse_latency (query, &live, &min, &max);
1011 
1012           GST_DEBUG_OBJECT (videorate, "Peer latency: min %"
1013               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1014               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1015 
1016           /* Drop only has no latency, other modes have one frame latency */
1017           if (!drop_only && videorate->from_rate_numerator != 0) {
1018             /* add latency. We don't really know since we hold on to the frames
1019              * until we get a next frame, which can be anything. We assume
1020              * however that this will take from_rate time. */
1021             latency = gst_util_uint64_scale (GST_SECOND,
1022                 videorate->from_rate_denominator,
1023                 videorate->from_rate_numerator);
1024           } else {
1025             /* no input framerate, we don't know */
1026             latency = 0;
1027           }
1028 
1029           GST_DEBUG_OBJECT (videorate, "Our latency: %"
1030               GST_TIME_FORMAT, GST_TIME_ARGS (latency));
1031 
1032           min += latency;
1033           if (max != -1)
1034             max += latency;
1035 
1036           GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %"
1037               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1038               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1039 
1040           gst_query_set_latency (query, live, min, max);
1041         }
1042         gst_object_unref (peer);
1043         break;
1044       }
1045       /* Simple fall back if we don't have a latency or a peer that we
1046        * can ask about its latency yet.. */
1047       res =
1048           GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1049           query);
1050       break;
1051     }
1052     case GST_QUERY_DURATION:
1053     {
1054       GstFormat format;
1055       gint64 duration;
1056       gdouble rate;
1057 
1058       res =
1059           GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1060           query);
1061 
1062       if (!res)
1063         break;
1064 
1065       GST_OBJECT_LOCK (videorate);
1066       rate = videorate->rate;
1067       GST_OBJECT_UNLOCK (videorate);
1068 
1069       if (rate == 1.0)
1070         break;
1071 
1072       gst_query_parse_duration (query, &format, &duration);
1073 
1074       if (format != GST_FORMAT_TIME) {
1075         GST_DEBUG_OBJECT (videorate, "not TIME format");
1076         break;
1077       }
1078       GST_LOG_OBJECT (videorate, "upstream duration: %" G_GINT64_FORMAT,
1079           duration);
1080       /* Shouldn't this be a multiplication if the direction is downstream? */
1081       if (GST_CLOCK_TIME_IS_VALID (duration)) {
1082         duration = (gint64) (duration / rate);
1083       }
1084       GST_LOG_OBJECT (videorate, "our duration: %" G_GINT64_FORMAT, duration);
1085       gst_query_set_duration (query, format, duration);
1086       break;
1087     }
1088     case GST_QUERY_POSITION:
1089     {
1090       GstFormat dst_format;
1091       gint64 dst_value;
1092       gdouble rate;
1093 
1094       GST_OBJECT_LOCK (videorate);
1095       rate = videorate->rate;
1096       GST_OBJECT_UNLOCK (videorate);
1097 
1098       gst_query_parse_position (query, &dst_format, NULL);
1099 
1100       if (dst_format != GST_FORMAT_TIME) {
1101         GST_DEBUG_OBJECT (videorate, "not TIME format");
1102         break;
1103       }
1104       /* Shouldn't this be a multiplication if the direction is downstream? */
1105       dst_value =
1106           (gint64) (gst_segment_to_stream_time (&videorate->segment,
1107               GST_FORMAT_TIME, videorate->last_ts / rate));
1108       GST_LOG_OBJECT (videorate, "our position: %" GST_TIME_FORMAT,
1109           GST_TIME_ARGS (dst_value));
1110       gst_query_set_position (query, dst_format, dst_value);
1111       res = TRUE;
1112       break;
1113     }
1114     default:
1115       res =
1116           GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1117           query);
1118       break;
1119   }
1120 
1121   return res;
1122 }
1123 
1124 static gboolean
gst_video_rate_propose_allocation(GstBaseTransform * trans,GstQuery * decide_query,GstQuery * query)1125 gst_video_rate_propose_allocation (GstBaseTransform * trans,
1126     GstQuery * decide_query, GstQuery * query)
1127 {
1128   GstBaseTransformClass *klass = GST_BASE_TRANSFORM_CLASS (parent_class);
1129   gboolean res;
1130 
1131   /* We should always be passthrough */
1132   g_return_val_if_fail (decide_query == NULL, FALSE);
1133 
1134   res = klass->propose_allocation (trans, NULL, query);
1135 
1136   if (res) {
1137     guint i = 0;
1138     guint n_allocation;
1139     guint down_min = 0;
1140 
1141     n_allocation = gst_query_get_n_allocation_pools (query);
1142 
1143     while (i < n_allocation) {
1144       GstBufferPool *pool = NULL;
1145       guint size, min, max;
1146 
1147       gst_query_parse_nth_allocation_pool (query, i, &pool, &size, &min, &max);
1148 
1149       if (min == max) {
1150         if (pool)
1151           gst_object_unref (pool);
1152         gst_query_remove_nth_allocation_pool (query, i);
1153         n_allocation--;
1154         down_min = MAX (min, down_min);
1155         continue;
1156       }
1157 
1158       gst_query_set_nth_allocation_pool (query, i, pool, size, min + 1, max);
1159       if (pool)
1160         gst_object_unref (pool);
1161       i++;
1162     }
1163 
1164     if (n_allocation == 0) {
1165       GstCaps *caps;
1166       GstVideoInfo info;
1167 
1168       gst_query_parse_allocation (query, &caps, NULL);
1169       gst_video_info_from_caps (&info, caps);
1170 
1171       gst_query_add_allocation_pool (query, NULL, info.size, down_min + 1, 0);
1172     }
1173   }
1174 
1175   return res;
1176 }
1177 
1178 static GstFlowReturn
gst_video_rate_trans_ip_max_avg(GstVideoRate * videorate,GstBuffer * buf)1179 gst_video_rate_trans_ip_max_avg (GstVideoRate * videorate, GstBuffer * buf)
1180 {
1181   GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
1182 
1183   videorate->in++;
1184 
1185   if (!GST_CLOCK_TIME_IS_VALID (ts) || videorate->wanted_diff == 0)
1186     goto push;
1187 
1188   /* drop frames if they exceed our output rate */
1189   if (GST_CLOCK_TIME_IS_VALID (videorate->last_ts)) {
1190     GstClockTimeDiff diff =
1191         videorate->segment.rate <
1192         0 ? videorate->last_ts - ts : ts - videorate->last_ts;
1193 
1194     /* Drop buffer if its early compared to the desired frame rate and
1195      * the current average is higher than the desired average
1196      */
1197     if (diff < videorate->wanted_diff &&
1198         videorate->average < videorate->wanted_diff)
1199       goto drop;
1200 
1201     /* Update average */
1202     if (videorate->average) {
1203       GstClockTimeDiff wanted_diff;
1204 
1205       if (G_LIKELY (videorate->average_period > videorate->wanted_diff))
1206         wanted_diff = videorate->wanted_diff;
1207       else
1208         wanted_diff = videorate->average_period * 10;
1209 
1210       videorate->average =
1211           gst_util_uint64_scale_round (videorate->average,
1212           videorate->average_period - wanted_diff,
1213           videorate->average_period) +
1214           gst_util_uint64_scale_round (diff, wanted_diff,
1215           videorate->average_period);
1216     } else {
1217       videorate->average = diff;
1218     }
1219   }
1220 
1221   videorate->last_ts = ts;
1222 
1223 push:
1224   videorate->out++;
1225   return GST_FLOW_OK;
1226 
1227 drop:
1228   if (!videorate->silent)
1229     gst_video_rate_notify_drop (videorate);
1230   return GST_BASE_TRANSFORM_FLOW_DROPPED;
1231 }
1232 
1233 /* Check if downstream forces variable framerate (0/1) and if
1234  * it is the case, use variable framerate ourself
1235  * Otherwise compute the framerate from the 2 buffers that we
1236  * have already received and make use of it as wanted framerate
1237  */
1238 static void
gst_video_rate_check_variable_rate(GstVideoRate * videorate,GstBuffer * buffer)1239 gst_video_rate_check_variable_rate (GstVideoRate * videorate,
1240     GstBuffer * buffer)
1241 {
1242   GstStructure *st;
1243   gint fps_d, fps_n;
1244   GstCaps *srcpadcaps, *tmpcaps, *downstream_caps;
1245   GstPad *pad = NULL;
1246 
1247   srcpadcaps =
1248       gst_pad_get_current_caps (GST_BASE_TRANSFORM_SRC_PAD (videorate));
1249 
1250   gst_video_guess_framerate (GST_BUFFER_PTS (buffer) -
1251       GST_BUFFER_PTS (videorate->prevbuf), &fps_n, &fps_d);
1252 
1253   tmpcaps = gst_caps_copy (srcpadcaps);
1254   st = gst_caps_get_structure (tmpcaps, 0);
1255   gst_structure_set (st, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
1256   gst_caps_unref (srcpadcaps);
1257 
1258   pad = gst_pad_get_peer (GST_BASE_TRANSFORM_SRC_PAD (videorate));
1259   downstream_caps = gst_pad_query_caps (pad, NULL);
1260   if (pad && !gst_caps_can_intersect (tmpcaps, downstream_caps)) {
1261     videorate->force_variable_rate = TRUE;
1262     gst_caps_unref (downstream_caps);
1263     GST_DEBUG_OBJECT (videorate, "Downstream forces variable framerate"
1264         " respecting it");
1265 
1266     goto done;
1267   }
1268   gst_caps_unref (downstream_caps);
1269 
1270   videorate->to_rate_numerator = fps_n;
1271   videorate->to_rate_denominator = fps_d;
1272 
1273   GST_INFO_OBJECT (videorate, "Computed framerate to %d/%d",
1274       videorate->to_rate_numerator, videorate->to_rate_denominator);
1275 
1276   videorate->updating_caps = TRUE;
1277   gst_base_transform_update_src_caps (GST_BASE_TRANSFORM (videorate), tmpcaps);
1278 
1279 done:
1280   gst_caps_unref (tmpcaps);
1281   if (pad)
1282     gst_object_unref (pad);
1283 }
1284 
1285 static GstFlowReturn
gst_video_rate_transform_ip(GstBaseTransform * trans,GstBuffer * buffer)1286 gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
1287 {
1288   GstVideoRate *videorate;
1289   GstFlowReturn res = GST_BASE_TRANSFORM_FLOW_DROPPED;
1290   GstClockTime intime, in_ts, in_dur, last_ts;
1291   GstClockTime avg_period;
1292   gboolean skip = FALSE;
1293 
1294   videorate = GST_VIDEO_RATE (trans);
1295 
1296   /* make sure the denominators are not 0 */
1297   if (videorate->from_rate_denominator == 0 ||
1298       videorate->to_rate_denominator == 0)
1299     goto not_negotiated;
1300 
1301   if (videorate->to_rate_numerator == 0 && videorate->prevbuf &&
1302       !videorate->force_variable_rate) {
1303     gst_video_rate_check_variable_rate (videorate, buffer);
1304   }
1305 
1306   GST_OBJECT_LOCK (videorate);
1307   avg_period = videorate->average_period_set;
1308   GST_OBJECT_UNLOCK (videorate);
1309 
1310   /* MT-safe switching between modes */
1311   if (G_UNLIKELY (avg_period != videorate->average_period)) {
1312     gboolean switch_mode = (avg_period == 0 || videorate->average_period == 0);
1313     videorate->average_period = avg_period;
1314     videorate->last_ts = GST_CLOCK_TIME_NONE;
1315 
1316     if (switch_mode) {
1317       if (avg_period) {
1318         /* enabling average mode */
1319         videorate->average = 0;
1320         /* make sure no cached buffers from regular mode are left */
1321         gst_video_rate_swap_prev (videorate, NULL, 0);
1322       } else {
1323         /* enable regular mode */
1324         videorate->next_ts = GST_CLOCK_TIME_NONE;
1325         skip = TRUE;
1326       }
1327 
1328       /* max averaging mode has a no latency, normal mode does */
1329       gst_element_post_message (GST_ELEMENT (videorate),
1330           gst_message_new_latency (GST_OBJECT (videorate)));
1331     }
1332   }
1333 
1334   if (videorate->average_period > 0)
1335     return gst_video_rate_trans_ip_max_avg (videorate, buffer);
1336 
1337   in_ts = GST_BUFFER_TIMESTAMP (buffer);
1338   in_dur = GST_BUFFER_DURATION (buffer);
1339 
1340   if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) {
1341     /* For reverse playback, we need all input timestamps as we can't
1342      * guess from the previous buffers timestamp and duration */
1343     if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE
1344             && videorate->segment.rate < 0.0))
1345       goto invalid_buffer;
1346     in_ts = videorate->last_ts;
1347     if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE))
1348       goto invalid_buffer;
1349   }
1350 
1351   /* get the time of the next expected buffer timestamp, we use this when the
1352    * next buffer has -1 as a timestamp */
1353   last_ts = videorate->last_ts;
1354   videorate->last_ts = in_ts;
1355   if (in_dur != GST_CLOCK_TIME_NONE && videorate->segment.rate > 0.0)
1356     videorate->last_ts += in_dur;
1357 
1358   GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
1359       GST_TIME_ARGS (in_ts));
1360 
1361   /* the input time is the time in the segment + all previously accumulated
1362    * segments */
1363   intime = in_ts + videorate->segment.base;
1364 
1365   /* we need to have two buffers to compare */
1366   if (videorate->prevbuf == NULL || videorate->drop_only) {
1367     /* We can calculate the duration of the buffer here if not given for
1368      * reverse playback. We need this later */
1369     if (videorate->segment.rate < 0.0 && !GST_BUFFER_DURATION_IS_VALID (buffer)) {
1370       /* As we require valid timestamps all the time for reverse playback, we either
1371        * have a valid last_ts or we're at the very first buffer. */
1372       if (!GST_CLOCK_TIME_IS_VALID (last_ts))
1373         GST_BUFFER_DURATION (buffer) = videorate->segment.stop - in_ts;
1374       else
1375         GST_BUFFER_DURATION (buffer) = last_ts - in_ts;
1376     }
1377 
1378     gst_video_rate_swap_prev (videorate, buffer, intime);
1379     videorate->in++;
1380     if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
1381       /* new buffer, we expect to output a buffer that matches the first
1382        * timestamp in the segment */
1383       if (videorate->skip_to_first || skip) {
1384         videorate->next_ts = intime;
1385         if (videorate->segment.rate < 0.0) {
1386           videorate->base_ts = videorate->segment.stop - in_ts;
1387         } else {
1388           videorate->base_ts = in_ts - videorate->segment.start;
1389         }
1390         videorate->out_frame_count = 0;
1391       } else {
1392         if (videorate->segment.rate < 0.0) {
1393           if (videorate->to_rate_numerator) {
1394             GstClockTime frame_duration = gst_util_uint64_scale (1,
1395                 videorate->to_rate_denominator * GST_SECOND,
1396                 videorate->to_rate_numerator);
1397 
1398             videorate->next_ts =
1399                 videorate->segment.stop + videorate->segment.base;
1400 
1401             if (videorate->next_ts > frame_duration)
1402               videorate->next_ts =
1403                   MAX (videorate->segment.start,
1404                   videorate->next_ts - frame_duration);
1405             else
1406               videorate->next_ts = videorate->segment.start;
1407           } else {
1408             /* What else can we do? */
1409             videorate->next_ts = intime;
1410           }
1411         } else {
1412           videorate->next_ts =
1413               videorate->segment.start + videorate->segment.base;
1414         }
1415       }
1416     }
1417 
1418     /* In drop-only mode we can already decide here if we should output the
1419      * current frame or drop it because it's coming earlier than our minimum
1420      * allowed frame period. This also keeps latency down to 0 frames
1421      */
1422     if (videorate->drop_only) {
1423       if ((videorate->segment.rate > 0.0 && intime >= videorate->next_ts) ||
1424           (videorate->segment.rate < 0.0 && intime <= videorate->next_ts)) {
1425         GstFlowReturn r;
1426 
1427         /* The buffer received from basetransform is garanteed to be writable.
1428          * It just needs to be reffed so the buffer won't be consumed once pushed and
1429          * GstBaseTransform can get its reference back. */
1430         if ((r = gst_video_rate_push_buffer (videorate,
1431                     gst_buffer_ref (buffer), FALSE,
1432                     GST_CLOCK_TIME_NONE)) != GST_FLOW_OK) {
1433           res = r;
1434           goto done;
1435         }
1436       }
1437       /* No need to keep the buffer around for longer */
1438       gst_buffer_replace (&videorate->prevbuf, NULL);
1439     }
1440   } else {
1441     GstClockTime prevtime;
1442     gint count = 0;
1443     gint64 diff1, diff2;
1444 
1445     prevtime = videorate->prev_ts;
1446 
1447     GST_LOG_OBJECT (videorate,
1448         "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
1449         " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
1450         GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
1451 
1452     videorate->in++;
1453 
1454     /* drop new buffer if it's before previous one */
1455     if ((videorate->segment.rate > 0.0 && intime < prevtime) ||
1456         (videorate->segment.rate < 0.0 && intime > prevtime)) {
1457       GST_DEBUG_OBJECT (videorate,
1458           "The new buffer (%" GST_TIME_FORMAT
1459           ") is before the previous buffer (%"
1460           GST_TIME_FORMAT "). Dropping new buffer.",
1461           GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
1462       videorate->drop++;
1463       if (!videorate->silent)
1464         gst_video_rate_notify_drop (videorate);
1465       goto done;
1466     }
1467 
1468     if (videorate->max_duplication_time > 0) {
1469       /* We already know that intime and prevtime are not out of order, based
1470        * on the previous condition. Using ABS in case rate < 0, in which case
1471        * the order is reversed. */
1472       if (ABS (GST_CLOCK_DIFF (intime,
1473                   prevtime)) > videorate->max_duplication_time) {
1474         GST_DEBUG_OBJECT (videorate,
1475             "The new buffer (%" GST_TIME_FORMAT
1476             ") is further away from previous buffer (%"
1477             GST_TIME_FORMAT ") than max-duplication-time (%" GST_TIME_FORMAT
1478             ")", GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime),
1479             GST_TIME_ARGS (videorate->max_duplication_time));
1480         /* First send out enough buffers to actually reach the time of the
1481          * previous buffer */
1482         if (videorate->segment.rate < 0.0) {
1483           while (videorate->next_ts > prevtime) {
1484             gst_video_rate_flush_prev (videorate, count > 0,
1485                 GST_CLOCK_TIME_NONE);
1486             count += 1;
1487           }
1488         } else {
1489           while (videorate->next_ts <= prevtime) {
1490             gst_video_rate_flush_prev (videorate, count > 0,
1491                 GST_CLOCK_TIME_NONE);
1492             count += 1;
1493           }
1494         }
1495 
1496         if (count > 1) {
1497           videorate->dup += count - 1;
1498           if (!videorate->silent)
1499             gst_video_rate_notify_duplicate (videorate);
1500         }
1501 
1502         /* The gap between the two buffers is too large. Don't fill it, just
1503          * let a discont through */
1504         videorate->discont = TRUE;
1505 
1506         if (videorate->segment.rate < 0.0) {
1507           videorate->base_ts -= prevtime - intime;
1508         } else {
1509           videorate->base_ts += intime - prevtime;
1510         }
1511         videorate->next_ts = intime;
1512         /* Swap in new buffer and get rid of old buffer so that starting with
1513          * the next input buffer we output from the new position */
1514         gst_video_rate_swap_prev (videorate, buffer, intime);
1515         goto done;
1516       }
1517     }
1518 
1519     /* got 2 buffers, see which one is the best */
1520     do {
1521       GstClockTime next_ts;
1522 
1523       if (videorate->segment.rate < 0.0) {
1524         /* Make sure that we have a duration for this buffer. The previous
1525          * buffer already has a duration given by either exactly this code,
1526          * or the code above for the very first buffer */
1527         g_assert (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf));
1528         if (!GST_BUFFER_DURATION_IS_VALID (buffer))
1529           GST_BUFFER_DURATION (buffer) =
1530               prevtime > intime ? prevtime - intime : 0;
1531       } else {
1532         /* Make sure that we have a duration for previous buffer */
1533         if (!GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf))
1534           GST_BUFFER_DURATION (videorate->prevbuf) =
1535               intime > prevtime ? intime - prevtime : 0;
1536       }
1537 
1538 #ifndef ABSDIFF
1539 #define ABSDIFF(a, b) (((a) > (b)) ? (a) - (b) : (b) - (a))
1540 #endif
1541 
1542       /* take absolute diffs */
1543       if (videorate->segment.rate < 0.0) {
1544         GstClockTime next_end_ts;
1545         GstClockTime prev_endtime;
1546         GstClockTime in_endtime;
1547 
1548         next_ts = videorate->next_ts;
1549 
1550         prev_endtime =
1551             MAX (prevtime + GST_BUFFER_DURATION (videorate->prevbuf),
1552             videorate->segment.stop);
1553         in_endtime =
1554             MAX (intime + GST_BUFFER_DURATION (buffer),
1555             videorate->segment.stop);
1556 
1557         if (videorate->to_rate_numerator) {
1558           GstClockTime frame_duration = gst_util_uint64_scale (1,
1559               videorate->to_rate_denominator * GST_SECOND,
1560               videorate->to_rate_numerator);
1561           next_end_ts = MAX (next_ts + frame_duration, videorate->segment.stop);
1562         } else {
1563           next_end_ts =
1564               MAX (next_ts + GST_BUFFER_DURATION (videorate->prevbuf),
1565               videorate->segment.stop);
1566         }
1567         next_ts *= videorate->rate;
1568         next_end_ts *= videorate->rate;
1569 
1570         diff1 = ABSDIFF (prev_endtime, next_end_ts);
1571         diff2 = ABSDIFF (in_endtime, next_end_ts);
1572 
1573         GST_LOG_OBJECT (videorate,
1574             "diff with prev %" GST_TIME_FORMAT " diff with new %"
1575             GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
1576             GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
1577             GST_TIME_ARGS (next_end_ts));
1578       } else {
1579         next_ts = videorate->next_ts * videorate->rate;
1580 
1581         diff1 = ABSDIFF (prevtime, next_ts);
1582         diff2 = ABSDIFF (intime, next_ts);
1583 
1584         GST_LOG_OBJECT (videorate,
1585             "diff with prev %" GST_TIME_FORMAT " diff with new %"
1586             GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
1587             GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
1588             GST_TIME_ARGS (next_ts));
1589       }
1590 
1591       /* output first one when its the best */
1592       if (diff1 <= diff2) {
1593         GstFlowReturn r;
1594         count++;
1595 
1596         /* on error the _flush function posted a warning already */
1597         if ((r = gst_video_rate_flush_prev (videorate,
1598                     count > 1, intime)) != GST_FLOW_OK) {
1599           res = r;
1600           goto done;
1601         }
1602       }
1603 
1604       /* continue while the first one was the best, if they were equal avoid
1605        * going into an infinite loop */
1606     }
1607     while (diff1 < diff2);
1608 
1609     /* if we outputed the first buffer more then once, we have dups */
1610     if (count > 1) {
1611       videorate->dup += count - 1;
1612       if (!videorate->silent)
1613         gst_video_rate_notify_duplicate (videorate);
1614     }
1615     /* if we didn't output the first buffer, we have a drop */
1616     else if (count == 0) {
1617       videorate->drop++;
1618 
1619       if (!videorate->silent)
1620         gst_video_rate_notify_drop (videorate);
1621 
1622       GST_LOG_OBJECT (videorate,
1623           "new is best, old never used, drop, outgoing ts %"
1624           GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
1625     }
1626     GST_LOG_OBJECT (videorate,
1627         "END, putting new in old, diff1 %" GST_TIME_FORMAT
1628         ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
1629         ", in %" G_GUINT64_FORMAT ", out %" G_GUINT64_FORMAT ", drop %"
1630         G_GUINT64_FORMAT ", dup %" G_GUINT64_FORMAT, GST_TIME_ARGS (diff1),
1631         GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
1632         videorate->in, videorate->out, videorate->drop, videorate->dup);
1633 
1634     /* swap in new one when it's the best */
1635     gst_video_rate_swap_prev (videorate, buffer, intime);
1636   }
1637 done:
1638   return res;
1639 
1640   /* ERRORS */
1641 not_negotiated:
1642   {
1643     GST_WARNING_OBJECT (videorate, "no framerate negotiated");
1644     res = GST_FLOW_NOT_NEGOTIATED;
1645     goto done;
1646   }
1647 
1648 invalid_buffer:
1649   {
1650     GST_WARNING_OBJECT (videorate,
1651         "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
1652     res = GST_BASE_TRANSFORM_FLOW_DROPPED;
1653     goto done;
1654   }
1655 }
1656 
1657 static gboolean
gst_video_rate_start(GstBaseTransform * trans)1658 gst_video_rate_start (GstBaseTransform * trans)
1659 {
1660   gst_video_rate_reset (GST_VIDEO_RATE (trans));
1661   return TRUE;
1662 }
1663 
1664 static gboolean
gst_video_rate_stop(GstBaseTransform * trans)1665 gst_video_rate_stop (GstBaseTransform * trans)
1666 {
1667   gst_video_rate_reset (GST_VIDEO_RATE (trans));
1668   return TRUE;
1669 }
1670 
1671 static void
gst_videorate_update_duration(GstVideoRate * videorate)1672 gst_videorate_update_duration (GstVideoRate * videorate)
1673 {
1674   GstMessage *m;
1675 
1676   m = gst_message_new_duration_changed (GST_OBJECT (videorate));
1677   gst_element_post_message (GST_ELEMENT (videorate), m);
1678 }
1679 
1680 static void
gst_video_rate_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1681 gst_video_rate_set_property (GObject * object,
1682     guint prop_id, const GValue * value, GParamSpec * pspec)
1683 {
1684   GstVideoRate *videorate = GST_VIDEO_RATE (object);
1685   gboolean latency_changed = FALSE;
1686 
1687   GST_OBJECT_LOCK (videorate);
1688   switch (prop_id) {
1689     case PROP_SILENT:
1690       videorate->silent = g_value_get_boolean (value);
1691       break;
1692     case PROP_NEW_PREF:
1693       videorate->new_pref = g_value_get_double (value);
1694       break;
1695     case PROP_SKIP_TO_FIRST:
1696       videorate->skip_to_first = g_value_get_boolean (value);
1697       break;
1698     case PROP_DROP_ONLY:{
1699       gboolean new_value = g_value_get_boolean (value);
1700 
1701       /* Latency changes if we switch drop-only mode */
1702       latency_changed = new_value != videorate->drop_only;
1703       videorate->drop_only = g_value_get_boolean (value);
1704       goto reconfigure;
1705     }
1706     case PROP_AVERAGE_PERIOD:
1707       videorate->average_period_set = g_value_get_uint64 (value);
1708       break;
1709     case PROP_MAX_RATE:
1710       g_atomic_int_set (&videorate->max_rate, g_value_get_int (value));
1711       goto reconfigure;
1712     case PROP_RATE:
1713       videorate->rate = g_value_get_double (value);
1714       GST_OBJECT_UNLOCK (videorate);
1715 
1716       gst_videorate_update_duration (videorate);
1717       return;
1718     case PROP_MAX_DUPLICATION_TIME:
1719       videorate->max_duplication_time = g_value_get_uint64 (value);
1720       break;
1721     default:
1722       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1723       break;
1724   }
1725   GST_OBJECT_UNLOCK (videorate);
1726 
1727   return;
1728 
1729 reconfigure:
1730   GST_OBJECT_UNLOCK (videorate);
1731   gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (videorate));
1732 
1733   if (latency_changed) {
1734     gst_element_post_message (GST_ELEMENT (videorate),
1735         gst_message_new_latency (GST_OBJECT (videorate)));
1736   }
1737 }
1738 
1739 static void
gst_video_rate_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1740 gst_video_rate_get_property (GObject * object,
1741     guint prop_id, GValue * value, GParamSpec * pspec)
1742 {
1743   GstVideoRate *videorate = GST_VIDEO_RATE (object);
1744 
1745   GST_OBJECT_LOCK (videorate);
1746   switch (prop_id) {
1747     case PROP_IN:
1748       g_value_set_uint64 (value, videorate->in);
1749       break;
1750     case PROP_OUT:
1751       g_value_set_uint64 (value, videorate->out);
1752       break;
1753     case PROP_DUP:
1754       g_value_set_uint64 (value, videorate->dup);
1755       break;
1756     case PROP_DROP:
1757       g_value_set_uint64 (value, videorate->drop);
1758       break;
1759     case PROP_SILENT:
1760       g_value_set_boolean (value, videorate->silent);
1761       break;
1762     case PROP_NEW_PREF:
1763       g_value_set_double (value, videorate->new_pref);
1764       break;
1765     case PROP_SKIP_TO_FIRST:
1766       g_value_set_boolean (value, videorate->skip_to_first);
1767       break;
1768     case PROP_DROP_ONLY:
1769       g_value_set_boolean (value, videorate->drop_only);
1770       break;
1771     case PROP_AVERAGE_PERIOD:
1772       g_value_set_uint64 (value, videorate->average_period_set);
1773       break;
1774     case PROP_MAX_RATE:
1775       g_value_set_int (value, g_atomic_int_get (&videorate->max_rate));
1776       break;
1777     case PROP_RATE:
1778       g_value_set_double (value, videorate->rate);
1779       break;
1780     case PROP_MAX_DUPLICATION_TIME:
1781       g_value_set_uint64 (value, videorate->max_duplication_time);
1782       break;
1783     default:
1784       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1785       break;
1786   }
1787   GST_OBJECT_UNLOCK (videorate);
1788 }
1789 
1790 static gboolean
plugin_init(GstPlugin * plugin)1791 plugin_init (GstPlugin * plugin)
1792 {
1793   GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
1794       "VideoRate stream fixer");
1795 
1796   return gst_element_register (plugin, "videorate", GST_RANK_NONE,
1797       GST_TYPE_VIDEO_RATE);
1798 }
1799 
1800 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1801     GST_VERSION_MINOR,
1802     videorate,
1803     "Adjusts video frames",
1804     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
1805