ext/gnomevfs/: Fix URI interface implementation return type.
[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  * @short_description: retimestamps and drops/duplicates video frames to
23  *  match the source pad's framerate and create a perfect stream
24  *
25  * <refsect2>
26  * <para>
27  * This element takes an incoming stream of timestamped video frames.
28  * It will produce a perfect stream that matches the source pad's framerate.
29  *
30  * The correction is performed by dropping and duplicating frames, no fancy
31  * algorithm is used to interpolate frames (yet).
32  * </para>
33  * <para>
34  * By default the element will simply negotiate the same framerate on its
35  * source and sink pad.
36  * </para>
37  * <para>
38  * This operation is useful to link to elements that require a perfect stream.
39  * Typical examples are formats that do not store timestamps for video frames,
40  * but only store a framerate, like Ogg and AVI.
41  * </para>
42  * <para>
43  * A conversion to a specific framerate can be forced by using filtered caps on
44  * the source pad.
45  * </para>
46  * <para>
47  * The properties "in", "out", "duplicate" and "drop" can be read to obtain
48  * information about number of input frames, output frames, dropped frames
49  * (i.e. the number of unused input frames) and duplicated frames (i.e. the
50  *  number of times an input frame was duplicated, beside being used normally).
51  *
52  * An input stream that needs no adjustments will thus never have dropped or
53  * duplicated frames.
54  *
55  * When the "silent" property is set to FALSE, a GObject property notification
56  * will be emitted whenever one of the "duplicate" or "drop" values changes.
57  * This can potentially cause performance degradation.
58  * Note that property notification will happen from the streaming thread, so
59  * applications should be prepared for this.
60  * </para>
61  * <title>Example pipelines</title>
62  * <para>
63  * <programlisting>
64  * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! videorate ! video/x-raw-yuv,framerate=15/1 ! xvimagesink
65  * </programlisting>
66  * Decode an Ogg/Theora file and adjust the framerate to 15 fps before playing.
67  * To create the test Ogg/Theora file refer to the documentation of theoraenc.
68  * </para>
69  * <para>
70  * <programlisting>
71  * gst-launch -v v4lsrc ! videorate ! video/x-raw-yuv,framerate=25/2 ! theoraenc ! oggmux ! filesink location=v4l.ogg
72  * </programlisting>
73  * Capture video from a V4L device, and adjust the stream to 12.5 fps before
74  * encoding to Ogg/Theora.
75  * </para>
76   * </refsect2>
77  *
78  * Last reviewed on 2006-09-02 (0.10.11)
79  */
80
81 #ifdef HAVE_CONFIG_H
82 #include "config.h"
83 #endif
84
85 #include "gstvideorate.h"
86
87 GST_DEBUG_CATEGORY_STATIC (video_rate_debug);
88 #define GST_CAT_DEFAULT video_rate_debug
89
90 /* elementfactory information */
91 static const GstElementDetails video_rate_details =
92 GST_ELEMENT_DETAILS ("Video rate adjuster",
93     "Filter/Effect/Video",
94     "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
95     "Wim Taymans <wim@fluendo.com>");
96
97 /* GstVideoRate signals and args */
98 enum
99 {
100   /* FILL ME */
101   LAST_SIGNAL
102 };
103
104 #define DEFAULT_SILENT          TRUE
105 #define DEFAULT_NEW_PREF        1.0
106
107 enum
108 {
109   ARG_0,
110   ARG_IN,
111   ARG_OUT,
112   ARG_DUP,
113   ARG_DROP,
114   ARG_SILENT,
115   ARG_NEW_PREF,
116   /* FILL ME */
117 };
118
119 static GstStaticPadTemplate gst_video_rate_src_template =
120     GST_STATIC_PAD_TEMPLATE ("src",
121     GST_PAD_SRC,
122     GST_PAD_ALWAYS,
123     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
124     );
125
126 static GstStaticPadTemplate gst_video_rate_sink_template =
127     GST_STATIC_PAD_TEMPLATE ("sink",
128     GST_PAD_SINK,
129     GST_PAD_ALWAYS,
130     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
131     );
132
133 static void gst_video_rate_base_init (gpointer g_class);
134 static void gst_video_rate_class_init (GstVideoRateClass * klass);
135 static void gst_video_rate_init (GstVideoRate * videorate);
136
137 static void gst_video_rate_swap_prev (GstVideoRate * videorate,
138     GstBuffer * buffer, gint64 time);
139 static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
140 static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
141
142 static void gst_video_rate_set_property (GObject * object,
143     guint prop_id, const GValue * value, GParamSpec * pspec);
144 static void gst_video_rate_get_property (GObject * object,
145     guint prop_id, GValue * value, GParamSpec * pspec);
146
147 static GstStateChangeReturn gst_video_rate_change_state (GstElement * element,
148     GstStateChange transition);
149
150 static GstElementClass *parent_class = NULL;
151
152 /*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */
153
154 static GType
155 gst_video_rate_get_type (void)
156 {
157   static GType video_rate_type = 0;
158
159   if (!video_rate_type) {
160     static const GTypeInfo video_rate_info = {
161       sizeof (GstVideoRateClass),
162       gst_video_rate_base_init,
163       NULL,
164       (GClassInitFunc) gst_video_rate_class_init,
165       NULL,
166       NULL,
167       sizeof (GstVideoRate),
168       0,
169       (GInstanceInitFunc) gst_video_rate_init,
170     };
171
172     video_rate_type = g_type_register_static (GST_TYPE_ELEMENT,
173         "GstVideoRate", &video_rate_info, 0);
174   }
175
176   return video_rate_type;
177 }
178
179 static void
180 gst_video_rate_base_init (gpointer g_class)
181 {
182   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
183
184   gst_element_class_set_details (element_class, &video_rate_details);
185
186   gst_element_class_add_pad_template (element_class,
187       gst_static_pad_template_get (&gst_video_rate_sink_template));
188   gst_element_class_add_pad_template (element_class,
189       gst_static_pad_template_get (&gst_video_rate_src_template));
190 }
191 static void
192 gst_video_rate_class_init (GstVideoRateClass * klass)
193 {
194   GObjectClass *object_class = G_OBJECT_CLASS (klass);
195   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
196
197   parent_class = g_type_class_peek_parent (klass);
198
199   object_class->set_property = gst_video_rate_set_property;
200   object_class->get_property = gst_video_rate_get_property;
201
202   g_object_class_install_property (object_class, ARG_IN,
203       g_param_spec_uint64 ("in", "In",
204           "Number of input frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
205   g_object_class_install_property (object_class, ARG_OUT,
206       g_param_spec_uint64 ("out", "Out",
207           "Number of output frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
208   g_object_class_install_property (object_class, ARG_DUP,
209       g_param_spec_uint64 ("duplicate", "Duplicate",
210           "Number of duplicated frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
211   g_object_class_install_property (object_class, ARG_DROP,
212       g_param_spec_uint64 ("drop", "Drop",
213           "Number of dropped frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
214   g_object_class_install_property (object_class, ARG_SILENT,
215       g_param_spec_boolean ("silent", "silent",
216           "Don't emit notify for dropped and duplicated frames",
217           DEFAULT_SILENT, G_PARAM_READWRITE));
218   g_object_class_install_property (object_class, ARG_NEW_PREF,
219       g_param_spec_double ("new_pref", "New Pref",
220           "Value indicating how much to prefer new frames (unused)",
221           0.0, 1.0, DEFAULT_NEW_PREF, G_PARAM_READWRITE));
222
223   element_class->change_state = gst_video_rate_change_state;
224 }
225
226 /* return the caps that can be used on out_pad given in_caps on in_pad */
227 static gboolean
228 gst_video_rate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
229     GstPad * out_pad, GstCaps ** out_caps)
230 {
231   GstCaps *intersect;
232   const GstCaps *in_templ;
233   gint i;
234
235   in_templ = gst_pad_get_pad_template_caps (in_pad);
236   intersect = gst_caps_intersect (in_caps, in_templ);
237
238   /* all possible framerates are allowed */
239   for (i = 0; i < gst_caps_get_size (intersect); i++) {
240     GstStructure *structure;
241
242     structure = gst_caps_get_structure (intersect, i);
243
244     gst_structure_set (structure,
245         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
246   }
247   *out_caps = intersect;
248
249   return TRUE;
250 }
251
252 static GstCaps *
253 gst_video_rate_getcaps (GstPad * pad)
254 {
255   GstVideoRate *videorate;
256   GstPad *otherpad;
257   GstCaps *caps;
258
259   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
260
261   otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
262       videorate->srcpad;
263
264   /* we can do what the peer can */
265   caps = gst_pad_peer_get_caps (otherpad);
266   if (caps) {
267     GstCaps *transform;
268
269     gst_video_rate_transformcaps (otherpad, caps, pad, &transform);
270     gst_caps_unref (caps);
271     caps = transform;
272   } else {
273     /* no peer, our padtemplate is enough then */
274     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
275   }
276
277   return caps;
278 }
279
280 static gboolean
281 gst_video_rate_setcaps (GstPad * pad, GstCaps * caps)
282 {
283   GstVideoRate *videorate;
284   GstStructure *structure;
285   gboolean ret = TRUE;
286   GstPad *otherpad, *opeer;
287   gint rate_numerator, rate_denominator;
288
289   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
290
291   structure = gst_caps_get_structure (caps, 0);
292   if (!gst_structure_get_fraction (structure, "framerate",
293           &rate_numerator, &rate_denominator))
294     goto no_framerate;
295
296   if (pad == videorate->srcpad) {
297     videorate->to_rate_numerator = rate_numerator;
298     videorate->to_rate_denominator = rate_denominator;
299     otherpad = videorate->sinkpad;
300   } else {
301     videorate->from_rate_numerator = rate_numerator;
302     videorate->from_rate_denominator = rate_denominator;
303     otherpad = videorate->srcpad;
304   }
305   /* now try to find something for the peer */
306   opeer = gst_pad_get_peer (otherpad);
307   if (opeer) {
308     if (gst_pad_accept_caps (opeer, caps)) {
309       /* the peer accepts the caps as they are */
310       gst_pad_set_caps (otherpad, caps);
311
312       ret = TRUE;
313     } else {
314       GstCaps *peercaps;
315       GstCaps *intersect;
316       GstCaps *transform = NULL;
317
318       ret = FALSE;
319
320       /* see how we can transform the input caps */
321       if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform))
322         goto no_transform;
323
324       /* see what the peer can do */
325       peercaps = gst_pad_get_caps (opeer);
326
327       GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
328       GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
329
330       /* filter against our possibilities */
331       intersect = gst_caps_intersect (peercaps, transform);
332       gst_caps_unref (peercaps);
333       gst_caps_unref (transform);
334
335       GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
336
337       /* take first possibility */
338       caps = gst_caps_copy_nth (intersect, 0);
339       gst_caps_unref (intersect);
340       structure = gst_caps_get_structure (caps, 0);
341
342       /* and fixate */
343       gst_structure_fixate_field_nearest_fraction (structure, "framerate",
344           rate_numerator, rate_denominator);
345
346       gst_structure_get_fraction (structure, "framerate",
347           &rate_numerator, &rate_denominator);
348
349       if (otherpad == videorate->srcpad) {
350         videorate->to_rate_numerator = rate_numerator;
351         videorate->to_rate_denominator = rate_denominator;
352       } else {
353         videorate->from_rate_numerator = rate_numerator;
354         videorate->from_rate_denominator = rate_denominator;
355       }
356       gst_pad_set_caps (otherpad, caps);
357       ret = TRUE;
358     }
359     gst_object_unref (opeer);
360   }
361 done:
362   gst_object_unref (videorate);
363   return ret;
364
365 no_framerate:
366   {
367     GST_DEBUG_OBJECT (videorate, "no framerate specified");
368     goto done;
369   }
370 no_transform:
371   {
372     GST_DEBUG_OBJECT (videorate, "no framerate transform possible");
373     ret = FALSE;
374     goto done;
375   }
376 }
377
378 static void
379 gst_video_rate_reset (GstVideoRate * videorate)
380 {
381   GST_DEBUG ("resetting internal variables");
382
383   videorate->in = 0;
384   videorate->out = 0;
385   videorate->drop = 0;
386   videorate->dup = 0;
387   videorate->next_ts = G_GINT64_CONSTANT (0);
388   gst_video_rate_swap_prev (videorate, NULL, 0);
389
390   gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
391 }
392
393 static void
394 gst_video_rate_init (GstVideoRate * videorate)
395 {
396   GST_DEBUG ("gst_video_rate_init");
397   videorate->sinkpad =
398       gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
399   gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
400   gst_pad_set_event_function (videorate->sinkpad, gst_video_rate_event);
401   gst_pad_set_chain_function (videorate->sinkpad, gst_video_rate_chain);
402   gst_pad_set_getcaps_function (videorate->sinkpad, gst_video_rate_getcaps);
403   gst_pad_set_setcaps_function (videorate->sinkpad, gst_video_rate_setcaps);
404
405   videorate->srcpad =
406       gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
407   gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
408   gst_pad_set_getcaps_function (videorate->srcpad, gst_video_rate_getcaps);
409   gst_pad_set_setcaps_function (videorate->srcpad, gst_video_rate_setcaps);
410
411   gst_video_rate_reset (videorate);
412   videorate->silent = DEFAULT_SILENT;
413   videorate->new_pref = DEFAULT_NEW_PREF;
414
415   videorate->from_rate_numerator = 0;
416   videorate->from_rate_denominator = 0;
417   videorate->to_rate_numerator = 0;
418   videorate->to_rate_denominator = 0;
419 }
420
421 /* flush the oldest buffer */
422 static GstFlowReturn
423 gst_video_rate_flush_prev (GstVideoRate * videorate)
424 {
425   GstFlowReturn res;
426   GstBuffer *outbuf;
427   GstClockTime push_ts;
428
429   if (!videorate->prevbuf)
430     goto eos_before_buffers;
431
432   /* make sure we can write to the metadata */
433   outbuf = gst_buffer_make_metadata_writable
434       (gst_buffer_ref (videorate->prevbuf));
435
436   /* this is the timestamp we put on the buffer */
437   push_ts = videorate->next_ts;
438
439   videorate->out++;
440   if (videorate->to_rate_numerator) {
441     /* interpolate next expected timestamp in the segment */
442     videorate->next_ts = videorate->segment.accum + videorate->segment.start +
443         gst_util_uint64_scale (videorate->out,
444         videorate->to_rate_denominator * GST_SECOND,
445         videorate->to_rate_numerator);
446     GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
447   }
448
449   /* adapt for looping, bring back to time in current segment. */
450   GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.accum;
451
452   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
453
454   GST_LOG_OBJECT (videorate,
455       "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
456       GST_TIME_ARGS (push_ts));
457
458   res = gst_pad_push (videorate->srcpad, outbuf);
459
460   return res;
461
462   /* WARNINGS */
463 eos_before_buffers:
464   {
465     GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
466     return GST_FLOW_OK;
467   }
468 }
469
470 static void
471 gst_video_rate_swap_prev (GstVideoRate * videorate, GstBuffer * buffer,
472     gint64 time)
473 {
474   GST_LOG_OBJECT (videorate, "swap_prev: storing buffer %p in prev", buffer);
475   if (videorate->prevbuf)
476     gst_buffer_unref (videorate->prevbuf);
477   videorate->prevbuf = buffer;
478   videorate->prev_ts = time;
479 }
480
481 static gboolean
482 gst_video_rate_event (GstPad * pad, GstEvent * event)
483 {
484   GstVideoRate *videorate;
485   gboolean ret;
486
487   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
488
489   switch (GST_EVENT_TYPE (event)) {
490     case GST_EVENT_NEWSEGMENT:
491     {
492       gint64 start, stop, time;
493       gdouble rate, arate;
494       gboolean update;
495       GstFormat format;
496
497       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
498           &start, &stop, &time);
499
500       if (format != GST_FORMAT_TIME)
501         goto format_error;
502
503       GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT");
504
505       /* We just want to update the accumulated stream_time  */
506       gst_segment_set_newsegment_full (&videorate->segment, update, rate, arate,
507           format, start, stop, time);
508
509       GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT,
510           &videorate->segment);
511       break;
512     }
513     case GST_EVENT_EOS:
514       /* flush last queued frame */
515       GST_DEBUG_OBJECT (videorate, "Got EOS");
516       gst_video_rate_flush_prev (videorate);
517       break;
518     case GST_EVENT_FLUSH_STOP:
519       /* also resets the segment */
520       GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP");
521       gst_video_rate_reset (videorate);
522       break;
523     default:
524       break;
525   }
526
527   ret = gst_pad_push_event (videorate->srcpad, event);
528
529 done:
530   gst_object_unref (videorate);
531
532   return ret;
533
534   /* ERRORS */
535 format_error:
536   {
537     GST_WARNING_OBJECT (videorate,
538         "Got segment but doesn't have GST_FORMAT_TIME value");
539     gst_event_unref (event);
540     ret = FALSE;
541     goto done;
542   }
543 }
544
545 static GstFlowReturn
546 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
547 {
548   GstVideoRate *videorate;
549   GstFlowReturn res = GST_FLOW_OK;
550   GstClockTime intime, in_ts;
551
552   videorate = GST_VIDEO_RATE (gst_pad_get_parent (pad));
553
554   /* make sure the denominators are not 0 */
555   if (videorate->from_rate_denominator == 0 ||
556       videorate->to_rate_denominator == 0)
557     goto not_negotiated;
558
559   in_ts = GST_BUFFER_TIMESTAMP (buffer);
560
561   GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT,
562       GST_TIME_ARGS (in_ts));
563
564   /* the input time is the time in the segment + all previously accumulated
565    * segments */
566   intime = in_ts + videorate->segment.accum;
567
568   /* we need to have two buffers to compare */
569   if (videorate->prevbuf == NULL) {
570     gst_video_rate_swap_prev (videorate, buffer, intime);
571     videorate->in++;
572     /* new buffer, we expect to output a buffer that matches the first
573      * timestamp in the segment */
574     videorate->next_ts = videorate->segment.start;
575   } else {
576     GstClockTime prevtime;
577     gint count = 0;
578     gint64 diff1, diff2;
579
580     prevtime = videorate->prev_ts;
581
582     GST_LOG_OBJECT (videorate,
583         "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
584         " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
585         GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
586
587     videorate->in++;
588
589     /* drop new buffer if it's before previous one */
590     if (intime < prevtime) {
591       GST_DEBUG_OBJECT (videorate,
592           "The new buffer (%" GST_TIME_FORMAT
593           ") is before the previous buffer (%"
594           GST_TIME_FORMAT "). Dropping new buffer.",
595           GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime));
596       videorate->drop++;
597       if (!videorate->silent)
598         g_object_notify (G_OBJECT (videorate), "drop");
599       gst_buffer_unref (buffer);
600       goto done;
601     }
602
603     /* got 2 buffers, see which one is the best */
604     do {
605       diff1 = prevtime - videorate->next_ts;
606       diff2 = intime - videorate->next_ts;
607
608       /* take absolute values, beware: abs and ABS don't work for gint64 */
609       if (diff1 < 0)
610         diff1 = -diff1;
611       if (diff2 < 0)
612         diff2 = -diff2;
613
614       GST_LOG_OBJECT (videorate,
615           "diff with prev %" GST_TIME_FORMAT " diff with new %"
616           GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
617           GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
618           GST_TIME_ARGS (videorate->next_ts));
619
620       /* output first one when its the best */
621       if (diff1 < diff2) {
622         count++;
623
624         /* on error the _flush function posted a warning already */
625         if ((res = gst_video_rate_flush_prev (videorate)) != GST_FLOW_OK)
626           goto done;
627       }
628       /* continue while the first one was the best */
629     }
630     while (diff1 < diff2);
631
632     /* if we outputed the first buffer more then once, we have dups */
633     if (count > 1) {
634       videorate->dup += count - 1;
635       if (!videorate->silent)
636         g_object_notify (G_OBJECT (videorate), "duplicate");
637     }
638     /* if we didn't output the first buffer, we have a drop */
639     else if (count == 0) {
640       videorate->drop++;
641
642       if (!videorate->silent)
643         g_object_notify (G_OBJECT (videorate), "drop");
644
645       GST_LOG_OBJECT (videorate,
646           "new is best, old never used, drop, outgoing ts %"
647           GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
648     }
649     GST_LOG_OBJECT (videorate,
650         "END, putting new in old, diff1 %" GST_TIME_FORMAT
651         ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
652         ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1),
653         GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
654         videorate->in, videorate->out, videorate->drop, videorate->dup);
655
656     /* swap in new one when it's the best */
657     gst_video_rate_swap_prev (videorate, buffer, intime);
658   }
659 done:
660   gst_object_unref (videorate);
661   return res;
662
663   /* ERRORS */
664 not_negotiated:
665   {
666     GST_WARNING_OBJECT (videorate, "no framerate negotiated");
667     gst_buffer_unref (buffer);
668     res = GST_FLOW_NOT_NEGOTIATED;
669     goto done;
670   }
671 }
672
673 static void
674 gst_video_rate_set_property (GObject * object,
675     guint prop_id, const GValue * value, GParamSpec * pspec)
676 {
677   GstVideoRate *videorate = GST_VIDEO_RATE (object);
678
679   switch (prop_id) {
680     case ARG_SILENT:
681       videorate->silent = g_value_get_boolean (value);
682       break;
683     case ARG_NEW_PREF:
684       videorate->new_pref = g_value_get_double (value);
685       break;
686     default:
687       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
688       break;
689   }
690 }
691
692 static void
693 gst_video_rate_get_property (GObject * object,
694     guint prop_id, GValue * value, GParamSpec * pspec)
695 {
696   GstVideoRate *videorate = GST_VIDEO_RATE (object);
697
698   switch (prop_id) {
699     case ARG_IN:
700       g_value_set_uint64 (value, videorate->in);
701       break;
702     case ARG_OUT:
703       g_value_set_uint64 (value, videorate->out);
704       break;
705     case ARG_DUP:
706       g_value_set_uint64 (value, videorate->dup);
707       break;
708     case ARG_DROP:
709       g_value_set_uint64 (value, videorate->drop);
710       break;
711     case ARG_SILENT:
712       g_value_set_boolean (value, videorate->silent);
713       break;
714     case ARG_NEW_PREF:
715       g_value_set_double (value, videorate->new_pref);
716       break;
717     default:
718       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
719       break;
720   }
721 }
722
723 static GstStateChangeReturn
724 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
725 {
726   GstStateChangeReturn ret;
727   GstVideoRate *videorate;
728
729   videorate = GST_VIDEO_RATE (element);
730
731   switch (transition) {
732     default:
733       break;
734   }
735
736   ret = parent_class->change_state (element, transition);
737
738   switch (transition) {
739     case GST_STATE_CHANGE_PAUSED_TO_READY:
740       gst_video_rate_reset (videorate);
741       break;
742     default:
743       break;
744   }
745
746   return ret;
747 }
748
749 static gboolean
750 plugin_init (GstPlugin * plugin)
751 {
752   GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
753       "VideoRate stream fixer");
754
755   return gst_element_register (plugin, "videorate", GST_RANK_NONE,
756       GST_TYPE_VIDEO_RATE);
757 }
758
759 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
760     GST_VERSION_MINOR,
761     "videorate",
762     "Adjusts video frames",
763     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)