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,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 v4l2src ! videorate ! video/x-raw,framerate=25/2 ! theoraenc ! oggmux ! filesink location=recording.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;" "image/jpeg;" "image/png")
114 static GstStaticPadTemplate gst_video_rate_sink_template =
115 GST_STATIC_PAD_TEMPLATE ("sink",
118 GST_STATIC_CAPS ("video/x-raw;" "image/jpeg;" "image/png")
121 static void gst_video_rate_swap_prev (GstVideoRate * videorate,
122 GstBuffer * buffer, gint64 time);
123 static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
124 static gboolean gst_video_rate_query (GstPad * pad, GstQuery * query);
125 static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
127 static void gst_video_rate_set_property (GObject * object,
128 guint prop_id, const GValue * value, GParamSpec * pspec);
129 static void gst_video_rate_get_property (GObject * object,
130 guint prop_id, GValue * value, GParamSpec * pspec);
132 static GstStateChangeReturn gst_video_rate_change_state (GstElement * element,
133 GstStateChange transition);
135 /*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */
137 static GParamSpec *pspec_drop = NULL;
138 static GParamSpec *pspec_duplicate = NULL;
140 #define gst_video_rate_parent_class parent_class
141 G_DEFINE_TYPE (GstVideoRate, gst_video_rate, GST_TYPE_ELEMENT);
144 gst_video_rate_class_init (GstVideoRateClass * klass)
146 GObjectClass *object_class = G_OBJECT_CLASS (klass);
147 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
149 parent_class = g_type_class_peek_parent (klass);
151 object_class->set_property = gst_video_rate_set_property;
152 object_class->get_property = gst_video_rate_get_property;
154 g_object_class_install_property (object_class, ARG_IN,
155 g_param_spec_uint64 ("in", "In",
156 "Number of input frames", 0, G_MAXUINT64, 0,
157 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
158 g_object_class_install_property (object_class, ARG_OUT,
159 g_param_spec_uint64 ("out", "Out", "Number of output frames", 0,
160 G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
161 pspec_duplicate = g_param_spec_uint64 ("duplicate", "Duplicate",
162 "Number of duplicated frames", 0, G_MAXUINT64, 0,
163 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
164 g_object_class_install_property (object_class, ARG_DUP, pspec_duplicate);
165 pspec_drop = g_param_spec_uint64 ("drop", "Drop", "Number of dropped frames",
166 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
167 g_object_class_install_property (object_class, ARG_DROP, pspec_drop);
168 g_object_class_install_property (object_class, ARG_SILENT,
169 g_param_spec_boolean ("silent", "silent",
170 "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT,
171 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
172 g_object_class_install_property (object_class, ARG_NEW_PREF,
173 g_param_spec_double ("new-pref", "New Pref",
174 "Value indicating how much to prefer new frames (unused)", 0.0, 1.0,
175 DEFAULT_NEW_PREF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
178 * GstVideoRate:skip-to-first:
180 * Don't produce buffers before the first one we receive.
184 g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST,
185 g_param_spec_boolean ("skip-to-first", "Skip to first buffer",
186 "Don't produce buffers before the first one we receive",
187 DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
189 gst_element_class_set_details_simple (element_class,
190 "Video rate adjuster", "Filter/Effect/Video",
191 "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
192 "Wim Taymans <wim@fluendo.com>");
194 gst_element_class_add_pad_template (element_class,
195 gst_static_pad_template_get (&gst_video_rate_sink_template));
196 gst_element_class_add_pad_template (element_class,
197 gst_static_pad_template_get (&gst_video_rate_src_template));
200 * GstVideoRate:drop-only:
202 * Only drop frames, no duplicates are produced.
206 g_object_class_install_property (object_class, ARG_DROP_ONLY,
207 g_param_spec_boolean ("drop-only", "Only Drop",
208 "Only drop frames, no duplicates are produced",
209 DEFAULT_DROP_ONLY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212 * GstVideoRate:average-period:
214 * Arrange for maximum framerate by dropping frames beyond a certain framerate,
215 * where the framerate is calculated using a moving average over the
220 g_object_class_install_property (object_class, ARG_AVERAGE_PERIOD,
221 g_param_spec_uint64 ("average-period", "Period over which to average",
222 "Period over which to average the framerate (in ns) (0 = disabled)",
223 0, G_MAXINT64, DEFAULT_AVERAGE_PERIOD,
224 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
226 element_class->change_state = GST_DEBUG_FUNCPTR (gst_video_rate_change_state);
229 /* return the caps that can be used on out_pad given in_caps on in_pad */
231 gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
232 GstPad * out_pad, GstCaps ** out_caps, GstCaps * filter)
234 GstCaps *intersect, *in_templ;
236 GSList *extra_structures = NULL;
239 in_templ = gst_pad_get_pad_template_caps (in_pad);
241 gst_caps_intersect_full (in_caps, in_templ, GST_CAPS_INTERSECT_FIRST);
242 gst_caps_unref (in_templ);
244 /* all possible framerates are allowed */
245 for (i = 0; i < gst_caps_get_size (intersect); i++) {
246 GstStructure *structure;
248 structure = gst_caps_get_structure (intersect, i);
250 if (gst_structure_has_field (structure, "framerate")) {
251 GstStructure *copy_structure;
253 copy_structure = gst_structure_copy (structure);
254 gst_structure_set (copy_structure,
255 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
256 extra_structures = g_slist_append (extra_structures, copy_structure);
260 /* append the extra structures */
261 for (iter = extra_structures; iter != NULL; iter = g_slist_next (iter)) {
262 gst_caps_append_structure (intersect, (GstStructure *) iter->data);
264 g_slist_free (extra_structures);
269 tmp = gst_caps_intersect_full (filter, intersect, GST_CAPS_INTERSECT_FIRST);
270 gst_caps_unref (intersect);
274 *out_caps = intersect;
280 gst_video_rate_getcaps (GstPad * pad, GstCaps * filter)
282 GstVideoRate *videorate;
286 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
288 otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
291 /* we can do what the peer can */
292 caps = gst_pad_peer_get_caps (otherpad, filter);
294 GstCaps *transform, *intersect;
296 gst_video_rate_transformcaps (otherpad, caps, pad, &transform, filter);
298 /* Now prefer the downstream caps if possible */
300 gst_caps_intersect_full (caps, transform, GST_CAPS_INTERSECT_FIRST);
301 if (!gst_caps_is_empty (intersect)) {
302 gst_caps_append (intersect, transform);
303 gst_caps_unref (caps);
306 gst_caps_unref (intersect);
310 /* no peer, our padtemplate is enough then */
311 caps = gst_pad_get_pad_template_caps (pad);
313 GstCaps *intersection;
315 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
316 gst_caps_unref (caps);
325 gst_video_rate_set_src_caps (GstVideoRate * videorate, GstCaps * caps)
327 GstStructure *structure;
328 gint rate_numerator, rate_denominator;
330 GST_DEBUG_OBJECT (videorate, "src caps %" GST_PTR_FORMAT, caps);
332 structure = gst_caps_get_structure (caps, 0);
333 if (!gst_structure_get_fraction (structure, "framerate",
334 &rate_numerator, &rate_denominator))
337 /* out_frame_count is scaled by the frame rate caps when calculating next_ts.
338 * when the frame rate caps change, we must update base_ts and reset
340 if (videorate->to_rate_numerator) {
341 videorate->base_ts +=
342 gst_util_uint64_scale (videorate->out_frame_count,
343 videorate->to_rate_denominator * GST_SECOND,
344 videorate->to_rate_numerator);
346 videorate->out_frame_count = 0;
347 videorate->to_rate_numerator = rate_numerator;
348 videorate->to_rate_denominator = rate_denominator;
349 videorate->wanted_diff = gst_util_uint64_scale_int (GST_SECOND,
350 rate_denominator, rate_numerator);
352 gst_pad_push_event (videorate->srcpad, gst_event_new_caps (caps));
359 GST_DEBUG_OBJECT (videorate, "no framerate specified");
365 gst_video_rate_set_sink_caps (GstVideoRate * videorate, GstCaps * caps)
367 GstStructure *structure;
369 gint rate_numerator, rate_denominator;
371 GST_DEBUG_OBJECT (videorate, "sink caps %" GST_PTR_FORMAT, caps);
373 structure = gst_caps_get_structure (caps, 0);
374 if (!gst_structure_get_fraction (structure, "framerate",
375 &rate_numerator, &rate_denominator))
378 videorate->from_rate_numerator = rate_numerator;
379 videorate->from_rate_denominator = rate_denominator;
381 /* now try to find something for the peer */
382 if (gst_pad_peer_accept_caps (videorate->srcpad, caps)) {
383 /* the peer accepts the caps as they are */
384 ret = gst_video_rate_set_src_caps (videorate, caps);
386 GstCaps *transform = NULL;
390 /* see how we can transform the input caps */
391 if (!gst_video_rate_transformcaps (videorate->sinkpad, caps,
392 videorate->srcpad, &transform, NULL))
395 GST_DEBUG_OBJECT (videorate, "transform %" GST_PTR_FORMAT, transform);
397 /* see what the peer can do */
398 caps = gst_pad_peer_get_caps (videorate->srcpad, transform);
400 GST_DEBUG_OBJECT (videorate, "icaps %" GST_PTR_FORMAT, caps);
402 /* could turn up empty, due to e.g. colorspace etc */
403 if (gst_caps_get_size (caps) == 0) {
404 gst_caps_unref (caps);
408 /* take first possibility */
409 caps = gst_caps_make_writable (caps);
410 gst_caps_truncate (caps);
411 structure = gst_caps_get_structure (caps, 0);
414 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
415 rate_numerator, rate_denominator);
416 gst_structure_get_fraction (structure, "framerate",
417 &rate_numerator, &rate_denominator);
419 videorate->to_rate_numerator = rate_numerator;
420 videorate->to_rate_denominator = rate_denominator;
422 if (gst_structure_has_field (structure, "interlaced"))
423 gst_structure_fixate_field_boolean (structure, "interlaced", FALSE);
424 if (gst_structure_has_field (structure, "color-matrix"))
425 gst_structure_fixate_field_string (structure, "color-matrix", "sdtv");
426 if (gst_structure_has_field (structure, "chroma-site"))
427 gst_structure_fixate_field_string (structure, "chroma-site", "mpeg2");
428 if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
429 gst_structure_fixate_field_nearest_fraction (structure,
430 "pixel-aspect-ratio", 1, 1);
432 ret = gst_video_rate_set_src_caps (videorate, caps);
433 gst_caps_unref (caps);
436 /* After a setcaps, our caps may have changed. In that case, we can't use
437 * the old buffer, if there was one (it might have different dimensions) */
438 GST_DEBUG_OBJECT (videorate, "swapping old buffers");
439 gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE);
445 GST_DEBUG_OBJECT (videorate, "no framerate specified");
450 GST_DEBUG_OBJECT (videorate, "no framerate transform possible");
457 gst_video_rate_reset (GstVideoRate * videorate)
459 GST_DEBUG_OBJECT (videorate, "resetting internal variables");
463 videorate->base_ts = 0;
464 videorate->out_frame_count = 0;
467 videorate->next_ts = GST_CLOCK_TIME_NONE;
468 videorate->last_ts = GST_CLOCK_TIME_NONE;
469 videorate->discont = TRUE;
470 videorate->average = 0;
471 gst_video_rate_swap_prev (videorate, NULL, 0);
473 gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
477 gst_video_rate_init (GstVideoRate * videorate)
480 gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
481 gst_pad_set_event_function (videorate->sinkpad,
482 GST_DEBUG_FUNCPTR (gst_video_rate_event));
483 gst_pad_set_chain_function (videorate->sinkpad,
484 GST_DEBUG_FUNCPTR (gst_video_rate_chain));
485 gst_pad_set_getcaps_function (videorate->sinkpad,
486 GST_DEBUG_FUNCPTR (gst_video_rate_getcaps));
487 gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
490 gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
491 gst_pad_set_query_function (videorate->srcpad,
492 GST_DEBUG_FUNCPTR (gst_video_rate_query));
493 gst_pad_set_getcaps_function (videorate->srcpad,
494 GST_DEBUG_FUNCPTR (gst_video_rate_getcaps));
495 gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
497 gst_video_rate_reset (videorate);
498 videorate->silent = DEFAULT_SILENT;
499 videorate->new_pref = DEFAULT_NEW_PREF;
500 videorate->drop_only = DEFAULT_DROP_ONLY;
501 videorate->average_period = DEFAULT_AVERAGE_PERIOD;
503 videorate->from_rate_numerator = 0;
504 videorate->from_rate_denominator = 0;
505 videorate->to_rate_numerator = 0;
506 videorate->to_rate_denominator = 0;
509 /* flush the oldest buffer */
511 gst_video_rate_flush_prev (GstVideoRate * videorate, gboolean duplicate)
515 GstClockTime push_ts;
517 if (!videorate->prevbuf)
518 goto eos_before_buffers;
520 /* make sure we can write to the metadata */
521 outbuf = gst_buffer_make_writable (gst_buffer_ref (videorate->prevbuf));
523 GST_BUFFER_OFFSET (outbuf) = videorate->out;
524 GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
526 if (videorate->discont) {
527 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
528 videorate->discont = FALSE;
530 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
533 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
535 GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP);
537 /* this is the timestamp we put on the buffer */
538 push_ts = videorate->next_ts;
541 videorate->out_frame_count++;
542 if (videorate->to_rate_numerator) {
543 /* interpolate next expected timestamp in the segment */
545 videorate->segment.base + videorate->segment.start +
546 videorate->base_ts + gst_util_uint64_scale (videorate->out_frame_count,
547 videorate->to_rate_denominator * GST_SECOND,
548 videorate->to_rate_numerator);
549 GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
552 /* We do not need to update time in VFR (variable frame rate) mode */
553 if (!videorate->drop_only) {
554 /* adapt for looping, bring back to time in current segment. */
555 GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.base;
558 GST_LOG_OBJECT (videorate,
559 "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
560 GST_TIME_ARGS (push_ts));
562 res = gst_pad_push (videorate->srcpad, outbuf);
569 GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
575 gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
578 GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer);
579 if (videorate->prevbuf)
580 gst_buffer_unref (videorate->prevbuf);
581 videorate->prevbuf = buffer;
582 videorate->prev_ts = time;
586 gst_video_rate_notify_drop (GstVideoRate * videorate)
588 #if !GLIB_CHECK_VERSION(2,26,0)
589 g_object_notify ((GObject *) videorate, "drop");
591 g_object_notify_by_pspec ((GObject *) videorate, pspec_drop);
596 gst_video_rate_notify_duplicate (GstVideoRate * videorate)
598 #if !GLIB_CHECK_VERSION(2,26,0)
599 g_object_notify ((GObject *) videorate, "duplicate");
601 g_object_notify_by_pspec ((GObject *) videorate, pspec_duplicate);
605 #define MAGIC_LIMIT 25
607 gst_video_rate_event (GstPad * pad, GstEvent * event)
609 GstVideoRate *videorate;
612 videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
614 switch (GST_EVENT_TYPE (event)) {
619 gst_event_parse_caps (event, &caps);
620 ret = gst_video_rate_set_sink_caps (videorate, caps);
621 gst_event_unref (event);
626 case GST_EVENT_SEGMENT:
628 const GstSegment *segment;
630 gst_event_parse_segment (event, &segment);
632 if (segment->format != GST_FORMAT_TIME)
635 GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
637 /* close up the previous segment, if appropriate */
638 if (videorate->prevbuf) {
643 /* fill up to the end of current segment,
644 * or only send out the stored buffer if there is no specific stop.
645 * regardless, prevent going loopy in strange cases */
646 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
647 ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) &&
648 videorate->next_ts - videorate->segment.base
649 < videorate->segment.stop)
651 res = gst_video_rate_flush_prev (videorate, count > 0);
655 videorate->dup += count - 1;
656 if (!videorate->silent)
657 gst_video_rate_notify_duplicate (videorate);
658 } else if (count == 0) {
660 if (!videorate->silent)
661 gst_video_rate_notify_drop (videorate);
663 /* clean up for the new one; _chain will resume from the new start */
664 videorate->base_ts = 0;
665 videorate->out_frame_count = 0;
666 gst_video_rate_swap_prev (videorate, NULL, 0);
667 videorate->next_ts = GST_CLOCK_TIME_NONE;
670 /* We just want to update the accumulated stream_time */
671 gst_segment_copy_into (segment, &videorate->segment);
673 GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
674 &videorate->segment);
679 GstFlowReturn res = GST_FLOW_OK;
681 GST_DEBUG_OBJECT (videorate, "Got EOS");
683 /* If the segment has a stop position, fill the segment */
684 if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) {
685 /* fill up to the end of current segment,
686 * or only send out the stored buffer if there is no specific stop.
687 * regardless, prevent going loopy in strange cases */
688 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
689 ((videorate->next_ts - videorate->segment.base <
690 videorate->segment.stop)
692 res = gst_video_rate_flush_prev (videorate, count > 0);
695 } else if (videorate->prevbuf) {
696 /* Output at least one frame but if the buffer duration is valid, output
697 * enough frames to use the complete buffer duration */
698 if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) {
699 GstClockTime end_ts =
700 videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf);
702 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
703 ((videorate->next_ts - videorate->segment.base < end_ts)
705 res = gst_video_rate_flush_prev (videorate, count > 0);
709 res = gst_video_rate_flush_prev (videorate, FALSE);
715 videorate->dup += count - 1;
716 if (!videorate->silent)
717 gst_video_rate_notify_duplicate (videorate);
718 } else if (count == 0) {
720 if (!videorate->silent)
721 gst_video_rate_notify_drop (videorate);
726 case GST_EVENT_FLUSH_STOP:
727 /* also resets the segment */
728 GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP");
729 gst_video_rate_reset (videorate);
735 ret = gst_pad_push_event (videorate->srcpad, event);
738 gst_object_unref (videorate);
745 GST_WARNING_OBJECT (videorate,
746 "Got segment but doesn't have GST_FORMAT_TIME value");
747 gst_event_unref (event);
754 gst_video_rate_query (GstPad * pad, GstQuery * query)
756 GstVideoRate *videorate;
757 gboolean res = FALSE;
759 videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
761 switch (GST_QUERY_TYPE (query)) {
762 case GST_QUERY_LATENCY:
764 GstClockTime min, max;
769 if ((peer = gst_pad_get_peer (videorate->sinkpad))) {
770 if ((res = gst_pad_query (peer, query))) {
771 gst_query_parse_latency (query, &live, &min, &max);
773 GST_DEBUG_OBJECT (videorate, "Peer latency: min %"
774 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
775 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
777 if (videorate->from_rate_numerator != 0) {
778 /* add latency. We don't really know since we hold on to the frames
779 * until we get a next frame, which can be anything. We assume
780 * however that this will take from_rate time. */
781 latency = gst_util_uint64_scale (GST_SECOND,
782 videorate->from_rate_denominator,
783 videorate->from_rate_numerator);
785 /* no input framerate, we don't know */
789 GST_DEBUG_OBJECT (videorate, "Our latency: %"
790 GST_TIME_FORMAT, GST_TIME_ARGS (latency));
796 GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %"
797 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
798 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
800 gst_query_set_latency (query, live, min, max);
802 gst_object_unref (peer);
807 res = gst_pad_query_default (pad, query);
810 gst_object_unref (videorate);
816 gst_video_rate_chain_max_avg (GstVideoRate * videorate, GstBuffer * buf)
818 GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
822 if (!GST_CLOCK_TIME_IS_VALID (ts) || videorate->wanted_diff == 0)
825 /* drop frames if they exceed our output rate */
826 if (GST_CLOCK_TIME_IS_VALID (videorate->last_ts)) {
827 GstClockTimeDiff diff = ts - videorate->last_ts;
829 /* Drop buffer if its early compared to the desired frame rate and
830 * the current average is higher than the desired average
832 if (diff < videorate->wanted_diff &&
833 videorate->average < videorate->wanted_diff)
837 if (videorate->average) {
838 GstClockTimeDiff wanted_diff;
840 if (G_LIKELY (videorate->average_period > videorate->wanted_diff))
841 wanted_diff = videorate->wanted_diff;
843 wanted_diff = videorate->average_period * 10;
846 gst_util_uint64_scale_round (videorate->average,
847 videorate->average_period - wanted_diff,
848 videorate->average_period) +
849 gst_util_uint64_scale_round (diff, wanted_diff,
850 videorate->average_period);
852 videorate->average = diff;
856 videorate->last_ts = ts;
861 return gst_pad_push (videorate->srcpad, buf);
864 gst_buffer_unref (buf);
865 if (!videorate->silent)
866 gst_video_rate_notify_drop (videorate);
871 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
873 GstVideoRate *videorate;
874 GstFlowReturn res = GST_FLOW_OK;
875 GstClockTime intime, in_ts, in_dur;
876 GstClockTime avg_period;
877 gboolean skip = FALSE;
879 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
881 /* make sure the denominators are not 0 */
882 if (videorate->from_rate_denominator == 0 ||
883 videorate->to_rate_denominator == 0)
886 GST_OBJECT_LOCK (videorate);
887 avg_period = videorate->average_period_set;
888 GST_OBJECT_UNLOCK (videorate);
890 /* MT-safe switching between modes */
891 if (G_UNLIKELY (avg_period != videorate->average_period)) {
892 videorate->average_period = avg_period;
893 videorate->last_ts = GST_CLOCK_TIME_NONE;
894 if (avg_period && !videorate->average) {
895 /* enabling average mode */
896 videorate->average = 0;
898 /* enable regular mode */
899 gst_video_rate_swap_prev (videorate, NULL, 0);
900 /* arrange for skip-to-first behaviour */
901 videorate->next_ts = GST_CLOCK_TIME_NONE;
906 if (videorate->average_period > 0)
907 return gst_video_rate_chain_max_avg (videorate, buffer);
909 in_ts = GST_BUFFER_TIMESTAMP (buffer);
910 in_dur = GST_BUFFER_DURATION (buffer);
912 if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) {
913 in_ts = videorate->last_ts;
914 if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE))
918 /* get the time of the next expected buffer timestamp, we use this when the
919 * next buffer has -1 as a timestamp */
920 videorate->last_ts = in_ts;
921 if (in_dur != GST_CLOCK_TIME_NONE)
922 videorate->last_ts += in_dur;
924 GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
925 GST_TIME_ARGS (in_ts));
927 /* the input time is the time in the segment + all previously accumulated
929 intime = in_ts + videorate->segment.base;
931 /* we need to have two buffers to compare */
932 if (videorate->prevbuf == NULL) {
933 gst_video_rate_swap_prev (videorate, buffer, intime);
935 if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
936 /* new buffer, we expect to output a buffer that matches the first
937 * timestamp in the segment */
938 if (videorate->skip_to_first || skip) {
939 videorate->next_ts = intime;
940 videorate->base_ts = in_ts - videorate->segment.start;
941 videorate->out_frame_count = 0;
943 videorate->next_ts = videorate->segment.start + videorate->segment.base;
947 GstClockTime prevtime;
951 prevtime = videorate->prev_ts;
953 GST_LOG_OBJECT (videorate,
954 "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
955 " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
956 GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
960 /* drop new buffer if it's before previous one */
961 if (intime < prevtime) {
962 GST_DEBUG_OBJECT (videorate,
963 "The new buffer (%" GST_TIME_FORMAT
964 ") is before the previous buffer (%"
965 GST_TIME_FORMAT "). Dropping new buffer.",
966 GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
968 if (!videorate->silent)
969 gst_video_rate_notify_drop (videorate);
970 gst_buffer_unref (buffer);
974 /* got 2 buffers, see which one is the best */
977 diff1 = prevtime - videorate->next_ts;
978 diff2 = intime - videorate->next_ts;
980 /* take absolute values, beware: abs and ABS don't work for gint64 */
986 GST_LOG_OBJECT (videorate,
987 "diff with prev %" GST_TIME_FORMAT " diff with new %"
988 GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
989 GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
990 GST_TIME_ARGS (videorate->next_ts));
992 /* output first one when its the best */
993 if (diff1 <= diff2) {
996 /* on error the _flush function posted a warning already */
998 gst_video_rate_flush_prev (videorate,
999 count > 1)) != GST_FLOW_OK) {
1000 gst_buffer_unref (buffer);
1005 /* Do not produce any dups. We can exit loop now */
1006 if (videorate->drop_only)
1008 /* continue while the first one was the best, if they were equal avoid
1009 * going into an infinite loop */
1011 while (diff1 < diff2);
1013 /* if we outputed the first buffer more then once, we have dups */
1015 videorate->dup += count - 1;
1016 if (!videorate->silent)
1017 gst_video_rate_notify_duplicate (videorate);
1019 /* if we didn't output the first buffer, we have a drop */
1020 else if (count == 0) {
1023 if (!videorate->silent)
1024 gst_video_rate_notify_drop (videorate);
1026 GST_LOG_OBJECT (videorate,
1027 "new is best, old never used, drop, outgoing ts %"
1028 GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
1030 GST_LOG_OBJECT (videorate,
1031 "END, putting new in old, diff1 %" GST_TIME_FORMAT
1032 ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
1033 ", in %" G_GUINT64_FORMAT ", out %" G_GUINT64_FORMAT ", drop %"
1034 G_GUINT64_FORMAT ", dup %" G_GUINT64_FORMAT, GST_TIME_ARGS (diff1),
1035 GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
1036 videorate->in, videorate->out, videorate->drop, videorate->dup);
1038 /* swap in new one when it's the best */
1039 gst_video_rate_swap_prev (videorate, buffer, intime);
1047 GST_WARNING_OBJECT (videorate, "no framerate negotiated");
1048 gst_buffer_unref (buffer);
1049 res = GST_FLOW_NOT_NEGOTIATED;
1055 GST_WARNING_OBJECT (videorate,
1056 "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
1057 gst_buffer_unref (buffer);
1063 gst_video_rate_set_property (GObject * object,
1064 guint prop_id, const GValue * value, GParamSpec * pspec)
1066 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1068 GST_OBJECT_LOCK (videorate);
1071 videorate->silent = g_value_get_boolean (value);
1074 videorate->new_pref = g_value_get_double (value);
1076 case ARG_SKIP_TO_FIRST:
1077 videorate->skip_to_first = g_value_get_boolean (value);
1080 videorate->drop_only = g_value_get_boolean (value);
1082 case ARG_AVERAGE_PERIOD:
1083 videorate->average_period = g_value_get_uint64 (value);
1086 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1089 GST_OBJECT_UNLOCK (videorate);
1093 gst_video_rate_get_property (GObject * object,
1094 guint prop_id, GValue * value, GParamSpec * pspec)
1096 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1098 GST_OBJECT_LOCK (videorate);
1101 g_value_set_uint64 (value, videorate->in);
1104 g_value_set_uint64 (value, videorate->out);
1107 g_value_set_uint64 (value, videorate->dup);
1110 g_value_set_uint64 (value, videorate->drop);
1113 g_value_set_boolean (value, videorate->silent);
1116 g_value_set_double (value, videorate->new_pref);
1118 case ARG_SKIP_TO_FIRST:
1119 g_value_set_boolean (value, videorate->skip_to_first);
1122 g_value_set_boolean (value, videorate->drop_only);
1124 case ARG_AVERAGE_PERIOD:
1125 g_value_set_uint64 (value, videorate->average_period);
1128 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1131 GST_OBJECT_UNLOCK (videorate);
1134 static GstStateChangeReturn
1135 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
1137 GstStateChangeReturn ret;
1138 GstVideoRate *videorate;
1140 videorate = GST_VIDEO_RATE (element);
1142 switch (transition) {
1143 case GST_STATE_CHANGE_READY_TO_PAUSED:
1144 videorate->discont = TRUE;
1145 videorate->last_ts = -1;
1151 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1153 switch (transition) {
1154 case GST_STATE_CHANGE_PAUSED_TO_READY:
1155 gst_video_rate_reset (videorate);
1165 plugin_init (GstPlugin * plugin)
1167 GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
1168 "VideoRate stream fixer");
1170 return gst_element_register (plugin, "videorate", GST_RANK_NONE,
1171 GST_TYPE_VIDEO_RATE);
1174 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1177 "Adjusts video frames",
1178 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)