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