videorate: Add Since marker for the new skip-to-first property
[platform/upstream/gstreamer.git] / gst / videorate / gstvideorate.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-videorate
22  *
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.
25  *
26  * The correction is performed by dropping and duplicating frames, no fancy
27  * algorithm is used to interpolate frames (yet).
28  *
29  * By default the element will simply negotiate the same framerate on its
30  * source and sink pad.
31  *
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.
35  *
36  * A conversion to a specific framerate can be forced by using filtered caps on
37  * the source pad.
38  *
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).
44  *
45  * An input stream that needs no adjustments will thus never have dropped or
46  * duplicated frames.
47  *
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.
54  *
55  * <refsect2>
56  * <title>Example pipelines</title>
57  * |[
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.
61  * |[
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.
65  * </refsect2>
66  *
67  * Last reviewed on 2006-09-02 (0.10.11)
68  */
69
70 #ifdef HAVE_CONFIG_H
71 #include "config.h"
72 #endif
73
74 #include "gstvideorate.h"
75
76 GST_DEBUG_CATEGORY_STATIC (video_rate_debug);
77 #define GST_CAT_DEFAULT video_rate_debug
78
79 /* elementfactory information */
80 static const GstElementDetails video_rate_details =
81 GST_ELEMENT_DETAILS ("Video rate adjuster",
82     "Filter/Effect/Video",
83     "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
84     "Wim Taymans <wim@fluendo.com>");
85
86 /* GstVideoRate signals and args */
87 enum
88 {
89   /* FILL ME */
90   LAST_SIGNAL
91 };
92
93 #define DEFAULT_SILENT          TRUE
94 #define DEFAULT_NEW_PREF        1.0
95 #define DEFAULT_SKIP_TO_FIRST   FALSE
96
97 enum
98 {
99   ARG_0,
100   ARG_IN,
101   ARG_OUT,
102   ARG_DUP,
103   ARG_DROP,
104   ARG_SILENT,
105   ARG_NEW_PREF,
106   ARG_SKIP_TO_FIRST
107       /* FILL ME */
108 };
109
110 static GstStaticPadTemplate gst_video_rate_src_template =
111     GST_STATIC_PAD_TEMPLATE ("src",
112     GST_PAD_SRC,
113     GST_PAD_ALWAYS,
114     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png")
115     );
116
117 static GstStaticPadTemplate gst_video_rate_sink_template =
118     GST_STATIC_PAD_TEMPLATE ("sink",
119     GST_PAD_SINK,
120     GST_PAD_ALWAYS,
121     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png")
122     );
123
124 static void gst_video_rate_swap_prev (GstVideoRate * videorate,
125     GstBuffer * buffer, gint64 time);
126 static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
127 static gboolean gst_video_rate_query (GstPad * pad, GstQuery * query);
128 static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
129
130 static void gst_video_rate_set_property (GObject * object,
131     guint prop_id, const GValue * value, GParamSpec * pspec);
132 static void gst_video_rate_get_property (GObject * object,
133     guint prop_id, GValue * value, GParamSpec * pspec);
134
135 static GstStateChangeReturn gst_video_rate_change_state (GstElement * element,
136     GstStateChange transition);
137
138 /*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */
139
140 GST_BOILERPLATE (GstVideoRate, gst_video_rate, GstElement, GST_TYPE_ELEMENT);
141
142 static void
143 gst_video_rate_base_init (gpointer g_class)
144 {
145   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
146
147   gst_element_class_set_details (element_class, &video_rate_details);
148
149   gst_element_class_add_pad_template (element_class,
150       gst_static_pad_template_get (&gst_video_rate_sink_template));
151   gst_element_class_add_pad_template (element_class,
152       gst_static_pad_template_get (&gst_video_rate_src_template));
153 }
154
155 static void
156 gst_video_rate_class_init (GstVideoRateClass * klass)
157 {
158   GObjectClass *object_class = G_OBJECT_CLASS (klass);
159   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
160
161   parent_class = g_type_class_peek_parent (klass);
162
163   object_class->set_property = gst_video_rate_set_property;
164   object_class->get_property = gst_video_rate_get_property;
165
166   g_object_class_install_property (object_class, ARG_IN,
167       g_param_spec_uint64 ("in", "In",
168           "Number of input frames", 0, G_MAXUINT64, 0,
169           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
170   g_object_class_install_property (object_class, ARG_OUT,
171       g_param_spec_uint64 ("out", "Out", "Number of output frames", 0,
172           G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
173   g_object_class_install_property (object_class, ARG_DUP,
174       g_param_spec_uint64 ("duplicate", "Duplicate",
175           "Number of duplicated frames", 0, G_MAXUINT64, 0,
176           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
177   g_object_class_install_property (object_class, ARG_DROP,
178       g_param_spec_uint64 ("drop", "Drop", "Number of dropped frames", 0,
179           G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
180   g_object_class_install_property (object_class, ARG_SILENT,
181       g_param_spec_boolean ("silent", "silent",
182           "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT,
183           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
184   g_object_class_install_property (object_class, ARG_NEW_PREF,
185       g_param_spec_double ("new-pref", "New Pref",
186           "Value indicating how much to prefer new frames (unused)", 0.0, 1.0,
187           DEFAULT_NEW_PREF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
188
189   /**
190    * GstVideoRate:skip-to-first:
191    * 
192    * Don't produce buffers before the first one we receive.
193    *
194    * Since: 0.10.25
195    */
196   g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST,
197       g_param_spec_boolean ("skip-to-first", "Skip to first buffer",
198           "Don't produce buffers before the first one we receive",
199           DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
200
201   element_class->change_state = gst_video_rate_change_state;
202 }
203
204 /* return the caps that can be used on out_pad given in_caps on in_pad */
205 static gboolean
206 gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
207     GstPad * out_pad, GstCaps ** out_caps)
208 {
209   GstCaps *intersect;
210   const GstCaps *in_templ;
211   gint i;
212
213   in_templ = gst_pad_get_pad_template_caps (in_pad);
214   intersect = gst_caps_intersect (in_caps, in_templ);
215
216   /* all possible framerates are allowed */
217   for (i = 0; i < gst_caps_get_size (intersect); i++) {
218     GstStructure *structure;
219
220     structure = gst_caps_get_structure (intersect, i);
221
222     gst_structure_set (structure,
223         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
224   }
225   *out_caps = intersect;
226
227   return TRUE;
228 }
229
230 static GstCaps *
231 gst_video_rate_getcaps (GstPad * pad)
232 {
233   GstVideoRate *videorate;
234   GstPad *otherpad;
235   GstCaps *caps;
236
237   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
238
239   otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
240       videorate->srcpad;
241
242   /* we can do what the peer can */
243   caps = gst_pad_peer_get_caps (otherpad);
244   if (caps) {
245     GstCaps *transform;
246
247     gst_video_rate_transformcaps (otherpad, caps, pad, &transform);
248     gst_caps_unref (caps);
249     caps = transform;
250   } else {
251     /* no peer, our padtemplate is enough then */
252     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
253   }
254
255   return caps;
256 }
257
258 static gboolean
259 gst_video_rate_setcaps (GstPad * pad, GstCaps * caps)
260 {
261   GstVideoRate *videorate;
262   GstStructure *structure;
263   gboolean ret = TRUE;
264   GstPad *otherpad, *opeer;
265   gint rate_numerator, rate_denominator;
266
267   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
268
269   GST_DEBUG ("setcaps called %" GST_PTR_FORMAT, caps);
270
271   structure = gst_caps_get_structure (caps, 0);
272   if (!gst_structure_get_fraction (structure, "framerate",
273           &rate_numerator, &rate_denominator))
274     goto no_framerate;
275
276   if (pad == videorate->srcpad) {
277     videorate->to_rate_numerator = rate_numerator;
278     videorate->to_rate_denominator = rate_denominator;
279     otherpad = videorate->sinkpad;
280   } else {
281     videorate->from_rate_numerator = rate_numerator;
282     videorate->from_rate_denominator = rate_denominator;
283     otherpad = videorate->srcpad;
284   }
285
286   /* now try to find something for the peer */
287   opeer = gst_pad_get_peer (otherpad);
288   if (opeer) {
289     if (gst_pad_accept_caps (opeer, caps)) {
290       /* the peer accepts the caps as they are */
291       gst_pad_set_caps (otherpad, caps);
292
293       ret = TRUE;
294     } else {
295       GstCaps *peercaps;
296       GstCaps *intersect;
297       GstCaps *transform = NULL;
298
299       ret = FALSE;
300
301       /* see how we can transform the input caps */
302       if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform))
303         goto no_transform;
304
305       /* see what the peer can do */
306       peercaps = gst_pad_get_caps (opeer);
307
308       GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
309       GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
310
311       /* filter against our possibilities */
312       intersect = gst_caps_intersect (peercaps, transform);
313       gst_caps_unref (peercaps);
314       gst_caps_unref (transform);
315
316       GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
317
318       /* take first possibility */
319       caps = gst_caps_copy_nth (intersect, 0);
320       gst_caps_unref (intersect);
321       structure = gst_caps_get_structure (caps, 0);
322
323       /* and fixate */
324       gst_structure_fixate_field_nearest_fraction (structure, "framerate",
325           rate_numerator, rate_denominator);
326
327       gst_structure_get_fraction (structure, "framerate",
328           &rate_numerator, &rate_denominator);
329
330       if (otherpad == videorate->srcpad) {
331         videorate->to_rate_numerator = rate_numerator;
332         videorate->to_rate_denominator = rate_denominator;
333       } else {
334         videorate->from_rate_numerator = rate_numerator;
335         videorate->from_rate_denominator = rate_denominator;
336       }
337       gst_pad_set_caps (otherpad, caps);
338       gst_caps_unref (caps);
339       ret = TRUE;
340     }
341     gst_object_unref (opeer);
342   }
343 done:
344   /* After a setcaps, our caps may have changed. In that case, we can't use
345    * the old buffer, if there was one (it might have different dimensions) */
346   GST_DEBUG ("swapping old buffers");
347   gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE);
348
349   gst_object_unref (videorate);
350   return ret;
351
352 no_framerate:
353   {
354     GST_DEBUG_OBJECT (videorate, "no framerate specified");
355     goto done;
356   }
357 no_transform:
358   {
359     GST_DEBUG_OBJECT (videorate, "no framerate transform possible");
360     ret = FALSE;
361     goto done;
362   }
363 }
364
365 static void
366 gst_video_rate_reset (GstVideoRate * videorate)
367 {
368   GST_DEBUG ("resetting internal variables");
369
370   videorate->in = 0;
371   videorate->out = 0;
372   videorate->segment_out = 0;
373   videorate->drop = 0;
374   videorate->dup = 0;
375   videorate->next_ts = GST_CLOCK_TIME_NONE;
376   videorate->last_ts = GST_CLOCK_TIME_NONE;
377   videorate->discont = TRUE;
378   gst_video_rate_swap_prev (videorate, NULL, 0);
379
380   gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
381 }
382
383 static void
384 gst_video_rate_init (GstVideoRate * videorate, GstVideoRateClass * klass)
385 {
386   GST_DEBUG ("gst_video_rate_init");
387   videorate->sinkpad =
388       gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
389   gst_pad_set_event_function (videorate->sinkpad, gst_video_rate_event);
390   gst_pad_set_chain_function (videorate->sinkpad, gst_video_rate_chain);
391   gst_pad_set_getcaps_function (videorate->sinkpad, gst_video_rate_getcaps);
392   gst_pad_set_setcaps_function (videorate->sinkpad, gst_video_rate_setcaps);
393   gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
394
395   videorate->srcpad =
396       gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
397   gst_pad_set_query_function (videorate->srcpad, gst_video_rate_query);
398   gst_pad_set_getcaps_function (videorate->srcpad, gst_video_rate_getcaps);
399   gst_pad_set_setcaps_function (videorate->srcpad, gst_video_rate_setcaps);
400   gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
401
402   gst_video_rate_reset (videorate);
403   videorate->silent = DEFAULT_SILENT;
404   videorate->new_pref = DEFAULT_NEW_PREF;
405
406   videorate->from_rate_numerator = 0;
407   videorate->from_rate_denominator = 0;
408   videorate->to_rate_numerator = 0;
409   videorate->to_rate_denominator = 0;
410 }
411
412 /* flush the oldest buffer */
413 static GstFlowReturn
414 gst_video_rate_flush_prev (GstVideoRate * videorate)
415 {
416   GstFlowReturn res;
417   GstBuffer *outbuf;
418   GstClockTime push_ts;
419
420   if (!videorate->prevbuf)
421     goto eos_before_buffers;
422
423   /* make sure we can write to the metadata */
424   outbuf = gst_buffer_make_metadata_writable
425       (gst_buffer_ref (videorate->prevbuf));
426
427   GST_BUFFER_OFFSET (outbuf) = videorate->out;
428   GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
429
430   if (videorate->discont) {
431     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
432     videorate->discont = FALSE;
433   } else
434     GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
435
436   /* this is the timestamp we put on the buffer */
437   push_ts = videorate->next_ts;
438
439   videorate->out++;
440   videorate->segment_out++;
441   if (videorate->to_rate_numerator) {
442     /* interpolate next expected timestamp in the segment */
443     videorate->next_ts = videorate->segment.accum + videorate->segment.start +
444         gst_util_uint64_scale (videorate->segment_out,
445         videorate->to_rate_denominator * GST_SECOND,
446         videorate->to_rate_numerator);
447     GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
448   }
449
450   /* adapt for looping, bring back to time in current segment. */
451   GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.accum;
452
453   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
454
455   GST_LOG_OBJECT (videorate,
456       "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
457       GST_TIME_ARGS (push_ts));
458
459   res = gst_pad_push (videorate->srcpad, outbuf);
460
461   return res;
462
463   /* WARNINGS */
464 eos_before_buffers:
465   {
466     GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
467     return GST_FLOW_OK;
468   }
469 }
470
471 static void
472 gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
473     gint64 time)
474 {
475   GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer);
476   if (videorate->prevbuf)
477     gst_buffer_unref (videorate->prevbuf);
478   videorate->prevbuf = buffer;
479   videorate->prev_ts = time;
480 }
481
482 #define MAGIC_LIMIT  25
483 static gboolean
484 gst_video_rate_event (GstPad * pad, GstEvent * event)
485 {
486   GstVideoRate *videorate;
487   gboolean ret;
488
489   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
490
491   switch (GST_EVENT_TYPE (event)) {
492     case GST_EVENT_NEWSEGMENT:
493     {
494       gint64 start, stop, time;
495       gdouble rate, arate;
496       gboolean update;
497       GstFormat format;
498
499       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
500           &start, &stop, &time);
501
502       if (format != GST_FORMAT_TIME)
503         goto format_error;
504
505       GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
506
507       /* close up the previous segment, if appropriate */
508       if (!update && videorate->prevbuf) {
509         gint count = 0;
510         GstFlowReturn res;
511
512         res = GST_FLOW_OK;
513         /* fill up to the end of current segment,
514          * or only send out the stored buffer if there is no specific stop.
515          * regardless, prevent going loopy in strange cases */
516         while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
517             ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) &&
518                     videorate->next_ts - videorate->segment.accum
519                     < videorate->segment.stop)
520                 || count < 1)) {
521           gst_video_rate_flush_prev (videorate);
522           count++;
523         }
524         if (count > 1) {
525           videorate->dup += count - 1;
526           if (!videorate->silent)
527             g_object_notify (G_OBJECT (videorate), "duplicate");
528         } else if (count == 0) {
529           videorate->drop++;
530           if (!videorate->silent)
531             g_object_notify (G_OBJECT (videorate), "drop");
532         }
533         /* clean up for the new one; _chain will resume from the new start */
534         videorate->segment_out = 0;
535         gst_video_rate_swap_prev (videorate, NULL, 0);
536         videorate->next_ts = GST_CLOCK_TIME_NONE;
537       }
538
539       /* We just want to update the accumulated stream_time  */
540       gst_segment_set_newsegment_full (&videorate->segment, update, rate, arate,
541           format, start, stop, time);
542
543       GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
544           &videorate->segment);
545       break;
546     }
547     case GST_EVENT_EOS:
548       /* flush last queued frame */
549       GST_DEBUG_OBJECT (videorate, "Got EOS");
550       gst_video_rate_flush_prev (videorate);
551       break;
552     case GST_EVENT_FLUSH_STOP:
553       /* also resets the segment */
554       GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP");
555       gst_video_rate_reset (videorate);
556       break;
557     default:
558       break;
559   }
560
561   ret = gst_pad_push_event (videorate->srcpad, event);
562
563 done:
564   gst_object_unref (videorate);
565
566   return ret;
567
568   /* ERRORS */
569 format_error:
570   {
571     GST_WARNING_OBJECT (videorate,
572         "Got segment but doesn't have GST_FORMAT_TIME value");
573     gst_event_unref (event);
574     ret = FALSE;
575     goto done;
576   }
577 }
578
579 static gboolean
580 gst_video_rate_query (GstPad * pad, GstQuery * query)
581 {
582   GstVideoRate *videorate;
583   gboolean res = FALSE;
584
585   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
586
587   switch (GST_QUERY_TYPE (query)) {
588     case GST_QUERY_LATENCY:
589     {
590       GstClockTime min, max;
591       gboolean live;
592       guint64 latency;
593       GstPad *peer;
594
595       if ((peer = gst_pad_get_peer (videorate->sinkpad))) {
596         if ((res = gst_pad_query (peer, query))) {
597           gst_query_parse_latency (query, &live, &min, &max);
598
599           GST_DEBUG_OBJECT (videorate, "Peer latency: min %"
600               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
601               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
602
603           if (videorate->from_rate_numerator != 0) {
604             /* add latency. We don't really know since we hold on to the frames
605              * until we get a next frame, which can be anything. We assume
606              * however that this will take from_rate time. */
607             latency = gst_util_uint64_scale (GST_SECOND,
608                 videorate->from_rate_denominator,
609                 videorate->from_rate_numerator);
610           } else {
611             /* no input framerate, we don't know */
612             latency = 0;
613           }
614
615           GST_DEBUG_OBJECT (videorate, "Our latency: %"
616               GST_TIME_FORMAT, GST_TIME_ARGS (latency));
617
618           min += latency;
619           if (max != -1)
620             max += latency;
621
622           GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %"
623               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
624               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
625
626           gst_query_set_latency (query, live, min, max);
627         }
628         gst_object_unref (peer);
629       }
630       break;
631     }
632     default:
633       res = gst_pad_query_default (pad, query);
634       break;
635   }
636   gst_object_unref (videorate);
637
638   return res;
639 }
640
641 static GstFlowReturn
642 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
643 {
644   GstVideoRate *videorate;
645   GstFlowReturn res = GST_FLOW_OK;
646   GstClockTime intime, in_ts, in_dur;
647
648   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
649
650   /* make sure the denominators are not 0 */
651   if (videorate->from_rate_denominator == 0 ||
652       videorate->to_rate_denominator == 0)
653     goto not_negotiated;
654
655   in_ts = GST_BUFFER_TIMESTAMP (buffer);
656   in_dur = GST_BUFFER_DURATION (buffer);
657
658   if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) {
659     in_ts = videorate->last_ts;
660     if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE))
661       goto invalid_buffer;
662   }
663
664   /* get the time of the next expected buffer timestamp, we use this when the
665    * next buffer has -1 as a timestamp */
666   videorate->last_ts = in_ts;
667   if (in_dur != GST_CLOCK_TIME_NONE)
668     videorate->last_ts += in_dur;
669
670   GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
671       GST_TIME_ARGS (in_ts));
672
673   /* the input time is the time in the segment + all previously accumulated
674    * segments */
675   intime = in_ts + videorate->segment.accum;
676
677   /* we need to have two buffers to compare */
678   if (videorate->prevbuf == NULL) {
679     gst_video_rate_swap_prev (videorate, buffer, intime);
680     videorate->in++;
681     if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
682       /* new buffer, we expect to output a buffer that matches the first
683        * timestamp in the segment */
684       if (videorate->skip_to_first) {
685         videorate->next_ts = in_ts;
686         videorate->segment_out = gst_util_uint64_scale (in_ts,
687             videorate->to_rate_numerator,
688             videorate->to_rate_denominator * GST_SECOND) -
689             (videorate->segment.accum + videorate->segment.start);
690       } else {
691         videorate->next_ts =
692             videorate->segment.start + videorate->segment.accum;
693       }
694     }
695   } else {
696     GstClockTime prevtime;
697     gint count = 0;
698     gint64 diff1, diff2;
699
700     prevtime = videorate->prev_ts;
701
702     GST_LOG_OBJECT (videorate,
703         "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
704         " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
705         GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
706
707     videorate->in++;
708
709     /* drop new buffer if it's before previous one */
710     if (intime < prevtime) {
711       GST_DEBUG_OBJECT (videorate,
712           "The new buffer (%" GST_TIME_FORMAT
713           ") is before the previous buffer (%"
714           GST_TIME_FORMAT "). Dropping new buffer.",
715           GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
716       videorate->drop++;
717       if (!videorate->silent)
718         g_object_notify (G_OBJECT (videorate), "drop");
719       gst_buffer_unref (buffer);
720       goto done;
721     }
722
723     /* got 2 buffers, see which one is the best */
724     do {
725
726       diff1 = prevtime - videorate->next_ts;
727       diff2 = intime - videorate->next_ts;
728
729       /* take absolute values, beware: abs and ABS don't work for gint64 */
730       if (diff1 < 0)
731         diff1 = -diff1;
732       if (diff2 < 0)
733         diff2 = -diff2;
734
735       GST_LOG_OBJECT (videorate,
736           "diff with prev %" GST_TIME_FORMAT " diff with new %"
737           GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
738           GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
739           GST_TIME_ARGS (videorate->next_ts));
740
741       /* output first one when its the best */
742       if (diff1 <= diff2) {
743         count++;
744
745         /* on error the _flush function posted a warning already */
746         if ((res = gst_video_rate_flush_prev (videorate)) != GST_FLOW_OK) {
747           gst_buffer_unref (buffer);
748           goto done;
749         }
750       }
751       /* continue while the first one was the best, if they were equal avoid
752        * going into an infinite loop */
753     }
754     while (diff1 < diff2);
755
756     /* if we outputed the first buffer more then once, we have dups */
757     if (count > 1) {
758       videorate->dup += count - 1;
759       if (!videorate->silent)
760         g_object_notify (G_OBJECT (videorate), "duplicate");
761     }
762     /* if we didn't output the first buffer, we have a drop */
763     else if (count == 0) {
764       videorate->drop++;
765
766       if (!videorate->silent)
767         g_object_notify (G_OBJECT (videorate), "drop");
768
769       GST_LOG_OBJECT (videorate,
770           "new is best, old never used, drop, outgoing ts %"
771           GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
772     }
773     GST_LOG_OBJECT (videorate,
774         "END, putting new in old, diff1 %" GST_TIME_FORMAT
775         ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
776         ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1),
777         GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
778         videorate->in, videorate->out, videorate->drop, videorate->dup);
779
780     /* swap in new one when it's the best */
781     gst_video_rate_swap_prev (videorate, buffer, intime);
782   }
783 done:
784   return res;
785
786   /* ERRORS */
787 not_negotiated:
788   {
789     GST_WARNING_OBJECT (videorate, "no framerate negotiated");
790     gst_buffer_unref (buffer);
791     res = GST_FLOW_NOT_NEGOTIATED;
792     goto done;
793   }
794
795 invalid_buffer:
796   {
797     GST_WARNING_OBJECT (videorate,
798         "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
799     gst_buffer_unref (buffer);
800     goto done;
801   }
802 }
803
804 static void
805 gst_video_rate_set_property (GObject * object,
806     guint prop_id, const GValue * value, GParamSpec * pspec)
807 {
808   GstVideoRate *videorate = GST_VIDEO_RATE (object);
809
810   switch (prop_id) {
811     case ARG_SILENT:
812       videorate->silent = g_value_get_boolean (value);
813       break;
814     case ARG_NEW_PREF:
815       videorate->new_pref = g_value_get_double (value);
816       break;
817     case ARG_SKIP_TO_FIRST:
818       videorate->skip_to_first = g_value_get_boolean (value);
819       break;
820     default:
821       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
822       break;
823   }
824 }
825
826 static void
827 gst_video_rate_get_property (GObject * object,
828     guint prop_id, GValue * value, GParamSpec * pspec)
829 {
830   GstVideoRate *videorate = GST_VIDEO_RATE (object);
831
832   switch (prop_id) {
833     case ARG_IN:
834       g_value_set_uint64 (value, videorate->in);
835       break;
836     case ARG_OUT:
837       g_value_set_uint64 (value, videorate->out);
838       break;
839     case ARG_DUP:
840       g_value_set_uint64 (value, videorate->dup);
841       break;
842     case ARG_DROP:
843       g_value_set_uint64 (value, videorate->drop);
844       break;
845     case ARG_SILENT:
846       g_value_set_boolean (value, videorate->silent);
847       break;
848     case ARG_NEW_PREF:
849       g_value_set_double (value, videorate->new_pref);
850       break;
851     case ARG_SKIP_TO_FIRST:
852       g_value_set_boolean (value, videorate->skip_to_first);
853       break;
854     default:
855       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
856       break;
857   }
858 }
859
860 static GstStateChangeReturn
861 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
862 {
863   GstStateChangeReturn ret;
864   GstVideoRate *videorate;
865
866   videorate = GST_VIDEO_RATE (element);
867
868   switch (transition) {
869     case GST_STATE_CHANGE_READY_TO_PAUSED:
870       videorate->discont = TRUE;
871       videorate->last_ts = -1;
872       break;
873     default:
874       break;
875   }
876
877   ret = parent_class->change_state (element, transition);
878
879   switch (transition) {
880     case GST_STATE_CHANGE_PAUSED_TO_READY:
881       gst_video_rate_reset (videorate);
882       break;
883     default:
884       break;
885   }
886
887   return ret;
888 }
889
890 static gboolean
891 plugin_init (GstPlugin * plugin)
892 {
893   GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
894       "VideoRate stream fixer");
895
896   return gst_element_register (plugin, "videorate", GST_RANK_NONE,
897       GST_TYPE_VIDEO_RATE);
898 }
899
900 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
901     GST_VERSION_MINOR,
902     "videorate",
903     "Adjusts video frames",
904     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)