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 (video_rate_debug);
27 #define GST_CAT_DEFAULT video_rate_debug
29 #define GST_TYPE_VIDEO_RATE \
30 (gst_video_rate_get_type())
31 #define GST_VIDEO_RATE(obj) \
32 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_RATE,GstVideoRate))
33 #define GST_VIDEO_RATE_CLASS(klass) \
34 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_RATE,GstVideoRate))
35 #define GST_IS_VIDEO_RATE(obj) \
36 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_RATE))
37 #define GST_IS_VIDEO_RATE_CLASS(obj) \
38 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_RATE))
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 video_rate_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_video_rate_src_template =
102 GST_STATIC_PAD_TEMPLATE ("src",
105 GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
108 static GstStaticPadTemplate gst_video_rate_sink_template =
109 GST_STATIC_PAD_TEMPLATE ("sink",
112 GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb")
115 static void gst_video_rate_base_init (gpointer g_class);
116 static void gst_video_rate_class_init (GstVideoRateClass * klass);
117 static void gst_video_rate_init (GstVideoRate * videorate);
118 static gboolean gst_video_rate_event (GstPad * pad, GstEvent * event);
119 static GstFlowReturn gst_video_rate_chain (GstPad * pad, GstBuffer * buffer);
121 static void gst_video_rate_set_property (GObject * object,
122 guint prop_id, const GValue * value, GParamSpec * pspec);
123 static void gst_video_rate_get_property (GObject * object,
124 guint prop_id, GValue * value, GParamSpec * pspec);
126 static GstStateChangeReturn gst_video_rate_change_state (GstElement * element,
127 GstStateChange transition);
129 static GstElementClass *parent_class = NULL;
131 /*static guint gst_video_rate_signals[LAST_SIGNAL] = { 0 }; */
134 gst_video_rate_get_type (void)
136 static GType video_rate_type = 0;
138 if (!video_rate_type) {
139 static const GTypeInfo video_rate_info = {
140 sizeof (GstVideoRateClass),
141 gst_video_rate_base_init,
143 (GClassInitFunc) gst_video_rate_class_init,
146 sizeof (GstVideoRate),
148 (GInstanceInitFunc) gst_video_rate_init,
151 video_rate_type = g_type_register_static (GST_TYPE_ELEMENT,
152 "GstVideoRate", &video_rate_info, 0);
155 return video_rate_type;
159 gst_video_rate_base_init (gpointer g_class)
161 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
163 gst_element_class_set_details (element_class, &video_rate_details);
165 gst_element_class_add_pad_template (element_class,
166 gst_static_pad_template_get (&gst_video_rate_sink_template));
167 gst_element_class_add_pad_template (element_class,
168 gst_static_pad_template_get (&gst_video_rate_src_template));
171 gst_video_rate_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_video_rate_set_property;
179 object_class->get_property = gst_video_rate_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_video_rate_change_state;
205 /* return the caps that can be used on out_pad given in_caps on in_pad */
207 gst_video_rate_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_video_rate_getcaps (GstPad * pad)
234 GstVideoRate *videorate;
238 videorate = GST_VIDEO_RATE (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_video_rate_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_video_rate_setcaps (GstPad * pad, GstCaps * caps)
262 GstVideoRate *videorate;
263 GstStructure *structure;
265 GstPad *otherpad, *opeer;
266 gint rate_numerator, rate_denominator;
268 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
270 structure = gst_caps_get_structure (caps, 0);
271 if (!gst_structure_get_fraction (structure, "framerate",
272 &rate_numerator, &rate_denominator))
275 if (pad == videorate->srcpad) {
276 videorate->to_rate_numerator = rate_numerator;
277 videorate->to_rate_denominator = rate_denominator;
278 otherpad = videorate->sinkpad;
280 videorate->from_rate_numerator = rate_numerator;
281 videorate->from_rate_denominator = rate_denominator;
282 otherpad = videorate->srcpad;
284 /* now try to find something for the peer */
285 opeer = gst_pad_get_peer (otherpad);
287 if (gst_pad_accept_caps (opeer, caps)) {
288 /* the peer accepts the caps as they are */
289 gst_pad_set_caps (otherpad, caps);
295 GstCaps *transform = NULL;
299 /* see how we can transform the input caps */
300 if (!gst_video_rate_transformcaps (pad, caps, otherpad, &transform))
303 /* see what the peer can do */
304 peercaps = gst_pad_get_caps (opeer);
306 GST_DEBUG ("icaps %" GST_PTR_FORMAT, peercaps);
307 GST_DEBUG ("transform %" GST_PTR_FORMAT, transform);
309 /* filter against our possibilities */
310 intersect = gst_caps_intersect (peercaps, transform);
311 gst_caps_unref (peercaps);
312 gst_caps_unref (transform);
314 GST_DEBUG ("intersect %" GST_PTR_FORMAT, intersect);
316 /* take first possibility */
317 caps = gst_caps_copy_nth (intersect, 0);
318 gst_caps_unref (intersect);
319 structure = gst_caps_get_structure (caps, 0);
322 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
323 rate_numerator, rate_denominator);
325 gst_structure_get_fraction (structure, "framerate",
326 &rate_numerator, &rate_denominator);
328 if (otherpad == videorate->srcpad) {
329 videorate->to_rate_numerator = rate_numerator;
330 videorate->to_rate_denominator = rate_denominator;
332 videorate->from_rate_numerator = rate_numerator;
333 videorate->from_rate_denominator = rate_denominator;
335 gst_pad_set_caps (otherpad, caps);
338 gst_object_unref (opeer);
345 gst_video_rate_blank_data (GstVideoRate * videorate)
347 GST_DEBUG ("resetting data");
348 if (videorate->prevbuf)
349 gst_buffer_unref (videorate->prevbuf);
350 videorate->prevbuf = NULL;
352 videorate->from_rate_numerator = 0;
353 videorate->from_rate_denominator = 0;
354 videorate->to_rate_numerator = 0;
355 videorate->to_rate_denominator = 0;
360 videorate->next_ts = 0LL;
361 videorate->first_ts = 0LL;
362 videorate->prev_ts = 0LL;
364 videorate->segment_start = 0;
365 videorate->segment_stop = 0;
366 videorate->segment_accum = 0;
370 gst_video_rate_init (GstVideoRate * videorate)
372 GST_DEBUG ("gst_video_rate_init");
374 gst_pad_new_from_static_template (&gst_video_rate_sink_template, "sink");
375 gst_element_add_pad (GST_ELEMENT (videorate), videorate->sinkpad);
376 gst_pad_set_event_function (videorate->sinkpad, gst_video_rate_event);
377 gst_pad_set_chain_function (videorate->sinkpad, gst_video_rate_chain);
378 gst_pad_set_getcaps_function (videorate->sinkpad, gst_video_rate_getcaps);
379 gst_pad_set_setcaps_function (videorate->sinkpad, gst_video_rate_setcaps);
382 gst_pad_new_from_static_template (&gst_video_rate_src_template, "src");
383 gst_element_add_pad (GST_ELEMENT (videorate), videorate->srcpad);
384 gst_pad_set_getcaps_function (videorate->srcpad, gst_video_rate_getcaps);
385 gst_pad_set_setcaps_function (videorate->srcpad, gst_video_rate_setcaps);
387 gst_video_rate_blank_data (videorate);
388 videorate->silent = DEFAULT_SILENT;
389 videorate->new_pref = DEFAULT_NEW_PREF;
393 gst_video_rate_event (GstPad * pad, GstEvent * event)
395 GstVideoRate *videorate;
397 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
399 switch (GST_EVENT_TYPE (event)) {
400 case GST_EVENT_NEWSEGMENT:
402 gint64 start, stop, base;
407 gst_event_parse_new_segment (event, &update, &rate, &format, &start,
410 if (format != GST_FORMAT_TIME) {
411 GST_WARNING ("Got discont but doesn't have GST_FORMAT_TIME value");
414 We just want to update the accumulated stream_time.
416 videorate->segment_accum +=
417 videorate->segment_stop - videorate->segment_start;
418 videorate->segment_start = start;
419 videorate->segment_stop = stop;
420 GST_DEBUG_OBJECT (videorate, "Updated segment_accum:%" GST_TIME_FORMAT
421 " segment_start:%" GST_TIME_FORMAT " segment_stop:%"
422 GST_TIME_FORMAT, GST_TIME_ARGS (videorate->segment_accum),
423 GST_TIME_ARGS (videorate->segment_start),
424 GST_TIME_ARGS (videorate->segment_stop));
429 case GST_EVENT_FLUSH_STOP:
431 gst_video_rate_blank_data (videorate);
437 return gst_pad_event_default (pad, event);
441 gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
443 GstVideoRate *videorate;
444 GstFlowReturn res = GST_FLOW_OK;
446 videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
448 if (videorate->from_rate_numerator == 0 ||
449 videorate->from_rate_denominator == 0 ||
450 videorate->to_rate_denominator == 0 || videorate->to_rate_numerator == 0)
451 return GST_FLOW_NOT_NEGOTIATED;
453 /* pull in 2 buffers */
454 if (videorate->prevbuf == NULL) {
455 /* We're sure it's a GstBuffer here */
456 videorate->prevbuf = buffer;
457 videorate->next_ts = videorate->first_ts = videorate->prev_ts =
458 GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
459 videorate->segment_accum;
461 GstClockTime prevtime, intime;
465 prevtime = videorate->prev_ts;
467 GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
468 videorate->segment_accum;
470 GST_LOG_OBJECT (videorate,
471 "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT
472 " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime),
473 GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts));
477 /* got 2 buffers, see which one is the best */
479 diff1 = prevtime - videorate->next_ts;
480 diff2 = intime - videorate->next_ts;
482 /* take absolute values, beware: abs and ABS don't work for gint64 */
488 GST_LOG_OBJECT (videorate,
489 "diff with prev %" GST_TIME_FORMAT " diff with new %"
490 GST_TIME_FORMAT " outgoing ts %" GST_TIME_FORMAT,
491 GST_TIME_ARGS (diff1), GST_TIME_ARGS (diff2),
492 GST_TIME_ARGS (videorate->next_ts));
494 /* output first one when its the best */
500 gst_buffer_create_sub (videorate->prevbuf, 0,
501 GST_BUFFER_SIZE (videorate->prevbuf));
502 GST_BUFFER_TIMESTAMP (outbuf) = videorate->next_ts;
504 if (videorate->to_rate_numerator) {
506 videorate->first_ts +
507 gst_util_uint64_scale_int (videorate->out * GST_SECOND,
508 videorate->to_rate_denominator, videorate->to_rate_numerator);
509 GST_BUFFER_DURATION (outbuf) =
510 videorate->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
512 /* adapt for looping */
513 GST_BUFFER_TIMESTAMP (outbuf) -= videorate->segment_accum;
514 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (videorate->srcpad));
516 GST_LOG_OBJECT (videorate,
517 "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
518 GST_TIME_ARGS (videorate->next_ts));
520 if ((res = gst_pad_push (videorate->srcpad, outbuf)) != GST_FLOW_OK) {
521 GST_WARNING_OBJECT (videorate, "couldn't push buffer on srcpad:%d",
526 GST_LOG_OBJECT (videorate,
527 "old is best, dup, pushed buffer outgoing ts %" GST_TIME_FORMAT,
528 GST_TIME_ARGS (videorate->next_ts));
530 /* continue while the first one was the best */
532 while (diff1 < diff2);
534 /* if we outputed the first buffer more then once, we have dups */
536 videorate->dup += count - 1;
537 if (!videorate->silent)
538 g_object_notify (G_OBJECT (videorate), "duplicate");
540 /* if we didn't output the first buffer, we have a drop */
541 else if (count == 0) {
543 if (!videorate->silent)
544 g_object_notify (G_OBJECT (videorate), "drop");
545 GST_LOG_OBJECT (videorate,
546 "new is best, old never used, drop, outgoing ts %"
547 GST_TIME_FORMAT, GST_TIME_ARGS (videorate->next_ts));
549 GST_LOG_OBJECT (videorate,
550 "END, putting new in old, diff1 %" GST_TIME_FORMAT
551 ", diff2 %" GST_TIME_FORMAT ", next_ts %" GST_TIME_FORMAT
552 ", in %lld, out %lld, drop %lld, dup %lld", GST_TIME_ARGS (diff1),
553 GST_TIME_ARGS (diff2), GST_TIME_ARGS (videorate->next_ts),
554 videorate->in, videorate->out, videorate->drop, videorate->dup);
556 /* swap in new one when it's the best */
557 gst_buffer_unref (videorate->prevbuf);
558 videorate->prevbuf = buffer;
560 GST_BUFFER_TIMESTAMP (buffer) - videorate->segment_start +
561 videorate->segment_accum;
569 gst_video_rate_set_property (GObject * object,
570 guint prop_id, const GValue * value, GParamSpec * pspec)
572 GstVideoRate *videorate = GST_VIDEO_RATE (object);
576 videorate->silent = g_value_get_boolean (value);
579 videorate->new_pref = g_value_get_double (value);
582 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
588 gst_video_rate_get_property (GObject * object,
589 guint prop_id, GValue * value, GParamSpec * pspec)
591 GstVideoRate *videorate = GST_VIDEO_RATE (object);
595 g_value_set_uint64 (value, videorate->in);
598 g_value_set_uint64 (value, videorate->out);
601 g_value_set_uint64 (value, videorate->dup);
604 g_value_set_uint64 (value, videorate->drop);
607 g_value_set_boolean (value, videorate->silent);
610 g_value_set_double (value, videorate->new_pref);
613 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
618 static GstStateChangeReturn
619 gst_video_rate_change_state (GstElement * element, GstStateChange transition)
621 GstStateChangeReturn ret;
622 GstVideoRate *videorate;
624 videorate = GST_VIDEO_RATE (element);
626 switch (transition) {
631 ret = parent_class->change_state (element, transition);
633 switch (transition) {
634 case GST_STATE_CHANGE_PAUSED_TO_READY:
635 gst_video_rate_blank_data (videorate);
645 plugin_init (GstPlugin * plugin)
647 GST_DEBUG_CATEGORY_INIT (video_rate_debug, "videorate", 0,
648 "VideoRate stream fixer");
650 return gst_element_register (plugin, "videorate", GST_RANK_NONE,
651 GST_TYPE_VIDEO_RATE);
654 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
657 "Adjusts video frames",
658 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)