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