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