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_setcaps (GstPad * pad, GstCaps * caps)
329 GstVideoRate *videorate;
330 GstStructure *structure;
332 GstPad *otherpad, *opeer;
333 gint rate_numerator, rate_denominator;
335 videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
337 GST_DEBUG_OBJECT (pad, "setcaps called %" GST_PTR_FORMAT, caps);
339 structure = gst_caps_get_structure (caps, 0);
340 if (!gst_structure_get_fraction (structure, "framerate",
341 &rate_numerator, &rate_denominator))
344 if (pad == videorate->srcpad) {
345 /* out_frame_count is scaled by the frame rate caps when calculating next_ts.
346 * when the frame rate caps change, we must update base_ts and reset
348 if (videorate->to_rate_numerator) {
349 videorate->base_ts +=
350 gst_util_uint64_scale (videorate->out_frame_count,
351 videorate->to_rate_denominator * GST_SECOND,
352 videorate->to_rate_numerator);
354 videorate->out_frame_count = 0;
355 videorate->to_rate_numerator = rate_numerator;
356 videorate->to_rate_denominator = rate_denominator;
357 videorate->wanted_diff = gst_util_uint64_scale_int (GST_SECOND,
358 rate_denominator, rate_numerator);
359 otherpad = videorate->sinkpad;
361 videorate->from_rate_numerator = rate_numerator;
362 videorate->from_rate_denominator = rate_denominator;
363 otherpad = videorate->srcpad;
366 /* now try to find something for the peer */
367 opeer = gst_pad_get_peer (otherpad);
369 if (gst_pad_accept_caps (opeer, caps)) {
370 /* the peer accepts the caps as they are */
371 gst_pad_set_caps (otherpad, caps);
375 GstCaps *transform = NULL;
379 /* see how we can transform the input caps */
380 if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform, NULL))
383 GST_DEBUG_OBJECT (videorate, "transform %" GST_PTR_FORMAT, transform);
385 /* see what the peer can do */
386 caps = gst_pad_get_caps (opeer, transform);
388 GST_DEBUG_OBJECT (opeer, "icaps %" GST_PTR_FORMAT, caps);
390 /* could turn up empty, due to e.g. colorspace etc */
391 if (gst_caps_get_size (caps) == 0) {
392 gst_caps_unref (caps);
396 /* take first possibility */
397 caps = gst_caps_make_writable (caps);
398 gst_caps_truncate (caps);
399 structure = gst_caps_get_structure (caps, 0);
402 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
403 rate_numerator, rate_denominator);
405 gst_structure_get_fraction (structure, "framerate",
406 &rate_numerator, &rate_denominator);
408 if (otherpad == videorate->srcpad) {
409 videorate->to_rate_numerator = rate_numerator;
410 videorate->to_rate_denominator = rate_denominator;
412 videorate->from_rate_numerator = rate_numerator;
413 videorate->from_rate_denominator = rate_denominator;
416 if (gst_structure_has_field (structure, "interlaced"))
417 gst_structure_fixate_field_boolean (structure, "interlaced", FALSE);
418 if (gst_structure_has_field (structure, "color-matrix"))
419 gst_structure_fixate_field_string (structure, "color-matrix", "sdtv");
420 if (gst_structure_has_field (structure, "chroma-site"))
421 gst_structure_fixate_field_string (structure, "chroma-site", "mpeg2");
422 if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
423 gst_structure_fixate_field_nearest_fraction (structure,
424 "pixel-aspect-ratio", 1, 1);
426 gst_pad_set_caps (otherpad, caps);
427 gst_caps_unref (caps);
430 gst_object_unref (opeer);
433 /* After a setcaps, our caps may have changed. In that case, we can't use
434 * the old buffer, if there was one (it might have different dimensions) */
435 GST_DEBUG_OBJECT (videorate, "swapping old buffers");
436 gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE);
438 gst_object_unref (videorate);
443 GST_DEBUG_OBJECT (videorate, "no framerate specified");
448 GST_DEBUG_OBJECT (videorate, "no framerate transform possible");
455 gst_video_rate_reset (GstVideoRate * videorate)
457 GST_DEBUG_OBJECT (videorate, "resetting internal variables");
461 videorate->base_ts = 0;
462 videorate->out_frame_count = 0;
465 videorate->next_ts = GST_CLOCK_TIME_NONE;
466 videorate->last_ts = GST_CLOCK_TIME_NONE;
467 videorate->discont = TRUE;
468 videorate->average = 0;
469 gst_video_rate_swap_prev (videorate, NULL, 0);
471 gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
475 gst_video_rate_init (GstVideoRate * videorate)
478 gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
479 gst_pad_set_event_function (videorate->sinkpad,
480 GST_DEBUG_FUNCPTR (gst_video_rate_event));
481 gst_pad_set_chain_function (videorate->sinkpad,
482 GST_DEBUG_FUNCPTR (gst_video_rate_chain));
483 gst_pad_set_getcaps_function (videorate->sinkpad,
484 GST_DEBUG_FUNCPTR (gst_video_rate_getcaps));
485 gst_pad_set_setcaps_function (videorate->sinkpad,
486 GST_DEBUG_FUNCPTR (gst_video_rate_setcaps));
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_pad_set_setcaps_function (videorate->srcpad,
496 GST_DEBUG_FUNCPTR (gst_video_rate_setcaps));
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)) {
617 case GST_EVENT_SEGMENT:
619 const GstSegment *segment;
621 gst_event_parse_segment (event, &segment);
623 if (segment->format != GST_FORMAT_TIME)
626 GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
628 /* close up the previous segment, if appropriate */
629 if (videorate->prevbuf) {
634 /* fill up to the end of current segment,
635 * or only send out the stored buffer if there is no specific stop.
636 * regardless, prevent going loopy in strange cases */
637 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
638 ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) &&
639 videorate->next_ts - videorate->segment.base
640 < videorate->segment.stop)
642 res = gst_video_rate_flush_prev (videorate, count > 0);
646 videorate->dup += count - 1;
647 if (!videorate->silent)
648 gst_video_rate_notify_duplicate (videorate);
649 } else if (count == 0) {
651 if (!videorate->silent)
652 gst_video_rate_notify_drop (videorate);
654 /* clean up for the new one; _chain will resume from the new start */
655 videorate->base_ts = 0;
656 videorate->out_frame_count = 0;
657 gst_video_rate_swap_prev (videorate, NULL, 0);
658 videorate->next_ts = GST_CLOCK_TIME_NONE;
661 /* We just want to update the accumulated stream_time */
662 gst_segment_copy_into (segment, &videorate->segment);
664 GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
665 &videorate->segment);
670 GstFlowReturn res = GST_FLOW_OK;
672 GST_DEBUG_OBJECT (videorate, "Got EOS");
674 /* If the segment has a stop position, fill the segment */
675 if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) {
676 /* fill up to the end of current segment,
677 * or only send out the stored buffer if there is no specific stop.
678 * regardless, prevent going loopy in strange cases */
679 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
680 ((videorate->next_ts - videorate->segment.base <
681 videorate->segment.stop)
683 res = gst_video_rate_flush_prev (videorate, count > 0);
686 } else if (videorate->prevbuf) {
687 /* Output at least one frame but if the buffer duration is valid, output
688 * enough frames to use the complete buffer duration */
689 if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) {
690 GstClockTime end_ts =
691 videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf);
693 while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
694 ((videorate->next_ts - videorate->segment.base < end_ts)
696 res = gst_video_rate_flush_prev (videorate, count > 0);
700 res = gst_video_rate_flush_prev (videorate, FALSE);
706 videorate->dup += count - 1;
707 if (!videorate->silent)
708 gst_video_rate_notify_duplicate (videorate);
709 } else if (count == 0) {
711 if (!videorate->silent)
712 gst_video_rate_notify_drop (videorate);
717 case GST_EVENT_FLUSH_STOP:
718 /* also resets the segment */
719 GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP");
720 gst_video_rate_reset (videorate);
726 ret = gst_pad_push_event (videorate->srcpad, event);
729 gst_object_unref (videorate);
736 GST_WARNING_OBJECT (videorate,
737 "Got segment but doesn't have GST_FORMAT_TIME value");
738 gst_event_unref (event);
745 gst_video_rate_query (GstPad * pad, GstQuery * query)
747 GstVideoRate *videorate;
748 gboolean res = FALSE;
750 videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
752 switch (GST_QUERY_TYPE (query)) {
753 case GST_QUERY_LATENCY:
755 GstClockTime min, max;
760 if ((peer = gst_pad_get_peer (videorate->sinkpad))) {
761 if ((res = gst_pad_query (peer, query))) {
762 gst_query_parse_latency (query, &live, &min, &max);
764 GST_DEBUG_OBJECT (videorate, "Peer latency: min %"
765 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
766 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
768 if (videorate->from_rate_numerator != 0) {
769 /* add latency. We don't really know since we hold on to the frames
770 * until we get a next frame, which can be anything. We assume
771 * however that this will take from_rate time. */
772 latency = gst_util_uint64_scale (GST_SECOND,
773 videorate->from_rate_denominator,
774 videorate->from_rate_numerator);
776 /* no input framerate, we don't know */
780 GST_DEBUG_OBJECT (videorate, "Our latency: %"
781 GST_TIME_FORMAT, GST_TIME_ARGS (latency));
787 GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %"
788 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
789 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
791 gst_query_set_latency (query, live, min, max);
793 gst_object_unref (peer);
798 res = gst_pad_query_default (pad, query);
801 gst_object_unref (videorate);
807 gst_video_rate_chain_max_avg (GstVideoRate * videorate, GstBuffer * buf)
809 GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
813 if (!GST_CLOCK_TIME_IS_VALID (ts) || videorate->wanted_diff == 0)
816 /* drop frames if they exceed our output rate */
817 if (GST_CLOCK_TIME_IS_VALID (videorate->last_ts)) {
818 GstClockTimeDiff diff = ts - videorate->last_ts;
820 /* Drop buffer if its early compared to the desired frame rate and
821 * the current average is higher than the desired average
823 if (diff < videorate->wanted_diff &&
824 videorate->average < videorate->wanted_diff)
828 if (videorate->average) {
829 GstClockTimeDiff wanted_diff;
831 if (G_LIKELY (videorate->average_period > videorate->wanted_diff))
832 wanted_diff = videorate->wanted_diff;
834 wanted_diff = videorate->average_period * 10;
837 gst_util_uint64_scale_round (videorate->average,
838 videorate->average_period - wanted_diff,
839 videorate->average_period) +
840 gst_util_uint64_scale_round (diff, wanted_diff,
841 videorate->average_period);
843 videorate->average = diff;
847 videorate->last_ts = ts;
852 return gst_pad_push (videorate->srcpad, buf);
855 gst_buffer_unref (buf);
856 if (!videorate->silent)
857 gst_video_rate_notify_drop (videorate);
862 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
864 GstVideoRate *videorate;
865 GstFlowReturn res = GST_FLOW_OK;
866 GstClockTime intime, in_ts, in_dur;
867 GstClockTime avg_period;
868 gboolean skip = FALSE;
870 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
872 /* make sure the denominators are not 0 */
873 if (videorate->from_rate_denominator == 0 ||
874 videorate->to_rate_denominator == 0)
877 GST_OBJECT_LOCK (videorate);
878 avg_period = videorate->average_period_set;
879 GST_OBJECT_UNLOCK (videorate);
881 /* MT-safe switching between modes */
882 if (G_UNLIKELY (avg_period != videorate->average_period)) {
883 videorate->average_period = avg_period;
884 videorate->last_ts = GST_CLOCK_TIME_NONE;
885 if (avg_period && !videorate->average) {
886 /* enabling average mode */
887 videorate->average = 0;
889 /* enable regular mode */
890 gst_video_rate_swap_prev (videorate, NULL, 0);
891 /* arrange for skip-to-first behaviour */
892 videorate->next_ts = GST_CLOCK_TIME_NONE;
897 if (videorate->average_period > 0)
898 return gst_video_rate_chain_max_avg (videorate, buffer);
900 in_ts = GST_BUFFER_TIMESTAMP (buffer);
901 in_dur = GST_BUFFER_DURATION (buffer);
903 if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) {
904 in_ts = videorate->last_ts;
905 if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE))
909 /* get the time of the next expected buffer timestamp, we use this when the
910 * next buffer has -1 as a timestamp */
911 videorate->last_ts = in_ts;
912 if (in_dur != GST_CLOCK_TIME_NONE)
913 videorate->last_ts += in_dur;
915 GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
916 GST_TIME_ARGS (in_ts));
918 /* the input time is the time in the segment + all previously accumulated
920 intime = in_ts + videorate->segment.base;
922 /* we need to have two buffers to compare */
923 if (videorate->prevbuf == NULL) {
924 gst_video_rate_swap_prev (videorate, buffer, intime);
926 if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
927 /* new buffer, we expect to output a buffer that matches the first
928 * timestamp in the segment */
929 if (videorate->skip_to_first || skip) {
930 videorate->next_ts = intime;
931 videorate->base_ts = in_ts - videorate->segment.start;
932 videorate->out_frame_count = 0;
934 videorate->next_ts = videorate->segment.start + videorate->segment.base;
938 GstClockTime prevtime;
942 prevtime = videorate->prev_ts;
944 GST_LOG_OBJECT (videorate,
945 "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
946 " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
947 GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
951 /* drop new buffer if it's before previous one */
952 if (intime < prevtime) {
953 GST_DEBUG_OBJECT (videorate,
954 "The new buffer (%" GST_TIME_FORMAT
955 ") is before the previous buffer (%"
956 GST_TIME_FORMAT "). Dropping new buffer.",
957 GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
959 if (!videorate->silent)
960 gst_video_rate_notify_drop (videorate);
961 gst_buffer_unref (buffer);
965 /* got 2 buffers, see which one is the best */
968 diff1 = prevtime - videorate->next_ts;
969 diff2 = intime - videorate->next_ts;
971 /* take absolute values, beware: abs and ABS don't work for gint64 */
977 GST_LOG_OBJECT (videorate,
978 "diff with prev %" GST_TIME_FORMAT " diff with new %"
979 GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
980 GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
981 GST_TIME_ARGS (videorate->next_ts));
983 /* output first one when its the best */
984 if (diff1 <= diff2) {
987 /* on error the _flush function posted a warning already */
989 gst_video_rate_flush_prev (videorate,
990 count > 1)) != GST_FLOW_OK) {
991 gst_buffer_unref (buffer);
996 /* Do not produce any dups. We can exit loop now */
997 if (videorate->drop_only)
999 /* continue while the first one was the best, if they were equal avoid
1000 * going into an infinite loop */
1002 while (diff1 < diff2);
1004 /* if we outputed the first buffer more then once, we have dups */
1006 videorate->dup += count - 1;
1007 if (!videorate->silent)
1008 gst_video_rate_notify_duplicate (videorate);
1010 /* if we didn't output the first buffer, we have a drop */
1011 else if (count == 0) {
1014 if (!videorate->silent)
1015 gst_video_rate_notify_drop (videorate);
1017 GST_LOG_OBJECT (videorate,
1018 "new is best, old never used, drop, outgoing ts %"
1019 GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
1021 GST_LOG_OBJECT (videorate,
1022 "END, putting new in old, diff1 %" GST_TIME_FORMAT
1023 ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
1024 ", in %" G_GUINT64_FORMAT ", out %" G_GUINT64_FORMAT ", drop %"
1025 G_GUINT64_FORMAT ", dup %" G_GUINT64_FORMAT, GST_TIME_ARGS (diff1),
1026 GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
1027 videorate->in, videorate->out, videorate->drop, videorate->dup);
1029 /* swap in new one when it's the best */
1030 gst_video_rate_swap_prev (videorate, buffer, intime);
1038 GST_WARNING_OBJECT (videorate, "no framerate negotiated");
1039 gst_buffer_unref (buffer);
1040 res = GST_FLOW_NOT_NEGOTIATED;
1046 GST_WARNING_OBJECT (videorate,
1047 "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
1048 gst_buffer_unref (buffer);
1054 gst_video_rate_set_property (GObject * object,
1055 guint prop_id, const GValue * value, GParamSpec * pspec)
1057 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1059 GST_OBJECT_LOCK (videorate);
1062 videorate->silent = g_value_get_boolean (value);
1065 videorate->new_pref = g_value_get_double (value);
1067 case ARG_SKIP_TO_FIRST:
1068 videorate->skip_to_first = g_value_get_boolean (value);
1071 videorate->drop_only = g_value_get_boolean (value);
1073 case ARG_AVERAGE_PERIOD:
1074 videorate->average_period = g_value_get_uint64 (value);
1077 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1080 GST_OBJECT_UNLOCK (videorate);
1084 gst_video_rate_get_property (GObject * object,
1085 guint prop_id, GValue * value, GParamSpec * pspec)
1087 GstVideoRate *videorate = GST_VIDEO_RATE (object);
1089 GST_OBJECT_LOCK (videorate);
1092 g_value_set_uint64 (value, videorate->in);
1095 g_value_set_uint64 (value, videorate->out);
1098 g_value_set_uint64 (value, videorate->dup);
1101 g_value_set_uint64 (value, videorate->drop);
1104 g_value_set_boolean (value, videorate->silent);
1107 g_value_set_double (value, videorate->new_pref);
1109 case ARG_SKIP_TO_FIRST:
1110 g_value_set_boolean (value, videorate->skip_to_first);
1113 g_value_set_boolean (value, videorate->drop_only);
1115 case ARG_AVERAGE_PERIOD:
1116 g_value_set_uint64 (value, videorate->average_period);
1119 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1122 GST_OBJECT_UNLOCK (videorate);
1125 static GstStateChangeReturn
1126 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
1128 GstStateChangeReturn ret;
1129 GstVideoRate *videorate;
1131 videorate = GST_VIDEO_RATE (element);
1133 switch (transition) {
1134 case GST_STATE_CHANGE_READY_TO_PAUSED:
1135 videorate->discont = TRUE;
1136 videorate->last_ts = -1;
1142 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1144 switch (transition) {
1145 case GST_STATE_CHANGE_PAUSED_TO_READY:
1146 gst_video_rate_reset (videorate);
1156 plugin_init (GstPlugin * plugin)
1158 GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
1159 "VideoRate stream fixer");
1161 return gst_element_register (plugin, "videorate", GST_RANK_NONE,
1162 GST_TYPE_VIDEO_RATE);
1165 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1168 "Adjusts video frames",
1169 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)