expand tabs
[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 (video_rate_debug);
27 #define GST_CAT_DEFAULT video_rate_debug
28
29 #define GST_TYPE_VIDEO_RATE \
30   (gst_video_rate_get_type())
31 #define GST_VIDEO_RATE(obj) \
32   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_RATE,GstVideoRate))
33 #define GST_VIDEO_RATE_CLASS(klass) \
34   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_RATE,GstVideoRate))
35 #define GST_IS_VIDEO_RATE(obj) \
36   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_RATE))
37 #define GST_IS_VIDEO_RATE_CLASS(obj) \
38   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_RATE))
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 video_rate_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_video_rate_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_video_rate_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_video_rate_base_init (gpointer g_class);
116 static void gst_video_rate_class_init (GstVideoRateClass * klass);
117 static void gst_video_rate_init (GstVideoRate * videorate);
118 static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
119 static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
120
121 static void gst_video_rate_set_property (GObject * object,
122     guint prop_id, const GValue * value, GParamSpec * pspec);
123 static void gst_video_rate_get_property (GObject * object,
124     guint prop_id, GValue * value, GParamSpec * pspec);
125
126 static GstStateChangeReturn gst_video_rate_change_state (GstElement * element,
127     GstStateChange transition);
128
129 static GstElementClass *parent_class = NULL;
130
131 /*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */
132
133 static GType
134 gst_video_rate_get_type (void)
135 {
136   static GType video_rate_type = 0;
137
138   if (!video_rate_type) {
139     static const GTypeInfo video_rate_info = {
140       sizeof (GstVideoRateClass),
141       gst_video_rate_base_init,
142       NULL,
143       (GClassInitFunc) gst_video_rate_class_init,
144       NULL,
145       NULL,
146       sizeof (GstVideoRate),
147       0,
148       (GInstanceInitFunc) gst_video_rate_init,
149     };
150
151     video_rate_type = g_type_register_static (GST_TYPE_ELEMENT,
152         "GstVideoRate", &video_rate_info, 0);
153   }
154
155   return video_rate_type;
156 }
157
158 static void
159 gst_video_rate_base_init (gpointer g_class)
160 {
161   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
162
163   gst_element_class_set_details (element_class, &video_rate_details);
164
165   gst_element_class_add_pad_template (element_class,
166       gst_static_pad_template_get (&gst_video_rate_sink_template));
167   gst_element_class_add_pad_template (element_class,
168       gst_static_pad_template_get (&gst_video_rate_src_template));
169 }
170 static void
171 gst_video_rate_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_video_rate_set_property;
179   object_class->get_property = gst_video_rate_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_video_rate_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_video_rate_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_video_rate_getcaps (GstPad * pad)
233 {
234   GstVideoRate *videorate;
235   GstPad *otherpad;
236   GstCaps *caps;
237
238   videorate = GST_VIDEO_RATE (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_video_rate_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_video_rate_setcaps (GstPad * pad, GstCaps * caps)
261 {
262   GstVideoRate *videorate;
263   GstStructure *structure;
264   gboolean ret = TRUE;
265   GstPad *otherpad, *opeer;
266   gint rate_numerator, rate_denominator;
267
268   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
269
270   structure = gst_caps_get_structure (caps, 0);
271   if (!gst_structure_get_fraction (structure, "framerate",
272           &rate_numerator, &rate_denominator))
273     goto done;
274
275   if (pad == videorate->srcpad) {
276     videorate->to_rate_numerator = rate_numerator;
277     videorate->to_rate_denominator = rate_denominator;
278     otherpad = videorate->sinkpad;
279   } else {
280     videorate->from_rate_numerator = rate_numerator;
281     videorate->from_rate_denominator = rate_denominator;
282     otherpad = videorate->srcpad;
283   }
284   /* now try to find something for the peer */
285   opeer = gst_pad_get_peer (otherpad);
286   if (opeer) {
287     if (gst_pad_accept_caps (opeer, caps)) {
288       /* the peer accepts the caps as they are */
289       gst_pad_set_caps (otherpad, caps);
290
291       ret = TRUE;
292     } else {
293       GstCaps *peercaps;
294       GstCaps *intersect;
295       GstCaps *transform = NULL;
296
297       ret = FALSE;
298
299       /* see how we can transform the input caps */
300       if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform))
301         goto done;
302
303       /* see what the peer can do */
304       peercaps = gst_pad_get_caps (opeer);
305
306       GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
307       GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
308
309       /* filter against our possibilities */
310       intersect = gst_caps_intersect (peercaps, transform);
311       gst_caps_unref (peercaps);
312       gst_caps_unref (transform);
313
314       GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
315
316       /* take first possibility */
317       caps = gst_caps_copy_nth (intersect, 0);
318       gst_caps_unref (intersect);
319       structure = gst_caps_get_structure (caps, 0);
320
321       /* and fixate */
322       gst_structure_fixate_field_nearest_fraction (structure, "framerate",
323           rate_numerator, rate_denominator);
324
325       gst_structure_get_fraction (structure, "framerate",
326           &rate_numerator, &rate_denominator);
327
328       if (otherpad == videorate->srcpad) {
329         videorate->to_rate_numerator = rate_numerator;
330         videorate->to_rate_denominator = rate_denominator;
331       } else {
332         videorate->from_rate_numerator = rate_numerator;
333         videorate->from_rate_denominator = rate_denominator;
334       }
335       gst_pad_set_caps (otherpad, caps);
336       ret = TRUE;
337     }
338     gst_object_unref (opeer);
339   }
340 done:
341   return ret;
342 }
343
344 static void
345 gst_video_rate_blank_data (GstVideoRate * videorate)
346 {
347   GST_DEBUG ("resetting data");
348   if (videorate->prevbuf)
349     gst_buffer_unref (videorate->prevbuf);
350   videorate->prevbuf = NULL;
351
352   videorate->from_rate_numerator = 0;
353   videorate->from_rate_denominator = 0;
354   videorate->to_rate_numerator = 0;
355   videorate->to_rate_denominator = 0;
356   videorate->in = 0;
357   videorate->out = 0;
358   videorate->drop = 0;
359   videorate->dup = 0;
360   videorate->next_ts = 0LL;
361   videorate->first_ts = 0LL;
362   videorate->prev_ts = 0LL;
363
364   videorate->segment_start = 0;
365   videorate->segment_stop = 0;
366   videorate->segment_accum = 0;
367 }
368
369 static void
370 gst_video_rate_init (GstVideoRate * videorate)
371 {
372   GST_DEBUG ("gst_video_rate_init");
373   videorate->sinkpad =
374       gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
375   gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
376   gst_pad_set_event_function (videorate->sinkpad, gst_video_rate_event);
377   gst_pad_set_chain_function (videorate->sinkpad, gst_video_rate_chain);
378   gst_pad_set_getcaps_function (videorate->sinkpad, gst_video_rate_getcaps);
379   gst_pad_set_setcaps_function (videorate->sinkpad, gst_video_rate_setcaps);
380
381   videorate->srcpad =
382       gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
383   gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
384   gst_pad_set_getcaps_function (videorate->srcpad, gst_video_rate_getcaps);
385   gst_pad_set_setcaps_function (videorate->srcpad, gst_video_rate_setcaps);
386
387   gst_video_rate_blank_data (videorate);
388   videorate->silent = DEFAULT_SILENT;
389   videorate->new_pref = DEFAULT_NEW_PREF;
390 }
391
392 static GstFlowReturn
393 gst_video_rate_event (GstPad * pad, GstEvent * event)
394 {
395   GstVideoRate *videorate;
396
397   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
398
399   switch (GST_EVENT_TYPE (event)) {
400     case GST_EVENT_NEWSEGMENT:
401     {
402       gint64 start, stop, base;
403       gdouble rate;
404       gboolean update;
405       GstFormat format;
406
407       gst_event_parse_new_segment (event, &update, &rate, &format, &start,
408           &stop, &base);
409
410       if (format != GST_FORMAT_TIME) {
411         GST_WARNING ("Got discont but doesn't have GST_FORMAT_TIME value");
412       } else {
413         /* 
414            We just want to update the accumulated stream_time.
415          */
416         videorate->segment_accum +=
417             videorate->segment_stop - videorate->segment_start;
418         videorate->segment_start = start;
419         videorate->segment_stop = stop;
420         GST_DEBUG_OBJECT (videorate, "Updated segment_accum:%" GST_TIME_FORMAT
421             " segment_start:%" GST_TIME_FORMAT " segment_stop:%"
422             GST_TIME_FORMAT, GST_TIME_ARGS (videorate->segment_accum),
423             GST_TIME_ARGS (videorate->segment_start),
424             GST_TIME_ARGS (videorate->segment_stop));
425       }
426
427       break;
428     }
429     case GST_EVENT_FLUSH_STOP:
430     {
431       gst_video_rate_blank_data (videorate);
432     }
433     default:
434       break;
435   }
436
437   return gst_pad_event_default (pad, event);
438 }
439
440 static GstFlowReturn
441 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
442 {
443   GstVideoRate *videorate;
444   GstFlowReturn res = GST_FLOW_OK;
445
446   videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
447
448   if (videorate->from_rate_numerator == 0 ||
449       videorate->from_rate_denominator == 0 ||
450       videorate->to_rate_denominator == 0 || videorate->to_rate_numerator == 0)
451     return GST_FLOW_NOT_NEGOTIATED;
452
453   /* pull in 2 buffers */
454   if (videorate->prevbuf == NULL) {
455     /* We're sure it's a GstBuffer here */
456     videorate->prevbuf = buffer;
457     videorate->next_ts = videorate->first_ts = videorate->prev_ts =
458         GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
459         videorate->segment_accum;
460   } else {
461     GstClockTime prevtime, intime;
462     gint count = 0;
463     gint64 diff1, diff2;
464
465     prevtime = videorate->prev_ts;
466     intime =
467         GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
468         videorate->segment_accum;
469
470     GST_LOG_OBJECT (videorate,
471         "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
472         " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
473         GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
474
475     videorate->in++;
476
477     /* got 2 buffers, see which one is the best */
478     do {
479       diff1 = prevtime - videorate->next_ts;
480       diff2 = intime - videorate->next_ts;
481
482       /* take absolute values, beware: abs and ABS don't work for gint64 */
483       if (diff1 < 0)
484         diff1 = -diff1;
485       if (diff2 < 0)
486         diff2 = -diff2;
487
488       GST_LOG_OBJECT (videorate,
489           "diff with prev %" GST_TIME_FORMAT " diff with new %"
490           GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
491           GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
492           GST_TIME_ARGS (videorate->next_ts));
493
494       /* output first one when its the best */
495       if (diff1 < diff2) {
496         GstBuffer *outbuf;
497
498         count++;
499         outbuf =
500             gst_buffer_create_sub (videorate->prevbuf, 0,
501             GST_BUFFER_SIZE (videorate->prevbuf));
502         GST_BUFFER_TIMESTAMP (outbuf) = videorate->next_ts;
503         videorate->out++;
504         if (videorate->to_rate_numerator) {
505           videorate->next_ts =
506               videorate->first_ts +
507               gst_util_uint64_scale_int (videorate->out * GST_SECOND,
508               videorate->to_rate_denominator, videorate->to_rate_numerator);
509           GST_BUFFER_DURATION (outbuf) =
510               videorate->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
511         }
512         /* adapt for looping */
513         GST_BUFFER_TIMESTAMP (outbuf) -= videorate->segment_accum;
514         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
515
516         GST_LOG_OBJECT (videorate,
517             "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
518             GST_TIME_ARGS (videorate->next_ts));
519
520         if ((res = gst_pad_push (videorate->srcpad, outbuf)) != GST_FLOW_OK) {
521           GST_WARNING_OBJECT (videorate, "couldn't push buffer on srcpad:%d",
522               res);
523           goto done;
524         }
525
526         GST_LOG_OBJECT (videorate,
527             "old is best, dup, pushed buffer outgoing ts %" GST_TIME_FORMAT,
528             GST_TIME_ARGS (videorate->next_ts));
529       }
530       /* continue while the first one was the best */
531     }
532     while (diff1 < diff2);
533
534     /* if we outputed the first buffer more then once, we have dups */
535     if (count > 1) {
536       videorate->dup += count - 1;
537       if (!videorate->silent)
538         g_object_notify (G_OBJECT (videorate), "duplicate");
539     }
540     /* if we didn't output the first buffer, we have a drop */
541     else if (count == 0) {
542       videorate->drop++;
543       if (!videorate->silent)
544         g_object_notify (G_OBJECT (videorate), "drop");
545       GST_LOG_OBJECT (videorate,
546           "new is best, old never used, drop, outgoing ts %"
547           GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
548     }
549     GST_LOG_OBJECT (videorate,
550         "END, putting new in old, diff1 %" GST_TIME_FORMAT
551         ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
552         ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1),
553         GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
554         videorate->in, videorate->out, videorate->drop, videorate->dup);
555
556     /* swap in new one when it's the best */
557     gst_buffer_unref (videorate->prevbuf);
558     videorate->prevbuf = buffer;
559     videorate->prev_ts =
560         GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
561         videorate->segment_accum;
562   }
563 done:
564
565   return res;
566 }
567
568 static void
569 gst_video_rate_set_property (GObject * object,
570     guint prop_id, const GValue * value, GParamSpec * pspec)
571 {
572   GstVideoRate *videorate = GST_VIDEO_RATE (object);
573
574   switch (prop_id) {
575     case ARG_SILENT:
576       videorate->silent = g_value_get_boolean (value);
577       break;
578     case ARG_NEW_PREF:
579       videorate->new_pref = g_value_get_double (value);
580       break;
581     default:
582       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
583       break;
584   }
585 }
586
587 static void
588 gst_video_rate_get_property (GObject * object,
589     guint prop_id, GValue * value, GParamSpec * pspec)
590 {
591   GstVideoRate *videorate = GST_VIDEO_RATE (object);
592
593   switch (prop_id) {
594     case ARG_IN:
595       g_value_set_uint64 (value, videorate->in);
596       break;
597     case ARG_OUT:
598       g_value_set_uint64 (value, videorate->out);
599       break;
600     case ARG_DUP:
601       g_value_set_uint64 (value, videorate->dup);
602       break;
603     case ARG_DROP:
604       g_value_set_uint64 (value, videorate->drop);
605       break;
606     case ARG_SILENT:
607       g_value_set_boolean (value, videorate->silent);
608       break;
609     case ARG_NEW_PREF:
610       g_value_set_double (value, videorate->new_pref);
611       break;
612     default:
613       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
614       break;
615   }
616 }
617
618 static GstStateChangeReturn
619 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
620 {
621   GstStateChangeReturn ret;
622   GstVideoRate *videorate;
623
624   videorate = GST_VIDEO_RATE (element);
625
626   switch (transition) {
627     default:
628       break;
629   }
630
631   ret = parent_class->change_state (element, transition);
632
633   switch (transition) {
634     case GST_STATE_CHANGE_PAUSED_TO_READY:
635       gst_video_rate_blank_data (videorate);
636       break;
637     default:
638       break;
639   }
640
641   return ret;
642 }
643
644 static gboolean
645 plugin_init (GstPlugin * plugin)
646 {
647   GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
648       "VideoRate stream fixer");
649
650   return gst_element_register (plugin, "videorate", GST_RANK_NONE,
651       GST_TYPE_VIDEO_RATE);
652 }
653
654 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
655     GST_VERSION_MINOR,
656     "videorate",
657     "Adjusts video frames",
658     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)