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