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.
26 GST_DEBUG_CATEGORY (videorate_debug);
27 #define GST_CAT_DEFAULT videorate_debug
29 #define GST_TYPE_VIDEORATE \
30 (gst_videorate_get_type())
31 #define GST_VIDEORATE(obj) \
32 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEORATE,GstVideorate))
33 #define GST_VIDEORATE_CLASS(klass) \
34 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEORATE,GstVideorate))
35 #define GST_IS_VIDEORATE(obj) \
36 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEORATE))
37 #define GST_IS_VIDEORATE_CLASS(obj) \
38 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEORATE))
40 typedef struct _GstVideorate GstVideorate;
41 typedef struct _GstVideorateClass GstVideorateClass;
47 GstPad *sinkpad, *srcpad;
50 gint from_rate_numerator, from_rate_denominator;
51 gint to_rate_numerator, to_rate_denominator;
52 guint64 next_ts; /* Timestamp of next buffer to output */
53 guint64 first_ts; /* Timestamp of first buffer */
55 guint64 prev_ts; /* Previous buffer timestamp */
56 guint64 in, out, dup, drop;
58 /* segment handling */
67 struct _GstVideorateClass
69 GstElementClass parent_class;
72 /* elementfactory information */
73 static GstElementDetails videorate_details =
74 GST_ELEMENT_DETAILS ("Video rate adjuster",
75 "Filter/Effect/Video",
76 "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
77 "Wim Taymans <wim@fluendo.com>");
79 /* GstVideorate signals and args */
86 #define DEFAULT_SILENT TRUE
87 #define DEFAULT_NEW_PREF 1.0
101 static GstStaticPadTemplate gst_videorate_src_template =
102 GST_STATIC_PAD_TEMPLATE ("src",
105 GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
108 static GstStaticPadTemplate gst_videorate_sink_template =
109 GST_STATIC_PAD_TEMPLATE ("sink",
112 GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
115 static void gst_videorate_base_init (gpointer g_class);
116 static void gst_videorate_class_init (GstVideorateClass * klass);
117 static void gst_videorate_init (GstVideorate * videorate);
118 static gboolean gst_videorate_event (GstPad * pad, GstEvent * event);
119 static GstFlowReturn gst_videorate_chain (GstPad * pad, GstBuffer * buffer);
121 static void gst_videorate_set_property (GObject * object,
122 guint prop_id, const GValue * value, GParamSpec * pspec);
123 static void gst_videorate_get_property (GObject * object,
124 guint prop_id, GValue * value, GParamSpec * pspec);
126 static GstStateChangeReturn gst_videorate_change_state (GstElement * element,
127 GstStateChange transition);
129 static GstElementClass *parent_class = NULL;
131 /*static guint gst_videorate_signals[LAST_SIGNAL] = { 0 }; */
134 gst_videorate_get_type (void)
136 static GType videorate_type = 0;
138 if (!videorate_type) {
139 static const GTypeInfo videorate_info = {
140 sizeof (GstVideorateClass),
141 gst_videorate_base_init,
143 (GClassInitFunc) gst_videorate_class_init,
146 sizeof (GstVideorate),
148 (GInstanceInitFunc) gst_videorate_init,
151 videorate_type = g_type_register_static (GST_TYPE_ELEMENT,
152 "GstVideorate", &videorate_info, 0);
155 return videorate_type;
159 gst_videorate_base_init (gpointer g_class)
161 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
163 gst_element_class_set_details (element_class, &videorate_details);
165 gst_element_class_add_pad_template (element_class,
166 gst_static_pad_template_get (&gst_videorate_sink_template));
167 gst_element_class_add_pad_template (element_class,
168 gst_static_pad_template_get (&gst_videorate_src_template));
171 gst_videorate_class_init (GstVideorateClass * klass)
173 GObjectClass *object_class = G_OBJECT_CLASS (klass);
174 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
176 parent_class = g_type_class_peek_parent (klass);
178 object_class->set_property = gst_videorate_set_property;
179 object_class->get_property = gst_videorate_get_property;
181 g_object_class_install_property (object_class, ARG_IN,
182 g_param_spec_uint64 ("in", "In",
183 "Number of input frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
184 g_object_class_install_property (object_class, ARG_OUT,
185 g_param_spec_uint64 ("out", "Out",
186 "Number of output frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
187 g_object_class_install_property (object_class, ARG_DUP,
188 g_param_spec_uint64 ("duplicate", "Duplicate",
189 "Number of duplicated frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
190 g_object_class_install_property (object_class, ARG_DROP,
191 g_param_spec_uint64 ("drop", "Drop",
192 "Number of dropped frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
193 g_object_class_install_property (object_class, ARG_SILENT,
194 g_param_spec_boolean ("silent", "silent",
195 "Don't emit notify for dropped and duplicated frames",
196 DEFAULT_SILENT, G_PARAM_READWRITE));
197 g_object_class_install_property (object_class, ARG_NEW_PREF,
198 g_param_spec_double ("new_pref", "New Pref",
199 "Value indicating how much to prefer new frames",
200 0.0, 1.0, DEFAULT_NEW_PREF, G_PARAM_READWRITE));
202 element_class->change_state = gst_videorate_change_state;
205 /* return the caps that can be used on out_pad given in_caps on in_pad */
207 gst_videorate_transformcaps (GstPad * in_pad, GstCaps * in_caps,
208 GstPad * out_pad, GstCaps ** out_caps)
211 const GstCaps *in_templ;
214 in_templ = gst_pad_get_pad_template_caps (in_pad);
215 intersect = gst_caps_intersect (in_caps, in_templ);
217 /* all possible framerates are allowed */
218 for (i = 0; i < gst_caps_get_size (intersect); i++) {
219 GstStructure *structure;
221 structure = gst_caps_get_structure (intersect, i);
223 gst_structure_set (structure,
224 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
226 *out_caps = intersect;
232 gst_videorate_getcaps (GstPad * pad)
234 GstVideorate *videorate;
238 videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
240 otherpad = (pad == videorate->srcpad) ? videorate->sinkpad :
243 /* we can do what the peer can */
244 caps = gst_pad_peer_get_caps (otherpad);
248 gst_videorate_transformcaps (otherpad, caps, pad, &transform);
249 gst_caps_unref (caps);
252 /* no peer, our padtemplate is enough then */
253 caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
260 gst_videorate_setcaps (GstPad * pad, GstCaps * caps)
262 GstVideorate *videorate;
263 GstStructure *structure;
265 GstPad *otherpad, *opeer;
267 gint rate_numerator, rate_denominator;
269 videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
271 structure = gst_caps_get_structure (caps, 0);
272 rate = gst_structure_get_value (structure, "framerate");
276 rate_numerator = gst_value_get_fraction_numerator (rate);
277 rate_denominator = gst_value_get_fraction_denominator (rate);
279 if (pad == videorate->srcpad) {
280 videorate->to_rate_numerator = rate_numerator;
281 videorate->to_rate_denominator = rate_denominator;
282 otherpad = videorate->sinkpad;
284 videorate->from_rate_numerator = rate_numerator;
285 videorate->from_rate_denominator = rate_denominator;
286 otherpad = videorate->srcpad;
288 /* now try to find something for the peer */
289 opeer = gst_pad_get_peer (otherpad);
291 if (gst_pad_accept_caps (opeer, caps)) {
292 /* the peer accepts the caps as they are */
293 gst_pad_set_caps (otherpad, caps);
299 GstCaps *transform = NULL;
303 /* see how we can transform the input caps */
304 if (!gst_videorate_transformcaps (pad, caps, otherpad, &transform))
307 /* see what the peer can do */
308 peercaps = gst_pad_get_caps (opeer);
310 GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
311 GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
313 /* filter against our possibilities */
314 intersect = gst_caps_intersect (peercaps, transform);
315 gst_caps_unref (peercaps);
316 gst_caps_unref (transform);
318 GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
320 /* take first possibility */
321 caps = gst_caps_copy_nth (intersect, 0);
322 gst_caps_unref (intersect);
323 structure = gst_caps_get_structure (caps, 0);
326 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
329 rate = gst_structure_get_value (structure, "framerate");
331 rate_numerator = gst_value_get_fraction_numerator (rate);
332 rate_denominator = gst_value_get_fraction_denominator (rate);
334 if (otherpad == videorate->srcpad) {
335 videorate->to_rate_numerator = rate_numerator;
336 videorate->to_rate_denominator = rate_denominator;
338 videorate->from_rate_numerator = rate_numerator;
339 videorate->from_rate_denominator = rate_denominator;
341 gst_pad_set_caps (otherpad, caps);
344 gst_object_unref (opeer);
351 gst_videorate_blank_data (GstVideorate * videorate)
353 GST_DEBUG ("resetting data");
354 if (videorate->prevbuf)
355 gst_buffer_unref (videorate->prevbuf);
356 videorate->prevbuf = NULL;
358 videorate->from_rate_numerator = 0;
359 videorate->from_rate_denominator = 0;
360 videorate->to_rate_numerator = 0;
361 videorate->to_rate_denominator = 0;
366 videorate->next_ts = 0LL;
367 videorate->first_ts = 0LL;
368 videorate->prev_ts = 0LL;
370 videorate->segment_start = 0;
371 videorate->segment_stop = 0;
372 videorate->segment_accum = 0;
376 gst_videorate_init (GstVideorate * videorate)
378 GST_DEBUG ("gst_videorate_init");
380 gst_pad_new_from_static_template (&gst_videorate_sink_template, "sink");
381 gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
382 gst_pad_set_event_function (videorate->sinkpad, gst_videorate_event);
383 gst_pad_set_chain_function (videorate->sinkpad, gst_videorate_chain);
384 gst_pad_set_getcaps_function (videorate->sinkpad, gst_videorate_getcaps);
385 gst_pad_set_setcaps_function (videorate->sinkpad, gst_videorate_setcaps);
388 gst_pad_new_from_static_template (&gst_videorate_src_template, "src");
389 gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
390 gst_pad_set_getcaps_function (videorate->srcpad, gst_videorate_getcaps);
391 gst_pad_set_setcaps_function (videorate->srcpad, gst_videorate_setcaps);
393 gst_videorate_blank_data (videorate);
394 videorate->silent = DEFAULT_SILENT;
395 videorate->new_pref = DEFAULT_NEW_PREF;
399 gst_videorate_event (GstPad * pad, GstEvent * event)
401 GstVideorate *videorate;
403 videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
405 switch (GST_EVENT_TYPE (event)) {
406 case GST_EVENT_NEWSEGMENT:
408 gint64 start, stop, base;
413 gst_event_parse_new_segment (event, &update, &rate, &format, &start,
416 if (format != GST_FORMAT_TIME) {
417 GST_WARNING ("Got discont but doesn't have GST_FORMAT_TIME value");
420 We just want to update the accumulated stream_time.
422 videorate->segment_accum +=
423 videorate->segment_stop - videorate->segment_start;
424 videorate->segment_start = start;
425 videorate->segment_stop = stop;
426 GST_DEBUG_OBJECT (videorate, "Updated segment_accum:%" GST_TIME_FORMAT
427 " segment_start:%" GST_TIME_FORMAT " segment_stop:%"
428 GST_TIME_FORMAT, GST_TIME_ARGS (videorate->segment_accum),
429 GST_TIME_ARGS (videorate->segment_start),
430 GST_TIME_ARGS (videorate->segment_stop));
435 case GST_EVENT_FLUSH_STOP:
437 gst_videorate_blank_data (videorate);
443 return gst_pad_event_default (pad, event);
447 gst_videorate_chain (GstPad * pad, GstBuffer * buffer)
449 GstVideorate *videorate;
450 GstFlowReturn res = GST_FLOW_OK;
452 videorate = GST_VIDEORATE (GST_PAD_PARENT (pad));
454 if (videorate->from_rate_numerator == 0 ||
455 videorate->from_rate_denominator == 0 ||
456 videorate->to_rate_denominator == 0 || videorate->to_rate_numerator == 0)
457 return GST_FLOW_NOT_NEGOTIATED;
459 /* pull in 2 buffers */
460 if (videorate->prevbuf == NULL) {
461 /* We're sure it's a GstBuffer here */
462 videorate->prevbuf = buffer;
463 videorate->next_ts = videorate->first_ts = videorate->prev_ts =
464 GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
465 videorate->segment_accum;
467 GstClockTime prevtime, intime;
471 prevtime = videorate->prev_ts;
473 GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
474 videorate->segment_accum;
476 GST_LOG_OBJECT (videorate,
477 "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
478 " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
479 GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
483 /* got 2 buffers, see which one is the best */
485 diff1 = prevtime - videorate->next_ts;
486 diff2 = intime - videorate->next_ts;
488 /* take absolute values, beware: abs and ABS don't work for gint64 */
494 GST_LOG_OBJECT (videorate,
495 "diff with prev %" GST_TIME_FORMAT " diff with new %"
496 GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
497 GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
498 GST_TIME_ARGS (videorate->next_ts));
500 /* output first one when its the best */
506 gst_buffer_create_sub (videorate->prevbuf, 0,
507 GST_BUFFER_SIZE (videorate->prevbuf));
508 GST_BUFFER_TIMESTAMP (outbuf) = videorate->next_ts;
510 if (videorate->to_rate_numerator) {
512 videorate->first_ts +
513 gst_util_clock_time_scale (videorate->out * GST_SECOND,
514 videorate->to_rate_denominator, videorate->to_rate_numerator);
515 GST_BUFFER_DURATION (outbuf) =
516 videorate->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
518 /* adapt for looping */
519 GST_BUFFER_TIMESTAMP (outbuf) -= videorate->segment_accum;
520 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
522 GST_LOG_OBJECT (videorate,
523 "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
524 GST_TIME_ARGS (videorate->next_ts));
526 if ((res = gst_pad_push (videorate->srcpad, outbuf)) != GST_FLOW_OK) {
527 GST_WARNING_OBJECT (videorate, "couldn't push buffer on srcpad:%d",
532 GST_LOG_OBJECT (videorate,
533 "old is best, dup, pushed buffer outgoing ts %" GST_TIME_FORMAT,
534 GST_TIME_ARGS (videorate->next_ts));
536 /* continue while the first one was the best */
538 while (diff1 < diff2);
540 /* if we outputed the first buffer more then once, we have dups */
542 videorate->dup += count - 1;
543 if (!videorate->silent)
544 g_object_notify (G_OBJECT (videorate), "duplicate");
546 /* if we didn't output the first buffer, we have a drop */
547 else if (count == 0) {
549 if (!videorate->silent)
550 g_object_notify (G_OBJECT (videorate), "drop");
551 GST_LOG_OBJECT (videorate,
552 "new is best, old never used, drop, outgoing ts %"
553 GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
555 GST_LOG_OBJECT (videorate,
556 "END, putting new in old, diff1 %" GST_TIME_FORMAT
557 ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
558 ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1),
559 GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
560 videorate->in, videorate->out, videorate->drop, videorate->dup);
562 /* swap in new one when it's the best */
563 gst_buffer_unref (videorate->prevbuf);
564 videorate->prevbuf = buffer;
566 GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
567 videorate->segment_accum;
575 gst_videorate_set_property (GObject * object,
576 guint prop_id, const GValue * value, GParamSpec * pspec)
578 GstVideorate *videorate = GST_VIDEORATE (object);
582 videorate->silent = g_value_get_boolean (value);
585 videorate->new_pref = g_value_get_double (value);
588 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
594 gst_videorate_get_property (GObject * object,
595 guint prop_id, GValue * value, GParamSpec * pspec)
597 GstVideorate *videorate = GST_VIDEORATE (object);
601 g_value_set_uint64 (value, videorate->in);
604 g_value_set_uint64 (value, videorate->out);
607 g_value_set_uint64 (value, videorate->dup);
610 g_value_set_uint64 (value, videorate->drop);
613 g_value_set_boolean (value, videorate->silent);
616 g_value_set_double (value, videorate->new_pref);
619 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
624 static GstStateChangeReturn
625 gst_videorate_change_state (GstElement * element, GstStateChange transition)
627 GstStateChangeReturn ret;
628 GstVideorate *videorate;
630 videorate = GST_VIDEORATE (element);
632 switch (transition) {
637 ret = parent_class->change_state (element, transition);
639 switch (transition) {
640 case GST_STATE_CHANGE_PAUSED_TO_READY:
641 gst_videorate_blank_data (videorate);
651 plugin_init (GstPlugin * plugin)
653 GST_DEBUG_CATEGORY_INIT (videorate_debug, "videorate", 0,
654 "Videorate stream fixer");
656 return gst_element_register (plugin, "videorate", GST_RANK_NONE,
660 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
663 "Adjusts video frames",
664 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)