gst/: Added some logging, fixed an overflow bug in videorate.
[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 #include <gst/video/video.h>
26
27 #define GST_TYPE_VIDEORATE \
28   (gst_videorate_get_type())
29 #define GST_VIDEORATE(obj) \
30   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEORATE,GstVideorate))
31 #define GST_VIDEORATE_CLASS(klass) \
32   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEORATE,GstVideorate))
33 #define GST_IS_VIDEORATE(obj) \
34   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEORATE))
35 #define GST_IS_VIDEORATE_CLASS(obj) \
36   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEORATE))
37
38 typedef struct _GstVideorate GstVideorate;
39 typedef struct _GstVideorateClass GstVideorateClass;
40
41 struct _GstVideorate
42 {
43   GstElement element;
44
45   GstPad *sinkpad, *srcpad;
46
47   /* video state */
48   gdouble from_fps, to_fps;
49   guint64 next_ts;
50   GstBuffer *prevbuf;
51   guint64 in, out, dup, drop;
52
53   gboolean silent;
54   gdouble new_pref;
55 };
56
57 struct _GstVideorateClass
58 {
59   GstElementClass parent_class;
60 };
61
62 /* elementfactory information */
63 static GstElementDetails videorate_details =
64 GST_ELEMENT_DETAILS ("Video rate adjuster",
65     "Filter/Effect/Video",
66     "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
67     "Wim Taymans <wim@fluendo.com>");
68
69 /* GstVideorate signals and args */
70 enum
71 {
72   /* FILL ME */
73   LAST_SIGNAL
74 };
75
76 #define DEFAULT_SILENT          TRUE
77 #define DEFAULT_NEW_PREF        1.0
78
79 enum
80 {
81   ARG_0,
82   ARG_IN,
83   ARG_OUT,
84   ARG_DUP,
85   ARG_DROP,
86   ARG_SILENT,
87   ARG_NEW_PREF,
88   /* FILL ME */
89 };
90
91 static GstStaticPadTemplate gst_videorate_src_template =
92 GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ YUY2, I420, YV12, YUYV, UYVY }")
96     )
97     );
98
99 static GstStaticPadTemplate gst_videorate_sink_template =
100 GST_STATIC_PAD_TEMPLATE ("sink",
101     GST_PAD_SINK,
102     GST_PAD_ALWAYS,
103     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ YUY2, I420, YV12, YUYV, UYVY }")
104     )
105     );
106
107 static void gst_videorate_base_init (gpointer g_class);
108 static void gst_videorate_class_init (GstVideorateClass * klass);
109 static void gst_videorate_init (GstVideorate * videorate);
110 static void gst_videorate_chain (GstPad * pad, GstData * _data);
111
112 static void gst_videorate_set_property (GObject * object,
113     guint prop_id, const GValue * value, GParamSpec * pspec);
114 static void gst_videorate_get_property (GObject * object,
115     guint prop_id, GValue * value, GParamSpec * pspec);
116
117 static GstElementStateReturn gst_videorate_change_state (GstElement * element);
118
119 static GstElementClass *parent_class = NULL;
120
121 /*static guint gst_videorate_signals[LAST_SIGNAL] = { 0 }; */
122
123 static GType
124 gst_videorate_get_type (void)
125 {
126   static GType videorate_type = 0;
127
128   if (!videorate_type) {
129     static const GTypeInfo videorate_info = {
130       sizeof (GstVideorateClass),
131       gst_videorate_base_init,
132       NULL,
133       (GClassInitFunc) gst_videorate_class_init,
134       NULL,
135       NULL,
136       sizeof (GstVideorate),
137       0,
138       (GInstanceInitFunc) gst_videorate_init,
139     };
140
141     videorate_type = g_type_register_static (GST_TYPE_ELEMENT,
142         "GstVideorate", &videorate_info, 0);
143   }
144
145   return videorate_type;
146 }
147
148 static void
149 gst_videorate_base_init (gpointer g_class)
150 {
151   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
152
153   gst_element_class_set_details (element_class, &videorate_details);
154
155   gst_element_class_add_pad_template (element_class,
156       gst_static_pad_template_get (&gst_videorate_sink_template));
157   gst_element_class_add_pad_template (element_class,
158       gst_static_pad_template_get (&gst_videorate_src_template));
159 }
160 static void
161 gst_videorate_class_init (GstVideorateClass * klass)
162 {
163   GObjectClass *object_class = G_OBJECT_CLASS (klass);
164   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
165
166   parent_class = g_type_class_peek_parent (klass);
167
168   g_object_class_install_property (object_class, ARG_IN,
169       g_param_spec_uint64 ("in", "In",
170           "Number of input frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
171   g_object_class_install_property (object_class, ARG_OUT,
172       g_param_spec_uint64 ("out", "Out",
173           "Number of output frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
174   g_object_class_install_property (object_class, ARG_DUP,
175       g_param_spec_uint64 ("duplicate", "Duplicate",
176           "Number of duplicated frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
177   g_object_class_install_property (object_class, ARG_DROP,
178       g_param_spec_uint64 ("drop", "Drop",
179           "Number of dropped frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
180   g_object_class_install_property (object_class, ARG_SILENT,
181       g_param_spec_boolean ("silent", "silent",
182           "Don't emit notify for dropped and duplicated frames",
183           DEFAULT_SILENT, G_PARAM_READWRITE));
184   g_object_class_install_property (object_class, ARG_NEW_PREF,
185       g_param_spec_double ("new_pref", "New Pref",
186           "Value indicating how much to prefer new frames",
187           0.0, 1.0, DEFAULT_NEW_PREF, G_PARAM_READWRITE));
188
189   object_class->set_property = gst_videorate_set_property;
190   object_class->get_property = gst_videorate_get_property;
191
192   element_class->change_state = gst_videorate_change_state;
193 }
194
195 static GstCaps *
196 gst_videorate_getcaps (GstPad * pad)
197 {
198   GstVideorate *videorate;
199   GstPad *otherpad;
200   GstCaps *caps, *copy, *copy2 = NULL;
201   int i;
202   gdouble otherfps;
203   GstStructure *structure;
204   gboolean negotiated;
205
206   videorate = GST_VIDEORATE (gst_pad_get_parent (pad));
207
208   otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
209       videorate->srcpad;
210   negotiated = gst_pad_is_negotiated (otherpad);
211   otherfps = (pad == videorate->srcpad) ? videorate->from_fps :
212       videorate->to_fps;
213
214   caps = gst_pad_get_allowed_caps (otherpad);
215   copy = gst_caps_copy (caps);
216   if (negotiated) {
217     copy2 = gst_caps_copy (caps);
218   }
219   for (i = 0; i < gst_caps_get_size (caps); i++) {
220     structure = gst_caps_get_structure (caps, i);
221
222     gst_structure_set (structure,
223         "framerate", GST_TYPE_DOUBLE_RANGE, 0.0, G_MAXDOUBLE, NULL);
224   }
225   if ((negotiated)) {
226     for (i = 0; i < gst_caps_get_size (copy2); i++) {
227       structure = gst_caps_get_structure (copy2, i);
228
229       gst_structure_set (structure, "framerate", G_TYPE_DOUBLE, otherfps, NULL);
230     }
231     gst_caps_append (copy2, copy);
232     copy = copy2;
233   }
234   gst_caps_append (copy, caps);
235
236   return copy;
237 }
238
239 static GstPadLinkReturn
240 gst_videorate_link (GstPad * pad, const GstCaps * caps)
241 {
242   GstVideorate *videorate;
243   GstStructure *structure;
244   gboolean ret;
245   double fps;
246   GstPad *otherpad;
247
248   videorate = GST_VIDEORATE (gst_pad_get_parent (pad));
249
250   otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
251       videorate->srcpad;
252
253   structure = gst_caps_get_structure (caps, 0);
254   ret = gst_structure_get_double (structure, "framerate", &fps);
255   if (!ret)
256     return GST_PAD_LINK_REFUSED;
257
258   if (pad == videorate->srcpad) {
259     videorate->to_fps = fps;
260   } else {
261     videorate->from_fps = fps;
262   }
263
264   if (gst_pad_is_negotiated (otherpad)) {
265     /* 
266      * Ensure that the other side talks the format we're trying to set
267      */
268     GstCaps *newcaps = gst_caps_copy (caps);
269
270     if (pad == videorate->srcpad) {
271       gst_caps_set_simple (newcaps,
272           "framerate", G_TYPE_DOUBLE, videorate->from_fps, NULL);
273     } else {
274       gst_caps_set_simple (newcaps,
275           "framerate", G_TYPE_DOUBLE, videorate->to_fps, NULL);
276     }
277     ret = gst_pad_try_set_caps (otherpad, newcaps);
278     if (GST_PAD_LINK_FAILED (ret)) {
279       return GST_PAD_LINK_REFUSED;
280     }
281   }
282
283   return GST_PAD_LINK_OK;
284 }
285
286 static void
287 gst_videorate_init (GstVideorate * videorate)
288 {
289   GST_FLAG_SET (videorate, GST_ELEMENT_EVENT_AWARE);
290
291   GST_DEBUG ("gst_videorate_init");
292   videorate->sinkpad =
293       gst_pad_new_from_template (gst_static_pad_template_get
294       (&gst_videorate_sink_template), "sink");
295   gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
296   gst_pad_set_chain_function (videorate->sinkpad, gst_videorate_chain);
297   gst_pad_set_getcaps_function (videorate->sinkpad, gst_videorate_getcaps);
298   gst_pad_set_link_function (videorate->sinkpad, gst_videorate_link);
299
300   videorate->srcpad =
301       gst_pad_new_from_template (gst_static_pad_template_get
302       (&gst_videorate_src_template), "src");
303   gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
304   gst_pad_set_getcaps_function (videorate->srcpad, gst_videorate_getcaps);
305   gst_pad_set_link_function (videorate->srcpad, gst_videorate_link);
306
307   videorate->prevbuf = NULL;
308   videorate->in = 0;
309   videorate->out = 0;
310   videorate->drop = 0;
311   videorate->dup = 0;
312   videorate->silent = DEFAULT_SILENT;
313   videorate->new_pref = DEFAULT_NEW_PREF;
314 }
315
316 static void
317 gst_videorate_chain (GstPad * pad, GstData * data)
318 {
319   GstVideorate *videorate = GST_VIDEORATE (gst_pad_get_parent (pad));
320   GstBuffer *buf;
321
322   if (GST_IS_EVENT (data)) {
323     GstEvent *event = GST_EVENT (data);
324
325     gst_pad_event_default (pad, event);
326     return;
327   }
328
329   buf = GST_BUFFER (data);
330
331   /* pull in 2 buffers */
332   if (videorate->prevbuf == NULL) {
333     videorate->prevbuf = buf;
334   } else {
335     GstClockTime prevtime, intime;
336     gint count = 0;
337     gint64 diff1, diff2;
338
339     prevtime = GST_BUFFER_TIMESTAMP (videorate->prevbuf);
340     intime = GST_BUFFER_TIMESTAMP (buf);
341
342     GST_LOG_OBJECT (videorate,
343         "videorate: prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
344         " outgoing ts %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (prevtime),
345         GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
346
347     videorate->in++;
348
349     /* got 2 buffers, see which one is the best */
350     do {
351       diff1 = ABS (prevtime - videorate->next_ts);
352       diff2 = ABS (intime - videorate->next_ts);
353
354       /* take absolute values, beware: abs and ABS don't work for gint64 */
355       if (diff1 < 0)
356         diff1 = -diff1;
357       if (diff2 < 0)
358         diff2 = -diff2;
359
360       GST_LOG_OBJECT (videorate,
361           "videorate: diff with prev %" GST_TIME_FORMAT " diff with new %"
362           GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT "\n",
363           GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
364           GST_TIME_ARGS (videorate->next_ts));
365
366       /* output first one when its the best */
367       if (diff1 <= diff2) {
368         GstBuffer *outbuf;
369
370         count++;
371         outbuf =
372             gst_buffer_create_sub (videorate->prevbuf, 0,
373             GST_BUFFER_SIZE (videorate->prevbuf));
374         GST_BUFFER_TIMESTAMP (outbuf) = videorate->next_ts;
375         videorate->out++;
376         videorate->next_ts = videorate->out / videorate->to_fps * GST_SECOND;
377         GST_BUFFER_DURATION (outbuf) =
378             videorate->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
379         gst_pad_push (videorate->srcpad, GST_DATA (outbuf));
380
381         GST_LOG_OBJECT (videorate,
382             "videorate: old is best, dup, outgoing ts %" GST_TIME_FORMAT " \n",
383             GST_TIME_ARGS (videorate->next_ts));
384       }
385       /* continue while the first one was the best */
386     }
387     while (diff1 <= diff2);
388
389     /* if we outputed the first buffer more then once, we have dups */
390     if (count > 1) {
391       videorate->dup += count - 1;
392       if (!videorate->silent)
393         g_object_notify (G_OBJECT (videorate), "duplicate");
394     }
395     /* if we didn't output the first buffer, we have a drop */
396     else if (count == 0) {
397       videorate->drop++;
398       if (!videorate->silent)
399         g_object_notify (G_OBJECT (videorate), "drop");
400       GST_LOG_OBJECT (videorate,
401           "videorate: new is best, old never used, drop, outgoing ts %"
402           GST_TIME_FORMAT " \n", GST_TIME_ARGS (videorate->next_ts));
403     }
404     GST_LOG_OBJECT (videorate,
405         "videorate: left loop, putting new in old, diff1 %" GST_TIME_FORMAT
406         ", diff2 %" GST_TIME_FORMAT
407         ", in %lld, out %lld, drop %lld, dup %lld\n", GST_TIME_ARGS (diff1),
408         GST_TIME_ARGS (diff2), videorate->in, videorate->out, videorate->drop,
409         videorate->dup);
410
411     /* swap in new one when it's the best */
412     gst_buffer_unref (videorate->prevbuf);
413     videorate->prevbuf = buf;
414   }
415 }
416
417 static void
418 gst_videorate_set_property (GObject * object,
419     guint prop_id, const GValue * value, GParamSpec * pspec)
420 {
421   GstVideorate *videorate = GST_VIDEORATE (object);
422
423   switch (prop_id) {
424     case ARG_SILENT:
425       videorate->silent = g_value_get_boolean (value);
426       break;
427     case ARG_NEW_PREF:
428       videorate->new_pref = g_value_get_double (value);
429       break;
430     default:
431       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
432       break;
433   }
434 }
435
436 static void
437 gst_videorate_get_property (GObject * object,
438     guint prop_id, GValue * value, GParamSpec * pspec)
439 {
440   GstVideorate *videorate = GST_VIDEORATE (object);
441
442   switch (prop_id) {
443     case ARG_IN:
444       g_value_set_uint64 (value, videorate->in);
445       break;
446     case ARG_OUT:
447       g_value_set_uint64 (value, videorate->out);
448       break;
449     case ARG_DUP:
450       g_value_set_uint64 (value, videorate->dup);
451       break;
452     case ARG_DROP:
453       g_value_set_uint64 (value, videorate->drop);
454       break;
455     case ARG_SILENT:
456       g_value_set_boolean (value, videorate->silent);
457       break;
458     case ARG_NEW_PREF:
459       g_value_set_double (value, videorate->new_pref);
460       break;
461     default:
462       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
463       break;
464   }
465 }
466
467 static GstElementStateReturn
468 gst_videorate_change_state (GstElement * element)
469 {
470   //GstVideorate *videorate = GST_VIDEORATE (element);
471
472   switch (GST_STATE_TRANSITION (element)) {
473     case GST_STATE_PAUSED_TO_READY:
474       break;
475     default:
476       break;
477   }
478
479   if (parent_class->change_state)
480     return parent_class->change_state (element);
481
482   return GST_STATE_SUCCESS;
483 }
484
485 static gboolean
486 plugin_init (GstPlugin * plugin)
487 {
488   return gst_element_register (plugin, "videorate", GST_RANK_NONE,
489       GST_TYPE_VIDEORATE);
490 }
491
492 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
493     GST_VERSION_MINOR,
494     "videorate",
495     "Adjusts video frames",
496     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)