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, ×tamp);
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