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
This commit is contained in:
Ognyan Tonchev 2018-09-12 11:55:15 +02:00 committed by Sebastian Dröge
parent 982efec468
commit 7b5e232a9e
5 changed files with 439 additions and 2 deletions

View file

@ -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 \

View file

@ -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',

View file

@ -0,0 +1,357 @@
/* GStreamer
* Copyright (C) 2018 Ognyan Tonchev <ognyan@axis.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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#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;
}

View file

@ -0,0 +1,59 @@
/* GStreamer
* Copyright (C) 2018 Ognyan Tonchev <ognyan@axis.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_RTSP_LATENCY_BIN_H__
#define __GST_RTSP_LATENCY_BIN_H__
#include <gst/gst.h>
#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__ */

View file

@ -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: