2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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.
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.
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.
21 * SECTION:element-videorate
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.
27 * The correction is performed by dropping and duplicating frames, no fancy
28 * algorithm is used to interpolate frames (yet).
30 * By default the element will simply negotiate the same framerate on its
31 * source and sink pad.
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.
37 * A conversion to a specific framerate can be forced by using filtered caps on
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).
46 * An input stream that needs no adjustments will thus never have dropped or
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.
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.
60 * ## Example pipelines
62 * gst-launch-1.0 -v uridecodebin uri=file:///path/to/video.ogg ! videoconvert ! videoscale ! videorate ! video/x-raw,framerate=15/1 ! autovideosink
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.
67 * gst-launch-1.0 -v v4l2src ! videorate ! video/x-raw,framerate=25/2 ! theoraenc ! oggmux ! filesink location=recording.ogg
69 * Capture video from a V4L device, and adjust the stream to 12.5 fps before
70 * encoding to Ogg/Theora.
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
74 * Decode a video file and save a snapshot every 5 seconds as consecutively numbered jpeg file.
82 #include "gstvideorate.h"
83 #include <gst/video/video.h>
85 GST_DEBUG_CATEGORY_STATIC (video_rate_debug);
86 #define GST_CAT_DEFAULT video_rate_debug
88 /* GstVideoRate signals and args */
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
118 PROP_MAX_DUPLICATION_TIME
121 static GstStaticPadTemplate gst_video_rate_src_template =
122 GST_STATIC_PAD_TEMPLATE ("src",
125 GST_STATIC_CAPS ("video/x-raw(ANY);" "video/x-bayer(ANY);"
126 "image/jpeg(ANY);" "image/png(ANY)")
129 static GstStaticPadTemplate gst_video_rate_sink_template =
130 GST_STATIC_PAD_TEMPLATE ("sink",
133 GST_STATIC_CAPS ("video/x-raw(ANY);" "video/x-bayer(ANY);"
134 "image/jpeg(ANY);" "image/png(ANY)")
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,
141 static gboolean gst_video_rate_src_event (GstBaseTransform * trans,
143 static gboolean gst_video_rate_query (GstBaseTransform * trans,
144 GstPadDirection direction, GstQuery * query);
146 static gboolean gst_video_rate_setcaps (GstBaseTransform * trans,
147 GstCaps * in_caps, GstCaps * out_caps);
149 static GstCaps *gst_video_rate_transform_caps (GstBaseTransform * trans,
150 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
152 static GstCaps *gst_video_rate_fixate_caps (GstBaseTransform * trans,
153 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
155 static GstFlowReturn gst_video_rate_transform_ip (GstBaseTransform * trans,
158 static gboolean gst_video_rate_propose_allocation (GstBaseTransform * trans,
159 GstQuery * decide_query, GstQuery * query);
161 static gboolean gst_video_rate_start (GstBaseTransform * trans);
162 static gboolean gst_video_rate_stop (GstBaseTransform * trans);
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);
170 static GParamSpec *pspec_drop = NULL;
171 static GParamSpec *pspec_duplicate = NULL;
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);
179 gst_video_rate_class_init (GstVideoRateClass * klass)
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);
185 object_class->set_property = gst_video_rate_set_property;
186 object_class->get_property = gst_video_rate_get_property;
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);
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));
225 * GstVideoRate:skip-to-first:
227 * Don't produce buffers before the first one we receive.
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));
235 * GstVideoRate:drop-only:
237 * Only drop frames, no duplicates are produced.
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));
245 * GstVideoRate:average-period:
247 * Arrange for maximum framerate by dropping frames beyond a certain framerate,
248 * where the framerate is calculated using a moving average over the
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));
258 * GstVideoRate:max-rate:
260 * maximum framerate to pass through
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));
272 * Factor of speed for frame displaying
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));
283 * GstVideoRate:max-duplication-time:
285 * Duplicate frames only if the gap between two consecutive frames does not
286 * exceed this duration.
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));
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>");
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);
310 gst_value_fraction_get_extremes (const GValue * v,
311 gint * min_num, gint * min_denom, gint * max_num, gint * max_denom)
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;
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);
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;
335 n = gst_value_list_get_size (v);
339 for (i = 0; i < n; i++) {
340 const GValue *t = gst_value_list_get_value (v, i);
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) {
348 if (gst_util_fraction_compare (max_n, max_d, *max_num, *max_denom) > 0) {
354 g_warning ("Unknown type for framerate");
362 /* Clamp the framerate in a caps structure to be a smaller range then
363 * [1...max_rate], otherwise return false */
365 gst_video_max_rate_clamp_structure (GstStructure * s, gint maxrate,
366 gint * min_num, gint * min_denom, gint * max_num, gint * max_denom)
368 gboolean ret = FALSE;
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 */
376 GValue intersection = { 0, };
377 GValue clamp = { 0, };
378 gint tmp_num, tmp_denom;
380 g_value_init (&clamp, GST_TYPE_FRACTION_RANGE);
381 gst_value_set_fraction_range_full (&clamp, 0, 1, maxrate, 1);
383 v = gst_structure_get_value (s, "framerate");
384 ret = gst_value_intersect (&intersection, v, &clamp);
385 g_value_unset (&clamp);
390 gst_value_fraction_get_extremes (&intersection,
391 min_num, min_denom, max_num, max_denom);
393 gst_value_fraction_get_extremes (v,
394 &tmp_num, &tmp_denom, max_num, max_denom);
396 if (gst_util_fraction_compare (*max_num, *max_denom, maxrate, 1) > 0) {
401 gst_structure_take_value (s, "framerate", &intersection);
409 gst_video_rate_transform_caps (GstBaseTransform * trans,
410 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
412 GstVideoRate *videorate = GST_VIDEO_RATE (trans);
414 GstStructure *s, *s1, *s2, *s3 = NULL;
415 int maxrate = g_atomic_int_get (&videorate->max_rate);
418 ret = gst_caps_new_empty ();
420 for (i = 0; i < gst_caps_get_size (caps); i++) {
421 s = gst_caps_get_structure (caps, i);
423 s1 = gst_structure_copy (s);
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);
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);
437 s2 = gst_structure_copy (s);
440 if (videorate->drop_only) {
441 gint min_num = 0, min_denom = 1;
442 gint max_num = G_MAXINT, max_denom = 1;
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)) {
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 :)
457 * In case [X..maxrate] == [X..maxint], skip as we'll set it later
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);
463 gst_structure_free (s1);
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);
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);
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);
484 } else if (direction == GST_PAD_SINK) {
485 gint min_num = 0, min_denom = 1;
486 gint max_num = G_MAXINT, max_denom = 1;
488 if (!gst_video_max_rate_clamp_structure (s1, maxrate,
489 &min_num, &min_denom, &max_num, &max_denom)) {
490 gst_structure_free (s1);
493 gst_structure_set (s2, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
496 /* set the framerate as a range */
497 gst_structure_set (s2, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
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)));
506 ret = gst_caps_merge_structure_full (ret, s3,
507 gst_caps_features_copy (gst_caps_get_features (caps, i)));
510 GstCaps *intersection;
513 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
514 gst_caps_unref (ret);
521 gst_video_rate_fixate_caps (GstBaseTransform * trans,
522 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
528 s = gst_caps_get_structure (caps, 0);
529 if (G_UNLIKELY (!gst_structure_get_fraction (s, "framerate", &num, &denom)))
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);
537 if ((par = gst_structure_get_value (s, "pixel-aspect-ratio")))
538 gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
540 return gst_caps_fixate (othercaps);
544 gst_video_rate_setcaps (GstBaseTransform * trans, GstCaps * in_caps,
547 GstVideoRate *videorate = GST_VIDEO_RATE (trans);
548 GstStructure *structure;
550 gint rate_numerator, rate_denominator;
552 GST_DEBUG_OBJECT (trans, "setcaps called in: %" GST_PTR_FORMAT
553 " out: %" GST_PTR_FORMAT, in_caps, out_caps);
555 structure = gst_caps_get_structure (in_caps, 0);
556 if (!gst_structure_get_fraction (structure, "framerate",
557 &rate_numerator, &rate_denominator))
560 videorate->from_rate_numerator = rate_numerator;
561 videorate->from_rate_denominator = rate_denominator;
563 structure = gst_caps_get_structure (out_caps, 0);
564 if (!gst_structure_get_fraction (structure, "framerate",
565 &rate_numerator, &rate_denominator))
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
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);
578 videorate->out_frame_count = 0;
579 videorate->to_rate_numerator = rate_numerator;
580 videorate->to_rate_denominator = rate_denominator;
583 videorate->wanted_diff = gst_util_uint64_scale_int (GST_SECOND,
584 rate_denominator, rate_numerator);
586 videorate->wanted_diff = 0;
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;
600 GST_DEBUG_OBJECT (videorate, "no framerate specified");
607 gst_video_rate_reset (GstVideoRate * videorate)
609 GST_DEBUG_OBJECT (videorate, "resetting internal variables");
613 videorate->base_ts = 0;
614 videorate->out_frame_count = 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);
624 gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
628 gst_video_rate_init (GstVideoRate * videorate)
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;
641 videorate->from_rate_numerator = 0;
642 videorate->from_rate_denominator = 0;
643 videorate->to_rate_numerator = 0;
644 videorate->to_rate_denominator = 0;
646 gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (videorate), TRUE);
649 /* @outbuf: (transfer full) needs to be writable */
651 gst_video_rate_push_buffer (GstVideoRate * videorate, GstBuffer * outbuf,
652 gboolean duplicate, GstClockTime next_intime, gboolean invalid_duration)
655 GstClockTime push_ts;
657 GST_BUFFER_OFFSET (outbuf) = videorate->out;
658 GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
660 if (videorate->discont) {
661 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
662 videorate->discont = FALSE;
664 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
667 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
669 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP);
671 /* this is the timestamp we put on the buffer */
672 push_ts = videorate->next_ts;
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 -
682 gst_util_uint64_scale (videorate->out_frame_count + 1,
683 videorate->to_rate_denominator * GST_SECOND,
684 videorate->to_rate_numerator);
686 videorate->next_ts = next_ts < 0 ? GST_CLOCK_TIME_NONE : next_ts;
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;
698 GST_FIXME_OBJECT (videorate, "No next intime for reverse playback");
701 if (videorate->to_rate_numerator) {
702 /* interpolate next expected timestamp in the segment */
704 videorate->segment.base + videorate->segment.start +
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);
718 = GST_BUFFER_PTS (outbuf) + GST_BUFFER_DURATION (outbuf);
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;
728 GST_LOG_OBJECT (videorate,
729 "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
730 GST_TIME_ARGS (push_ts));
732 res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (videorate), outbuf);
737 /* flush the oldest buffer */
739 gst_video_rate_flush_prev (GstVideoRate * videorate, gboolean duplicate,
740 GstClockTime next_intime, gboolean invalid_duration)
744 if (!videorate->prevbuf)
745 goto eos_before_buffers;
747 outbuf = gst_buffer_ref (videorate->prevbuf);
748 /* make sure we can write to the metadata */
749 outbuf = gst_buffer_make_writable (outbuf);
751 return gst_video_rate_push_buffer (videorate, outbuf, duplicate, next_intime,
757 GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
763 gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
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;
774 gst_video_rate_notify_drop (GstVideoRate * videorate)
776 g_object_notify_by_pspec ((GObject *) videorate, pspec_drop);
780 gst_video_rate_notify_duplicate (GstVideoRate * videorate)
782 g_object_notify_by_pspec ((GObject *) videorate, pspec_duplicate);
785 #define MAGIC_LIMIT 25
787 gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
789 GstVideoRate *videorate;
791 videorate = GST_VIDEO_RATE (trans);
793 switch (GST_EVENT_TYPE (event)) {
794 case GST_EVENT_SEGMENT:
799 gst_event_copy_segment (event, &segment);
800 if (segment.format != GST_FORMAT_TIME)
803 GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
805 /* close up the previous segment, if appropriate */
806 if (videorate->prevbuf) {
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)
827 gst_video_rate_flush_prev (videorate, count > 0,
828 GST_CLOCK_TIME_NONE, FALSE);
832 videorate->dup += count - 1;
833 if (!videorate->silent)
834 gst_video_rate_notify_duplicate (videorate);
836 /* clean up for the new one; _chain will resume from the new start */
837 gst_video_rate_swap_prev (videorate, NULL, 0);
840 videorate->base_ts = 0;
841 videorate->out_frame_count = 0;
842 videorate->next_ts = GST_CLOCK_TIME_NONE;
844 /* We just want to update the accumulated stream_time */
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);
852 gst_segment_copy_into (&segment, &videorate->segment);
853 GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
854 &videorate->segment);
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);
864 case GST_EVENT_SEGMENT_DONE:
867 GstFlowReturn res = GST_FLOW_OK;
869 GST_DEBUG_OBJECT (videorate, "Got %s",
870 gst_event_type_get_name (GST_EVENT_TYPE (event)));
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)
889 res = gst_video_rate_flush_prev (videorate, count > 0,
890 GST_CLOCK_TIME_NONE, FALSE);
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);
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)
907 gst_video_rate_flush_prev (videorate, count > 0,
908 GST_CLOCK_TIME_NONE, FALSE);
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. */
915 gst_video_rate_flush_prev (videorate, FALSE, GST_CLOCK_TIME_NONE,
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)) {
928 if (!videorate->silent)
929 gst_video_rate_notify_drop (videorate);
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);
940 /* no gaps after videorate, ignore the event */
941 gst_event_unref (event);
947 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
952 GST_WARNING_OBJECT (videorate,
953 "Got segment but doesn't have GST_FORMAT_TIME value");
959 gst_video_rate_src_event (GstBaseTransform * trans, GstEvent * event)
961 GstVideoRate *videorate;
963 gboolean res = FALSE;
965 videorate = GST_VIDEO_RATE (trans);
966 sinkpad = GST_BASE_TRANSFORM_SINK_PAD (trans);
967 switch (GST_EVENT_TYPE (event)) {
972 GstSeekType start_type, stop_type;
974 gint seqnum = gst_event_get_seqnum (event);
976 gst_event_parse_seek (event, &srate, NULL, &flags, &start_type, &start,
979 start = (gint64) (start * videorate->rate);
980 if (GST_CLOCK_TIME_IS_VALID (stop)) {
981 stop = (gint64) (stop * videorate->rate);
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);
989 res = gst_pad_push_event (sinkpad, event);
996 GstClockTimeDiff diff;
997 GstClockTime timestamp;
999 gst_event_parse_qos (event, &type, &proportion, &diff, ×tamp);
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));
1011 if (videorate->segment.rate < 0.0)
1013 (videorate->segment.stop - videorate->base_ts) -
1014 ((videorate->segment.stop - videorate->base_ts -
1015 timestamp) * videorate->rate);
1018 videorate->base_ts + ((timestamp -
1019 videorate->base_ts) * videorate->rate);
1021 diff *= videorate->rate;
1022 GST_OBJECT_UNLOCK (trans);
1024 gst_event_unref (event);
1025 event = gst_event_new_qos (type, proportion, diff, timestamp);
1030 res = gst_pad_push_event (sinkpad, event);
1037 gst_video_rate_query (GstBaseTransform * trans, GstPadDirection direction,
1040 GstVideoRate *videorate = GST_VIDEO_RATE (trans);
1041 gboolean res = FALSE;
1044 otherpad = (direction == GST_PAD_SRC) ?
1045 GST_BASE_TRANSFORM_SINK_PAD (trans) : GST_BASE_TRANSFORM_SRC_PAD (trans);
1047 switch (GST_QUERY_TYPE (query)) {
1048 case GST_QUERY_LATENCY:
1050 GstClockTime min, max;
1057 GST_OBJECT_LOCK (videorate);
1058 avg_period = videorate->average_period_set;
1059 drop_only = videorate->drop_only;
1060 GST_OBJECT_UNLOCK (videorate);
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);
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));
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);
1079 /* no input framerate, we don't know */
1083 GST_DEBUG_OBJECT (videorate, "Our latency: %"
1084 GST_TIME_FORMAT, GST_TIME_ARGS (latency));
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));
1094 gst_query_set_latency (query, live, min, max);
1096 gst_object_unref (peer);
1099 /* Simple fall back if we don't have a latency or a peer that we
1100 * can ask about its latency yet.. */
1102 GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1106 case GST_QUERY_DURATION:
1113 GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1119 GST_OBJECT_LOCK (videorate);
1120 rate = videorate->pending_rate;
1121 GST_OBJECT_UNLOCK (videorate);
1126 gst_query_parse_duration (query, &format, &duration);
1128 if (format != GST_FORMAT_TIME) {
1129 GST_DEBUG_OBJECT (videorate, "not TIME format");
1132 GST_LOG_OBJECT (videorate, "upstream duration: %" G_GINT64_FORMAT,
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);
1138 GST_LOG_OBJECT (videorate, "our duration: %" G_GINT64_FORMAT, duration);
1139 gst_query_set_duration (query, format, duration);
1142 case GST_QUERY_POSITION:
1144 GstFormat dst_format;
1148 GST_OBJECT_LOCK (videorate);
1149 rate = videorate->rate;
1150 GST_OBJECT_UNLOCK (videorate);
1152 gst_query_parse_position (query, &dst_format, NULL);
1154 if (dst_format != GST_FORMAT_TIME) {
1155 GST_DEBUG_OBJECT (videorate, "not TIME format");
1158 /* Shouldn't this be a multiplication if the direction is downstream? */
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);
1170 GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1179 gst_video_rate_propose_allocation (GstBaseTransform * trans,
1180 GstQuery * decide_query, GstQuery * query)
1182 GstBaseTransformClass *klass = GST_BASE_TRANSFORM_CLASS (parent_class);
1185 /* We should always be passthrough */
1186 g_return_val_if_fail (decide_query == NULL, FALSE);
1188 res = klass->propose_allocation (trans, NULL, query);
1195 n_allocation = gst_query_get_n_allocation_pools (query);
1197 while (i < n_allocation) {
1198 GstBufferPool *pool = NULL;
1199 guint size, min, max;
1201 gst_query_parse_nth_allocation_pool (query, i, &pool, &size, &min, &max);
1205 gst_object_unref (pool);
1206 gst_query_remove_nth_allocation_pool (query, i);
1208 down_min = MAX (min, down_min);
1212 gst_query_set_nth_allocation_pool (query, i, pool, size, min + 1, max);
1214 gst_object_unref (pool);
1218 if (n_allocation == 0) {
1222 gst_query_parse_allocation (query, &caps, NULL);
1223 gst_video_info_from_caps (&info, caps);
1225 gst_query_add_allocation_pool (query, NULL, info.size, down_min + 1, 0);
1232 static GstFlowReturn
1233 gst_video_rate_trans_ip_max_avg (GstVideoRate * videorate, GstBuffer * buf)
1235 GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
1239 if (!GST_CLOCK_TIME_IS_VALID (ts) || videorate->wanted_diff == 0)
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;
1248 /* Drop buffer if its early compared to the desired frame rate and
1249 * the current average is higher than the desired average
1251 if (diff < videorate->wanted_diff &&
1252 videorate->average < videorate->wanted_diff)
1255 /* Update average */
1256 if (videorate->average) {
1257 GstClockTimeDiff wanted_diff;
1259 if (G_LIKELY (videorate->average_period > videorate->wanted_diff))
1260 wanted_diff = videorate->wanted_diff;
1262 wanted_diff = videorate->average_period * 10;
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);
1271 videorate->average = diff;
1275 videorate->last_ts = ts;
1282 if (!videorate->silent)
1283 gst_video_rate_notify_drop (videorate);
1284 return GST_BASE_TRANSFORM_FLOW_DROPPED;
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
1293 gst_video_rate_check_variable_rate (GstVideoRate * videorate,
1298 GstCaps *srcpadcaps, *tmpcaps, *downstream_caps;
1302 gst_pad_get_current_caps (GST_BASE_TRANSFORM_SRC_PAD (videorate));
1304 gst_video_guess_framerate (GST_BUFFER_PTS (buffer) -
1305 GST_BUFFER_PTS (videorate->prevbuf), &fps_n, &fps_d);
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);
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"
1322 gst_caps_unref (downstream_caps);
1324 videorate->to_rate_numerator = fps_n;
1325 videorate->to_rate_denominator = fps_d;
1327 GST_INFO_OBJECT (videorate, "Computed framerate to %d/%d",
1328 videorate->to_rate_numerator, videorate->to_rate_denominator);
1330 videorate->updating_caps = TRUE;
1331 gst_base_transform_update_src_caps (GST_BASE_TRANSFORM (videorate), tmpcaps);
1333 /* also reconfigure sink so that buffer pool can be updated again */
1334 gst_base_transform_reconfigure_sink (GST_BASE_TRANSFORM (videorate));
1337 gst_caps_unref (tmpcaps);
1339 gst_object_unref (pad);
1343 gst_video_rate_switch_mode_if_needed (GstVideoRate * videorate)
1345 gboolean switch_mode;
1346 GstClockTime avg_period;
1347 gboolean skip = FALSE;
1349 GST_OBJECT_LOCK (videorate);
1350 avg_period = videorate->average_period_set;
1351 GST_OBJECT_UNLOCK (videorate);
1353 /* MT-safe switching between modes */
1354 if (G_LIKELY (avg_period == videorate->average_period))
1357 switch_mode = (avg_period == 0 || videorate->average_period == 0);
1363 videorate->average_period = avg_period;
1364 videorate->last_ts = GST_CLOCK_TIME_NONE;
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);
1371 /* enable regular mode */
1372 videorate->next_ts = GST_CLOCK_TIME_NONE;
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)));
1384 gst_video_rate_do_max_duplicate (GstVideoRate * videorate, GstBuffer * buffer,
1385 GstClockTime intime, GstClockTime prevtime, gint * count)
1387 if (videorate->max_duplication_time <= 0)
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,
1409 while (videorate->next_ts <= prevtime) {
1410 gst_video_rate_flush_prev (videorate, *count > 0, GST_CLOCK_TIME_NONE,
1417 videorate->dup += *count - 1;
1418 if (!videorate->silent)
1419 gst_video_rate_notify_duplicate (videorate);
1422 /* The gap between the two buffers is too large. Don't fill it, just
1423 * let a discont through */
1424 videorate->discont = TRUE;
1426 if (videorate->segment.rate < 0.0) {
1427 videorate->base_ts -= prevtime - intime;
1429 videorate->base_ts += intime - prevtime;
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);
1442 gst_video_rate_apply_pending_rate (GstVideoRate * videorate)
1444 gboolean ret = FALSE;
1446 GST_OBJECT_LOCK (videorate);
1447 if (videorate->pending_rate == videorate->rate)
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;
1458 GST_OBJECT_UNLOCK (videorate);
1463 static GstFlowReturn
1464 gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer)
1466 GstVideoRate *videorate;
1467 GstFlowReturn res = GST_BASE_TRANSFORM_FLOW_DROPPED;
1468 GstClockTime intime, in_ts, in_dur, last_ts;
1471 videorate = GST_VIDEO_RATE (trans);
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;
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"
1486 return GST_FLOW_ERROR;
1488 gst_video_rate_check_variable_rate (videorate, buffer);
1491 skip = gst_video_rate_switch_mode_if_needed (videorate);
1493 if (videorate->average_period > 0)
1494 return gst_video_rate_trans_ip_max_avg (videorate, buffer);
1496 gst_video_rate_apply_pending_rate (videorate);
1497 in_ts = GST_BUFFER_TIMESTAMP (buffer);
1498 in_dur = GST_BUFFER_DURATION (buffer);
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;
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;
1517 GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
1518 GST_TIME_ARGS (in_ts));
1520 /* the input time is the time in the segment + all previously accumulated
1522 intime = in_ts + videorate->segment.base;
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;
1534 GST_BUFFER_DURATION (buffer) = last_ts - in_ts;
1537 gst_video_rate_swap_prev (videorate, buffer, intime);
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;
1547 videorate->base_ts = in_ts - videorate->segment.start;
1549 videorate->out_frame_count = 0;
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);
1557 videorate->next_ts =
1558 videorate->segment.stop + videorate->segment.base;
1560 if (videorate->next_ts > frame_duration)
1561 videorate->next_ts =
1562 MAX (videorate->segment.start,
1563 videorate->next_ts - frame_duration);
1565 videorate->next_ts = videorate->segment.start;
1567 /* What else can we do? */
1568 videorate->next_ts = intime;
1571 videorate->next_ts =
1572 videorate->segment.start + videorate->segment.base;
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
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)) {
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) {
1596 /* No need to keep the buffer around for longer */
1597 gst_buffer_replace (&videorate->prevbuf, NULL);
1600 GstClockTime prevtime;
1602 gint64 diff1 = 0, diff2 = 0;
1604 prevtime = videorate->prev_ts;
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));
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));
1622 if (!videorate->silent)
1623 gst_video_rate_notify_drop (videorate);
1627 if (!gst_video_rate_do_max_duplicate (videorate, buffer, intime, prevtime,
1631 /* got 2 buffers, see which one is the best */
1633 GstClockTime next_ts;
1635 if (gst_video_rate_apply_pending_rate (videorate))
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;
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;
1654 #define ABSDIFF(a, b) (((a) > (b)) ? (a) - (b) : (b) - (a))
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;
1663 next_ts = videorate->next_ts;
1665 if (!GST_CLOCK_TIME_IS_VALID (next_ts)) {
1666 GST_DEBUG_OBJECT (videorate, "Already reached segment start,"
1671 prev_endtime = prevtime + GST_BUFFER_DURATION (videorate->prevbuf);
1672 in_endtime = intime + GST_BUFFER_DURATION (buffer);
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;
1680 next_end_ts = next_ts + GST_BUFFER_DURATION (videorate->prevbuf);
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);
1689 diff1 = ABSDIFF (prev_endtime, next_end_ts);
1690 diff2 = ABSDIFF (in_endtime, next_end_ts);
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));
1699 videorate->base_ts + ((videorate->next_ts -
1700 videorate->base_ts) * videorate->rate);
1702 diff1 = ABSDIFF (prevtime, next_ts);
1703 diff2 = ABSDIFF (intime, next_ts);
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));
1712 /* output first one when its the best */
1713 if (diff1 <= diff2) {
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) {
1725 /* continue while the first one was the best, if they were equal avoid
1726 * going into an infinite loop */
1728 while (diff1 < diff2);
1730 /* if we outputted the first buffer more then once, we have dups */
1732 videorate->dup += count - 1;
1733 if (!videorate->silent)
1734 gst_video_rate_notify_duplicate (videorate);
1736 /* if we didn't output the first buffer, we have a drop */
1737 else if (count == 0) {
1740 if (!videorate->silent)
1741 gst_video_rate_notify_drop (videorate);
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));
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);
1755 /* swap in new one when it's the best */
1756 gst_video_rate_swap_prev (videorate, buffer, intime);
1764 GST_WARNING_OBJECT (videorate, "no framerate negotiated");
1765 res = GST_FLOW_NOT_NEGOTIATED;
1771 GST_WARNING_OBJECT (videorate,
1772 "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
1773 res = GST_BASE_TRANSFORM_FLOW_DROPPED;
1779 gst_video_rate_start (GstBaseTransform * trans)
1781 gst_video_rate_reset (GST_VIDEO_RATE (trans));
1786 gst_video_rate_stop (GstBaseTransform * trans)
1788 gst_video_rate_reset (GST_VIDEO_RATE (trans));
1793 gst_videorate_update_duration (GstVideoRate * videorate)
1797 m = gst_message_new_duration_changed (GST_OBJECT (videorate));
1798 gst_element_post_message (GST_ELEMENT (videorate), m);
1802 gst_video_rate_set_property (GObject * object,
1803 guint prop_id, const GValue * value, GParamSpec * pspec)
1805 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1806 gboolean latency_changed = FALSE;
1808 GST_OBJECT_LOCK (videorate);
1811 videorate->silent = g_value_get_boolean (value);
1814 videorate->new_pref = g_value_get_double (value);
1816 case PROP_SKIP_TO_FIRST:
1817 videorate->skip_to_first = g_value_get_boolean (value);
1819 case PROP_DROP_ONLY:{
1820 gboolean new_value = g_value_get_boolean (value);
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);
1827 case PROP_AVERAGE_PERIOD:
1828 videorate->average_period_set = g_value_get_uint64 (value);
1831 g_atomic_int_set (&videorate->max_rate, g_value_get_int (value));
1834 videorate->pending_rate = g_value_get_double (value);
1835 GST_OBJECT_UNLOCK (videorate);
1837 gst_videorate_update_duration (videorate);
1839 case PROP_MAX_DUPLICATION_TIME:
1840 videorate->max_duplication_time = g_value_get_uint64 (value);
1843 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1846 GST_OBJECT_UNLOCK (videorate);
1851 GST_OBJECT_UNLOCK (videorate);
1852 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (videorate));
1854 if (latency_changed) {
1855 gst_element_post_message (GST_ELEMENT (videorate),
1856 gst_message_new_latency (GST_OBJECT (videorate)));
1861 gst_video_rate_get_property (GObject * object,
1862 guint prop_id, GValue * value, GParamSpec * pspec)
1864 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1866 GST_OBJECT_LOCK (videorate);
1869 g_value_set_uint64 (value, videorate->in);
1872 g_value_set_uint64 (value, videorate->out);
1875 g_value_set_uint64 (value, videorate->dup);
1878 g_value_set_uint64 (value, videorate->drop);
1881 g_value_set_boolean (value, videorate->silent);
1884 g_value_set_double (value, videorate->new_pref);
1886 case PROP_SKIP_TO_FIRST:
1887 g_value_set_boolean (value, videorate->skip_to_first);
1889 case PROP_DROP_ONLY:
1890 g_value_set_boolean (value, videorate->drop_only);
1892 case PROP_AVERAGE_PERIOD:
1893 g_value_set_uint64 (value, videorate->average_period_set);
1896 g_value_set_int (value, g_atomic_int_get (&videorate->max_rate));
1899 g_value_set_double (value, videorate->pending_rate);
1901 case PROP_MAX_DUPLICATION_TIME:
1902 g_value_set_uint64 (value, videorate->max_duplication_time);
1905 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1908 GST_OBJECT_UNLOCK (videorate);
1912 plugin_init (GstPlugin * plugin)
1914 GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
1915 "VideoRate stream fixer");
1917 return GST_ELEMENT_REGISTER (videorate, plugin);
1920 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1923 "Adjusts video frames",
1924 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)