diff options
Diffstat (limited to 'gst')
-rw-r--r-- | gst/meson.build | 1 | ||||
-rw-r--r-- | gst/proxy/Makefile.am | 27 | ||||
-rw-r--r-- | gst/proxy/gstproxy-priv.h | 37 | ||||
-rw-r--r-- | gst/proxy/gstproxy.c | 43 | ||||
-rw-r--r-- | gst/proxy/gstproxysink.c | 301 | ||||
-rw-r--r-- | gst/proxy/gstproxysink.h | 56 | ||||
-rw-r--r-- | gst/proxy/gstproxysrc.c | 358 | ||||
-rw-r--r-- | gst/proxy/gstproxysrc.h | 56 | ||||
-rw-r--r-- | gst/proxy/meson.build | 14 |
9 files changed, 893 insertions, 0 deletions
diff --git a/gst/meson.build b/gst/meson.build index 1017adf30..b56b68d26 100644 --- a/gst/meson.build +++ b/gst/meson.build @@ -42,6 +42,7 @@ subdir('netsim') subdir('onvif') subdir('pcapparse') subdir('pnm') +subdir('proxy') subdir('rawparse') subdir('removesilence') subdir('sdp') diff --git a/gst/proxy/Makefile.am b/gst/proxy/Makefile.am new file mode 100644 index 000000000..476a3534e --- /dev/null +++ b/gst/proxy/Makefile.am @@ -0,0 +1,27 @@ +plugin_LTLIBRARIES = libgstproxy.la + +libgstproxy_la_SOURCES = \ + gstproxy.c \ + gstproxysink.c \ + gstproxysrc.c + +noinst_HEADERS = \ + gstproxysink.h \ + gstproxysrc.h \ + gstproxy-priv.h + +libgstproxy_la_CFLAGS = $(GST_CFLAGS) +libgstproxy_la_LIBADD = $(GST_LIBS) +libgstproxy_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstproxy_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +gstproxytest_SOURCES = \ + gstproxytest.c + +gstproxytest_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) + +gstproxytest_LDADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + $(GST_LIBS) diff --git a/gst/proxy/gstproxy-priv.h b/gst/proxy/gstproxy-priv.h new file mode 100644 index 000000000..d15da9aa5 --- /dev/null +++ b/gst/proxy/gstproxy-priv.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 Centricular Ltd. + * Author: Nirbheek Chauhan <nirbheek@centricular.com> + * + * 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_PROXY_PRIV_H__ +#define __GST_PROXY_PRIV_H__ + +#include "gstproxysrc.h" +#include "gstproxysink.h" + +G_BEGIN_DECLS + +void gst_proxy_sink_set_proxysrc (GstProxySink *sink, GstProxySrc *src); + +GstPad* gst_proxy_sink_get_internal_sinkpad (GstProxySink *sink); + +GstPad* gst_proxy_src_get_internal_srcpad (GstProxySrc *src); + +G_END_DECLS + +#endif /* __GST_PROXY_PRIV_H__ */ diff --git a/gst/proxy/gstproxy.c b/gst/proxy/gstproxy.c new file mode 100644 index 000000000..03e188891 --- /dev/null +++ b/gst/proxy/gstproxy.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge <sebastian@centricular.com> + * Author: Nirbheek Chauhan <nirbheek@centricular.com> + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstproxysrc.h" +#include "gstproxysink.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gst_element_register (plugin, "proxysrc", GST_RANK_NONE, GST_TYPE_PROXY_SRC); + gst_element_register (plugin, "proxysink", GST_RANK_NONE, + GST_TYPE_PROXY_SINK); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + proxy, + "plugin for proxied inter-pipeline communication", + plugin_init, VERSION, "LGPL", "gstproxy", "http://centricular.com") diff --git a/gst/proxy/gstproxysink.c b/gst/proxy/gstproxysink.c new file mode 100644 index 000000000..8976783df --- /dev/null +++ b/gst/proxy/gstproxysink.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2018 Centricular Ltd. + * Author: Sebastian Dröge <sebastian@centricular.com> + * Author: Nirbheek Chauhan <nirbheek@centricular.com> + * + * 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. + */ + +/** + * SECTION:element-proxysink + * @title: proxysink + * + * Proxysink is a sink element that proxies events, queries, and buffers to + * another pipeline that contains a matching proxysrc element. The purpose is + * to allow two decoupled pipelines to function as though they are one without + * having to manually shuttle buffers, events, queries, etc between the two. + * + * This element also copies sticky events onto the matching proxysrc element. + * + * For example usage, see proxysrc. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gstproxysink.h" +#include "gstproxysrc.h" +#include "gstproxy-priv.h" + +#define GST_CAT_DEFAULT gst_proxy_sink_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +struct _GstProxySinkPrivate +{ + GstPad *sinkpad; + /* The proxysrc that we push events, buffers, queries to */ + GWeakRef proxysrc; + /* Whether there are sticky events pending */ + gboolean pending_sticky_events; +}; + +/* We're not subclassing from basesink because we don't want any of the special + * handling it has for events/queries/etc. We just pass-through everything. */ + +/* Unlink proxysrc, we don't contain any elements so our parent is GstElement */ +#define parent_class gst_proxy_sink_parent_class +G_DEFINE_TYPE (GstProxySink, gst_proxy_sink, GST_TYPE_ELEMENT); + +static gboolean gst_proxy_sink_sink_query (GstPad * pad, GstObject * parent, + GstQuery * query); +static GstFlowReturn gst_proxy_sink_sink_chain (GstPad * pad, + GstObject * parent, GstBuffer * buffer); +static GstFlowReturn gst_proxy_sink_sink_chain_list (GstPad * pad, + GstObject * parent, GstBufferList * list); +static gboolean gst_proxy_sink_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); + +static GstStateChangeReturn gst_proxy_sink_change_state (GstElement * element, + GstStateChange transition); + +static void +gst_proxy_sink_class_init (GstProxySinkClass * klass) +{ + GstElementClass *gstelement_class = (GstElementClass *) klass; + + GST_DEBUG_CATEGORY_INIT (gst_proxy_sink_debug, "proxysink", 0, "proxy sink"); + + g_type_class_add_private (klass, sizeof (GstProxySinkPrivate)); + + gstelement_class->change_state = gst_proxy_sink_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "Proxy Sink", + "Sink", "Proxy source for internal process communication", + "Sebastian Dröge <sebastian@centricular.com>"); +} + +static void +gst_proxy_sink_init (GstProxySink * self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_PROXY_SINK, + GstProxySinkPrivate); + self->priv->sinkpad = + gst_pad_new_from_static_template (&sink_template, "sink"); + gst_pad_set_chain_function (self->priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_chain)); + gst_pad_set_chain_list_function (self->priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_chain_list)); + gst_pad_set_event_function (self->priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_event)); + gst_pad_set_query_function (self->priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_query)); + gst_element_add_pad (GST_ELEMENT (self), self->priv->sinkpad); +} + +static GstStateChangeReturn +gst_proxy_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstElementClass *gstelement_class = + GST_ELEMENT_CLASS (gst_proxy_sink_parent_class); + GstProxySink *self = GST_PROXY_SINK (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + self->priv->pending_sticky_events = FALSE; + break; + default: + break; + } + + ret = gstelement_class->change_state (element, transition); + + return ret; +} + +static gboolean +gst_proxy_sink_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) +{ + GstProxySink *self = GST_PROXY_SINK (parent); + GstProxySrc *src; + gboolean ret = FALSE; + + GST_LOG_OBJECT (pad, "Handling query of type '%s'", + gst_query_type_get_name (GST_QUERY_TYPE (query))); + + src = g_weak_ref_get (&self->priv->proxysrc); + if (src) { + GstPad *srcpad; + srcpad = gst_proxy_src_get_internal_srcpad (src); + + ret = gst_pad_peer_query (srcpad, query); + gst_object_unref (srcpad); + gst_object_unref (src); + } + + return ret; +} + +typedef struct +{ + GstPad *otherpad; + GstFlowReturn ret; +} CopyStickyEventsData; + +static gboolean +copy_sticky_events (G_GNUC_UNUSED GstPad * pad, GstEvent ** event, + gpointer user_data) +{ + CopyStickyEventsData *data = user_data; + + data->ret = gst_pad_store_sticky_event (data->otherpad, *event); + + return data->ret == GST_FLOW_OK; +} + +static gboolean +gst_proxy_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstProxySink *self = GST_PROXY_SINK (parent); + GstProxySrc *src; + gboolean ret = FALSE; + gboolean sticky = GST_EVENT_IS_STICKY (event); + + GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); + + if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) + self->priv->pending_sticky_events = FALSE; + + src = g_weak_ref_get (&self->priv->proxysrc); + if (src) { + GstPad *srcpad; + srcpad = gst_proxy_src_get_internal_srcpad (src); + + if (sticky && self->priv->pending_sticky_events) { + CopyStickyEventsData data = { srcpad, GST_FLOW_OK }; + + gst_pad_sticky_events_foreach (pad, copy_sticky_events, &data); + self->priv->pending_sticky_events = data.ret != GST_FLOW_OK; + } + + ret = gst_pad_push_event (srcpad, event); + gst_object_unref (srcpad); + gst_object_unref (src); + + if (!ret && sticky) { + self->priv->pending_sticky_events = TRUE; + ret = TRUE; + } + } else + gst_event_unref (event); + + return ret; +} + +static GstFlowReturn +gst_proxy_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstProxySink *self = GST_PROXY_SINK (parent); + GstProxySrc *src; + GstFlowReturn ret = GST_FLOW_OK; + + GST_LOG_OBJECT (pad, "Chaining buffer %p", buffer); + + src = g_weak_ref_get (&self->priv->proxysrc); + if (src) { + GstPad *srcpad; + srcpad = gst_proxy_src_get_internal_srcpad (src); + + if (self->priv->pending_sticky_events) { + CopyStickyEventsData data = { srcpad, GST_FLOW_OK }; + + gst_pad_sticky_events_foreach (pad, copy_sticky_events, &data); + self->priv->pending_sticky_events = data.ret != GST_FLOW_OK; + } + + ret = gst_pad_push (srcpad, buffer); + gst_object_unref (srcpad); + gst_object_unref (src); + + GST_LOG_OBJECT (pad, "Chained buffer %p: %s", buffer, + gst_flow_get_name (ret)); + } else { + gst_buffer_unref (buffer); + GST_LOG_OBJECT (pad, "Dropped buffer %p: no otherpad", buffer); + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_proxy_sink_sink_chain_list (GstPad * pad, GstObject * parent, + GstBufferList * list) +{ + GstProxySink *self = GST_PROXY_SINK (parent); + GstProxySrc *src; + GstFlowReturn ret = GST_FLOW_OK; + + GST_LOG_OBJECT (pad, "Chaining buffer list %p", list); + + src = g_weak_ref_get (&self->priv->proxysrc); + if (src) { + GstPad *srcpad; + srcpad = gst_proxy_src_get_internal_srcpad (src); + + if (self->priv->pending_sticky_events) { + CopyStickyEventsData data = { srcpad, GST_FLOW_OK }; + + gst_pad_sticky_events_foreach (pad, copy_sticky_events, &data); + self->priv->pending_sticky_events = data.ret != GST_FLOW_OK; + } + + ret = gst_pad_push_list (srcpad, list); + gst_object_unref (srcpad); + gst_object_unref (src); + GST_LOG_OBJECT (pad, "Chained buffer list %p: %s", list, + gst_flow_get_name (ret)); + } else { + gst_buffer_list_unref (list); + GST_LOG_OBJECT (pad, "Dropped buffer list %p: no otherpad", list); + } + + return GST_FLOW_OK; +} + +/* Wrapper function for accessing private member + * This can also be retrieved with gst_element_get_static_pad, but that depends + * on the implementation of GstProxySink */ +GstPad * +gst_proxy_sink_get_internal_sinkpad (GstProxySink * self) +{ + g_return_val_if_fail (self, NULL); + return gst_object_ref (self->priv->sinkpad); +} + +void +gst_proxy_sink_set_proxysrc (GstProxySink * self, GstProxySrc * src) +{ + g_return_if_fail (self); + g_weak_ref_set (&self->priv->proxysrc, src); +} diff --git a/gst/proxy/gstproxysink.h b/gst/proxy/gstproxysink.h new file mode 100644 index 000000000..9952cfdef --- /dev/null +++ b/gst/proxy/gstproxysink.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge <sebastian@centricular.com> + * Author: Nirbheek Chauhan <nirbheek@centricular.com> + * + * 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_PROXY_SINK_H__ +#define __GST_PROXY_SINK_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_PROXY_SINK (gst_proxy_sink_get_type()) +#define GST_PROXY_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_PROXY_SINK, GstProxySink)) +#define GST_IS_PROXY_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_PROXY_SINK)) +#define GST_PROXY_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) , GST_TYPE_PROXY_SINK, GstProxySinkClass)) +#define GST_IS_PROXY_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) , GST_TYPE_PROXY_SINK)) +#define GST_PROXY_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) , GST_TYPE_PROXY_SINK, GstProxySinkClass)) + +typedef struct _GstProxySink GstProxySink; +typedef struct _GstProxySinkClass GstProxySinkClass; +typedef struct _GstProxySinkPrivate GstProxySinkPrivate; + +struct _GstProxySink { + GstElement parent; + + /* < private > */ + GstProxySinkPrivate *priv; + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstProxySinkClass { + GstElementClass parent_class; +}; + +GType gst_proxy_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_PROXY_SINK_H__ */ diff --git a/gst/proxy/gstproxysrc.c b/gst/proxy/gstproxysrc.c new file mode 100644 index 000000000..e2c4af0ba --- /dev/null +++ b/gst/proxy/gstproxysrc.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2018 Centricular Ltd. + * Author: Sebastian Dröge <sebastian@centricular.com> + * Author: Nirbheek Chauhan <nirbheek@centricular.com> + * + * 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. + */ + +/** + * SECTION:element-proxysrc + * @title: proxysrc + * + * Proxysrc is a source element that proxies events, queries, and buffers from + * another pipeline that contains a matching proxysink element. The purpose is + * to allow two decoupled pipelines to function as though they are one without + * having to manually shuttle buffers, events, queries, etc between the two. + * + * The element queues buffers from the matching proxysink to an internal queue, + * so everything downstream is properly decoupled from the upstream pipeline. + * However, the queue may get filled up if the downstream pipeline does not + * accept buffers quickly enough; perhaps because it is not yet PLAYING. + * + * ## Usage + * + * |[<!-- language="C" --> + * GstElement *pipe1, *pipe2, *psink, *psrc; + * GstClock *clock; + * + * pipe1 = gst_parse_launch ("audiotestsrc ! proxysink name=psink", NULL); + * psink = gst_bin_get_by_name (GST_BIN (pipe1), "psink"); + * + * pipe2 = gst_parse_launch ("proxysrc name=psrc ! autoaudiosink", NULL); + * psrc = gst_bin_get_by_name (GST_BIN (pipe2), "psrc"); + * + * // Connect the two pipelines + * g_object_set (psrc, "proxysink", psink, NULL); + * + * // Both pipelines must agree on the timing information or we'll get glitches + * // or overruns/underruns. Ideally, we should tell pipe1 to use the same clock + * // as pipe2, but since that will be set asynchronously to the audio clock, it + * // is simpler and likely accurate enough to use the system clock for both + * // pipelines. If no element in either pipeline will provide a clock, this + * // is not needed. + * clock = gst_system_clock_obtain (); + * gst_pipeline_use_clock (GST_PIPELINE (pipe1), clock); + * gst_pipeline_use_clock (GST_PIPELINE (pipe2), clock); + * g_object_unref (clock); + * + * // This is not really needed in this case since the pipelines are created and + * // started at the same time. However, an application that dynamically + * // generates pipelines must ensure that all the pipelines that will be + * // connected together share the same base time. + * gst_element_set_base_time (pipe1, 0); + * gst_element_set_base_time (pipe2, 0); + * + * gst_element_set_state (pipe1, GST_STATE_PLAYING); + * gst_element_set_state (pipe2, GST_STATE_PLAYING); + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gstproxysrc.h" +#include "gstproxysink.h" +#include "gstproxy-priv.h" + +#define GST_CAT_DEFAULT gst_proxy_src_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +enum +{ + PROP_0, + PROP_PROXYSINK, +}; + +struct _GstProxySrcPrivate +{ + /* Queue to hold buffers from proxysink */ + GstElement *queue; + /* Source pad of the above queue and the proxysrc element itself */ + GstPad *srcpad; + /* Our internal srcpad that proxysink pushes buffers/events/queries into */ + GstPad *internal_srcpad; + /* An unlinked dummy sinkpad; see gst_proxy_src_init() */ + GstPad *dummy_sinkpad; + /* The matching proxysink; queries and events are sent to its sinkpad */ + GWeakRef proxysink; +}; + +/* We're not subclassing from basesrc because we don't want any of the special + * handling it has for events/queries/etc. We just pass-through everything. */ + +/* Our parent type is a GstBin instead of GstElement because we contain a queue + * element */ +#define parent_class gst_proxy_src_parent_class +G_DEFINE_TYPE (GstProxySrc, gst_proxy_src, GST_TYPE_BIN); + +static gboolean gst_proxy_src_internal_src_query (GstPad * pad, + GstObject * parent, GstQuery * query); +static gboolean gst_proxy_src_internal_src_event (GstPad * pad, + GstObject * parent, GstEvent * event); + +static GstStateChangeReturn gst_proxy_src_change_state (GstElement * element, + GstStateChange transition); +static void gst_proxy_src_dispose (GObject * object); + +static void +gst_proxy_src_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * spec) +{ + GstProxySrc *self = GST_PROXY_SRC (object); + + switch (prop_id) { + case PROP_PROXYSINK: + g_value_take_object (value, g_weak_ref_get (&self->priv->proxysink)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); + break; + } +} + +static void +gst_proxy_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * spec) +{ + GstProxySrc *self = GST_PROXY_SRC (object); + GstProxySink *sink; + + switch (prop_id) { + case PROP_PROXYSINK: + sink = g_value_dup_object (value); + if (sink == NULL) { + /* Unset proxysrc property on the existing proxysink to break the + * connection in that direction */ + GstProxySink *old_sink = g_weak_ref_get (&self->priv->proxysink); + if (old_sink) { + gst_proxy_sink_set_proxysrc (old_sink, NULL); + g_object_unref (old_sink); + } + g_weak_ref_set (&self->priv->proxysink, NULL); + } else { + /* Set proxysrc property on the new proxysink to point to us */ + gst_proxy_sink_set_proxysrc (sink, self); + g_weak_ref_set (&self->priv->proxysink, sink); + g_object_unref (sink); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); + } +} + +static void +gst_proxy_src_class_init (GstProxySrcClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + GST_DEBUG_CATEGORY_INIT (gst_proxy_src_debug, "proxysrc", 0, "proxy sink"); + + g_type_class_add_private (klass, sizeof (GstProxySrcPrivate)); + + gobject_class->dispose = gst_proxy_src_dispose; + + gobject_class->get_property = gst_proxy_src_get_property; + gobject_class->set_property = gst_proxy_src_set_property; + + g_object_class_install_property (gobject_class, PROP_PROXYSINK, + g_param_spec_object ("proxysink", "Proxysink", "Matching proxysink", + GST_TYPE_PROXY_SINK, G_PARAM_READWRITE)); + + gstelement_class->change_state = gst_proxy_src_change_state; + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "Proxy source", + "Source", "Proxy source for internal process communication", + "Sebastian Dröge <sebastian@centricular.com>"); +} + +static void +gst_proxy_src_init (GstProxySrc * self) +{ + GstPad *srcpad, *sinkpad; + GstPadTemplate *templ; + + GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_SOURCE); + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_PROXY_SRC, + GstProxySrcPrivate); + + /* We feed incoming buffers into a queue to decouple the downstream pipeline + * from the upstream pipeline */ + self->priv->queue = gst_element_factory_make ("queue", NULL); + gst_bin_add (GST_BIN (self), self->priv->queue); + + srcpad = gst_element_get_static_pad (self->priv->queue, "src"); + templ = gst_static_pad_template_get (&src_template); + self->priv->srcpad = gst_ghost_pad_new_from_template ("src", srcpad, templ); + gst_object_unref (templ); + gst_object_unref (srcpad); + + gst_element_add_pad (GST_ELEMENT (self), self->priv->srcpad); + + /* A dummy sinkpad that's not actually used anywhere + * Explanation for why this is needed is below */ + self->priv->dummy_sinkpad = gst_pad_new ("dummy_sinkpad", GST_PAD_SINK); + gst_object_set_parent (GST_OBJECT (self->priv->dummy_sinkpad), + GST_OBJECT (self)); + + self->priv->internal_srcpad = gst_pad_new ("internal_src", GST_PAD_SRC); + gst_object_set_parent (GST_OBJECT (self->priv->internal_srcpad), + GST_OBJECT (self->priv->dummy_sinkpad)); + gst_pad_set_event_function (self->priv->internal_srcpad, + gst_proxy_src_internal_src_event); + gst_pad_set_query_function (self->priv->internal_srcpad, + gst_proxy_src_internal_src_query); + + /* We need to link internal_srcpad from proxysink to the sinkpad of our + * queue. However, two pads can only be linked if they share a common parent. + * Above, we set the parent of the dummy_sinkpad as proxysrc, and then we set + * the parent of internal_srcpad as dummy_sinkpad. This causes both these pads + * to share a parent allowing us to link them. + * Yes, this is a hack/workaround. */ + sinkpad = gst_element_get_static_pad (self->priv->queue, "sink"); + gst_pad_link (self->priv->internal_srcpad, sinkpad); + gst_object_unref (sinkpad); +} + +static void +gst_proxy_src_dispose (GObject * object) +{ + GstProxySrc *self = GST_PROXY_SRC (object); + + gst_object_unparent (GST_OBJECT (self->priv->dummy_sinkpad)); + self->priv->dummy_sinkpad = NULL; + + gst_object_unparent (GST_OBJECT (self->priv->internal_srcpad)); + self->priv->internal_srcpad = NULL; + + g_weak_ref_set (&self->priv->proxysink, NULL); + + G_OBJECT_CLASS (gst_proxy_src_parent_class)->dispose (object); +} + +static GstStateChangeReturn +gst_proxy_src_change_state (GstElement * element, GstStateChange transition) +{ + GstElementClass *gstelement_class = + GST_ELEMENT_CLASS (gst_proxy_src_parent_class); + GstProxySrc *self = GST_PROXY_SRC (element); + GstStateChangeReturn ret; + + ret = gstelement_class->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + ret = GST_STATE_CHANGE_NO_PREROLL; + gst_pad_set_active (self->priv->internal_srcpad, TRUE); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_pad_set_active (self->priv->internal_srcpad, FALSE); + break; + default: + break; + } + + return ret; +} + +static gboolean +gst_proxy_src_internal_src_query (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstProxySrc *self = GST_PROXY_SRC (gst_object_get_parent (parent)); + GstProxySink *sink; + gboolean ret = FALSE; + + if (!self) + return ret; + + GST_LOG_OBJECT (pad, "Handling query of type '%s'", + gst_query_type_get_name (GST_QUERY_TYPE (query))); + + sink = g_weak_ref_get (&self->priv->proxysink); + if (sink) { + GstPad *sinkpad; + sinkpad = gst_proxy_sink_get_internal_sinkpad (sink); + + ret = gst_pad_peer_query (sinkpad, query); + gst_object_unref (sinkpad); + gst_object_unref (sink); + } + + gst_object_unref (self); + + return ret; +} + +static gboolean +gst_proxy_src_internal_src_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstProxySrc *self = GST_PROXY_SRC (gst_object_get_parent (parent)); + GstProxySink *sink; + gboolean ret = FALSE; + + if (!self) + return ret; + + GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); + + sink = g_weak_ref_get (&self->priv->proxysink); + if (sink) { + GstPad *sinkpad; + sinkpad = gst_proxy_sink_get_internal_sinkpad (sink); + + ret = gst_pad_push_event (sinkpad, event); + gst_object_unref (sinkpad); + gst_object_unref (sink); + } else + gst_event_unref (event); + + + gst_object_unref (self); + + return ret; +} + +/* Wrapper function for accessing private member */ +GstPad * +gst_proxy_src_get_internal_srcpad (GstProxySrc * self) +{ + return gst_object_ref (self->priv->internal_srcpad); +} diff --git a/gst/proxy/gstproxysrc.h b/gst/proxy/gstproxysrc.h new file mode 100644 index 000000000..cc8577fc2 --- /dev/null +++ b/gst/proxy/gstproxysrc.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge <sebastian@centricular.com> + * Author: Nirbheek Chauhan <nirbheek@centricular.com> + * + * 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_PROXY_SRC_H__ +#define __GST_PROXY_SRC_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_PROXY_SRC (gst_proxy_src_get_type()) +#define GST_PROXY_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_PROXY_SRC, GstProxySrc)) +#define GST_IS_PROXY_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_PROXY_SRC)) +#define GST_PROXY_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) , GST_TYPE_PROXY_SRC, GstProxySrcClass)) +#define GST_IS_PROXY_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) , GST_TYPE_PROXY_SRC)) +#define GST_PROXY_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) , GST_TYPE_PROXY_SRC, GstProxySrcClass)) + +typedef struct _GstProxySrc GstProxySrc; +typedef struct _GstProxySrcClass GstProxySrcClass; +typedef struct _GstProxySrcPrivate GstProxySrcPrivate; + +struct _GstProxySrc { + GstBin parent; + + /* < private > */ + GstProxySrcPrivate *priv; + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstProxySrcClass { + GstBinClass parent_class; +}; + +GType gst_proxy_src_get_type(void); + +G_END_DECLS + +#endif /* __GST_PROXY_SRC_H__ */ diff --git a/gst/proxy/meson.build b/gst/proxy/meson.build new file mode 100644 index 000000000..769727816 --- /dev/null +++ b/gst/proxy/meson.build @@ -0,0 +1,14 @@ +proxy_sources = [ + 'gstproxy.c', + 'gstproxysink.c', + 'gstproxysrc.c' +] + +gstproxy = library('gstproxy', + proxy_sources, + c_args : gst_plugins_bad_args, + include_directories : [configinc], + dependencies : [gstbase_dep], + install : true, + install_dir : plugins_install_dir, +) |