summaryrefslogtreecommitdiffstats
path: root/gst
diff options
context:
space:
mode:
authorStefan Kost <ensonic@users.sf.net>2009-04-16 18:36:13 +0300
committerStefan Kost <ensonic@users.sf.net>2009-04-16 18:39:29 +0300
commite01373662a135eb01be6b7c9466a62aaf7e0f96f (patch)
tree31b1be8d47db57bb8720df1f1308785e07251622 /gst
parent78b208125ab915c94f90806442b33cd4eb3d85e1 (diff)
downloadgst-plugins-bad-e01373662a135eb01be6b7c9466a62aaf7e0f96f.tar.gz
gst-plugins-bad-e01373662a135eb01be6b7c9466a62aaf7e0f96f.tar.bz2
gst-plugins-bad-e01373662a135eb01be6b7c9466a62aaf7e0f96f.zip
fpsdisplaysink: add a initial port of a sink with fps display
This now works with a event probe. Needs some extra work.
Diffstat (limited to 'gst')
-rw-r--r--gst/debugutils/Makefile.am10
-rw-r--r--gst/debugutils/fpsdisplaysink.c524
-rw-r--r--gst/debugutils/fpsdisplaysink.h58
3 files changed, 592 insertions, 0 deletions
diff --git a/gst/debugutils/Makefile.am b/gst/debugutils/Makefile.am
new file mode 100644
index 00000000..0d2a989b
--- /dev/null
+++ b/gst/debugutils/Makefile.am
@@ -0,0 +1,10 @@
+plugin_LTLIBRARIES = libgstfpsdisplaysink.la
+
+libgstfpsdisplaysink_la_SOURCES = fpsdisplaysink.c
+libgstfpsdisplaysink_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
+libgstfpsdisplaysink_la_LIBADD = $(GST_BASE_LIBS) -lgstinterfaces-$(GST_MAJORMINOR)
+libgstfpsdisplaysink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstfpsdisplaysink_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = fpsdisplaysink.h
+
diff --git a/gst/debugutils/fpsdisplaysink.c b/gst/debugutils/fpsdisplaysink.c
new file mode 100644
index 00000000..4cc9da01
--- /dev/null
+++ b/gst/debugutils/fpsdisplaysink.c
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2009 Nokia Corporation <multimedia@maemo.org>
+ * 2006 Zeeshan Ali <zeeshan.ali@nokia.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ *
+ * |[
+ * gst-launch videotestsrc ! fpsdisplaysink
+ * gst-launch videotestsrc ! fpsdisplaysink text-overlay=false
+ * gst-launch filesrc location=video.avi ! decodebin2 name=d ! queue ! fpsdisplaysink d. ! queue ! fakesink sync=true
+ * ]|
+ */
+/* FIXME:
+ * - do we need to proxy xoverlay stuff?
+ * - can we avoid the
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "fpsdisplaysink.h"
+#include <gst/interfaces/xoverlay.h>
+
+#define FPS_DISPLAY_INTERVAL_MS 500 /* 500 ms */
+#define DEFAULT_FONT "20"
+
+static GstElementDetails fps_display_sink_details = {
+ "Wrapper for xvimagesink's frame-rate display",
+ "Sink/Video",
+ "Shows the current frame-rate and drop-rate of the xvimagesink on the xvimagesink's video output",
+ "Zeeshan Ali <zeeshan.ali@nokia.com>, Stefan Kost <stefan.kost@nokia.com>"
+};
+
+/* generic templates */
+static GstStaticPadTemplate fps_display_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (fps_display_sink_debug);
+#define GST_CAT_DEFAULT fps_display_sink_debug
+
+struct _FPSDisplaySinkPrivate
+{
+ GstElement *text_overlay;
+ GstElement *xvimagesink;
+
+ guint64 frames_rendered, last_frames_rendered;
+ guint64 frames_dropped, last_frames_dropped;
+ GstClockTime last_ts;
+ GstClockTime next_ts;
+
+ guint timeout_id;
+
+ gchar *font;
+ gboolean sync;
+ gboolean use_text_overlay;
+};
+
+enum
+{
+ ARG_0,
+ ARG_SYNC,
+ ARG_TEXT_OVERLAY,
+ /* FILL ME */
+};
+
+static GstBinClass *parent_class = NULL;
+
+static GstStateChangeReturn fps_display_sink_change_state (GstElement * element,
+ GstStateChange transition);
+static void fps_display_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void fps_display_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void fps_display_sink_dispose (GObject * object);
+
+static gboolean
+fps_display_sink_interface_supported (GstImplementsInterface * iface,
+ GType type)
+{
+ g_assert (type == GST_TYPE_X_OVERLAY);
+ return TRUE;
+}
+
+static void
+fps_display_sink_interface_init (GstImplementsInterfaceClass * klass)
+{
+ klass->supported = fps_display_sink_interface_supported;
+}
+
+static void
+fps_display_sink_set_xwindow_id (GstXOverlay * overlay, gulong xwindow_id)
+{
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (overlay);
+
+ gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (self->priv->xvimagesink),
+ xwindow_id);
+}
+
+static void
+fps_display_sink_expose (GstXOverlay * overlay)
+{
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (overlay);
+
+ gst_x_overlay_expose (GST_X_OVERLAY (self->priv->xvimagesink));
+}
+
+static void
+fps_display_sink_xoverlay_init (GstXOverlayClass * iface)
+{
+ iface->set_xwindow_id = fps_display_sink_set_xwindow_id;
+ iface->expose = fps_display_sink_expose;
+}
+
+static void
+fps_display_sink_handle_message (GstBin * bin, GstMessage * message)
+{
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (bin);
+
+ if (GST_MESSAGE_SRC (message) != NULL &&
+ GST_MESSAGE_SRC (message) == GST_OBJECT (self->priv->xvimagesink)) {
+ gst_object_unref (GST_MESSAGE_SRC (message));
+ GST_MESSAGE_SRC (message) = gst_object_ref (GST_OBJECT (bin));
+ }
+
+ GST_BIN_CLASS (parent_class)->handle_message (bin, message);
+}
+
+static void
+fps_display_sink_class_init (FPSDisplaySinkClass * klass)
+{
+ GObjectClass *gobject_klass = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (klass);
+ GstBinClass *gstbin_klass = GST_BIN_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_klass->set_property = fps_display_sink_set_property;
+ gobject_klass->get_property = fps_display_sink_get_property;
+ gobject_klass->dispose = fps_display_sink_dispose;
+
+ g_object_class_install_property (gobject_klass, ARG_SYNC,
+ g_param_spec_boolean ("sync",
+ "Sync", "Sync on the clock", FALSE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_klass, ARG_TEXT_OVERLAY,
+ g_param_spec_boolean ("text-overlay",
+ "text-overlay",
+ "Wether to use text-overlay", TRUE, G_PARAM_READWRITE));
+
+ gstelement_klass->change_state = fps_display_sink_change_state;
+
+ gstbin_klass->handle_message = fps_display_sink_handle_message;
+
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&fps_display_sink_template));
+
+ gst_element_class_set_details (gstelement_klass, &fps_display_sink_details);
+
+ GST_DEBUG_CATEGORY_INIT (fps_display_sink_debug, "fpsdisplaysink", 0,
+ "FPS Display Sink");
+
+ g_type_class_add_private (klass, sizeof (FPSDisplaySinkPrivate));
+}
+
+static void
+fps_display_sink_base_init (gpointer klass)
+{
+}
+
+#if 0
+static void
+on_xvimagesink_caps_negotiated (GstPad * pad, GParamSpec * arg,
+ gpointer user_data)
+{
+ GstCaps *caps = gst_pad_get_negotiated_caps (pad);
+
+ g_print ("caps negotiated on xvimagesink = %s\n", gst_caps_to_string (caps));
+ gst_caps_unref (caps);
+}
+#endif
+
+static gboolean
+on_xvimagesink_data_flow (GstPad * pad, GstMiniObject * mini_obj,
+ gpointer user_data)
+{
+ FPSDisplaySink *self = 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->priv->next_ts)) {
+ if (GST_BUFFER_TIMESTAMP (buf) <= self->priv->next_ts) {
+ self->priv->frames_rendered++;
+ } else {
+ g_debug ("dropping frame : ts %" GST_TIME_FORMAT " < expected_ts %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (self->priv->next_ts));
+ self->priv->frames_dropped++;
+ }
+ } else {
+ self->priv->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->priv->next_ts = ts + diff;
+ if (diff <= 0.0) {
+ self->priv->frames_rendered++;
+ } else {
+ self->priv->frames_dropped++;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static void
+fps_display_sink_init (FPSDisplaySink * self, FPSDisplaySinkClass * g_class)
+{
+ GstPad *sink_pad, *target_pad;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FPS_TYPE_DISPLAY_SINK,
+ FPSDisplaySinkPrivate);
+
+ self->priv->font = DEFAULT_FONT;
+ self->priv->sync = FALSE;
+ self->priv->use_text_overlay = TRUE;
+
+ self->priv->xvimagesink =
+ gst_element_factory_make ("xvimagesink", "fps_display_xvimagesink");
+ self->priv->text_overlay =
+ gst_element_factory_make ("textoverlay", "text-overlay");
+
+ if (!self->priv->xvimagesink || !self->priv->text_overlay) {
+ g_debug ("element could not be created");
+ return;
+ }
+
+ g_object_set (self->priv->xvimagesink, "sync", self->priv->sync, NULL);
+ g_object_set (self->priv->text_overlay, "font-desc", self->priv->font, NULL);
+
+ /* take a ref before bin takes the ownership */
+ gst_object_ref (self->priv->text_overlay);
+ gst_object_ref (self->priv->xvimagesink);
+
+ gst_bin_add_many (GST_BIN (self),
+ self->priv->text_overlay, self->priv->xvimagesink, NULL);
+
+ if (!gst_element_link_many (self->priv->text_overlay, self->priv->xvimagesink,
+ NULL)) {
+ g_error ("Could not link elements");
+ }
+
+ target_pad =
+ gst_element_get_static_pad (self->priv->text_overlay, "video_sink");
+ sink_pad = gst_ghost_pad_new ("sink_pad", target_pad);
+ gst_object_unref (target_pad);
+ gst_element_add_pad (GST_ELEMENT (self), sink_pad);
+
+ sink_pad = gst_element_get_static_pad (self->priv->xvimagesink, "sink");
+#if 0
+ g_signal_connect (sink_pad, "notify::caps",
+ G_CALLBACK (on_xvimagesink_caps_negotiated), NULL);
+#endif
+ gst_pad_add_data_probe (sink_pad, G_CALLBACK (on_xvimagesink_data_flow),
+ (gpointer) self);
+
+ gst_object_unref (sink_pad);
+}
+
+static gboolean
+display_current_fps (gpointer data)
+{
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (data);
+ gint64 current_ts;
+#if 0
+ guint64 frames_rendered, frames_dropped;
+#endif
+ GstQuery *query;
+
+#if 0
+ query = gst_query_new_qos ();
+ gst_element_query (self->priv->xvimagesink, query);
+ gst_query_parse_qos (query, &frames_rendered, &frames_dropped);
+ gst_query_unref (query);
+#endif
+
+ query = gst_query_new_position (GST_FORMAT_TIME);
+ gst_element_query (self->priv->xvimagesink, query);
+ gst_query_parse_position (query, NULL, &current_ts);
+ gst_query_unref (query);
+
+ if (GST_CLOCK_TIME_IS_VALID (self->priv->last_ts)) {
+ gdouble rr, dr, average_fps;
+ gchar fps_message[256];
+ gdouble time_diff =
+ (gdouble) (current_ts - self->priv->last_ts) / GST_SECOND;
+
+ rr = (gdouble) (self->priv->frames_rendered -
+ self->priv->last_frames_rendered) / time_diff;
+ dr = (gdouble) (self->priv->frames_dropped -
+ self->priv->last_frames_dropped) / time_diff;
+
+ average_fps =
+ self->priv->frames_rendered / (gdouble) (current_ts / GST_SECOND);
+
+ if (dr == 0.0) {
+ sprintf (fps_message, "current: %.2f\naverage: %.2f", rr, average_fps);
+ } else {
+ sprintf (fps_message, "fps: %.2f\ndrop rate: %.2f", rr, dr);
+ }
+
+ if (self->priv->use_text_overlay) {
+ g_object_set (self->priv->text_overlay, "text", fps_message, NULL);
+ } else {
+ g_print ("%s\n", fps_message);
+ }
+ }
+
+ self->priv->last_frames_rendered = self->priv->frames_rendered;
+ self->priv->last_frames_dropped = self->priv->frames_dropped;
+ self->priv->last_ts = current_ts;
+
+ return TRUE;
+}
+
+static void
+fps_display_sink_start_the_show (FPSDisplaySink * self)
+{
+ /* Init counters */
+ self->priv->next_ts = GST_CLOCK_TIME_NONE;
+ self->priv->last_ts = GST_CLOCK_TIME_NONE;
+ self->priv->frames_rendered = G_GUINT64_CONSTANT (0);
+ self->priv->frames_dropped = G_GUINT64_CONSTANT (0);
+
+ /* Set a timeout for the fps display */
+ self->priv->timeout_id =
+ g_timeout_add (FPS_DISPLAY_INTERVAL_MS,
+ display_current_fps, (gpointer) self);
+}
+
+static void
+fps_display_sink_stop_the_show (FPSDisplaySink * self)
+{
+ /* remove the timeout */
+ if (self->priv->timeout_id) {
+ g_source_remove (self->priv->timeout_id);
+ self->priv->timeout_id = 0;
+ }
+}
+
+static void
+fps_display_sink_dispose (GObject * object)
+{
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (object);
+
+ if (self->priv->xvimagesink) {
+ gst_object_unref (self->priv->xvimagesink);
+ self->priv->xvimagesink = NULL;
+ }
+
+ if (self->priv->text_overlay) {
+ gst_object_unref (self->priv->text_overlay);
+ self->priv->text_overlay = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+fps_display_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (object);
+
+ switch (prop_id) {
+ case ARG_SYNC:
+ self->priv->sync = g_value_get_boolean (value);
+ g_object_set (self->priv->xvimagesink, "sync", self->priv->sync, NULL);
+ break;
+ case ARG_TEXT_OVERLAY:
+ self->priv->use_text_overlay = g_value_get_boolean (value);
+
+ if (!(self->priv->use_text_overlay)) {
+ g_print ("text-overlay set to false\n");
+ g_object_set (self->priv->text_overlay, "text", "", "silent", TRUE,
+ NULL);
+ } else {
+ g_print ("text-overlay set to true\n");
+ g_object_set (self->priv->text_overlay, "silent", FALSE, NULL);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+fps_display_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (object);
+
+ switch (prop_id) {
+ case ARG_SYNC:
+ g_value_set_boolean (value, self->priv->sync);
+ break;
+ case ARG_TEXT_OVERLAY:
+ g_value_set_boolean (value, self->priv->use_text_overlay);
+ 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;
+ FPSDisplaySink *self = FPS_DISPLAY_SINK (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ fps_display_sink_start_the_show (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_the_show (self);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+GType
+fps_display_sink_get_type (void)
+{
+ static GType fps_display_sink_type = 0;
+
+ if (!fps_display_sink_type) {
+ static const GTypeInfo fps_display_sink_info = {
+ sizeof (FPSDisplaySinkClass),
+ fps_display_sink_base_init,
+ NULL,
+ (GClassInitFunc) fps_display_sink_class_init,
+ NULL,
+ NULL,
+ sizeof (FPSDisplaySink),
+ 0,
+ (GInstanceInitFunc) fps_display_sink_init,
+ };
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) fps_display_sink_interface_init,
+ NULL,
+ NULL,
+ };
+ static const GInterfaceInfo overlay_info = {
+ (GInterfaceInitFunc) fps_display_sink_xoverlay_init,
+ NULL,
+ NULL,
+ };
+
+ fps_display_sink_type = g_type_register_static (GST_TYPE_BIN,
+ "FPSDisplaySink", &fps_display_sink_info, 0);
+
+ g_type_add_interface_static (fps_display_sink_type,
+ GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
+ g_type_add_interface_static (fps_display_sink_type, GST_TYPE_X_OVERLAY,
+ &overlay_info);
+ }
+
+ return fps_display_sink_type;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "fpsdisplaysink",
+ GST_RANK_NONE, FPS_TYPE_DISPLAY_SINK);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "fpsdisplaysink",
+ "A custom sink that show the current FPS of the sink on the video screen",
+ plugin_init, VERSION, "LGPL", "Nokia Real-time Communications", "")
diff --git a/gst/debugutils/fpsdisplaysink.h b/gst/debugutils/fpsdisplaysink.h
new file mode 100644
index 00000000..c6230075
--- /dev/null
+++ b/gst/debugutils/fpsdisplaysink.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2009 Nokia Corporation <multimedia@maemo.org>
+ * 2006 Zeeshan Ali <zeeshan.ali@nokia.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __FPS_DISPLAY_SINK_H__
+#define __FPS_DISPLAY_SINK_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define FPS_TYPE_DISPLAY_SINK \
+ (fps_display_sink_get_type())
+#define FPS_DISPLAY_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),FPS_TYPE_DISPLAY_SINK,FPSDisplaySink))
+#define FPS_DISPLAY_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),FPS_TYPE_DISPLAY_SINK,FPSDisplaySinkClass))
+#define FPS_IS_DISPLAY_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),FPS_TYPE_DISPLAY_SINK))
+#define FPS_IS_DISPLAY_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),FPS_TYPE_DISPLAY_SINK))
+
+GType fps_display_sink_get_type (void);
+
+typedef struct _FPSDisplaySink FPSDisplaySink;
+typedef struct _FPSDisplaySinkClass FPSDisplaySinkClass;
+
+typedef struct _FPSDisplaySinkPrivate FPSDisplaySinkPrivate;
+
+struct _FPSDisplaySink
+{
+ GstBin bin; /* we extend GstBin */
+ FPSDisplaySinkPrivate *priv;
+};
+
+struct _FPSDisplaySinkClass
+{
+ GstBinClass parent_class;
+};
+
+G_END_DECLS
+
+#endif /* __FPS_DISPLAY_SINK_H__ */