Add underscore.
[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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <gst/gst.h>
25
26 GST_DEBUG_CATEGORY (videorate_debug);
27 #define GST_CAT_DEFAULT videorate_debug
28
29 #define GST_TYPE_VIDEORATE \
30   (gst_videorate_get_type())
31 #define GST_VIDEORATE(obj) \
32   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEORATE,GstVideorate))
33 #define GST_VIDEORATE_CLASS(klass) \
34   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEORATE,GstVideorate))
35 #define GST_IS_VIDEORATE(obj) \
36   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEORATE))
37 #define GST_IS_VIDEORATE_CLASS(obj) \
38   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEORATE))
39
40 typedef struct _GstVideorate GstVideorate;
41 typedef struct _GstVideorateClass GstVideorateClass;
42
43 struct _GstVideorate
44 {
45   GstElement element;
46
47   GstPad *sinkpad, *srcpad;
48
49   /* video state */
50   gint from_rate_numerator, from_rate_denominator;
51   gint to_rate_numerator, to_rate_denominator;
52   guint64 next_ts;              /* Timestamp of next buffer to output */
53   guint64 first_ts;             /* Timestamp of first buffer */
54   GstBuffer *prevbuf;
55   guint64 prev_ts;              /* Previous buffer timestamp */
56   guint64 in, out, dup, drop;
57
58   /* segment handling */
59   gint64 segment_start;
60   gint64 segment_stop;
61   gint64 segment_accum;
62
63   gboolean silent;
64   gdouble new_pref;
65 };
66
67 struct _GstVideorateClass
68 {
69   GstElementClass parent_class;
70 };
71
72 /* elementfactory information */
73 static GstElementDetails videorate_details =
74 GST_ELEMENT_DETAILS ("Video rate adjuster",
75     "Filter/Effect/Video",
76     "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
77     "Wim Taymans <wim@fluendo.com>");
78
79 /* GstVideorate signals and args */
80 enum
81 {
82   /* FILL ME */
83   LAST_SIGNAL
84 };
85
86 #define DEFAULT_SILENT          TRUE
87 #define DEFAULT_NEW_PREF        1.0
88
89 enum
90 {
91   ARG_0,
92   ARG_IN,
93   ARG_OUT,
94   ARG_DUP,
95   ARG_DROP,
96   ARG_SILENT,
97   ARG_NEW_PREF,
98   /* FILL ME */
99 };
100
101 static GstStaticPadTemplate gst_videorate_src_template =
102     GST_STATIC_PAD_TEMPLATE ("src",
103     GST_PAD_SRC,
104     GST_PAD_ALWAYS,
105     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
106     );
107
108 static GstStaticPadTemplate gst_videorate_sink_template =
109     GST_STATIC_PAD_TEMPLATE ("sink",
110     GST_PAD_SINK,
111     GST_PAD_ALWAYS,
112     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
113     );
114
115 static void gst_videorate_base_init (gpointer g_class);
116 static void gst_videorate_class_init (GstVideorateClass * klass);
117 static void gst_videorate_init (GstVideorate * videorate);
118 static gboolean gst_videorate_event (GstPad * pad, GstEvent * event);
119 static GstFlowReturn gst_videorate_chain (GstPad * pad, GstBuffer * buffer);
120
121 static void gst_videorate_set_property (GObject * object,
122     guint prop_id, const GValue * value, GParamSpec * pspec);
123 static void gst_videorate_get_property (GObject * object,
124     guint prop_id, GValue * value, GParamSpec * pspec);
125
126 static GstStateChangeReturn gst_videorate_change_state (GstElement * element,
127     GstStateChange transition);
128
129 static GstElementClass *parent_class = NULL;
130
131 /*static guint gst_videorate_signals[LAST_SIGNAL] = { 0 }; */
132
133 static GType
134 gst_videorate_get_type (void)
135 {
136   static GType videorate_type = 0;
137
138   if (!videorate_type) {
139     static const GTypeInfo videorate_info = {
140       sizeof (GstVideorateClass),
141       gst_videorate_base_init,
142       NULL,
143       (GClassInitFunc) gst_videorate_class_init,
144       NULL,
145       NULL,
146       sizeof (GstVideorate),
147       0,
148       (GInstanceInitFunc) gst_videorate_init,
149     };
150
151     videorate_type = g_type_register_static (GST_TYPE_ELEMENT,
152         "GstVideorate", &videorate_info, 0);
153   }
154
155   return videorate_type;
156 }
157
158 static void
159 gst_videorate_base_init (gpointer g_class)
160 {
161   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
162
163   gst_element_class_set_details (element_class, &videorate_details);
164
165   gst_element_class_add_pad_template (element_class,
166       gst_static_pad_template_get (&gst_videorate_sink_template));
167   gst_element_class_add_pad_template (element_class,
168       gst_static_pad_template_get (&gst_videorate_src_template));
169 }
170 static void
171 gst_videorate_class_init (GstVideorateClass * klass)
172 {
173   GObjectClass *object_class = G_OBJECT_CLASS (klass);
174   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
175
176   parent_class = g_type_class_peek_parent (klass);
177
178   object_class->set_property = gst_videorate_set_property;
179   object_class->get_property = gst_videorate_get_property;
180
181   g_object_class_install_property (object_class, ARG_IN,
182       g_param_spec_uint64 ("in", "In",
183           "Number of input frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
184   g_object_class_install_property (object_class, ARG_OUT,
185       g_param_spec_uint64 ("out", "Out",
186           "Number of output frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
187   g_object_class_install_property (object_class, ARG_DUP,
188       g_param_spec_uint64 ("duplicate", "Duplicate",
189           "Number of duplicated frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
190   g_object_class_install_property (object_class, ARG_DROP,
191       g_param_spec_uint64 ("drop", "Drop",
192           "Number of dropped frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
193   g_object_class_install_property (object_class, ARG_SILENT,
194       g_param_spec_boolean ("silent", "silent",
195           "Don't emit notify for dropped and duplicated frames",
196           DEFAULT_SILENT, G_PARAM_READWRITE));
197   g_object_class_install_property (object_class, ARG_NEW_PREF,
198       g_param_spec_double ("new_pref", "New Pref",
199           "Value indicating how much to prefer new frames",
200           0.0, 1.0, DEFAULT_NEW_PREF, G_PARAM_READWRITE));
201
202   element_class->change_state = gst_videorate_change_state;
203 }
204
205 /* return the caps that can be used on out_pad given in_caps on in_pad */
206 static gboolean
207 gst_videorate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
208     GstPad * out_pad, GstCaps ** out_caps)
209 {
210   GstCaps *intersect;
211   const GstCaps *in_templ;
212   gint i;
213
214   in_templ = gst_pad_get_pad_template_caps (in_pad);
215   intersect = gst_caps_intersect (in_caps, in_templ);
216
217   /* all possible framerates are allowed */
218   for (i = 0; i < gst_caps_get_size (intersect); i++) {
219     GstStructure *structure;
220
221     structure = gst_caps_get_structure (intersect, i);
222
223     gst_structure_set (structure,
224         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
225   }
226   *out_caps = intersect;
227
228   return TRUE;
229 }
230
231 static GstCaps *
232 gst_videorate_getcaps (GstPad * pad)
233 {
234   GstVideorate *videorate;
235   GstPad *otherpad;
236   GstCaps *caps;
237
238   videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
239
240   otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
241       videorate->srcpad;
242
243   /* we can do what the peer can */
244   caps = gst_pad_peer_get_caps (otherpad);
245   if (caps) {
246     GstCaps *transform;
247
248     gst_videorate_transformcaps (otherpad, caps, pad, &transform);
249     gst_caps_unref (caps);
250     caps = transform;
251   } else {
252     /* no peer, our padtemplate is enough then */
253     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
254   }
255
256   return caps;
257 }
258
259 static gboolean
260 gst_videorate_setcaps (GstPad * pad, GstCaps * caps)
261 {
262   GstVideorate *videorate;
263   GstStructure *structure;
264   gboolean ret = TRUE;
265   GstPad *otherpad, *opeer;
266   const GValue *rate;
267   gint rate_numerator, rate_denominator;
268
269   videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
270
271   structure = gst_caps_get_structure (caps, 0);
272   rate = gst_structure_get_value (structure, "framerate");
273   if (!rate)
274     goto done;
275
276   rate_numerator = gst_value_get_fraction_numerator (rate);
277   rate_denominator = gst_value_get_fraction_denominator (rate);
278
279   if (pad == videorate->srcpad) {
280     videorate->to_rate_numerator = rate_numerator;
281     videorate->to_rate_denominator = rate_denominator;
282     otherpad = videorate->sinkpad;
283   } else {
284     videorate->from_rate_numerator = rate_numerator;
285     videorate->from_rate_denominator = rate_denominator;
286     otherpad = videorate->srcpad;
287   }
288   /* now try to find something for the peer */
289   opeer = gst_pad_get_peer (otherpad);
290   if (opeer) {
291     if (gst_pad_accept_caps (opeer, caps)) {
292       /* the peer accepts the caps as they are */
293       gst_pad_set_caps (otherpad, caps);
294
295       ret = TRUE;
296     } else {
297       GstCaps *peercaps;
298       GstCaps *intersect;
299       GstCaps *transform = NULL;
300
301       ret = FALSE;
302
303       /* see how we can transform the input caps */
304       if (!gst_videorate_transformcaps (pad, caps, otherpad, &transform))
305         goto done;
306
307       /* see what the peer can do */
308       peercaps = gst_pad_get_caps (opeer);
309
310       GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
311       GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
312
313       /* filter against our possibilities */
314       intersect = gst_caps_intersect (peercaps, transform);
315       gst_caps_unref (peercaps);
316       gst_caps_unref (transform);
317
318       GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
319
320       /* take first possibility */
321       caps = gst_caps_copy_nth (intersect, 0);
322       gst_caps_unref (intersect);
323       structure = gst_caps_get_structure (caps, 0);
324
325       /* and fixate */
326       gst_structure_fixate_field_nearest_fraction (structure, "framerate",
327           rate);
328
329       rate = gst_structure_get_value (structure, "framerate");
330
331       rate_numerator = gst_value_get_fraction_numerator (rate);
332       rate_denominator = gst_value_get_fraction_denominator (rate);
333
334       if (otherpad == videorate->srcpad) {
335         videorate->to_rate_numerator = rate_numerator;
336         videorate->to_rate_denominator = rate_denominator;
337       } else {
338         videorate->from_rate_numerator = rate_numerator;
339         videorate->from_rate_denominator = rate_denominator;
340       }
341       gst_pad_set_caps (otherpad, caps);
342       ret = TRUE;
343     }
344     gst_object_unref (opeer);
345   }
346 done:
347   return ret;
348 }
349
350 static void
351 gst_videorate_blank_data (GstVideorate * videorate)
352 {
353   GST_DEBUG ("resetting data");
354   if (videorate->prevbuf)
355     gst_buffer_unref (videorate->prevbuf);
356   videorate->prevbuf = NULL;
357
358   videorate->from_rate_numerator = 0;
359   videorate->from_rate_denominator = 0;
360   videorate->to_rate_numerator = 0;
361   videorate->to_rate_denominator = 0;
362   videorate->in = 0;
363   videorate->out = 0;
364   videorate->drop = 0;
365   videorate->dup = 0;
366   videorate->next_ts = 0LL;
367   videorate->first_ts = 0LL;
368   videorate->prev_ts = 0LL;
369
370   videorate->segment_start = 0;
371   videorate->segment_stop = 0;
372   videorate->segment_accum = 0;
373 }
374
375 static void
376 gst_videorate_init (GstVideorate * videorate)
377 {
378   GST_DEBUG ("gst_videorate_init");
379   videorate->sinkpad =
380       gst_pad_new_from_static_template (&gst_videorate_sink_template, "sink");
381   gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
382   gst_pad_set_event_function (videorate->sinkpad, gst_videorate_event);
383   gst_pad_set_chain_function (videorate->sinkpad, gst_videorate_chain);
384   gst_pad_set_getcaps_function (videorate->sinkpad, gst_videorate_getcaps);
385   gst_pad_set_setcaps_function (videorate->sinkpad, gst_videorate_setcaps);
386
387   videorate->srcpad =
388       gst_pad_new_from_static_template (&gst_videorate_src_template, "src");
389   gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
390   gst_pad_set_getcaps_function (videorate->srcpad, gst_videorate_getcaps);
391   gst_pad_set_setcaps_function (videorate->srcpad, gst_videorate_setcaps);
392
393   gst_videorate_blank_data (videorate);
394   videorate->silent = DEFAULT_SILENT;
395   videorate->new_pref = DEFAULT_NEW_PREF;
396 }
397
398 static GstFlowReturn
399 gst_videorate_event (GstPad * pad, GstEvent * event)
400 {
401   GstVideorate *videorate;
402
403   videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
404
405   switch (GST_EVENT_TYPE (event)) {
406     case GST_EVENT_NEWSEGMENT:
407     {
408       gint64 start, stop, base;
409       gdouble rate;
410       gboolean update;
411       GstFormat format;
412
413       gst_event_parse_new_segment (event, &update, &rate, &format, &start,
414           &stop, &base);
415
416       if (format != GST_FORMAT_TIME) {
417         GST_WARNING ("Got discont but doesn't have GST_FORMAT_TIME value");
418       } else {
419         /* 
420            We just want to update the accumulated stream_time.
421          */
422         videorate->segment_accum +=
423             videorate->segment_stop - videorate->segment_start;
424         videorate->segment_start = start;
425         videorate->segment_stop = stop;
426         GST_DEBUG_OBJECT (videorate, "Updated segment_accum:%" GST_TIME_FORMAT
427             " segment_start:%" GST_TIME_FORMAT " segment_stop:%"
428             GST_TIME_FORMAT, GST_TIME_ARGS (videorate->segment_accum),
429             GST_TIME_ARGS (videorate->segment_start),
430             GST_TIME_ARGS (videorate->segment_stop));
431       }
432
433       break;
434     }
435     case GST_EVENT_FLUSH_STOP:
436     {
437       gst_videorate_blank_data (videorate);
438     }
439     default:
440       break;
441   }
442
443   return gst_pad_event_default (pad, event);
444 }
445
446 static GstFlowReturn
447 gst_videorate_chain (GstPad * pad, GstBuffer * buffer)
448 {
449   GstVideorate *videorate;
450   GstFlowReturn res = GST_FLOW_OK;
451
452   videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
453
454   if (videorate->from_rate_numerator == 0 ||
455       videorate->from_rate_denominator == 0 ||
456       videorate->to_rate_denominator == 0 || videorate->to_rate_numerator == 0)
457     return GST_FLOW_NOT_NEGOTIATED;
458
459   /* pull in 2 buffers */
460   if (videorate->prevbuf == NULL) {
461     /* We're sure it's a GstBuffer here */
462     videorate->prevbuf = buffer;
463     videorate->next_ts = videorate->first_ts = videorate->prev_ts =
464         GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
465         videorate->segment_accum;
466   } else {
467     GstClockTime prevtime, intime;
468     gint count = 0;
469     gint64 diff1, diff2;
470
471     prevtime = videorate->prev_ts;
472     intime =
473         GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
474         videorate->segment_accum;
475
476     GST_LOG_OBJECT (videorate,
477         "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
478         " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
479         GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
480
481     videorate->in++;
482
483     /* got 2 buffers, see which one is the best */
484     do {
485       diff1 = prevtime - videorate->next_ts;
486       diff2 = intime - videorate->next_ts;
487
488       /* take absolute values, beware: abs and ABS don't work for gint64 */
489       if (diff1 < 0)
490         diff1 = -diff1;
491       if (diff2 < 0)
492         diff2 = -diff2;
493
494       GST_LOG_OBJECT (videorate,
495           "diff with prev %" GST_TIME_FORMAT " diff with new %"
496           GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
497           GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
498           GST_TIME_ARGS (videorate->next_ts));
499
500       /* output first one when its the best */
501       if (diff1 < diff2) {
502         GstBuffer *outbuf;
503
504         count++;
505         outbuf =
506             gst_buffer_create_sub (videorate->prevbuf, 0,
507             GST_BUFFER_SIZE (videorate->prevbuf));
508         GST_BUFFER_TIMESTAMP (outbuf) = videorate->next_ts;
509         videorate->out++;
510         if (videorate->to_rate_numerator) {
511           videorate->next_ts =
512               videorate->first_ts +
513               gst_util_clock_time_scale (videorate->out * GST_SECOND,
514               videorate->to_rate_denominator, videorate->to_rate_numerator);
515           GST_BUFFER_DURATION (outbuf) =
516               videorate->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
517         }
518         /* adapt for looping */
519         GST_BUFFER_TIMESTAMP (outbuf) -= videorate->segment_accum;
520         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
521
522         GST_LOG_OBJECT (videorate,
523             "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
524             GST_TIME_ARGS (videorate->next_ts));
525
526         if ((res = gst_pad_push (videorate->srcpad, outbuf)) != GST_FLOW_OK) {
527           GST_WARNING_OBJECT (videorate, "couldn't push buffer on srcpad:%d",
528               res);
529           goto done;
530         }
531
532         GST_LOG_OBJECT (videorate,
533             "old is best, dup, pushed buffer outgoing ts %" GST_TIME_FORMAT,
534             GST_TIME_ARGS (videorate->next_ts));
535       }
536       /* continue while the first one was the best */
537     }
538     while (diff1 < diff2);
539
540     /* if we outputed the first buffer more then once, we have dups */
541     if (count > 1) {
542       videorate->dup += count - 1;
543       if (!videorate->silent)
544         g_object_notify (G_OBJECT (videorate), "duplicate");
545     }
546     /* if we didn't output the first buffer, we have a drop */
547     else if (count == 0) {
548       videorate->drop++;
549       if (!videorate->silent)
550         g_object_notify (G_OBJECT (videorate), "drop");
551       GST_LOG_OBJECT (videorate,
552           "new is best, old never used, drop, outgoing ts %"
553           GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
554     }
555     GST_LOG_OBJECT (videorate,
556         "END, putting new in old, diff1 %" GST_TIME_FORMAT
557         ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
558         ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1),
559         GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
560         videorate->in, videorate->out, videorate->drop, videorate->dup);
561
562     /* swap in new one when it's the best */
563     gst_buffer_unref (videorate->prevbuf);
564     videorate->prevbuf = buffer;
565     videorate->prev_ts =
566         GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
567         videorate->segment_accum;
568   }
569 done:
570
571   return res;
572 }
573
574 static void
575 gst_videorate_set_property (GObject * object,
576     guint prop_id, const GValue * value, GParamSpec * pspec)
577 {
578   GstVideorate *videorate = GST_VIDEORATE (object);
579
580   switch (prop_id) {
581     case ARG_SILENT:
582       videorate->silent = g_value_get_boolean (value);
583       break;
584     case ARG_NEW_PREF:
585       videorate->new_pref = g_value_get_double (value);
586       break;
587     default:
588       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
589       break;
590   }
591 }
592
593 static void
594 gst_videorate_get_property (GObject * object,
595     guint prop_id, GValue * value, GParamSpec * pspec)
596 {
597   GstVideorate *videorate = GST_VIDEORATE (object);
598
599   switch (prop_id) {
600     case ARG_IN:
601       g_value_set_uint64 (value, videorate->in);
602       break;
603     case ARG_OUT:
604       g_value_set_uint64 (value, videorate->out);
605       break;
606     case ARG_DUP:
607       g_value_set_uint64 (value, videorate->dup);
608       break;
609     case ARG_DROP:
610       g_value_set_uint64 (value, videorate->drop);
611       break;
612     case ARG_SILENT:
613       g_value_set_boolean (value, videorate->silent);
614       break;
615     case ARG_NEW_PREF:
616       g_value_set_double (value, videorate->new_pref);
617       break;
618     default:
619       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
620       break;
621   }
622 }
623
624 static GstStateChangeReturn
625 gst_videorate_change_state (GstElement * element, GstStateChange transition)
626 {
627   GstStateChangeReturn ret;
628   GstVideorate *videorate;
629
630   videorate = GST_VIDEORATE (element);
631
632   switch (transition) {
633     default:
634       break;
635   }
636
637   ret = parent_class->change_state (element, transition);
638
639   switch (transition) {
640     case GST_STATE_CHANGE_PAUSED_TO_READY:
641       gst_videorate_blank_data (videorate);
642       break;
643     default:
644       break;
645   }
646
647   return ret;
648 }
649
650 static gboolean
651 plugin_init (GstPlugin * plugin)
652 {
653   GST_DEBUG_CATEGORY_INIT (videorate_debug, "videorate", 0,
654       "Videorate stream fixer");
655
656   return gst_element_register (plugin, "videorate", GST_RANK_NONE,
657       GST_TYPE_VIDEORATE);
658 }
659
660 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
661     GST_VERSION_MINOR,
662     "videorate",
663     "Adjusts video frames",
664     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)