static GstPadProbeReturn on_video_sink_data_flow (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { GstMiniObject *mini_obj = GST_PAD_PROBE_INFO_DATA (info); GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (user_data); if (GST_IS_BUFFER (mini_obj)) { GstClockTime ts; /* assume the frame is going to be rendered. If it isnt', we'll get a qos * message and reset ->frames_rendered from there. */ g_atomic_int_inc (&self->frames_rendered); ts = gst_util_get_timestamp (); if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (self->start_ts))) { self->interval_ts = self->last_ts = self->start_ts = ts; } if (GST_CLOCK_DIFF (self->interval_ts, ts) > self->fps_update_interval) { display_current_fps (self); self->interval_ts = ts; } } return GST_PAD_PROBE_OK; }
static gboolean on_video_sink_data_flow (GstPad * pad, GstMiniObject * mini_obj, gpointer user_data) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (user_data); #if 0 if (GST_IS_BUFFER (mini_obj)) { GstBuffer *buf = GST_BUFFER_CAST (mini_obj); if (GST_CLOCK_TIME_IS_VALID (self->next_ts)) { if (GST_BUFFER_TIMESTAMP (buf) <= self->next_ts) { self->frames_rendered++; } else { GST_WARNING_OBJECT (self, "dropping frame : ts %" GST_TIME_FORMAT " < expected_ts %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (self->next_ts)); self->frames_dropped++; } } else { self->frames_rendered++; } } else #endif if (GST_IS_EVENT (mini_obj)) { GstEvent *ev = GST_EVENT_CAST (mini_obj); if (GST_EVENT_TYPE (ev) == GST_EVENT_QOS) { GstClockTimeDiff diff; GstClockTime ts; gst_event_parse_qos (ev, NULL, &diff, &ts); if (diff <= 0.0) { g_atomic_int_inc (&self->frames_rendered); } else { g_atomic_int_inc (&self->frames_dropped); } ts = gst_util_get_timestamp (); if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (self->start_ts))) { self->interval_ts = self->last_ts = self->start_ts = ts; } if (GST_CLOCK_DIFF (self->interval_ts, ts) > self->fps_update_interval) { display_current_fps (self); self->interval_ts = ts; } } } return TRUE; }
static void fps_display_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (object); switch (prop_id) { case PROP_SYNC: self->sync = g_value_get_boolean (value); fps_display_sink_update_sink_sync (self); break; case PROP_TEXT_OVERLAY: self->use_text_overlay = g_value_get_boolean (value); if (self->text_overlay) { if (!self->use_text_overlay) { GST_DEBUG_OBJECT (self, "text-overlay set to false"); g_object_set (self->text_overlay, "text", "", "silent", TRUE, NULL); } else { GST_DEBUG_OBJECT (self, "text-overlay set to true"); g_object_set (self->text_overlay, "silent", FALSE, NULL); } } break; case PROP_VIDEO_SINK: /* FIXME should we add a state-lock or a lock around here? * need to check if it is possible that a state change NULL->READY can * happen while this code is executing on a different thread */ if (GST_STATE (self) != GST_STATE_NULL) { g_warning ("Can't set video-sink property of fpsdisplaysink if not on " "NULL state"); break; } update_video_sink (self, (GstElement *) g_value_get_object (value)); break; case PROP_FPS_UPDATE_INTERVAL: self->fps_update_interval = GST_MSECOND * (GstClockTime) g_value_get_int (value); break; case PROP_SIGNAL_FPS_MEASUREMENTS: self->signal_measurements = g_value_get_boolean (value); break; case PROP_SILENT: self->silent = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static GstStateChangeReturn fps_display_sink_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: if (self->video_sink == NULL) { GstElement *video_sink; GST_DEBUG_OBJECT (self, "No video sink set, creating autovideosink"); video_sink = gst_element_factory_make ("autovideosink", "fps-display-video_sink"); update_video_sink (self, video_sink); } if (self->video_sink != NULL) { fps_display_sink_start (self); } else { GST_ELEMENT_ERROR (self, LIBRARY, INIT, ("No video sink set and autovideosink is not available"), (NULL)); ret = GST_STATE_CHANGE_FAILURE; } break; case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_PAUSED_TO_PLAYING: /* reinforce our sync to children, as they might have changed * internally */ fps_display_sink_update_sink_sync (self); break; default: break; } ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, (element, transition), GST_STATE_CHANGE_SUCCESS); switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: fps_display_sink_stop (self); break; default: break; } return ret; }
static void fps_display_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (object); switch (prop_id) { case PROP_SYNC: g_value_set_boolean (value, self->sync); break; case PROP_TEXT_OVERLAY: g_value_set_boolean (value, self->use_text_overlay); break; case PROP_VIDEO_SINK: g_value_set_object (value, self->video_sink); break; case PROP_FPS_UPDATE_INTERVAL: g_value_set_int (value, (gint) (self->fps_update_interval / GST_MSECOND)); break; case PROP_MAX_FPS: g_value_set_double (value, self->max_fps); break; case PROP_MIN_FPS: g_value_set_double (value, self->min_fps); break; case PROP_FRAMES_DROPPED: g_value_set_uint (value, g_atomic_int_get (&self->frames_dropped)); break; case PROP_FRAMES_RENDERED: g_value_set_uint (value, g_atomic_int_get (&self->frames_rendered)); break; case PROP_SIGNAL_FPS_MEASUREMENTS: g_value_set_boolean (value, self->signal_measurements); break; case PROP_SILENT: g_value_set_boolean (value, self->silent); break; case PROP_LAST_MESSAGE: GST_OBJECT_LOCK (self); g_value_set_string (value, self->last_message); GST_OBJECT_UNLOCK (self); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void fps_display_sink_dispose (GObject * object) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (object); if (self->video_sink) { gst_object_unref (self->video_sink); self->video_sink = NULL; } if (self->text_overlay) { gst_object_unref (self->text_overlay); self->text_overlay = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); }
static gboolean display_current_fps (gpointer data) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (data); gint64 current_ts; /* if query failed try again on next timer tick */ if (!gst_element_query (self->video_sink, self->query)) return TRUE; gst_query_parse_position (self->query, NULL, ¤t_ts); if (GST_CLOCK_TIME_IS_VALID (self->last_ts)) { gdouble rr, dr, average_fps; gchar fps_message[256]; gdouble time_diff = (gdouble) (current_ts - self->last_ts) / GST_SECOND; rr = (gdouble) (self->frames_rendered - self->last_frames_rendered) / time_diff; dr = (gdouble) (self->frames_dropped - self->last_frames_dropped) / time_diff; average_fps = self->frames_rendered / (gdouble) (current_ts / GST_SECOND); if (dr == 0.0) { g_snprintf (fps_message, 255, "current: %.2f\naverage: %.2f", rr, average_fps); } else { g_snprintf (fps_message, 255, "fps: %.2f\ndrop rate: %.2f", rr, dr); } if (self->use_text_overlay) { g_object_set (self->text_overlay, "text", fps_message, NULL); } else { g_print ("%s\n", fps_message); } } self->last_frames_rendered = self->frames_rendered; self->last_frames_dropped = self->frames_dropped; self->last_ts = current_ts; return TRUE; }
static gboolean on_video_sink_data_flow (GstPad * pad, GstMiniObject * mini_obj, gpointer user_data) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (user_data); #if 0 if (GST_IS_BUFFER (mini_obj)) { GstBuffer *buf = GST_BUFFER_CAST (mini_obj); if (GST_CLOCK_TIME_IS_VALID (self->next_ts)) { if (GST_BUFFER_TIMESTAMP (buf) <= self->next_ts) { self->frames_rendered++; } else { GST_WARNING_OBJECT (self, "dropping frame : ts %" GST_TIME_FORMAT " < expected_ts %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (self->next_ts)); self->frames_dropped++; } } else { self->frames_rendered++; } } else #endif if (GST_IS_EVENT (mini_obj)) { GstEvent *ev = GST_EVENT_CAST (mini_obj); if (GST_EVENT_TYPE (ev) == GST_EVENT_QOS) { GstClockTimeDiff diff; GstClockTime ts; gst_event_parse_qos (ev, NULL, &diff, &ts); self->next_ts = ts + diff; if (diff <= 0.0) { self->frames_rendered++; } else { self->frames_dropped++; } } } return TRUE; }
static void fps_display_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (object); switch (prop_id) { case ARG_SYNC: g_value_set_boolean (value, self->sync); break; case ARG_TEXT_OVERLAY: g_value_set_boolean (value, self->use_text_overlay); break; case ARG_VIDEO_SINK: g_value_set_object (value, self->video_sink); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean display_current_fps (gpointer data) { GstFPSDisplaySink *self = GST_FPS_DISPLAY_SINK (data); guint64 frames_rendered, frames_dropped; gdouble rr, dr, average_fps; gchar fps_message[256]; gdouble time_diff, time_elapsed; GstClockTime current_ts = gst_util_get_timestamp (); frames_rendered = g_atomic_int_get (&self->frames_rendered); frames_dropped = g_atomic_int_get (&self->frames_dropped); if ((frames_rendered + frames_dropped) == 0) { /* in case timer fired and we didn't yet get any QOS events */ return TRUE; } time_diff = (gdouble) (current_ts - self->last_ts) / GST_SECOND; time_elapsed = (gdouble) (current_ts - self->start_ts) / GST_SECOND; rr = (gdouble) (frames_rendered - self->last_frames_rendered) / time_diff; dr = (gdouble) (frames_dropped - self->last_frames_dropped) / time_diff; average_fps = (gdouble) frames_rendered / time_elapsed; if (self->max_fps == -1 || rr > self->max_fps) { self->max_fps = rr; GST_DEBUG_OBJECT (self, "Updated max-fps to %f", rr); } if (self->min_fps == -1 || rr < self->min_fps) { self->min_fps = rr; GST_DEBUG_OBJECT (self, "Updated min-fps to %f", rr); } if (self->signal_measurements) { GST_LOG_OBJECT (self, "Signaling measurements: fps:%f droprate:%f " "avg-fps:%f", rr, dr, average_fps); g_signal_emit (G_OBJECT (self), fpsdisplaysink_signals[SIGNAL_FPS_MEASUREMENTS], 0, rr, dr, average_fps); } /* Display on a single line to make it easier to read and import * into, for example, excel.. note: it would be nice to show * timestamp too.. need to check if there is a sane way to log * timestamp of last rendered buffer, so we could correlate dips * in framerate to certain positions in the stream. */ if (dr == 0.0) { g_snprintf (fps_message, 255, "rendered: %" G_GUINT64_FORMAT "\t dropped: %" G_GUINT64_FORMAT "\t current: %.2f\t average: %.2f", frames_rendered, frames_dropped, rr, average_fps); } else { g_snprintf (fps_message, 255, "rendered: %" G_GUINT64_FORMAT "\t dropped: %" G_GUINT64_FORMAT "\t fps: %.2f\t drop rate: %.2f", frames_rendered, frames_dropped, rr, dr); } if (self->use_text_overlay) { g_object_set (self->text_overlay, "text", fps_message, NULL); } else { g_print ("%s\n", fps_message); } self->last_frames_rendered = frames_rendered; self->last_frames_dropped = frames_dropped; self->last_ts = current_ts; return TRUE; }