2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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.
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.
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.
25 #include <gst/video/video.h>
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))
38 typedef struct _GstVideorate GstVideorate;
39 typedef struct _GstVideorateClass GstVideorateClass;
45 GstPad *sinkpad, *srcpad;
48 gdouble from_fps, to_fps;
51 guint64 in, out, dup, drop;
57 struct _GstVideorateClass
59 GstElementClass parent_class;
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>");
69 /* GstVideorate signals and args */
76 #define DEFAULT_SILENT TRUE
77 #define DEFAULT_NEW_PREF 1.0
91 static GstStaticPadTemplate gst_videorate_src_template =
92 GST_STATIC_PAD_TEMPLATE ("src",
95 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ YUY2, I420, YV12, YUYV, UYVY }")
99 static GstStaticPadTemplate gst_videorate_sink_template =
100 GST_STATIC_PAD_TEMPLATE ("sink",
103 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ YUY2, I420, YV12, YUYV, UYVY }")
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);
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);
117 static GstElementStateReturn gst_videorate_change_state (GstElement * element);
119 static GstElementClass *parent_class = NULL;
121 /*static guint gst_videorate_signals[LAST_SIGNAL] = { 0 }; */
124 gst_videorate_get_type (void)
126 static GType videorate_type = 0;
128 if (!videorate_type) {
129 static const GTypeInfo videorate_info = {
130 sizeof (GstVideorateClass),
131 gst_videorate_base_init,
133 (GClassInitFunc) gst_videorate_class_init,
136 sizeof (GstVideorate),
138 (GInstanceInitFunc) gst_videorate_init,
141 videorate_type = g_type_register_static (GST_TYPE_ELEMENT,
142 "GstVideorate", &videorate_info, 0);
145 return videorate_type;
149 gst_videorate_base_init (gpointer g_class)
151 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
153 gst_element_class_set_details (element_class, &videorate_details);
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));
161 gst_videorate_class_init (GstVideorateClass * klass)
163 GObjectClass *object_class = G_OBJECT_CLASS (klass);
164 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
166 parent_class = g_type_class_peek_parent (klass);
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));
189 object_class->set_property = gst_videorate_set_property;
190 object_class->get_property = gst_videorate_get_property;
192 element_class->change_state = gst_videorate_change_state;
196 gst_videorate_getcaps (GstPad * pad)
198 GstVideorate *videorate;
200 GstCaps *caps, *copy, *copy2 = NULL;
203 GstStructure *structure;
206 videorate = GST_VIDEORATE (gst_pad_get_parent (pad));
208 otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
210 negotiated = gst_pad_is_negotiated (otherpad);
211 otherfps = (pad == videorate->srcpad) ? videorate->from_fps :
214 caps = gst_pad_get_allowed_caps (otherpad);
215 copy = gst_caps_copy (caps);
217 copy2 = gst_caps_copy (caps);
219 for (i = 0; i < gst_caps_get_size (caps); i++) {
220 structure = gst_caps_get_structure (caps, i);
222 gst_structure_set (structure,
223 "framerate", GST_TYPE_DOUBLE_RANGE, 0.0, G_MAXDOUBLE, NULL);
226 for (i = 0; i < gst_caps_get_size (copy2); i++) {
227 structure = gst_caps_get_structure (copy2, i);
229 gst_structure_set (structure, "framerate", G_TYPE_DOUBLE, otherfps, NULL);
231 gst_caps_append (copy2, copy);
234 gst_caps_append (copy, caps);
239 static GstPadLinkReturn
240 gst_videorate_link (GstPad * pad, const GstCaps * caps)
242 GstVideorate *videorate;
243 GstStructure *structure;
248 videorate = GST_VIDEORATE (gst_pad_get_parent (pad));
250 otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
253 structure = gst_caps_get_structure (caps, 0);
254 ret = gst_structure_get_double (structure, "framerate", &fps);
256 return GST_PAD_LINK_REFUSED;
258 if (pad == videorate->srcpad) {
259 videorate->to_fps = fps;
261 videorate->from_fps = fps;
264 if (gst_pad_is_negotiated (otherpad)) {
266 * Ensure that the other side talks the format we're trying to set
268 GstCaps *newcaps = gst_caps_copy (caps);
270 if (pad == videorate->srcpad) {
271 gst_caps_set_simple (newcaps,
272 "framerate", G_TYPE_DOUBLE, videorate->from_fps, NULL);
274 gst_caps_set_simple (newcaps,
275 "framerate", G_TYPE_DOUBLE, videorate->to_fps, NULL);
277 ret = gst_pad_try_set_caps (otherpad, newcaps);
278 if (GST_PAD_LINK_FAILED (ret)) {
279 return GST_PAD_LINK_REFUSED;
283 return GST_PAD_LINK_OK;
287 gst_videorate_init (GstVideorate * videorate)
289 GST_FLAG_SET (videorate, GST_ELEMENT_EVENT_AWARE);
291 GST_DEBUG ("gst_videorate_init");
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);
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);
307 videorate->prevbuf = NULL;
312 videorate->silent = DEFAULT_SILENT;
313 videorate->new_pref = DEFAULT_NEW_PREF;
317 gst_videorate_chain (GstPad * pad, GstData * data)
319 GstVideorate *videorate = GST_VIDEORATE (gst_pad_get_parent (pad));
322 if (GST_IS_EVENT (data)) {
323 GstEvent *event = GST_EVENT (data);
325 gst_pad_event_default (pad, event);
329 buf = GST_BUFFER (data);
331 /* pull in 2 buffers */
332 if (videorate->prevbuf == NULL) {
333 videorate->prevbuf = buf;
335 GstClockTime prevtime, intime;
339 prevtime = GST_BUFFER_TIMESTAMP (videorate->prevbuf);
340 intime = GST_BUFFER_TIMESTAMP (buf);
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));
349 /* got 2 buffers, see which one is the best */
351 diff1 = ABS (prevtime - videorate->next_ts);
352 diff2 = ABS (intime - videorate->next_ts);
354 /* take absolute values, beware: abs and ABS don't work for gint64 */
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));
366 /* output first one when its the best */
367 if (diff1 <= diff2) {
372 gst_buffer_create_sub (videorate->prevbuf, 0,
373 GST_BUFFER_SIZE (videorate->prevbuf));
374 GST_BUFFER_TIMESTAMP (outbuf) = videorate->next_ts;
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));
381 GST_LOG_OBJECT (videorate,
382 "videorate: old is best, dup, outgoing ts %" GST_TIME_FORMAT " \n",
383 GST_TIME_ARGS (videorate->next_ts));
385 /* continue while the first one was the best */
387 while (diff1 <= diff2);
389 /* if we outputed the first buffer more then once, we have dups */
391 videorate->dup += count - 1;
392 if (!videorate->silent)
393 g_object_notify (G_OBJECT (videorate), "duplicate");
395 /* if we didn't output the first buffer, we have a drop */
396 else if (count == 0) {
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));
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,
411 /* swap in new one when it's the best */
412 gst_buffer_unref (videorate->prevbuf);
413 videorate->prevbuf = buf;
418 gst_videorate_set_property (GObject * object,
419 guint prop_id, const GValue * value, GParamSpec * pspec)
421 GstVideorate *videorate = GST_VIDEORATE (object);
425 videorate->silent = g_value_get_boolean (value);
428 videorate->new_pref = g_value_get_double (value);
431 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
437 gst_videorate_get_property (GObject * object,
438 guint prop_id, GValue * value, GParamSpec * pspec)
440 GstVideorate *videorate = GST_VIDEORATE (object);
444 g_value_set_uint64 (value, videorate->in);
447 g_value_set_uint64 (value, videorate->out);
450 g_value_set_uint64 (value, videorate->dup);
453 g_value_set_uint64 (value, videorate->drop);
456 g_value_set_boolean (value, videorate->silent);
459 g_value_set_double (value, videorate->new_pref);
462 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
467 static GstElementStateReturn
468 gst_videorate_change_state (GstElement * element)
470 //GstVideorate *videorate = GST_VIDEORATE (element);
472 switch (GST_STATE_TRANSITION (element)) {
473 case GST_STATE_PAUSED_TO_READY:
479 if (parent_class->change_state)
480 return parent_class->change_state (element);
482 return GST_STATE_SUCCESS;
486 plugin_init (GstPlugin * plugin)
488 return gst_element_register (plugin, "videorate", GST_RANK_NONE,
492 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
495 "Adjusts video frames",
496 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)