From 8a056af05e23ad5b81cd1dc2699ae20abcf9b91a Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 26 Sep 2017 20:12:24 +0530 Subject: [PATCH] New element 'proxy' to send data to in-process pipelines This plugin is useful when you want to pipe arbitrary data to a different pipeline within the same process. Buffers, events, and caps are transmitted as-is without copying or manipulation. --- configure.ac | 2 + gst/meson.build | 1 + gst/proxy/Makefile.am | 9 ++ gst/proxy/gstproxy.c | 43 ++++++ gst/proxy/gstproxysink-priv.h | 41 ++++++ gst/proxy/gstproxysink.c | 305 +++++++++++++++++++++++++++++++++++++++ gst/proxy/gstproxysink.h | 62 ++++++++ gst/proxy/gstproxysrc-priv.h | 37 +++++ gst/proxy/gstproxysrc.c | 325 ++++++++++++++++++++++++++++++++++++++++++ gst/proxy/gstproxysrc.h | 62 ++++++++ gst/proxy/meson.build | 14 ++ 11 files changed, 901 insertions(+) create mode 100644 gst/proxy/Makefile.am create mode 100644 gst/proxy/gstproxy.c create mode 100644 gst/proxy/gstproxysink-priv.h create mode 100644 gst/proxy/gstproxysink.c create mode 100644 gst/proxy/gstproxysink.h create mode 100644 gst/proxy/gstproxysrc-priv.h create mode 100644 gst/proxy/gstproxysrc.c create mode 100644 gst/proxy/gstproxysrc.h create mode 100644 gst/proxy/meson.build diff --git a/configure.ac b/configure.ac index 9a401e8..a446c47 100644 --- a/configure.ac +++ b/configure.ac @@ -484,6 +484,7 @@ AG_GST_CHECK_PLUGIN(netsim) AG_GST_CHECK_PLUGIN(onvif) AG_GST_CHECK_PLUGIN(pcapparse) AG_GST_CHECK_PLUGIN(pnm) +AG_GST_CHECK_PLUGIN(proxy) AG_GST_CHECK_PLUGIN(rawparse) AG_GST_CHECK_PLUGIN(removesilence) AG_GST_CHECK_PLUGIN(sdp) @@ -3659,6 +3660,7 @@ gst/netsim/Makefile gst/onvif/Makefile gst/pcapparse/Makefile gst/pnm/Makefile +gst/proxy/Makefile gst/rawparse/Makefile gst/removesilence/Makefile gst/sdp/Makefile diff --git a/gst/meson.build b/gst/meson.build index 1017adf..b56b68d 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 0000000..6231ca3 --- /dev/null +++ b/gst/proxy/Makefile.am @@ -0,0 +1,9 @@ +plugin_LTLIBRARIES = libgstproxy.la + +libgstproxy_la_SOURCES = gstproxy.c gstproxysink.c gstproxysrc.c + +libgstproxy_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstproxy_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) +libgstproxy_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstproxysink.h gstproxysink-priv.h gstproxysrc.h gstproxysrc-priv.h diff --git a/gst/proxy/gstproxy.c b/gst/proxy/gstproxy.c new file mode 100644 index 0000000..03e1888 --- /dev/null +++ b/gst/proxy/gstproxy.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge + * Author: Nirbheek Chauhan + * + * 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-priv.h b/gst/proxy/gstproxysink-priv.h new file mode 100644 index 0000000..bd204b2 --- /dev/null +++ b/gst/proxy/gstproxysink-priv.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge + * Author: Nirbheek Chauhan + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef __GST_PROXY_SINK_PRIV_H__ +#define __GST_PROXY_SINK_PRIV_H__ + +#include "gstproxysrc.h" + +G_BEGIN_DECLS + +void gst_proxy_sink_set_proxysrc (GstProxySink *sink, GstProxySrc *src); + +GstPad* gst_proxy_sink_get_internal_sinkpad (GstProxySink *sink); + +G_END_DECLS + +#endif /* __GST_PROXY_SINK_PRIV_H__ */ diff --git a/gst/proxy/gstproxysink.c b/gst/proxy/gstproxysink.c new file mode 100644 index 0000000..fab2fcf --- /dev/null +++ b/gst/proxy/gstproxysink.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge + * Author: Nirbheek Chauhan + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/** + * SECTION:element-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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gstproxysink.h" +#include "gstproxysink-priv.h" +#include "gstproxysrc.h" +#include "gstproxysrc-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 "); +} + +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 0000000..00fe1ff --- /dev/null +++ b/gst/proxy/gstproxysink.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge + * Author: Nirbheek Chauhan + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef __GST_PROXY_SINK_H__ +#define __GST_PROXY_SINK_H__ + +#include + +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-priv.h b/gst/proxy/gstproxysrc-priv.h new file mode 100644 index 0000000..a8a92d8 --- /dev/null +++ b/gst/proxy/gstproxysrc-priv.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge + * Author: Nirbheek Chauhan + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef __GST_PROXY_SRC_PRIV_H__ +#define __GST_PROXY_SRC_PRIV_H__ + +G_BEGIN_DECLS + +GstPad* gst_proxy_src_get_internal_srcpad (GstProxySrc *src); + +G_END_DECLS + +#endif /* __GST_PROXY_SRC_PRIV_H__ */ diff --git a/gst/proxy/gstproxysrc.c b/gst/proxy/gstproxysrc.c new file mode 100644 index 0000000..e760d0f --- /dev/null +++ b/gst/proxy/gstproxysrc.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge + * Author: Nirbheek Chauhan + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/** + * SECTION:element-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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gstproxysrc.h" +#include "gstproxysrc-priv.h" +#include "gstproxysink.h" +#include "gstproxysink-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 "); +} + +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 0000000..2983fc8 --- /dev/null +++ b/gst/proxy/gstproxysrc.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Centricular Ltd. + * Author: Sebastian Dröge + * Author: Nirbheek Chauhan + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef __GST_PROXY_SRC_H__ +#define __GST_PROXY_SRC_H__ + +#include + +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 0000000..7697278 --- /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, +) -- 2.7.4