From f0c676c0f9aca859ed6c2fe4ebabb8dce5bb162c Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Mon, 19 Feb 2018 22:02:14 -0500 Subject: Add fakevideosink element This is a wrapper around fakesink that will advertise GstVideoMeta and other meta API in order to achieve zero-copy whenever possible. his new element is useful when doing performance testing with video stream and don't want the sink capability to change the upstream behaviour. https://bugzilla.gnome.org/show_bug.cgi?id=793624 --- gst/debugutils/Makefile.am | 6 +- gst/debugutils/debugutilsbad.c | 3 + gst/debugutils/gstfakevideosink.c | 207 ++++++++++++++++++++++++++++++++++++++ gst/debugutils/gstfakevideosink.h | 60 +++++++++++ gst/debugutils/meson.build | 1 + 5 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 gst/debugutils/gstfakevideosink.c create mode 100644 gst/debugutils/gstfakevideosink.h diff --git a/gst/debugutils/Makefile.am b/gst/debugutils/Makefile.am index ee8e57dd3..1113c5e55 100644 --- a/gst/debugutils/Makefile.am +++ b/gst/debugutils/Makefile.am @@ -8,7 +8,8 @@ libgstdebugutilsbad_la_SOURCES = \ gstchopmydata.c \ gstcompare.c \ gstwatchdog.c \ - gsterrorignore.c + gsterrorignore.c \ + gstfakevideosink.c libgstdebugutilsbad_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) libgstdebugutilsbad_la_LIBADD = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \ @@ -22,4 +23,5 @@ noinst_HEADERS = fpsdisplaysink.h \ gstcompare.h \ gstdebugspy.h \ gstwatchdog.h \ - gsterrorignore.h + gsterrorignore.h \ + gstfakevideosink.h diff --git a/gst/debugutils/debugutilsbad.c b/gst/debugutils/debugutilsbad.c index 68edd7829..cc7af306d 100644 --- a/gst/debugutils/debugutilsbad.c +++ b/gst/debugutils/debugutilsbad.c @@ -30,6 +30,7 @@ GType gst_compare_get_type (void); GType gst_debug_spy_get_type (void); GType gst_error_ignore_get_type (void); GType gst_watchdog_get_type (void); +GType gst_fake_video_sink_get_type (void); static gboolean plugin_init (GstPlugin * plugin) @@ -48,6 +49,8 @@ plugin_init (GstPlugin * plugin) gst_watchdog_get_type ()); gst_element_register (plugin, "errorignore", GST_RANK_NONE, gst_error_ignore_get_type ()); + gst_element_register (plugin, "fakevideosink", GST_RANK_NONE, + gst_fake_video_sink_get_type ()); return TRUE; } diff --git a/gst/debugutils/gstfakevideosink.c b/gst/debugutils/gstfakevideosink.c new file mode 100644 index 000000000..9e3cf59b9 --- /dev/null +++ b/gst/debugutils/gstfakevideosink.c @@ -0,0 +1,207 @@ +/* + * GStreamer + * Copyright (C) 2017 Collabora Inc. + * Author: Nicolas Dufresne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "gstfakevideosink.h" + +#include + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", + GST_VIDEO_FORMATS_ALL))); + +G_DEFINE_TYPE (GstFakeVideoSink, gst_fake_video_sink, GST_TYPE_BIN); + +static gboolean +gst_fake_video_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) +{ + GstCaps *caps; + GstVideoInfo info; + + if (GST_QUERY_TYPE (query) != GST_QUERY_ALLOCATION) + return gst_pad_query_default (pad, parent, query); + + gst_query_parse_allocation (query, &caps, NULL); + if (!gst_video_info_from_caps (&info, caps)) + return FALSE; + + gst_query_add_allocation_pool (query, NULL, info.size, 1, 0); + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL); + gst_query_add_allocation_meta (query, + GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL); + /* add here any meta API that would help support zero-copy */ + + return TRUE; +} + +/* TODO complete the types and make this an utility */ +static void +gst_fake_video_sink_proxy_properties (GstFakeVideoSink * self, + GstElement * child) +{ + static gsize initialized = 0; + + if (g_once_init_enter (&initialized)) { + GObjectClass *object_class; + GParamSpec **properties; + guint n_properties, i; + + object_class = G_OBJECT_CLASS (GST_FAKE_VIDEO_SINK_GET_CLASS (self)); + properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (child), + &n_properties); + + for (i = 0; i < n_properties; i++) { + if (properties[i]->owner_type != G_OBJECT_TYPE (child) && + properties[i]->owner_type != GST_TYPE_BASE_SINK) + continue; + + if (G_IS_PARAM_SPEC_BOOLEAN (properties[i])) { + GParamSpecBoolean *prop = G_PARAM_SPEC_BOOLEAN (properties[i]); + g_object_class_install_property (object_class, i + 1, + g_param_spec_boolean (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + prop->default_value, properties[i]->flags)); + } else if (G_IS_PARAM_SPEC_INT (properties[i])) { + GParamSpecInt *prop = G_PARAM_SPEC_INT (properties[i]); + g_object_class_install_property (object_class, i + 1, + g_param_spec_int (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + prop->minimum, prop->maximum, prop->default_value, + properties[i]->flags)); + } else if (G_IS_PARAM_SPEC_UINT (properties[i])) { + GParamSpecUInt *prop = G_PARAM_SPEC_UINT (properties[i]); + g_object_class_install_property (object_class, i + 1, + g_param_spec_uint (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + prop->minimum, prop->maximum, prop->default_value, + properties[i]->flags)); + } else if (G_IS_PARAM_SPEC_INT64 (properties[i])) { + GParamSpecInt64 *prop = G_PARAM_SPEC_INT64 (properties[i]); + g_object_class_install_property (object_class, i + 1, + g_param_spec_int64 (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + prop->minimum, prop->maximum, prop->default_value, + properties[i]->flags)); + } else if (G_IS_PARAM_SPEC_UINT64 (properties[i])) { + GParamSpecUInt64 *prop = G_PARAM_SPEC_UINT64 (properties[i]); + g_object_class_install_property (object_class, i + 1, + g_param_spec_uint64 (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + prop->minimum, prop->maximum, prop->default_value, + properties[i]->flags)); + } else if (G_IS_PARAM_SPEC_ENUM (properties[i])) { + GParamSpecEnum *prop = G_PARAM_SPEC_ENUM (properties[i]); + g_object_class_install_property (object_class, i + 1, + g_param_spec_enum (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + properties[i]->value_type, prop->default_value, + properties[i]->flags)); + } else if (G_IS_PARAM_SPEC_STRING (properties[i])) { + GParamSpecString *prop = G_PARAM_SPEC_STRING (properties[i]); + g_object_class_install_property (object_class, i + 1, + g_param_spec_string (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + prop->default_value, properties[i]->flags)); + } else if (G_IS_PARAM_SPEC_BOXED (properties[i])) { + g_object_class_install_property (object_class, i + 1, + g_param_spec_boxed (g_param_spec_get_name (properties[i]), + g_param_spec_get_nick (properties[i]), + g_param_spec_get_blurb (properties[i]), + properties[i]->value_type, properties[i]->flags)); + } + } + + g_free (properties); + g_once_init_leave (&initialized, 1); + } +} + +static void +gst_fake_video_sink_init (GstFakeVideoSink * self) +{ + GstElement *child; + + child = gst_element_factory_make ("fakesink", "sink"); + + if (child) { + GstPad *sink_pad = gst_element_get_static_pad (child, "sink"); + GstPad *ghost_pad; + + /* mimic GstVideoSink base class */ + g_object_set (child, "max-lateness", 20000000, "qos", TRUE, "sync", TRUE, + NULL); + + gst_bin_add (GST_BIN (self), child); + + ghost_pad = gst_ghost_pad_new ("sink", sink_pad); + gst_element_add_pad (GST_ELEMENT (self), ghost_pad); + gst_object_unref (sink_pad); + + gst_pad_set_query_function (ghost_pad, gst_fake_video_sink_query); + + self->child = child; + + gst_fake_video_sink_proxy_properties (self, child); + } else { + g_warning ("Check your GStreamer installation, " + "core element 'fakesink' is missing."); + } +} + +static void +gst_fake_video_sink_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (object); + g_object_get_property (G_OBJECT (self->child), pspec->name, value); +} + +static void +gst_fake_video_sink_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + GstFakeVideoSink *self = GST_FAKE_VIDEO_SINK (object); + g_object_set_property (G_OBJECT (self->child), pspec->name, value); +} + +static void +gst_fake_video_sink_class_init (GstFakeVideoSinkClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gst_fake_video_sink_get_property; + object_class->set_property = gst_fake_video_sink_set_property; + + gst_element_class_add_static_pad_template (element_class, &sink_factory); + gst_element_class_set_static_metadata (element_class, "Fake Video Sink", + "Video/Sink", "Fake video display that allow zero-copy", + "Nicolas Dufresne "); +} diff --git a/gst/debugutils/gstfakevideosink.h b/gst/debugutils/gstfakevideosink.h new file mode 100644 index 000000000..ec24c2df5 --- /dev/null +++ b/gst/debugutils/gstfakevideosink.h @@ -0,0 +1,60 @@ +/* + * GStreamer + * Copyright (C) 2017 Collabora Inc. + * Author: Nicolas Dufresne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_FAKE_VIDEO_SINK_H_ +#define _GST_FAKE_VIDEO_SINK_H_ + +#include + +#define GST_TYPE_FAKE_VIDEO_SINK \ + (gst_fake_video_sink_get_type()) +#define GST_FAKE_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_FAKE_VIDEO_SINK, GstFakeVideoSink)) +#define GST_FAKE_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_FAKE_VIDEO_SINK, GstFakeVideoSinkClass)) +#define GST_FAKE_VIDEO_SINK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_FAKE_VIDEO_SINK, GstFakeVideoSinkClass)) +#define GST_IS_FAKE_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_FAKE_VIDEO_SINK)) +#define GST_IS_FAKE_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_FAKE_VIDEO_SINK)) + +G_BEGIN_DECLS + +typedef struct _GstFakeVideoSink GstFakeVideoSink; +typedef struct _GstFakeVideoSinkClass GstFakeVideoSinkClass; + +struct _GstFakeVideoSink +{ + GstBin parent; + GstElement *child; +}; + +struct _GstFakeVideoSinkClass +{ + GstBinClass parent; +}; + +GType gst_fake_video_sink_get_type (void); + +G_END_DECLS + +#endif diff --git a/gst/debugutils/meson.build b/gst/debugutils/meson.build index 1b9434db1..fb236dbb7 100644 --- a/gst/debugutils/meson.build +++ b/gst/debugutils/meson.build @@ -6,6 +6,7 @@ debugutilsbad_sources = [ 'gstchecksumsink.c', 'gstchopmydata.c', 'gstcompare.c', + 'gstfakevideosink.c', 'gstwatchdog.c', ] -- cgit v1.2.1