/* * Copyright (C) 2018 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 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); /* 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"); 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->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); gst_pad_set_chain_function (self->sinkpad, GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_chain)); gst_pad_set_chain_list_function (self->sinkpad, GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_chain_list)); gst_pad_set_event_function (self->sinkpad, GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_event)); gst_pad_set_query_function (self->sinkpad, GST_DEBUG_FUNCPTR (gst_proxy_sink_sink_query)); gst_element_add_pad (GST_ELEMENT (self), self->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->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->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->pending_sticky_events = FALSE; src = g_weak_ref_get (&self->proxysrc); if (src) { GstPad *srcpad; srcpad = gst_proxy_src_get_internal_srcpad (src); if (sticky && self->pending_sticky_events) { CopyStickyEventsData data = { srcpad, GST_FLOW_OK }; gst_pad_sticky_events_foreach (pad, copy_sticky_events, &data); self->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->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->proxysrc); if (src) { GstPad *srcpad; srcpad = gst_proxy_src_get_internal_srcpad (src); if (self->pending_sticky_events) { CopyStickyEventsData data = { srcpad, GST_FLOW_OK }; gst_pad_sticky_events_foreach (pad, copy_sticky_events, &data); self->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->proxysrc); if (src) { GstPad *srcpad; srcpad = gst_proxy_src_get_internal_srcpad (src); if (self->pending_sticky_events) { CopyStickyEventsData data = { srcpad, GST_FLOW_OK }; gst_pad_sticky_events_foreach (pad, copy_sticky_events, &data); self->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->sinkpad); } void gst_proxy_sink_set_proxysrc (GstProxySink * self, GstProxySrc * src) { g_return_if_fail (self); g_weak_ref_set (&self->proxysrc, src); }