From 7b5e232a9ed1c414fef5d5426cbf382b06d57350 Mon Sep 17 00:00:00 2001 From: Ognyan Tonchev Date: Wed, 12 Sep 2018 11:55:15 +0200 Subject: [PATCH] onvif: encapsulate onvif part into a bin ...and thus do not let onvif affect pipelines latency https://bugzilla.gnome.org/show_bug.cgi?id=797174 --- gst/rtsp-server/Makefile.am | 1 + gst/rtsp-server/meson.build | 1 + gst/rtsp-server/rtsp-latency-bin.c | 357 +++++++++++++++++++++++++++++ gst/rtsp-server/rtsp-latency-bin.h | 59 +++++ gst/rtsp-server/rtsp-onvif-media.c | 23 +- 5 files changed, 439 insertions(+), 2 deletions(-) create mode 100644 gst/rtsp-server/rtsp-latency-bin.c create mode 100644 gst/rtsp-server/rtsp-latency-bin.h diff --git a/gst/rtsp-server/Makefile.am b/gst/rtsp-server/Makefile.am index d08de4e6d0..7009c56e8b 100644 --- a/gst/rtsp-server/Makefile.am +++ b/gst/rtsp-server/Makefile.am @@ -31,6 +31,7 @@ c_sources = \ rtsp-params.c \ rtsp-sdp.c \ rtsp-thread-pool.c \ + rtsp-latency-bin.c \ rtsp-media.c \ rtsp-media-factory.c \ rtsp-media-factory-uri.c \ diff --git a/gst/rtsp-server/meson.build b/gst/rtsp-server/meson.build index b798b889b7..143385f989 100644 --- a/gst/rtsp-server/meson.build +++ b/gst/rtsp-server/meson.build @@ -3,6 +3,7 @@ rtsp_server_sources = [ 'rtsp-auth.c', 'rtsp-client.c', 'rtsp-context.c', + 'rtsp-latency-bin.c', 'rtsp-media.c', 'rtsp-media-factory.c', 'rtsp-media-factory-uri.c', diff --git a/gst/rtsp-server/rtsp-latency-bin.c b/gst/rtsp-server/rtsp-latency-bin.c new file mode 100644 index 0000000000..6a12fbe9de --- /dev/null +++ b/gst/rtsp-server/rtsp-latency-bin.c @@ -0,0 +1,357 @@ +/* GStreamer + * Copyright (C) 2018 Ognyan Tonchev + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "rtsp-latency-bin.h" + +#define GST_RTSP_LATENCY_BIN_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_RTSP_LATENCY_BIN_TYPE, GstRTSPLatencyBinPrivate)) + +struct _GstRTSPLatencyBinPrivate +{ + GstPad *sinkpad; + GstElement *element; +}; + +enum +{ + PROP_0, + PROP_ELEMENT, + PROP_LAST +}; + +GST_DEBUG_CATEGORY_STATIC (rtsp_latency_bin_debug); +#define GST_CAT_DEFAULT rtsp_latency_bin_debug + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static void gst_rtsp_latency_bin_get_property (GObject * object, guint propid, + GValue * value, GParamSpec * pspec); +static void gst_rtsp_latency_bin_set_property (GObject * object, guint propid, + const GValue * value, GParamSpec * pspec); +static gboolean gst_rtsp_latency_bin_element_query (GstElement * element, + GstQuery * query); +static gboolean gst_rtsp_latency_bin_element_event (GstElement * element, + GstEvent * event); +static void gst_rtsp_latency_bin_message_handler (GstBin * bin, + GstMessage * message); +static gboolean gst_rtsp_latency_bin_add_element (GstRTSPLatencyBin * + latency_bin, GstElement * element); +static GstStateChangeReturn gst_rtsp_latency_bin_change_state (GstElement * + element, GstStateChange transition); + +G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPLatencyBin, gst_rtsp_latency_bin, + GST_TYPE_BIN); + +static void +gst_rtsp_latency_bin_class_init (GstRTSPLatencyBinClass * klass) +{ + GObjectClass *gobject_klass = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (klass); + GstBinClass *gstbin_klass = GST_BIN_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (rtsp_latency_bin_debug, + "rtsplatencybin", 0, "GstRTSPLatencyBin"); + + gobject_klass->get_property = gst_rtsp_latency_bin_get_property; + gobject_klass->set_property = gst_rtsp_latency_bin_set_property; + + g_object_class_install_property (gobject_klass, PROP_ELEMENT, + g_param_spec_object ("element", "The Element", + "The GstElement to prevent from affecting piplines latency", + GST_TYPE_ELEMENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + gstelement_klass->change_state = + GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_change_state); + gstelement_klass->query = + GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_element_query); + gstelement_klass->send_event = + GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_element_event); + + gstbin_klass->handle_message = + GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_message_handler); +} + +static void +gst_rtsp_latency_bin_init (GstRTSPLatencyBin * latency_bin) +{ + GST_OBJECT_FLAG_SET (latency_bin, GST_ELEMENT_FLAG_SINK); +} + +static void +gst_rtsp_latency_bin_get_property (GObject * object, guint propid, + GValue * value, GParamSpec * pspec) +{ + GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (object); + GstRTSPLatencyBinPrivate *priv = + GST_RTSP_LATENCY_BIN_GET_PRIVATE (latency_bin); + + switch (propid) { + case PROP_ELEMENT: + g_value_set_object (value, priv->element); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); + } +} + +static void +gst_rtsp_latency_bin_set_property (GObject * object, guint propid, + const GValue * value, GParamSpec * pspec) +{ + GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (object); + + switch (propid) { + case PROP_ELEMENT: + if (!gst_rtsp_latency_bin_add_element (latency_bin, + g_value_get_object (value))) { + GST_WARNING_OBJECT (latency_bin, "Could not add the element"); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); + } +} + +static gboolean +gst_rtsp_latency_bin_add_element (GstRTSPLatencyBin * latency_bin, + GstElement * element) +{ + GstRTSPLatencyBinPrivate *priv = + GST_RTSP_LATENCY_BIN_GET_PRIVATE (latency_bin); + GstPad *pad; + GstPadTemplate *templ; + + GST_DEBUG_OBJECT (latency_bin, "Adding element to latencybin : %s", + GST_ELEMENT_NAME (element)); + + if (!element) { + goto no_element; + } + + /* add the element to ourself */ + gst_object_ref (element); + gst_bin_add (GST_BIN (latency_bin), element); + priv->element = element; + + /* add ghost pad first */ + templ = gst_static_pad_template_get (&sinktemplate); + priv->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ); + gst_object_unref (templ); + g_assert (priv->sinkpad); + + gst_element_add_pad (GST_ELEMENT (latency_bin), priv->sinkpad); + + /* and link it to our element */ + pad = gst_element_get_static_pad (element, "sink"); + if (!pad) { + goto no_sink_pad; + } + + if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (priv->sinkpad), pad)) { + goto set_target_failed; + } + + gst_object_unref (pad); + + return TRUE; + + /* ERRORs */ +no_element: + { + GST_WARNING_OBJECT (latency_bin, "No element, not adding"); + return FALSE; + } +no_sink_pad: + { + GST_WARNING_OBJECT (latency_bin, "The element has no sink pad"); + return FALSE; + } +set_target_failed: + { + GST_WARNING_OBJECT (latency_bin, "Could not set target pad"); + gst_object_unref (pad); + return FALSE; + } +} + + +static gboolean +gst_rtsp_latency_bin_element_query (GstElement * element, + GstQuery * query) +{ + gboolean ret = TRUE; + + GST_LOG_OBJECT (element, "got query %s", GST_QUERY_TYPE_NAME (query)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_LATENCY: + /* ignoring latency query, we do not want our element to affect latency on + * the rest of the pipeline */ + GST_DEBUG_OBJECT (element, "ignoring latency query"); + gst_query_set_latency (query, FALSE, 0, -1); + break; + default: + ret = + GST_ELEMENT_CLASS (gst_rtsp_latency_bin_parent_class)->query ( + GST_ELEMENT (element), query); + break; + } + + return ret; +} + +static gboolean +gst_rtsp_latency_bin_element_event (GstElement * element, + GstEvent * event) +{ + gboolean ret = TRUE; + + GST_LOG_OBJECT (element, "got event %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_LATENCY: + /* ignoring latency event, we will configure latency on our element when + * going to PLAYING */ + GST_DEBUG_OBJECT (element, "ignoring latency event"); + gst_event_unref (event); + break; + default: + ret = GST_ELEMENT_CLASS ( + gst_rtsp_latency_bin_parent_class)->send_event ( + GST_ELEMENT (element), event); + break; + } + + return ret; +} + +static gboolean +gst_rtsp_latency_bin_recalculate_latency (GstRTSPLatencyBin * latency_bin) +{ + GstRTSPLatencyBinPrivate *priv = + GST_RTSP_LATENCY_BIN_GET_PRIVATE (latency_bin); + GstEvent *latency; + GstQuery *query; + GstClockTime min_latency; + + GST_DEBUG_OBJECT (latency_bin, "Recalculating latency"); + + if (!priv->element) { + GST_WARNING_OBJECT (latency_bin, "We do not have an element"); + return FALSE; + } + + query = gst_query_new_latency (); + + if (!gst_element_query (priv->element, query)) { + GST_WARNING_OBJECT (latency_bin, "Latency query failed"); + gst_query_unref (query); + return FALSE; + } + + gst_query_parse_latency (query, NULL, &min_latency, NULL); + gst_query_unref (query); + + GST_LOG_OBJECT (latency_bin, "Got min_latency from stream: %" + GST_TIME_FORMAT, GST_TIME_ARGS (min_latency)); + + latency = gst_event_new_latency (min_latency); + if (!gst_element_send_event (priv->element, latency)) { + GST_WARNING_OBJECT (latency_bin, "Sending latency event to stream failed"); + return FALSE; + } + + return TRUE; +} + +static void +gst_rtsp_latency_bin_message_handler (GstBin * bin, GstMessage * message) +{ + GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (bin); + + GST_LOG_OBJECT (bin, "Got message %s", GST_MESSAGE_TYPE_NAME (message)); + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_LATENCY:{ + if (!gst_rtsp_latency_bin_recalculate_latency (latency_bin)) { + GST_WARNING_OBJECT (latency_bin, "Could not recalculate latency"); + } + break; + } + default: + GST_BIN_CLASS (gst_rtsp_latency_bin_parent_class)->handle_message ( + bin, message); + break; + } +} + +static GstStateChangeReturn +gst_rtsp_latency_bin_change_state (GstElement * element, GstStateChange + transition) +{ + GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (element); + GstStateChangeReturn ret; + + GST_LOG_OBJECT (latency_bin, "Changing state %s", + gst_state_change_get_name (transition)); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + case GST_STATE_CHANGE_PLAYING_TO_PLAYING: + if (!gst_rtsp_latency_bin_recalculate_latency (latency_bin)) { + GST_WARNING_OBJECT (latency_bin, "Could not recalculate latency"); + } + default: + break; + } + + ret = GST_ELEMENT_CLASS (gst_rtsp_latency_bin_parent_class)->change_state + (element, transition); + + return ret; +} + +/** + * gst_rtsp_latency_bin_new: + * @element: (transfer full): a #GstElement + * + * Create a bin that encapsulates an @element and prevents it from affecting + * latency on the whole pipeline. + * + * Returns: A newly created #GstRTSPLatencyBin element, or %NULL on failure + */ +GstElement * +gst_rtsp_latency_bin_new (GstElement * element) +{ + GstElement *gst_rtsp_latency_bin; + + g_return_val_if_fail (element, NULL); + + gst_rtsp_latency_bin = g_object_new (GST_RTSP_LATENCY_BIN_TYPE, "element", + element, NULL); + gst_object_unref (element); + return gst_rtsp_latency_bin; +} diff --git a/gst/rtsp-server/rtsp-latency-bin.h b/gst/rtsp-server/rtsp-latency-bin.h new file mode 100644 index 0000000000..455e7c5713 --- /dev/null +++ b/gst/rtsp-server/rtsp-latency-bin.h @@ -0,0 +1,59 @@ +/* GStreamer + * Copyright (C) 2018 Ognyan Tonchev + * + * 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_RTSP_LATENCY_BIN_H__ +#define __GST_RTSP_LATENCY_BIN_H__ + +#include +#include "rtsp-server-prelude.h" + +G_BEGIN_DECLS + +typedef struct _GstRTSPLatencyBin GstRTSPLatencyBin; +typedef struct _GstRTSPLatencyBinClass GstRTSPLatencyBinClass; +typedef struct _GstRTSPLatencyBinPrivate GstRTSPLatencyBinPrivate; + +#define GST_RTSP_LATENCY_BIN_TYPE (gst_rtsp_latency_bin_get_type ()) +#define IS_GST_RTSP_LATENCY_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_RTSP_LATENCY_BIN_TYPE)) +#define IS_GST_RTSP_LATENCY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_RTSP_LATENCY_BIN_TYPE)) +#define GST_RTSP_LATENCY_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_RTSP_LATENCY_BIN_TYPE, GstRTSPLatencyBinClass)) +#define GST_RTSP_LATENCY_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_RTSP_LATENCY_BIN_TYPE, GstRTSPLatencyBin)) +#define GST_RTSP_LATENCY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_RTSP_LATENCY_BIN_TYPE, GstRTSPLatencyBinClass)) +#define GST_RTSP_LATENCY_BIN_CAST(obj) ((GstRTSPLatencyBin*)(obj)) +#define GST_RTSP_LATENCY_BIN_CLASS_CAST(klass) ((GstRTSPLatencyBinClass*)(klass)) + +struct _GstRTSPLatencyBin { + GstBin parent; + + GstRTSPLatencyBinPrivate *priv; +}; + +struct _GstRTSPLatencyBinClass { + GstBinClass parent_class; +}; + +GST_RTSP_SERVER_API +GType gst_rtsp_latency_bin_get_type (void); + +GST_RTSP_SERVER_API +GstElement * gst_rtsp_latency_bin_new (GstElement * element); + +G_END_DECLS + +#endif /* __GST_RTSP_LATENCY_BIN_H__ */ diff --git a/gst/rtsp-server/rtsp-onvif-media.c b/gst/rtsp-server/rtsp-onvif-media.c index dcd86f244c..29fdec294c 100644 --- a/gst/rtsp-server/rtsp-onvif-media.c +++ b/gst/rtsp-server/rtsp-onvif-media.c @@ -42,6 +42,7 @@ #endif #include "rtsp-onvif-media.h" +#include "rtsp-latency-bin.h" struct GstRTSPOnvifMediaPrivate { @@ -251,6 +252,7 @@ gboolean gst_rtsp_onvif_media_collect_backchannel (GstRTSPOnvifMedia * media) { GstElement *element, *backchannel_bin = NULL; + GstElement *latency_bin; GstPad *pad = NULL; gboolean ret = FALSE; @@ -265,11 +267,28 @@ gst_rtsp_onvif_media_collect_backchannel (GstRTSPOnvifMedia * media) if (!backchannel_bin) goto out; - pad = gst_element_get_static_pad (backchannel_bin, "sink"); + /* We don't want the backchannel element, which is a receiver, to affect + * latency on the complete pipeline. That's why we remove it from the + * pipeline and add it to a @GstRTSPLatencyBin which will prevent it from + * messing up pipelines latency. The extra reference is needed so that it + * is not freed in case the pipeline holds the the only ref to it. + * + * TODO: a more generic solution should be implemented in + * gst_rtsp_media_collect_streams() where all receivers are encapsulated + * in a @GstRTSPLatencyBin in cases when there are senders too. */ + gst_object_ref (backchannel_bin); + gst_bin_remove (GST_BIN (element), backchannel_bin); + + latency_bin = gst_rtsp_latency_bin_new (backchannel_bin); + g_assert (latency_bin); + + gst_bin_add (GST_BIN (element), latency_bin); + + pad = gst_element_get_static_pad (latency_bin, "sink"); if (!pad) goto out; - gst_rtsp_media_create_stream (GST_RTSP_MEDIA (media), backchannel_bin, pad); + gst_rtsp_media_create_stream (GST_RTSP_MEDIA (media), latency_bin, pad); ret = TRUE; out: