2 * Copyright 2009 Nokia Corporation <multimedia@maemo.org>
3 * 2006 Zeeshan Ali <zeeshan.ali@nokia.com>.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * SECTION:element-fpsdisplaysink
23 * Can display the current and average framerate as a testoverlay or on stdout.
26 * <title>Example launch lines</title>
28 * gst-launch videotestsrc ! fpsdisplaysink
29 * gst-launch videotestsrc ! fpsdisplaysink text-overlay=false
30 * gst-launch filesrc location=video.avi ! decodebin2 name=d ! queue ! fpsdisplaysink d. ! queue ! fakesink sync=true
31 * gst-launch playbin uri=file:///path/to/video.avi video-sink="fpsdisplaysink" audio-sink=fakesink
36 * - can we avoid plugging the textoverlay?
37 * - gst-seek 15 "videotestsrc ! fpsdisplaysink" dies when closing gst-seek
40 * - if we make ourself RANK_PRIMARY+10 or something that autovideosink would
41 * select and fpsdisplaysink is set to use autovideosink as its internal sink
42 * it doesn't work. Reason: autovideosink creates a fpsdisplaysink, that
43 * creates an autovideosink, that...
50 #include "debugutils-marshal.h"
51 #include "fpsdisplaysink.h"
53 #define DEFAULT_SIGNAL_FPS_MEASUREMENTS FALSE
54 #define DEFAULT_FPS_UPDATE_INTERVAL_MS 500 /* 500 ms */
55 #define DEFAULT_FONT "Sans 15"
56 #define DEFAULT_SILENT FALSE
57 #define DEFAULT_LAST_MESSAGE NULL
59 /* generic templates */
60 static GstStaticPadTemplate fps_display_sink_template =
61 GST_STATIC_PAD_TEMPLATE ("sink",
66 GST_DEBUG_CATEGORY_STATIC (fps_display_sink_debug);
67 #define GST_CAT_DEFAULT fps_display_sink_debug
69 #define DEFAULT_SYNC TRUE
74 SIGNAL_FPS_MEASUREMENTS,
84 PROP_FPS_UPDATE_INTERVAL,
87 PROP_SIGNAL_FPS_MEASUREMENTS,
95 static GstBinClass *parent_class = NULL;
97 static GstStateChangeReturn fps_display_sink_change_state (GstElement * element,
98 GstStateChange transition);
99 static void fps_display_sink_set_property (GObject * object, guint prop_id,
100 const GValue * value, GParamSpec * pspec);
101 static void fps_display_sink_get_property (GObject * object, guint prop_id,
102 GValue * value, GParamSpec * pspec);
103 static void fps_display_sink_dispose (GObject * object);
105 static gboolean display_current_fps (gpointer data);
107 static guint fpsdisplaysink_signals[LAST_SIGNAL] = { 0 };
109 static GParamSpec *pspec_last_message = NULL;
112 fps_display_sink_class_init (GstFPSDisplaySinkClass * klass)
114 GObjectClass *gobject_klass = G_OBJECT_CLASS (klass);
115 GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (klass);
117 parent_class = g_type_class_peek_parent (klass);
119 gobject_klass->set_property = fps_display_sink_set_property;
120 gobject_klass->get_property = fps_display_sink_get_property;
121 gobject_klass->dispose = fps_display_sink_dispose;
123 g_object_class_install_property (gobject_klass, PROP_SYNC,
124 g_param_spec_boolean ("sync",
125 "Sync", "Sync on the clock (if the internally used sink doesn't "
126 "have this property it will be ignored", DEFAULT_SYNC,
127 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
129 g_object_class_install_property (gobject_klass, PROP_TEXT_OVERLAY,
130 g_param_spec_boolean ("text-overlay",
132 "Whether to use text-overlay", TRUE,
133 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
135 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
136 g_param_spec_object ("video-sink",
138 "Video sink to use (Must only be called on NULL state)",
139 GST_TYPE_ELEMENT, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
141 g_object_class_install_property (gobject_klass, PROP_FPS_UPDATE_INTERVAL,
142 g_param_spec_int ("fps-update-interval", "Fps update interval",
143 "Time between consecutive frames per second measures and update "
144 " (in ms). Should be set on NULL state", 1, G_MAXINT,
145 DEFAULT_FPS_UPDATE_INTERVAL_MS,
146 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
148 g_object_class_install_property (gobject_klass, PROP_MAX_FPS,
149 g_param_spec_double ("max-fps", "Max fps",
150 "Maximum fps rate measured. Reset when going from NULL to READY."
151 "-1 means no measurement has yet been done", -1, G_MAXDOUBLE, -1,
152 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
154 g_object_class_install_property (gobject_klass, PROP_MIN_FPS,
155 g_param_spec_double ("min-fps", "Min fps",
156 "Minimum fps rate measured. Reset when going from NULL to READY."
157 "-1 means no measurement has yet been done", -1, G_MAXDOUBLE, -1,
158 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
160 g_object_class_install_property (gobject_klass, PROP_FRAMES_DROPPED,
161 g_param_spec_uint ("frames-dropped", "dropped frames",
162 "Number of frames dropped by the sink", 0, G_MAXUINT, 0,
163 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
165 g_object_class_install_property (gobject_klass, PROP_FRAMES_RENDERED,
166 g_param_spec_uint ("frames-rendered", "rendered frames",
167 "Number of frames rendered", 0, G_MAXUINT, 0,
168 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
170 g_object_class_install_property (gobject_klass, PROP_SILENT,
171 g_param_spec_boolean ("silent", "enable stdout output",
172 "Don't produce last_message events", DEFAULT_SILENT,
173 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
175 g_object_class_install_property (gobject_klass, PROP_SIGNAL_FPS_MEASUREMENTS,
176 g_param_spec_boolean ("signal-fps-measurements",
177 "Signal fps measurements",
178 "If the fps-measurements signal should be emited.",
179 DEFAULT_SIGNAL_FPS_MEASUREMENTS,
180 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
182 pspec_last_message = g_param_spec_string ("last-message", "Last Message",
183 "The message describing current status", DEFAULT_LAST_MESSAGE,
184 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
185 g_object_class_install_property (gobject_klass, PROP_LAST_MESSAGE,
189 * GstFPSDisplaySink::fps-measurements:
190 * @fpsdisplaysink: a #GstFPSDisplaySink
191 * @fps: The current measured fps
192 * @droprate: The rate at which buffers are being dropped
193 * @avgfps: The average fps
195 * Signals the application about the measured fps
199 fpsdisplaysink_signals[SIGNAL_FPS_MEASUREMENTS] =
200 g_signal_new ("fps-measurements", G_TYPE_FROM_CLASS (klass),
201 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
202 __gst_debugutils_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
203 G_TYPE_NONE, 3, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
205 gstelement_klass->change_state = fps_display_sink_change_state;
207 gst_element_class_add_pad_template (gstelement_klass,
208 gst_static_pad_template_get (&fps_display_sink_template));
210 gst_element_class_set_metadata (gstelement_klass,
211 "Measure and show framerate on videosink", "Sink/Video",
212 "Shows the current frame-rate and drop-rate of the videosink as overlay or text on stdout",
213 "Zeeshan Ali <zeeshan.ali@nokia.com>, Stefan Kost <stefan.kost@nokia.com>");
216 static GstPadProbeReturn
217 on_video_sink_data_flow (GstPad * pad, GstPadProbeInfo * info,
220 GstMiniObject *mini_obj = GST_PAD_PROBE_INFO_DATA (info);
221 GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (user_data);
224 if (GST_IS_BUFFER (mini_obj)) {
225 GstBuffer *buf = GST_BUFFER_CAST (mini_obj);
227 if (GST_CLOCK_TIME_IS_VALID (self->next_ts)) {
228 if (GST_BUFFER_TIMESTAMP (buf) <= self->next_ts) {
229 self->frames_rendered++;
231 GST_WARNING_OBJECT (self, "dropping frame : ts %" GST_TIME_FORMAT
232 " < expected_ts %" GST_TIME_FORMAT,
233 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
234 GST_TIME_ARGS (self->next_ts));
235 self->frames_dropped++;
238 self->frames_rendered++;
242 if (GST_IS_EVENT (mini_obj)) {
243 GstEvent *ev = GST_EVENT_CAST (mini_obj);
245 if (GST_EVENT_TYPE (ev) == GST_EVENT_QOS) {
246 GstClockTimeDiff diff;
249 gst_event_parse_qos (ev, NULL, NULL, &diff, &ts);
251 g_atomic_int_inc (&self->frames_rendered);
253 g_atomic_int_inc (&self->frames_dropped);
256 ts = gst_util_get_timestamp ();
257 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (self->start_ts))) {
258 self->interval_ts = self->last_ts = self->start_ts = ts;
260 if (GST_CLOCK_DIFF (self->interval_ts, ts) > self->fps_update_interval) {
261 display_current_fps (self);
262 self->interval_ts = ts;
266 return GST_PAD_PROBE_OK;
270 update_sub_sync (GstElement * sink, gpointer data)
272 /* Some sinks (like autovideosink) don't have the sync property so
273 * we check it exists before setting it to avoid a warning at
275 if (g_object_class_find_property (G_OBJECT_GET_CLASS (sink), "sync"))
276 g_object_set (sink, "sync", *((gboolean *) data), NULL);
278 GST_WARNING ("Internal sink doesn't have sync property");
282 update_sub_sync_foreach (const GValue * item, gpointer data)
284 GstElement *sink = g_value_get_object (item);
286 update_sub_sync (sink, data);
290 fps_display_sink_update_sink_sync (GstFPSDisplaySink * self)
292 GstIterator *iterator;
294 if (self->video_sink == NULL)
297 if (GST_IS_BIN (self->video_sink)) {
298 iterator = gst_bin_iterate_sinks (GST_BIN (self->video_sink));
299 gst_iterator_foreach (iterator,
300 (GstIteratorForeachFunction) update_sub_sync_foreach,
301 (void *) &self->sync);
302 gst_iterator_free (iterator);
304 update_sub_sync (self->video_sink, (void *) &self->sync);
309 update_video_sink (GstFPSDisplaySink * self, GstElement * video_sink)
313 if (self->video_sink) {
315 /* remove pad probe */
316 sink_pad = gst_element_get_static_pad (self->video_sink, "sink");
317 gst_pad_remove_probe (sink_pad, self->data_probe_id);
318 gst_object_unref (sink_pad);
319 self->data_probe_id = -1;
321 /* remove ghost pad target */
322 gst_ghost_pad_set_target (GST_GHOST_PAD (self->ghost_pad), NULL);
324 /* remove old sink */
325 gst_bin_remove (GST_BIN (self), self->video_sink);
326 gst_object_unref (self->video_sink);
329 /* create child elements */
330 self->video_sink = video_sink;
332 if (self->video_sink == NULL)
335 fps_display_sink_update_sink_sync (self);
337 /* take a ref before bin takes the ownership */
338 gst_object_ref (self->video_sink);
340 gst_bin_add (GST_BIN (self), self->video_sink);
342 /* attach or pad probe */
343 sink_pad = gst_element_get_static_pad (self->video_sink, "sink");
344 self->data_probe_id = gst_pad_add_probe (sink_pad,
345 GST_PAD_PROBE_TYPE_DATA_BOTH, on_video_sink_data_flow,
346 (gpointer) self, NULL);
347 gst_object_unref (sink_pad);
351 fps_display_sink_init (GstFPSDisplaySink * self,
352 GstFPSDisplaySinkClass * g_class)
354 self->sync = DEFAULT_SYNC;
355 self->signal_measurements = DEFAULT_SIGNAL_FPS_MEASUREMENTS;
356 self->use_text_overlay = TRUE;
357 self->fps_update_interval = GST_MSECOND * DEFAULT_FPS_UPDATE_INTERVAL_MS;
358 self->video_sink = NULL;
361 self->silent = DEFAULT_SILENT;
362 self->last_message = g_strdup (DEFAULT_LAST_MESSAGE);
364 self->ghost_pad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
365 gst_element_add_pad (GST_ELEMENT (self), self->ghost_pad);
369 display_current_fps (gpointer data)
371 GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (data);
372 guint64 frames_rendered, frames_dropped;
373 gdouble rr, dr, average_fps;
374 gchar fps_message[256];
375 gdouble time_diff, time_elapsed;
376 GstClockTime current_ts = gst_util_get_timestamp ();
378 frames_rendered = g_atomic_int_get (&self->frames_rendered);
379 frames_dropped = g_atomic_int_get (&self->frames_dropped);
381 if ((frames_rendered + frames_dropped) == 0) {
382 /* in case timer fired and we didn't yet get any QOS events */
386 time_diff = (gdouble) (current_ts - self->last_ts) / GST_SECOND;
387 time_elapsed = (gdouble) (current_ts - self->start_ts) / GST_SECOND;
389 rr = (gdouble) (frames_rendered - self->last_frames_rendered) / time_diff;
390 dr = (gdouble) (frames_dropped - self->last_frames_dropped) / time_diff;
392 average_fps = (gdouble) frames_rendered / time_elapsed;
394 if (self->max_fps == -1 || rr > self->max_fps) {
396 GST_DEBUG_OBJECT (self, "Updated max-fps to %f", rr);
398 if (self->min_fps == -1 || rr < self->min_fps) {
400 GST_DEBUG_OBJECT (self, "Updated min-fps to %f", rr);
403 if (self->signal_measurements) {
404 GST_LOG_OBJECT (self, "Signaling measurements: fps:%f droprate:%f "
405 "avg-fps:%f", rr, dr, average_fps);
406 g_signal_emit (G_OBJECT (self),
407 fpsdisplaysink_signals[SIGNAL_FPS_MEASUREMENTS], 0, rr, dr,
411 /* Display on a single line to make it easier to read and import
412 * into, for example, excel.. note: it would be nice to show
413 * timestamp too.. need to check if there is a sane way to log
414 * timestamp of last rendered buffer, so we could correlate dips
415 * in framerate to certain positions in the stream.
418 g_snprintf (fps_message, 255,
419 "rendered: %" G_GUINT64_FORMAT ", dropped: %" G_GUINT64_FORMAT
420 ", current: %.2f, average: %.2f", frames_rendered, frames_dropped, rr,
423 g_snprintf (fps_message, 255,
424 "rendered: %" G_GUINT64_FORMAT ", dropped: %" G_GUINT64_FORMAT
425 ", fps: %.2f, drop rate: %.2f", frames_rendered, frames_dropped, rr,
429 if (self->use_text_overlay) {
430 g_object_set (self->text_overlay, "text", fps_message, NULL);
434 GST_OBJECT_LOCK (self);
435 g_free (self->last_message);
436 self->last_message = g_strdup (fps_message);
437 GST_OBJECT_UNLOCK (self);
438 #if !GLIB_CHECK_VERSION(2,26,0)
439 g_object_notify ((GObject *) self, "last-message");
441 g_object_notify_by_pspec ((GObject *) self, pspec_last_message);
445 self->last_frames_rendered = frames_rendered;
446 self->last_frames_dropped = frames_dropped;
447 self->last_ts = current_ts;
453 fps_display_sink_start (GstFPSDisplaySink * self)
455 GstPad *target_pad = NULL;
458 self->frames_rendered = 0;
459 self->frames_dropped = 0;
460 self->last_frames_rendered = G_GUINT64_CONSTANT (0);
461 self->last_frames_dropped = G_GUINT64_CONSTANT (0);
465 /* init time stamps */
466 self->last_ts = self->start_ts = self->interval_ts = GST_CLOCK_TIME_NONE;
468 GST_DEBUG_OBJECT (self, "Use text-overlay? %d", self->use_text_overlay);
470 if (self->use_text_overlay) {
471 if (!self->text_overlay) {
473 gst_element_factory_make ("textoverlay", "fps-display-text-overlay");
474 if (!self->text_overlay) {
475 GST_WARNING_OBJECT (self, "text-overlay element could not be created");
476 self->use_text_overlay = FALSE;
477 goto no_text_overlay;
479 gst_object_ref (self->text_overlay);
480 g_object_set (self->text_overlay,
481 "font-desc", DEFAULT_FONT, "silent", FALSE, NULL);
482 gst_bin_add (GST_BIN (self), self->text_overlay);
484 if (!gst_element_link (self->text_overlay, self->video_sink)) {
485 GST_ERROR_OBJECT (self, "Could not link elements");
488 target_pad = gst_element_get_static_pad (self->text_overlay, "video_sink");
491 if (!self->use_text_overlay) {
492 if (self->text_overlay) {
493 gst_element_unlink (self->text_overlay, self->video_sink);
494 gst_bin_remove (GST_BIN (self), self->text_overlay);
495 self->text_overlay = NULL;
497 target_pad = gst_element_get_static_pad (self->video_sink, "sink");
499 gst_ghost_pad_set_target (GST_GHOST_PAD (self->ghost_pad), target_pad);
500 gst_object_unref (target_pad);
504 fps_display_sink_stop (GstFPSDisplaySink * self)
506 if (self->text_overlay) {
507 gst_element_unlink (self->text_overlay, self->video_sink);
508 gst_bin_remove (GST_BIN (self), self->text_overlay);
509 gst_object_unref (self->text_overlay);
510 self->text_overlay = NULL;
516 /* print the max and minimum fps values */
518 g_strdup_printf ("Max-fps: %0.2f, Min-fps: %0.2f", self->max_fps,
520 GST_OBJECT_LOCK (self);
521 g_free (self->last_message);
522 self->last_message = str;
523 GST_OBJECT_UNLOCK (self);
524 #if !GLIB_CHECK_VERSION(2,26,0)
525 g_object_notify ((GObject *) self, "last-message");
527 g_object_notify_by_pspec ((GObject *) self, pspec_last_message);
531 GST_OBJECT_LOCK (self);
532 g_free (self->last_message);
533 self->last_message = NULL;
534 GST_OBJECT_UNLOCK (self);
538 fps_display_sink_dispose (GObject * object)
540 GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (object);
542 if (self->video_sink) {
543 gst_object_unref (self->video_sink);
544 self->video_sink = NULL;
547 if (self->text_overlay) {
548 gst_object_unref (self->text_overlay);
549 self->text_overlay = NULL;
552 GST_OBJECT_LOCK (self);
553 g_free (self->last_message);
554 self->last_message = NULL;
555 GST_OBJECT_UNLOCK (self);
557 G_OBJECT_CLASS (parent_class)->dispose (object);
561 fps_display_sink_set_property (GObject * object, guint prop_id,
562 const GValue * value, GParamSpec * pspec)
564 GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (object);
568 self->sync = g_value_get_boolean (value);
569 fps_display_sink_update_sink_sync (self);
571 case PROP_TEXT_OVERLAY:
572 self->use_text_overlay = g_value_get_boolean (value);
574 if (self->text_overlay) {
575 if (!self->use_text_overlay) {
576 GST_DEBUG_OBJECT (self, "text-overlay set to false");
577 g_object_set (self->text_overlay, "text", "", "silent", TRUE, NULL);
579 GST_DEBUG_OBJECT (self, "text-overlay set to true");
580 g_object_set (self->text_overlay, "silent", FALSE, NULL);
584 case PROP_VIDEO_SINK:
585 /* FIXME should we add a state-lock or a lock around here?
586 * need to check if it is possible that a state change NULL->READY can
587 * happen while this code is executing on a different thread */
588 if (GST_STATE (self) != GST_STATE_NULL) {
589 g_warning ("Can't set video-sink property of fpsdisplaysink if not on "
593 update_video_sink (self, (GstElement *) g_value_get_object (value));
595 case PROP_FPS_UPDATE_INTERVAL:
596 self->fps_update_interval =
597 GST_MSECOND * (GstClockTime) g_value_get_int (value);
599 case PROP_SIGNAL_FPS_MEASUREMENTS:
600 self->signal_measurements = g_value_get_boolean (value);
603 self->silent = g_value_get_boolean (value);
606 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
612 fps_display_sink_get_property (GObject * object, guint prop_id,
613 GValue * value, GParamSpec * pspec)
615 GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (object);
619 g_value_set_boolean (value, self->sync);
621 case PROP_TEXT_OVERLAY:
622 g_value_set_boolean (value, self->use_text_overlay);
624 case PROP_VIDEO_SINK:
625 g_value_set_object (value, self->video_sink);
627 case PROP_FPS_UPDATE_INTERVAL:
628 g_value_set_int (value, (gint) (self->fps_update_interval / GST_MSECOND));
631 g_value_set_double (value, self->max_fps);
634 g_value_set_double (value, self->min_fps);
636 case PROP_FRAMES_DROPPED:
637 g_value_set_uint (value, g_atomic_int_get (&self->frames_dropped));
639 case PROP_FRAMES_RENDERED:
640 g_value_set_uint (value, g_atomic_int_get (&self->frames_rendered));
642 case PROP_SIGNAL_FPS_MEASUREMENTS:
643 g_value_set_boolean (value, self->signal_measurements);
646 g_value_set_boolean (value, self->silent);
648 case PROP_LAST_MESSAGE:
649 GST_OBJECT_LOCK (self);
650 g_value_set_string (value, self->last_message);
651 GST_OBJECT_UNLOCK (self);
654 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
659 static GstStateChangeReturn
660 fps_display_sink_change_state (GstElement * element, GstStateChange transition)
662 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
663 GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (element);
665 switch (transition) {
666 case GST_STATE_CHANGE_NULL_TO_READY:
668 if (self->video_sink == NULL) {
669 GstElement *video_sink;
671 GST_DEBUG_OBJECT (self, "No video sink set, creating autovideosink");
672 video_sink = gst_element_factory_make ("autovideosink",
673 "fps-display-video_sink");
674 update_video_sink (self, video_sink);
677 if (self->video_sink != NULL) {
678 fps_display_sink_start (self);
680 GST_ELEMENT_ERROR (self, LIBRARY, INIT,
681 ("No video sink set and autovideosink is not available"), (NULL));
682 ret = GST_STATE_CHANGE_FAILURE;
685 case GST_STATE_CHANGE_READY_TO_PAUSED:
686 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
687 /* reinforce our sync to children, as they might have changed
689 fps_display_sink_update_sink_sync (self);
695 ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state,
696 (element, transition), GST_STATE_CHANGE_SUCCESS);
698 switch (transition) {
699 case GST_STATE_CHANGE_READY_TO_NULL:
700 fps_display_sink_stop (self);
710 fps_display_sink_get_type (void)
712 static GType fps_display_sink_type = 0;
714 if (!fps_display_sink_type) {
715 static const GTypeInfo fps_display_sink_info = {
716 sizeof (GstFPSDisplaySinkClass),
719 (GClassInitFunc) fps_display_sink_class_init,
722 sizeof (GstFPSDisplaySink),
724 (GInstanceInitFunc) fps_display_sink_init,
727 fps_display_sink_type = g_type_register_static (GST_TYPE_BIN,
728 "GstFPSDisplaySink", &fps_display_sink_info, 0);
730 GST_DEBUG_CATEGORY_INIT (fps_display_sink_debug, "fpsdisplaysink", 0,
734 return fps_display_sink_type;