videorate: Make videorate work with a live source
[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   g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST,
189       g_param_spec_boolean ("skip-to-first", "Skip to first buffer",
190           "Don't produce buffers before the first one we receive",
191           DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
192
193   element_class->change_state = gst_video_rate_change_state;
194 }
195
196 /* return the caps that can be used on out_pad given in_caps on in_pad */
197 static gboolean
198 gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
199     GstPad * out_pad, GstCaps ** out_caps)
200 {
201   GstCaps *intersect;
202   const GstCaps *in_templ;
203   gint i;
204
205   in_templ = gst_pad_get_pad_template_caps (in_pad);
206   intersect = gst_caps_intersect (in_caps, in_templ);
207
208   /* all possible framerates are allowed */
209   for (i = 0; i < gst_caps_get_size (intersect); i++) {
210     GstStructure *structure;
211
212     structure = gst_caps_get_structure (intersect, i);
213
214     gst_structure_set (structure,
215         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
216   }
217   *out_caps = intersect;
218
219   return TRUE;
220 }
221
222 static GstCaps *
223 gst_video_rate_getcaps (GstPad * pad)
224 {
225   GstVideoRate *videorate;
226   GstPad *otherpad;
227   GstCaps *caps;
228
229   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
230
231   otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
232       videorate->srcpad;
233
234   /* we can do what the peer can */
235   caps = gst_pad_peer_get_caps (otherpad);
236   if (caps) {
237     GstCaps *transform;
238
239     gst_video_rate_transformcaps (otherpad, caps, pad, &transform);
240     gst_caps_unref (caps);
241     caps = transform;
242   } else {
243     /* no peer, our padtemplate is enough then */
244     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
245   }
246
247   return caps;
248 }
249
250 static gboolean
251 gst_video_rate_setcaps (GstPad * pad, GstCaps * caps)
252 {
253   GstVideoRate *videorate;
254   GstStructure *structure;
255   gboolean ret = TRUE;
256   GstPad *otherpad, *opeer;
257   gint rate_numerator, rate_denominator;
258
259   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
260
261   GST_DEBUG ("setcaps called %" GST_PTR_FORMAT, caps);
262
263   structure = gst_caps_get_structure (caps, 0);
264   if (!gst_structure_get_fraction (structure, "framerate",
265           &rate_numerator, &rate_denominator))
266     goto no_framerate;
267
268   if (pad == videorate->srcpad) {
269     videorate->to_rate_numerator = rate_numerator;
270     videorate->to_rate_denominator = rate_denominator;
271     otherpad = videorate->sinkpad;
272   } else {
273     videorate->from_rate_numerator = rate_numerator;
274     videorate->from_rate_denominator = rate_denominator;
275     otherpad = videorate->srcpad;
276   }
277
278   /* now try to find something for the peer */
279   opeer = gst_pad_get_peer (otherpad);
280   if (opeer) {
281     if (gst_pad_accept_caps (opeer, caps)) {
282       /* the peer accepts the caps as they are */
283       gst_pad_set_caps (otherpad, caps);
284
285       ret = TRUE;
286     } else {
287       GstCaps *peercaps;
288       GstCaps *intersect;
289       GstCaps *transform = NULL;
290
291       ret = FALSE;
292
293       /* see how we can transform the input caps */
294       if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform))
295         goto no_transform;
296
297       /* see what the peer can do */
298       peercaps = gst_pad_get_caps (opeer);
299
300       GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
301       GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
302
303       /* filter against our possibilities */
304       intersect = gst_caps_intersect (peercaps, transform);
305       gst_caps_unref (peercaps);
306       gst_caps_unref (transform);
307
308       GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
309
310       /* take first possibility */
311       caps = gst_caps_copy_nth (intersect, 0);
312       gst_caps_unref (intersect);
313       structure = gst_caps_get_structure (caps, 0);
314
315       /* and fixate */
316       gst_structure_fixate_field_nearest_fraction (structure, "framerate",
317           rate_numerator, rate_denominator);
318
319       gst_structure_get_fraction (structure, "framerate",
320           &rate_numerator, &rate_denominator);
321
322       if (otherpad == videorate->srcpad) {
323         videorate->to_rate_numerator = rate_numerator;
324         videorate->to_rate_denominator = rate_denominator;
325       } else {
326         videorate->from_rate_numerator = rate_numerator;
327         videorate->from_rate_denominator = rate_denominator;
328       }
329       gst_pad_set_caps (otherpad, caps);
330       gst_caps_unref (caps);
331       ret = TRUE;
332     }
333     gst_object_unref (opeer);
334   }
335 done:
336   /* After a setcaps, our caps may have changed. In that case, we can't use
337    * the old buffer, if there was one (it might have different dimensions) */
338   GST_DEBUG ("swapping old buffers");
339   gst_video_rate_swap_prev (videorate, NULL, GST_CLOCK_TIME_NONE);
340
341   gst_object_unref (videorate);
342   return ret;
343
344 no_framerate:
345   {
346     GST_DEBUG_OBJECT (videorate, "no framerate specified");
347     goto done;
348   }
349 no_transform:
350   {
351     GST_DEBUG_OBJECT (videorate, "no framerate transform possible");
352     ret = FALSE;
353     goto done;
354   }
355 }
356
357 static void
358 gst_video_rate_reset (GstVideoRate * videorate)
359 {
360   GST_DEBUG ("resetting internal variables");
361
362   videorate->in = 0;
363   videorate->out = 0;
364   videorate->segment_out = 0;
365   videorate->drop = 0;
366   videorate->dup = 0;
367   videorate->next_ts = GST_CLOCK_TIME_NONE;
368   videorate->last_ts = GST_CLOCK_TIME_NONE;
369   videorate->discont = TRUE;
370   gst_video_rate_swap_prev (videorate, NULL, 0);
371
372   gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
373 }
374
375 static void
376 gst_video_rate_init (GstVideoRate * videorate, GstVideoRateClass * klass)
377 {
378   GST_DEBUG ("gst_video_rate_init");
379   videorate->sinkpad =
380       gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
381   gst_pad_set_event_function (videorate->sinkpad, gst_video_rate_event);
382   gst_pad_set_chain_function (videorate->sinkpad, gst_video_rate_chain);
383   gst_pad_set_getcaps_function (videorate->sinkpad, gst_video_rate_getcaps);
384   gst_pad_set_setcaps_function (videorate->sinkpad, gst_video_rate_setcaps);
385   gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
386
387   videorate->srcpad =
388       gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
389   gst_pad_set_query_function (videorate->srcpad, gst_video_rate_query);
390   gst_pad_set_getcaps_function (videorate->srcpad, gst_video_rate_getcaps);
391   gst_pad_set_setcaps_function (videorate->srcpad, gst_video_rate_setcaps);
392   gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
393
394   gst_video_rate_reset (videorate);
395   videorate->silent = DEFAULT_SILENT;
396   videorate->new_pref = DEFAULT_NEW_PREF;
397
398   videorate->from_rate_numerator = 0;
399   videorate->from_rate_denominator = 0;
400   videorate->to_rate_numerator = 0;
401   videorate->to_rate_denominator = 0;
402 }
403
404 /* flush the oldest buffer */
405 static GstFlowReturn
406 gst_video_rate_flush_prev (GstVideoRate * videorate)
407 {
408   GstFlowReturn res;
409   GstBuffer *outbuf;
410   GstClockTime push_ts;
411
412   if (!videorate->prevbuf)
413     goto eos_before_buffers;
414
415   /* make sure we can write to the metadata */
416   outbuf = gst_buffer_make_metadata_writable
417       (gst_buffer_ref (videorate->prevbuf));
418
419   GST_BUFFER_OFFSET (outbuf) = videorate->out;
420   GST_BUFFER_OFFSET_END (outbuf) = videorate->out + 1;
421
422   if (videorate->discont) {
423     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
424     videorate->discont = FALSE;
425   } else
426     GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DISCONT);
427
428   /* this is the timestamp we put on the buffer */
429   push_ts = videorate->next_ts;
430
431   videorate->out++;
432   videorate->segment_out++;
433   if (videorate->to_rate_numerator) {
434     /* interpolate next expected timestamp in the segment */
435     videorate->next_ts = videorate->segment.accum + videorate->segment.start +
436         gst_util_uint64_scale (videorate->segment_out,
437         videorate->to_rate_denominator * GST_SECOND,
438         videorate->to_rate_numerator);
439     GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
440   }
441
442   /* adapt for looping, bring back to time in current segment. */
443   GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.accum;
444
445   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
446
447   GST_LOG_OBJECT (videorate,
448       "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
449       GST_TIME_ARGS (push_ts));
450
451   res = gst_pad_push (videorate->srcpad, outbuf);
452
453   return res;
454
455   /* WARNINGS */
456 eos_before_buffers:
457   {
458     GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
459     return GST_FLOW_OK;
460   }
461 }
462
463 static void
464 gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
465     gint64 time)
466 {
467   GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer);
468   if (videorate->prevbuf)
469     gst_buffer_unref (videorate->prevbuf);
470   videorate->prevbuf = buffer;
471   videorate->prev_ts = time;
472 }
473
474 #define MAGIC_LIMIT  25
475 static gboolean
476 gst_video_rate_event (GstPad * pad, GstEvent * event)
477 {
478   GstVideoRate *videorate;
479   gboolean ret;
480
481   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
482
483   switch (GST_EVENT_TYPE (event)) {
484     case GST_EVENT_NEWSEGMENT:
485     {
486       gint64 start, stop, time;
487       gdouble rate, arate;
488       gboolean update;
489       GstFormat format;
490
491       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
492           &start, &stop, &time);
493
494       if (format != GST_FORMAT_TIME)
495         goto format_error;
496
497       GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
498
499       /* close up the previous segment, if appropriate */
500       if (!update && videorate->prevbuf) {
501         gint count = 0;
502         GstFlowReturn res;
503
504         res = GST_FLOW_OK;
505         /* fill up to the end of current segment,
506          * or only send out the stored buffer if there is no specific stop.
507          * regardless, prevent going loopy in strange cases */
508         while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
509             ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) &&
510                     videorate->next_ts - videorate->segment.accum
511                     < videorate->segment.stop)
512                 || count < 1)) {
513           gst_video_rate_flush_prev (videorate);
514           count++;
515         }
516         if (count > 1) {
517           videorate->dup += count - 1;
518           if (!videorate->silent)
519             g_object_notify (G_OBJECT (videorate), "duplicate");
520         } else if (count == 0) {
521           videorate->drop++;
522           if (!videorate->silent)
523             g_object_notify (G_OBJECT (videorate), "drop");
524         }
525         /* clean up for the new one; _chain will resume from the new start */
526         videorate->segment_out = 0;
527         gst_video_rate_swap_prev (videorate, NULL, 0);
528         videorate->next_ts = GST_CLOCK_TIME_NONE;
529       }
530
531       /* We just want to update the accumulated stream_time  */
532       gst_segment_set_newsegment_full (&videorate->segment, update, rate, arate,
533           format, start, stop, time);
534
535       GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
536           &videorate->segment);
537       break;
538     }
539     case GST_EVENT_EOS:
540       /* flush last queued frame */
541       GST_DEBUG_OBJECT (videorate, "Got EOS");
542       gst_video_rate_flush_prev (videorate);
543       break;
544     case GST_EVENT_FLUSH_STOP:
545       /* also resets the segment */
546       GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP");
547       gst_video_rate_reset (videorate);
548       break;
549     default:
550       break;
551   }
552
553   ret = gst_pad_push_event (videorate->srcpad, event);
554
555 done:
556   gst_object_unref (videorate);
557
558   return ret;
559
560   /* ERRORS */
561 format_error:
562   {
563     GST_WARNING_OBJECT (videorate,
564         "Got segment but doesn't have GST_FORMAT_TIME value");
565     gst_event_unref (event);
566     ret = FALSE;
567     goto done;
568   }
569 }
570
571 static gboolean
572 gst_video_rate_query (GstPad * pad, GstQuery * query)
573 {
574   GstVideoRate *videorate;
575   gboolean res = FALSE;
576
577   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
578
579   switch (GST_QUERY_TYPE (query)) {
580     case GST_QUERY_LATENCY:
581     {
582       GstClockTime min, max;
583       gboolean live;
584       guint64 latency;
585       GstPad *peer;
586
587       if ((peer = gst_pad_get_peer (videorate->sinkpad))) {
588         if ((res = gst_pad_query (peer, query))) {
589           gst_query_parse_latency (query, &live, &min, &max);
590
591           GST_DEBUG_OBJECT (videorate, "Peer latency: min %"
592               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
593               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
594
595           if (videorate->from_rate_numerator != 0) {
596             /* add latency. We don't really know since we hold on to the frames
597              * until we get a next frame, which can be anything. We assume
598              * however that this will take from_rate time. */
599             latency = gst_util_uint64_scale (GST_SECOND,
600                 videorate->from_rate_denominator,
601                 videorate->from_rate_numerator);
602           } else {
603             /* no input framerate, we don't know */
604             latency = 0;
605           }
606
607           GST_DEBUG_OBJECT (videorate, "Our latency: %"
608               GST_TIME_FORMAT, GST_TIME_ARGS (latency));
609
610           min += latency;
611           if (max != -1)
612             max += latency;
613
614           GST_DEBUG_OBJECT (videorate, "Calculated total latency : min %"
615               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
616               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
617
618           gst_query_set_latency (query, live, min, max);
619         }
620         gst_object_unref (peer);
621       }
622       break;
623     }
624     default:
625       res = gst_pad_query_default (pad, query);
626       break;
627   }
628   gst_object_unref (videorate);
629
630   return res;
631 }
632
633 static GstFlowReturn
634 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
635 {
636   GstVideoRate *videorate;
637   GstFlowReturn res = GST_FLOW_OK;
638   GstClockTime intime, in_ts, in_dur;
639
640   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
641
642   /* make sure the denominators are not 0 */
643   if (videorate->from_rate_denominator == 0 ||
644       videorate->to_rate_denominator == 0)
645     goto not_negotiated;
646
647   in_ts = GST_BUFFER_TIMESTAMP (buffer);
648   in_dur = GST_BUFFER_DURATION (buffer);
649
650   if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE)) {
651     in_ts = videorate->last_ts;
652     if (G_UNLIKELY (in_ts == GST_CLOCK_TIME_NONE))
653       goto invalid_buffer;
654   }
655
656   /* get the time of the next expected buffer timestamp, we use this when the
657    * next buffer has -1 as a timestamp */
658   videorate->last_ts = in_ts;
659   if (in_dur != GST_CLOCK_TIME_NONE)
660     videorate->last_ts += in_dur;
661
662   GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
663       GST_TIME_ARGS (in_ts));
664
665   /* the input time is the time in the segment + all previously accumulated
666    * segments */
667   intime = in_ts + videorate->segment.accum;
668
669   /* we need to have two buffers to compare */
670   if (videorate->prevbuf == NULL) {
671     gst_video_rate_swap_prev (videorate, buffer, intime);
672     videorate->in++;
673     if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
674       /* new buffer, we expect to output a buffer that matches the first
675        * timestamp in the segment */
676       if (videorate->skip_to_first) {
677         videorate->next_ts = in_ts;
678         videorate->segment_out = gst_util_uint64_scale (in_ts,
679             videorate->to_rate_numerator,
680             videorate->to_rate_denominator * GST_SECOND) -
681             (videorate->segment.accum + videorate->segment.start);
682       } else {
683         videorate->next_ts =
684             videorate->segment.start + videorate->segment.accum;
685       }
686     }
687   } else {
688     GstClockTime prevtime;
689     gint count = 0;
690     gint64 diff1, diff2;
691
692     prevtime = videorate->prev_ts;
693
694     GST_LOG_OBJECT (videorate,
695         "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
696         " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
697         GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
698
699     videorate->in++;
700
701     /* drop new buffer if it's before previous one */
702     if (intime < prevtime) {
703       GST_DEBUG_OBJECT (videorate,
704           "The new buffer (%" GST_TIME_FORMAT
705           ") is before the previous buffer (%"
706           GST_TIME_FORMAT "). Dropping new buffer.",
707           GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
708       videorate->drop++;
709       if (!videorate->silent)
710         g_object_notify (G_OBJECT (videorate), "drop");
711       gst_buffer_unref (buffer);
712       goto done;
713     }
714
715     /* got 2 buffers, see which one is the best */
716     do {
717
718       diff1 = prevtime - videorate->next_ts;
719       diff2 = intime - videorate->next_ts;
720
721       /* take absolute values, beware: abs and ABS don't work for gint64 */
722       if (diff1 < 0)
723         diff1 = -diff1;
724       if (diff2 < 0)
725         diff2 = -diff2;
726
727       GST_LOG_OBJECT (videorate,
728           "diff with prev %" GST_TIME_FORMAT " diff with new %"
729           GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
730           GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
731           GST_TIME_ARGS (videorate->next_ts));
732
733       /* output first one when its the best */
734       if (diff1 <= diff2) {
735         count++;
736
737         /* on error the _flush function posted a warning already */
738         if ((res = gst_video_rate_flush_prev (videorate)) != GST_FLOW_OK) {
739           gst_buffer_unref (buffer);
740           goto done;
741         }
742       }
743       /* continue while the first one was the best, if they were equal avoid
744        * going into an infinite loop */
745     }
746     while (diff1 < diff2);
747
748     /* if we outputed the first buffer more then once, we have dups */
749     if (count > 1) {
750       videorate->dup += count - 1;
751       if (!videorate->silent)
752         g_object_notify (G_OBJECT (videorate), "duplicate");
753     }
754     /* if we didn't output the first buffer, we have a drop */
755     else if (count == 0) {
756       videorate->drop++;
757
758       if (!videorate->silent)
759         g_object_notify (G_OBJECT (videorate), "drop");
760
761       GST_LOG_OBJECT (videorate,
762           "new is best, old never used, drop, outgoing ts %"
763           GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
764     }
765     GST_LOG_OBJECT (videorate,
766         "END, putting new in old, diff1 %" GST_TIME_FORMAT
767         ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
768         ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1),
769         GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
770         videorate->in, videorate->out, videorate->drop, videorate->dup);
771
772     /* swap in new one when it's the best */
773     gst_video_rate_swap_prev (videorate, buffer, intime);
774   }
775 done:
776   return res;
777
778   /* ERRORS */
779 not_negotiated:
780   {
781     GST_WARNING_OBJECT (videorate, "no framerate negotiated");
782     gst_buffer_unref (buffer);
783     res = GST_FLOW_NOT_NEGOTIATED;
784     goto done;
785   }
786
787 invalid_buffer:
788   {
789     GST_WARNING_OBJECT (videorate,
790         "Got buffer with GST_CLOCK_TIME_NONE timestamp, discarding it");
791     gst_buffer_unref (buffer);
792     goto done;
793   }
794 }
795
796 static void
797 gst_video_rate_set_property (GObject * object,
798     guint prop_id, const GValue * value, GParamSpec * pspec)
799 {
800   GstVideoRate *videorate = GST_VIDEO_RATE (object);
801
802   switch (prop_id) {
803     case ARG_SILENT:
804       videorate->silent = g_value_get_boolean (value);
805       break;
806     case ARG_NEW_PREF:
807       videorate->new_pref = g_value_get_double (value);
808       break;
809     case ARG_SKIP_TO_FIRST:
810       videorate->skip_to_first = g_value_get_boolean (value);
811       break;
812     default:
813       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
814       break;
815   }
816 }
817
818 static void
819 gst_video_rate_get_property (GObject * object,
820     guint prop_id, GValue * value, GParamSpec * pspec)
821 {
822   GstVideoRate *videorate = GST_VIDEO_RATE (object);
823
824   switch (prop_id) {
825     case ARG_IN:
826       g_value_set_uint64 (value, videorate->in);
827       break;
828     case ARG_OUT:
829       g_value_set_uint64 (value, videorate->out);
830       break;
831     case ARG_DUP:
832       g_value_set_uint64 (value, videorate->dup);
833       break;
834     case ARG_DROP:
835       g_value_set_uint64 (value, videorate->drop);
836       break;
837     case ARG_SILENT:
838       g_value_set_boolean (value, videorate->silent);
839       break;
840     case ARG_NEW_PREF:
841       g_value_set_double (value, videorate->new_pref);
842       break;
843     case ARG_SKIP_TO_FIRST:
844       g_value_set_boolean (value, videorate->skip_to_first);
845       break;
846     default:
847       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
848       break;
849   }
850 }
851
852 static GstStateChangeReturn
853 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
854 {
855   GstStateChangeReturn ret;
856   GstVideoRate *videorate;
857
858   videorate = GST_VIDEO_RATE (element);
859
860   switch (transition) {
861     case GST_STATE_CHANGE_READY_TO_PAUSED:
862       videorate->discont = TRUE;
863       videorate->last_ts = -1;
864       break;
865     default:
866       break;
867   }
868
869   ret = parent_class->change_state (element, transition);
870
871   switch (transition) {
872     case GST_STATE_CHANGE_PAUSED_TO_READY:
873       gst_video_rate_reset (videorate);
874       break;
875     default:
876       break;
877   }
878
879   return ret;
880 }
881
882 static gboolean
883 plugin_init (GstPlugin * plugin)
884 {
885   GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
886       "VideoRate stream fixer");
887
888   return gst_element_register (plugin, "videorate", GST_RANK_NONE,
889       GST_TYPE_VIDEO_RATE);
890 }
891
892 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
893     GST_VERSION_MINOR,
894     "videorate",
895     "Adjusts video frames",
896     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)