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