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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-videorate
23 * This element takes an incoming stream of timestamped video frames.
24 * It will produce a perfect stream that matches the source pad's framerate.
26 * The correction is performed by dropping and duplicating frames, no fancy
27 * algorithm is used to interpolate frames (yet).
29 * By default the element will simply negotiate the same framerate on its
30 * source and sink pad.
32 * This operation is useful to link to elements that require a perfect stream.
33 * Typical examples are formats that do not store timestamps for video frames,
34 * but only store a framerate, like Ogg and AVI.
36 * A conversion to a specific framerate can be forced by using filtered caps on
39 * The properties #GstVideoRate:in, #GstVideoRate:out, #GstVideoRate:duplicate
40 * and #GstVideoRate:drop can be read to obtain information about number of
41 * input frames, output frames, dropped frames (i.e. the number of unused input
42 * frames) and duplicated frames (i.e. the number of times an input frame was
43 * duplicated, beside being used normally).
45 * An input stream that needs no adjustments will thus never have dropped or
48 * When the #GstVideoRate:silent property is set to FALSE, a GObject property
49 * notification will be emitted whenever one of the #GstVideoRate:duplicate or
50 * #GstVideoRate:drop values changes.
51 * This can potentially cause performance degradation.
52 * Note that property notification will happen from the streaming thread, so
53 * applications should be prepared for this.
56 * <title>Example pipelines</title>
58 * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videorate ! video/x-raw-yuv,framerate=15/1 ! xvimagesink
59 * ]| Decode an Ogg/Theora file and adjust the framerate to 15 fps before playing.
60 * To create the test Ogg/Theora file refer to the documentation of theoraenc.
62 * gst-launch -v v4lsrc ! videorate ! video/x-raw-yuv,framerate=25/2 ! theoraenc ! oggmux ! filesink location=v4l.ogg
63 * ]| Capture video from a V4L device, and adjust the stream to 12.5 fps before
64 * encoding to Ogg/Theora.
67 * Last reviewed on 2006-09-02 (0.10.11)
74 #include "gstvideorate.h"
76 GST_DEBUG_CATEGORY_STATIC (video_rate_debug);
77 #define GST_CAT_DEFAULT video_rate_debug
79 /* GstVideoRate signals and args */
86 #define DEFAULT_SILENT TRUE
87 #define DEFAULT_NEW_PREF 1.0
88 #define DEFAULT_SKIP_TO_FIRST FALSE
89 #define DEFAULT_DROP_ONLY FALSE
90 #define DEFAULT_AVERAGE_PERIOD 0
107 static GstStaticPadTemplate gst_video_rate_src_template =
108 GST_STATIC_PAD_TEMPLATE ("src",
111 GST_STATIC_CAPS ("video/x-raw-yuv;"
112 "video/x-raw-rgb;" "video/x-raw-gray;" "image/jpeg;" "image/png")
115 static GstStaticPadTemplate gst_video_rate_sink_template =
116 GST_STATIC_PAD_TEMPLATE ("sink",
119 GST_STATIC_CAPS ("video/x-raw-yuv;"
120 "video/x-raw-rgb;" "video/x-raw-gray;" "image/jpeg;" "image/png")
123 static void gst_video_rate_swap_prev (GstVideoRate * videorate,
124 GstBuffer * buffer, gint64 time);
125 static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
126 static gboolean gst_video_rate_query (GstPad * pad, GstQuery * query);
127 static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
129 static void gst_video_rate_set_property (GObject * object,
130 guint prop_id, const GValue * value, GParamSpec * pspec);
131 static void gst_video_rate_get_property (GObject * object,
132 guint prop_id, GValue * value, GParamSpec * pspec);
134 static GstStateChangeReturn gst_video_rate_change_state (GstElement * element,
135 GstStateChange transition);
137 /*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */
139 static GParamSpec *pspec_drop = NULL;
140 static GParamSpec *pspec_duplicate = NULL;
142 #define gst_video_rate_parent_class parent_class
143 G_DEFINE_TYPE (GstVideoRate, gst_video_rate, GST_TYPE_ELEMENT);
146 gst_video_rate_class_init (GstVideoRateClass * klass)
148 GObjectClass *object_class = G_OBJECT_CLASS (klass);
149 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
151 parent_class = g_type_class_peek_parent (klass);
153 object_class->set_property = gst_video_rate_set_property;
154 object_class->get_property = gst_video_rate_get_property;
156 g_object_class_install_property (object_class, ARG_IN,
157 g_param_spec_uint64 ("in", "In",
158 "Number of input frames", 0, G_MAXUINT64, 0,
159 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
160 g_object_class_install_property (object_class, ARG_OUT,
161 g_param_spec_uint64 ("out", "Out", "Number of output frames", 0,
162 G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
163 pspec_duplicate = g_param_spec_uint64 ("duplicate", "Duplicate",
164 "Number of duplicated frames", 0, G_MAXUINT64, 0,
165 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
166 g_object_class_install_property (object_class, ARG_DUP, pspec_duplicate);
167 pspec_drop = g_param_spec_uint64 ("drop", "Drop", "Number of dropped frames",
168 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
169 g_object_class_install_property (object_class, ARG_DROP, pspec_drop);
170 g_object_class_install_property (object_class, ARG_SILENT,
171 g_param_spec_boolean ("silent", "silent",
172 "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT,
173 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
174 g_object_class_install_property (object_class, ARG_NEW_PREF,
175 g_param_spec_double ("new-pref", "New Pref",
176 "Value indicating how much to prefer new frames (unused)", 0.0, 1.0,
177 DEFAULT_NEW_PREF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180 * GstVideoRate:skip-to-first:
182 * Don't produce buffers before the first one we receive.
186 g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST,
187 g_param_spec_boolean ("skip-to-first", "Skip to first buffer",
188 "Don't produce buffers before the first one we receive",
189 DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191 gst_element_class_set_details_simple (element_class,
192 "Video rate adjuster", "Filter/Effect/Video",
193 "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
194 "Wim Taymans <wim@fluendo.com>");
196 gst_element_class_add_pad_template (element_class,
197 gst_static_pad_template_get (&gst_video_rate_sink_template));
198 gst_element_class_add_pad_template (element_class,
199 gst_static_pad_template_get (&gst_video_rate_src_template));
202 * GstVideoRate:drop-only:
204 * Only drop frames, no duplicates are produced.
208 g_object_class_install_property (object_class, ARG_DROP_ONLY,
209 g_param_spec_boolean ("drop-only", "Only Drop",
210 "Only drop frames, no duplicates are produced",
211 DEFAULT_DROP_ONLY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
214 * GstVideoRate:average-period:
216 * Arrange for maximum framerate by dropping frames beyond a certain framerate,
217 * where the framerate is calculated using a moving average over the
222 g_object_class_install_property (object_class, ARG_AVERAGE_PERIOD,
223 g_param_spec_uint64 ("average-period", "Period over which to average",
224 "Period over which to average the framerate (in ns) (0 = disabled)",
225 0, G_MAXINT64, DEFAULT_AVERAGE_PERIOD,
226 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
228 element_class->change_state = GST_DEBUG_FUNCPTR (gst_video_rate_change_state);
231 /* return the caps that can be used on out_pad given in_caps on in_pad */
233 gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
234 GstPad * out_pad, GstCaps ** out_caps, GstCaps * filter)
236 GstCaps *intersect, *in_templ;
238 GSList *extra_structures = NULL;
241 in_templ = gst_pad_get_pad_template_caps (in_pad);
243 gst_caps_intersect_full (in_caps, in_templ, GST_CAPS_INTERSECT_FIRST);
244 gst_caps_unref (in_templ);
246 /* all possible framerates are allowed */
247 for (i = 0; i < gst_caps_get_size (intersect); i++) {
248 GstStructure *structure;
250 structure = gst_caps_get_structure (intersect, i);
252 if (gst_structure_has_field (structure, "framerate")) {
253 GstStructure *copy_structure;
255 copy_structure = gst_structure_copy (structure);
256 gst_structure_set (copy_structure,
257 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
258 extra_structures = g_slist_append (extra_structures, copy_structure);
262 /* append the extra structures */
263 for (iter = extra_structures; iter != NULL; iter = g_slist_next (iter)) {
264 gst_caps_append_structure (intersect, (GstStructure *) iter->data);
266 g_slist_free (extra_structures);
271 tmp = gst_caps_intersect_full (filter, intersect, GST_CAPS_INTERSECT_FIRST);
272 gst_caps_unref (intersect);
276 *out_caps = intersect;
282 gst_video_rate_getcaps (GstPad * pad, GstCaps * filter)
284 GstVideoRate *videorate;
288 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
290 otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
293 /* we can do what the peer can */
294 caps = gst_pad_peer_get_caps (otherpad, filter);
296 GstCaps *transform, *intersect;
298 gst_video_rate_transformcaps (otherpad, caps, pad, &transform, filter);
300 /* Now prefer the downstream caps if possible */
302 gst_caps_intersect_full (caps, transform, GST_CAPS_INTERSECT_FIRST);
303 if (!gst_caps_is_empty (intersect)) {
304 gst_caps_append (intersect, transform);
305 gst_caps_unref (caps);
308 gst_caps_unref (intersect);
312 /* no peer, our padtemplate is enough then */
313 caps = gst_pad_get_pad_template_caps (pad);
315 GstCaps *intersection;
317 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
318 gst_caps_unref (caps);
327 gst_video_rate_set_src_caps (GstVideoRate * videorate, GstCaps * caps)
329 GstStructure *structure;
330 gint rate_numerator, rate_denominator;
332 GST_DEBUG_OBJECT (videorate, "src caps %" GST_PTR_FORMAT, caps);
334 structure = gst_caps_get_structure (caps, 0);
335 if (!gst_structure_get_fraction (structure, "framerate",
336 &rate_numerator, &rate_denominator))
339 /* out_frame_count is scaled by the frame rate caps when calculating next_ts.
340 * when the frame rate caps change, we must update base_ts and reset
342 if (videorate->to_rate_numerator) {
343 videorate->base_ts +=
344 gst_util_uint64_scale (videorate->out_frame_count,
345 videorate->to_rate_denominator * GST_SECOND,
346 videorate->to_rate_numerator);
348 videorate->out_frame_count = 0;
349 videorate->to_rate_numerator = rate_numerator;
350 videorate->to_rate_denominator = rate_denominator;
351 videorate->wanted_diff = gst_util_uint64_scale_int (GST_SECOND,
352 rate_denominator, rate_numerator);
354 gst_pad_push_event (videorate->srcpad, gst_event_new_caps (caps));
361 GST_DEBUG_OBJECT (videorate, "no framerate specified");
367 gst_video_rate_set_sink_caps (GstVideoRate * videorate, GstCaps * caps)
369 GstStructure *structure;
371 gint rate_numerator, rate_denominator;
373 GST_DEBUG_OBJECT (videorate, "sink caps %" GST_PTR_FORMAT, caps);
375 structure = gst_caps_get_structure (caps, 0);
376 if (!gst_structure_get_fraction (structure, "framerate",
377 &rate_numerator, &rate_denominator))
380 videorate->from_rate_numerator = rate_numerator;
381 videorate->from_rate_denominator = rate_denominator;
383 /* now try to find something for the peer */
384 if (gst_pad_peer_accept_caps (videorate->srcpad, caps)) {
385 /* the peer accepts the caps as they are */
386 ret = gst_video_rate_set_src_caps (videorate, caps);
388 GstCaps *transform = NULL;
392 /* see how we can transform the input caps */
393 if (!gst_video_rate_transformcaps (videorate->sinkpad, caps,
394 videorate->srcpad, &transform, NULL))
397 GST_DEBUG_OBJECT (videorate, "transform %" GST_PTR_FORMAT, transform);
399 /* see what the peer can do */
400 caps = gst_pad_peer_get_caps (videorate->srcpad, transform);
402 GST_DEBUG_OBJECT (videorate, "icaps %" GST_PTR_FORMAT, caps);
404 /* could turn up empty, due to e.g. colorspace etc */
405 if (gst_caps_get_size (caps) == 0) {
406 gst_caps_unref (caps);
410 /* take first possibility */
411 caps = gst_caps_make_writable (caps);
412 gst_caps_truncate (caps);
413 structure = gst_caps_get_structure (caps, 0);
416 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
417 rate_numerator, rate_denominator);
418 gst_structure_get_fraction (structure, "framerate",
419 &rate_numerator, &rate_denominator);
421 videorate->to_rate_numerator = rate_numerator;
422 videorate->to_rate_denominator = rate_denominator;
424 if (gst_structure_has_field (structure, "interlaced"))
425 gst_structure_fixate_field_boolean (structure, "interlaced", FALSE);
426 if (gst_structure_has_field (structure, "color-matrix"))
427 gst_structure_fixate_field_string (structure, "color-matrix", "sdtv");
428 if (gst_structure_has_field (structure, "chroma-site"))
429 gst_structure_fixate_field_string (structure, "chroma-site", "mpeg2");
430 if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
431 gst_structure_fixate_field_nearest_fraction (structure,
432 "pixel-aspect-ratio", 1, 1);
434 ret = gst_video_rate_set_src_caps (videorate, caps);
435 gst_caps_unref (caps);
438 /* After a setcaps, our caps may have changed. In that case, we can't use
439 * the old buffer, if there was one (it might have different dimensions) */
440 GST_DEBUG_OBJECT (videorate, "swapping old buffers");
441 gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE);
447 GST_DEBUG_OBJECT (videorate, "no framerate specified");
452 GST_DEBUG_OBJECT (videorate, "no framerate transform possible");
459 gst_video_rate_reset (GstVideoRate * videorate)
461 GST_DEBUG_OBJECT (videorate, "resetting internal variables");
465 videorate->base_ts = 0;
466 videorate->out_frame_count = 0;
469 videorate->next_ts = GST_CLOCK_TIME_NONE;
470 videorate->last_ts = GST_CLOCK_TIME_NONE;
471 videorate->discont = TRUE;
472 videorate->average = 0;
473 gst_video_rate_swap_prev (videorate, NULL, 0);
475 gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
479 gst_video_rate_init (GstVideoRate * videorate)
482 gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
483 gst_pad_set_event_function (videorate->sinkpad,
484 GST_DEBUG_FUNCPTR (gst_video_rate_event));
485 gst_pad_set_chain_function (videorate->sinkpad,
486 GST_DEBUG_FUNCPTR (gst_video_rate_chain));
487 gst_pad_set_getcaps_function (videorate->sinkpad,
488 GST_DEBUG_FUNCPTR (gst_video_rate_getcaps));
489 gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
492 gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
493 gst_pad_set_query_function (videorate->srcpad,
494 GST_DEBUG_FUNCPTR (gst_video_rate_query));
495 gst_pad_set_getcaps_function (videorate->srcpad,
496 GST_DEBUG_FUNCPTR (gst_video_rate_getcaps));
497 gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
499 gst_video_rate_reset (videorate);
500 videorate->silent = DEFAULT_SILENT;
501 videorate->new_pref = DEFAULT_NEW_PREF;
502 videorate->drop_only = DEFAULT_DROP_ONLY;
503 videorate->average_period = DEFAULT_AVERAGE_PERIOD;
505 videorate->from_rate_numerator = 0;
506 videorate->from_rate_denominator = 0;
507 videorate->to_rate_numerator = 0;
508 videorate->to_rate_denominator = 0;
511 /* flush the oldest buffer */
513 gst_video_rate_flush_prev (GstVideoRate * videorate, gboolean duplicate)
517 GstClockTime push_ts;
519 if (!videorate->prevbuf)
520 goto eos_before_buffers;
522 /* make sure we can write to the metadata */
523 outbuf = gst_buffer_make_writable (gst_buffer_ref (videorate->prevbuf));
525 GST_BUFFER_OFFSET (outbuf) = videorate->out;
526 GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
528 if (videorate->discont) {
529 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
530 videorate->discont = FALSE;
532 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
535 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
537 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP);
539 /* this is the timestamp we put on the buffer */
540 push_ts = videorate->next_ts;
543 videorate->out_frame_count++;
544 if (videorate->to_rate_numerator) {
545 /* interpolate next expected timestamp in the segment */
547 videorate->segment.base + videorate->segment.start +
548 videorate->base_ts + gst_util_uint64_scale (videorate->out_frame_count,
549 videorate->to_rate_denominator * GST_SECOND,
550 videorate->to_rate_numerator);
551 GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
554 /* We do not need to update time in VFR (variable frame rate) mode */
555 if (!videorate->drop_only) {
556 /* adapt for looping, bring back to time in current segment. */
557 GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.base;
560 GST_LOG_OBJECT (videorate,
561 "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
562 GST_TIME_ARGS (push_ts));
564 res = gst_pad_push (videorate->srcpad, outbuf);
571 GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
577 gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
580 GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer);
581 if (videorate->prevbuf)
582 gst_buffer_unref (videorate->prevbuf);
583 videorate->prevbuf = buffer;
584 videorate->prev_ts = time;
588 gst_video_rate_notify_drop (GstVideoRate * videorate)
590 #if !GLIB_CHECK_VERSION(2,26,0)
591 g_object_notify ((GObject *) videorate, "drop");
593 g_object_notify_by_pspec ((GObject *) videorate, pspec_drop);
598 gst_video_rate_notify_duplicate (GstVideoRate * videorate)
600 #if !GLIB_CHECK_VERSION(2,26,0)
601 g_object_notify ((GObject *) videorate, "duplicate");
603 g_object_notify_by_pspec ((GObject *) videorate, pspec_duplicate);
607 #define MAGIC_LIMIT 25
609 gst_video_rate_event (GstPad * pad, GstEvent * event)
611 GstVideoRate *videorate;
614 videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
616 switch (GST_EVENT_TYPE (event)) {
621 gst_event_parse_caps (event, &caps);
622 ret = gst_video_rate_set_sink_caps (videorate, caps);
623 gst_event_unref (event);
628 case GST_EVENT_SEGMENT:
630 const GstSegment *segment;
632 gst_event_parse_segment (event, &segment);
634 if (segment->format != GST_FORMAT_TIME)
637 GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
639 /* close up the previous segment, if appropriate */
640 if (videorate->prevbuf) {
645 /* fill up to the end of current segment,
646 * or only send out the stored buffer if there is no specific stop.
647 * regardless, prevent going loopy in strange cases */
648 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
649 ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) &&
650 videorate->next_ts - videorate->segment.base
651 < videorate->segment.stop)
653 res = gst_video_rate_flush_prev (videorate, count > 0);
657 videorate->dup += count - 1;
658 if (!videorate->silent)
659 gst_video_rate_notify_duplicate (videorate);
660 } else if (count == 0) {
662 if (!videorate->silent)
663 gst_video_rate_notify_drop (videorate);
665 /* clean up for the new one; _chain will resume from the new start */
666 videorate->base_ts = 0;
667 videorate->out_frame_count = 0;
668 gst_video_rate_swap_prev (videorate, NULL, 0);
669 videorate->next_ts = GST_CLOCK_TIME_NONE;
672 /* We just want to update the accumulated stream_time */
673 gst_segment_copy_into (segment, &videorate->segment);
675 GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
676 &videorate->segment);
681 GstFlowReturn res = GST_FLOW_OK;
683 GST_DEBUG_OBJECT (videorate, "Got EOS");
685 /* If the segment has a stop position, fill the segment */
686 if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) {
687 /* fill up to the end of current segment,
688 * or only send out the stored buffer if there is no specific stop.
689 * regardless, prevent going loopy in strange cases */
690 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
691 ((videorate->next_ts - videorate->segment.base <
692 videorate->segment.stop)
694 res = gst_video_rate_flush_prev (videorate, count > 0);
697 } else if (videorate->prevbuf) {
698 /* Output at least one frame but if the buffer duration is valid, output
699 * enough frames to use the complete buffer duration */
700 if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) {
701 GstClockTime end_ts =
702 videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf);
704 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
705 ((videorate->next_ts - videorate->segment.base < end_ts)
707 res = gst_video_rate_flush_prev (videorate, count > 0);
711 res = gst_video_rate_flush_prev (videorate, FALSE);
717 videorate->dup += count - 1;
718 if (!videorate->silent)
719 gst_video_rate_notify_duplicate (videorate);
720 } else if (count == 0) {
722 if (!videorate->silent)
723 gst_video_rate_notify_drop (videorate);
728 case GST_EVENT_FLUSH_STOP:
729 /* also resets the segment */
730 GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP");
731 gst_video_rate_reset (videorate);
737 ret = gst_pad_push_event (videorate->srcpad, event);
740 gst_object_unref (videorate);
747 GST_WARNING_OBJECT (videorate,
748 "Got segment but doesn't have GST_FORMAT_TIME value");
749 gst_event_unref (event);
756 gst_video_rate_query (GstPad * pad, GstQuery * query)
758 GstVideoRate *videorate;
759 gboolean res = FALSE;
761 videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
763 switch (GST_QUERY_TYPE (query)) {
764 case GST_QUERY_LATENCY:
766 GstClockTime min, max;
771 if ((peer = gst_pad_get_peer (videorate->sinkpad))) {
772 if ((res = gst_pad_query (peer, query))) {
773 gst_query_parse_latency (query, &live, &min, &max);
775 GST_DEBUG_OBJECT (videorate, "Peer latency: min %"
776 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
777 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
779 if (videorate->from_rate_numerator != 0) {
780 /* add latency. We don't really know since we hold on to the frames
781 * until we get a next frame, which can be anything. We assume
782 * however that this will take from_rate time. */
783 latency = gst_util_uint64_scale (GST_SECOND,
784 videorate->from_rate_denominator,
785 videorate->from_rate_numerator);
787 /* no input framerate, we don't know */
791 GST_DEBUG_OBJECT (videorate, "Our latency: %"
792 GST_TIME_FORMAT, GST_TIME_ARGS (latency));
798 GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %"
799 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
800 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
802 gst_query_set_latency (query, live, min, max);
804 gst_object_unref (peer);
809 res = gst_pad_query_default (pad, query);
812 gst_object_unref (videorate);
818 gst_video_rate_chain_max_avg (GstVideoRate * videorate, GstBuffer * buf)
820 GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
824 if (!GST_CLOCK_TIME_IS_VALID (ts) || videorate->wanted_diff == 0)
827 /* drop frames if they exceed our output rate */
828 if (GST_CLOCK_TIME_IS_VALID (videorate->last_ts)) {
829 GstClockTimeDiff diff = ts - videorate->last_ts;
831 /* Drop buffer if its early compared to the desired frame rate and
832 * the current average is higher than the desired average
834 if (diff < videorate->wanted_diff &&
835 videorate->average < videorate->wanted_diff)
839 if (videorate->average) {
840 GstClockTimeDiff wanted_diff;
842 if (G_LIKELY (videorate->average_period > videorate->wanted_diff))
843 wanted_diff = videorate->wanted_diff;
845 wanted_diff = videorate->average_period * 10;
848 gst_util_uint64_scale_round (videorate->average,
849 videorate->average_period - wanted_diff,
850 videorate->average_period) +
851 gst_util_uint64_scale_round (diff, wanted_diff,
852 videorate->average_period);
854 videorate->average = diff;
858 videorate->last_ts = ts;
863 return gst_pad_push (videorate->srcpad, buf);
866 gst_buffer_unref (buf);
867 if (!videorate->silent)
868 gst_video_rate_notify_drop (videorate);
873 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
875 GstVideoRate *videorate;
876 GstFlowReturn res = GST_FLOW_OK;
877 GstClockTime intime, in_ts, in_dur;
878 GstClockTime avg_period;
879 gboolean skip = FALSE;
881 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
883 /* make sure the denominators are not 0 */
884 if (videorate->from_rate_denominator == 0 ||
885 videorate->to_rate_denominator == 0)
888 GST_OBJECT_LOCK (videorate);
889 avg_period = videorate->average_period_set;
890 GST_OBJECT_UNLOCK (videorate);
892 /* MT-safe switching between modes */
893 if (G_UNLIKELY (avg_period != videorate->average_period)) {
894 videorate->average_period = avg_period;
895 videorate->last_ts = GST_CLOCK_TIME_NONE;
896 if (avg_period && !videorate->average) {
897 /* enabling average mode */
898 videorate->average = 0;
900 /* enable regular mode */
901 gst_video_rate_swap_prev (videorate, NULL, 0);
902 /* arrange for skip-to-first behaviour */
903 videorate->next_ts = GST_CLOCK_TIME_NONE;
908 if (videorate->average_period > 0)
909 return gst_video_rate_chain_max_avg (videorate, buffer);
911 in_ts = GST_BUFFER_TIMESTAMP (buffer);
912 in_dur = GST_BUFFER_DURATION (buffer);
914 if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) {
915 in_ts = videorate->last_ts;
916 if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE))
920 /* get the time of the next expected buffer timestamp, we use this when the
921 * next buffer has -1 as a timestamp */
922 videorate->last_ts = in_ts;
923 if (in_dur != GST_CLOCK_TIME_NONE)
924 videorate->last_ts += in_dur;
926 GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
927 GST_TIME_ARGS (in_ts));
929 /* the input time is the time in the segment + all previously accumulated
931 intime = in_ts + videorate->segment.base;
933 /* we need to have two buffers to compare */
934 if (videorate->prevbuf == NULL) {
935 gst_video_rate_swap_prev (videorate, buffer, intime);
937 if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
938 /* new buffer, we expect to output a buffer that matches the first
939 * timestamp in the segment */
940 if (videorate->skip_to_first || skip) {
941 videorate->next_ts = intime;
942 videorate->base_ts = in_ts - videorate->segment.start;
943 videorate->out_frame_count = 0;
945 videorate->next_ts = videorate->segment.start + videorate->segment.base;
949 GstClockTime prevtime;
953 prevtime = videorate->prev_ts;
955 GST_LOG_OBJECT (videorate,
956 "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
957 " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
958 GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
962 /* drop new buffer if it's before previous one */
963 if (intime < prevtime) {
964 GST_DEBUG_OBJECT (videorate,
965 "The new buffer (%" GST_TIME_FORMAT
966 ") is before the previous buffer (%"
967 GST_TIME_FORMAT "). Dropping new buffer.",
968 GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
970 if (!videorate->silent)
971 gst_video_rate_notify_drop (videorate);
972 gst_buffer_unref (buffer);
976 /* got 2 buffers, see which one is the best */
979 diff1 = prevtime - videorate->next_ts;
980 diff2 = intime - videorate->next_ts;
982 /* take absolute values, beware: abs and ABS don't work for gint64 */
988 GST_LOG_OBJECT (videorate,
989 "diff with prev %" GST_TIME_FORMAT " diff with new %"
990 GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
991 GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
992 GST_TIME_ARGS (videorate->next_ts));
994 /* output first one when its the best */
995 if (diff1 <= diff2) {
998 /* on error the _flush function posted a warning already */
1000 gst_video_rate_flush_prev (videorate,
1001 count > 1)) != GST_FLOW_OK) {
1002 gst_buffer_unref (buffer);
1007 /* Do not produce any dups. We can exit loop now */
1008 if (videorate->drop_only)
1010 /* continue while the first one was the best, if they were equal avoid
1011 * going into an infinite loop */
1013 while (diff1 < diff2);
1015 /* if we outputed the first buffer more then once, we have dups */
1017 videorate->dup += count - 1;
1018 if (!videorate->silent)
1019 gst_video_rate_notify_duplicate (videorate);
1021 /* if we didn't output the first buffer, we have a drop */
1022 else if (count == 0) {
1025 if (!videorate->silent)
1026 gst_video_rate_notify_drop (videorate);
1028 GST_LOG_OBJECT (videorate,
1029 "new is best, old never used, drop, outgoing ts %"
1030 GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
1032 GST_LOG_OBJECT (videorate,
1033 "END, putting new in old, diff1 %" GST_TIME_FORMAT
1034 ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
1035 ", in %" G_GUINT64_FORMAT ", out %" G_GUINT64_FORMAT ", drop %"
1036 G_GUINT64_FORMAT ", dup %" G_GUINT64_FORMAT, GST_TIME_ARGS (diff1),
1037 GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
1038 videorate->in, videorate->out, videorate->drop, videorate->dup);
1040 /* swap in new one when it's the best */
1041 gst_video_rate_swap_prev (videorate, buffer, intime);
1049 GST_WARNING_OBJECT (videorate, "no framerate negotiated");
1050 gst_buffer_unref (buffer);
1051 res = GST_FLOW_NOT_NEGOTIATED;
1057 GST_WARNING_OBJECT (videorate,
1058 "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
1059 gst_buffer_unref (buffer);
1065 gst_video_rate_set_property (GObject * object,
1066 guint prop_id, const GValue * value, GParamSpec * pspec)
1068 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1070 GST_OBJECT_LOCK (videorate);
1073 videorate->silent = g_value_get_boolean (value);
1076 videorate->new_pref = g_value_get_double (value);
1078 case ARG_SKIP_TO_FIRST:
1079 videorate->skip_to_first = g_value_get_boolean (value);
1082 videorate->drop_only = g_value_get_boolean (value);
1084 case ARG_AVERAGE_PERIOD:
1085 videorate->average_period = g_value_get_uint64 (value);
1088 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1091 GST_OBJECT_UNLOCK (videorate);
1095 gst_video_rate_get_property (GObject * object,
1096 guint prop_id, GValue * value, GParamSpec * pspec)
1098 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1100 GST_OBJECT_LOCK (videorate);
1103 g_value_set_uint64 (value, videorate->in);
1106 g_value_set_uint64 (value, videorate->out);
1109 g_value_set_uint64 (value, videorate->dup);
1112 g_value_set_uint64 (value, videorate->drop);
1115 g_value_set_boolean (value, videorate->silent);
1118 g_value_set_double (value, videorate->new_pref);
1120 case ARG_SKIP_TO_FIRST:
1121 g_value_set_boolean (value, videorate->skip_to_first);
1124 g_value_set_boolean (value, videorate->drop_only);
1126 case ARG_AVERAGE_PERIOD:
1127 g_value_set_uint64 (value, videorate->average_period);
1130 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1133 GST_OBJECT_UNLOCK (videorate);
1136 static GstStateChangeReturn
1137 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
1139 GstStateChangeReturn ret;
1140 GstVideoRate *videorate;
1142 videorate = GST_VIDEO_RATE (element);
1144 switch (transition) {
1145 case GST_STATE_CHANGE_READY_TO_PAUSED:
1146 videorate->discont = TRUE;
1147 videorate->last_ts = -1;
1153 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1155 switch (transition) {
1156 case GST_STATE_CHANGE_PAUSED_TO_READY:
1157 gst_video_rate_reset (videorate);
1167 plugin_init (GstPlugin * plugin)
1169 GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
1170 "VideoRate stream fixer");
1172 return gst_element_register (plugin, "videorate", GST_RANK_NONE,
1173 GST_TYPE_VIDEO_RATE);
1176 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1179 "Adjusts video frames",
1180 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)