summaryrefslogtreecommitdiff
path: root/gst/rtsp-server/rtsp-onvif-media-factory.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/rtsp-server/rtsp-onvif-media-factory.c')
-rw-r--r--gst/rtsp-server/rtsp-onvif-media-factory.c491
1 files changed, 491 insertions, 0 deletions
diff --git a/gst/rtsp-server/rtsp-onvif-media-factory.c b/gst/rtsp-server/rtsp-onvif-media-factory.c
new file mode 100644
index 0000000..a200f1d
--- /dev/null
+++ b/gst/rtsp-server/rtsp-onvif-media-factory.c
@@ -0,0 +1,491 @@
+/* GStreamer
+ * Copyright (C) 2017 Sebastian Dröge <sebastian@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:rtsp-onvif-media-factory
+ * @short_description: A factory for ONVIF media pipelines
+ * @see_also: #GstRTSPMediaFactory, #GstRTSPOnvifMedia
+ *
+ * The #GstRTSPOnvifMediaFactory is responsible for creating or recycling
+ * #GstRTSPMedia objects based on the passed URL. Different to
+ * #GstRTSPMediaFactory, this supports special ONVIF features and can create
+ * #GstRTSPOnvifMedia in addition to normal #GstRTSPMedia.
+ *
+ * Special ONVIF features that are currently supported is a backchannel for
+ * the client to send back media to the server in a normal PLAY media, see
+ * gst_rtsp_onvif_media_factory_set_backchannel_launch() and
+ * gst_rtsp_onvif_media_factory_set_backchannel_bandwidth().
+ *
+ * Since: 1.14
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "rtsp-onvif-media-factory.h"
+#include "rtsp-onvif-media.h"
+#include "rtsp-onvif-server.h"
+
+struct GstRTSPOnvifMediaFactoryPrivate
+{
+ GMutex lock;
+ gchar *backchannel_launch;
+ guint backchannel_bandwidth;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPOnvifMediaFactory,
+ gst_rtsp_onvif_media_factory, GST_TYPE_RTSP_MEDIA_FACTORY);
+
+static gboolean
+requires_backchannel (GstRTSPMessage * msg)
+{
+ GstRTSPResult res;
+ gint i;
+ gchar *reqs = NULL;
+
+ i = 0;
+ do {
+ res = gst_rtsp_message_get_header (msg, GST_RTSP_HDR_REQUIRE, &reqs, i++);
+
+ if (res == GST_RTSP_ENOTIMPL)
+ break;
+
+ if (strcmp (reqs, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT) == 0)
+ return TRUE;
+ } while (TRUE);
+
+ return FALSE;
+}
+
+static gchar *
+gst_rtsp_onvif_media_factory_gen_key (GstRTSPMediaFactory * factory,
+ const GstRTSPUrl * url)
+{
+ GstRTSPContext *ctx = gst_rtsp_context_get_current ();
+ GstRTSPMessage *msg = ctx->request;
+
+ /* Only medias where no backchannel was requested can be shared */
+ if (requires_backchannel (msg))
+ return NULL;
+
+ return
+ GST_RTSP_MEDIA_FACTORY_CLASS
+ (gst_rtsp_onvif_media_factory_parent_class)->gen_key (factory, url);
+}
+
+static GstRTSPMedia *
+gst_rtsp_onvif_media_factory_construct (GstRTSPMediaFactory * factory,
+ const GstRTSPUrl * url)
+{
+ GstRTSPMedia *media;
+ GstElement *element, *pipeline;
+ GstRTSPMediaFactoryClass *klass;
+ GType media_gtype;
+ gboolean got_backchannel_stream;
+ GstRTSPContext *ctx = gst_rtsp_context_get_current ();
+
+ /* Mostly a copy of the default implementation but with backchannel support below,
+ * unfortunately we can't re-use the default one because of how the virtual
+ * method is define */
+
+ /* Everything but play is unsupported */
+ if (gst_rtsp_media_factory_get_transport_mode (factory) !=
+ GST_RTSP_TRANSPORT_MODE_PLAY)
+ return NULL;
+
+ /* we only support onvif media here: otherwise a plain GstRTSPMediaFactory
+ * could've been used as well */
+ media_gtype = gst_rtsp_media_factory_get_media_gtype (factory);
+ if (!g_type_is_a (media_gtype, GST_TYPE_RTSP_ONVIF_MEDIA))
+ return NULL;
+
+ klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
+
+ if (!klass->create_pipeline)
+ goto no_create;
+
+ element = gst_rtsp_media_factory_create_element (factory, url);
+ if (element == NULL)
+ goto no_element;
+
+ /* create a new empty media */
+ media =
+ g_object_new (media_gtype, "element", element,
+ "transport-mode", GST_RTSP_TRANSPORT_MODE_PLAY, NULL);
+
+ /* this adds the non-backchannel streams */
+ gst_rtsp_media_collect_streams (media);
+
+ /* this adds the backchannel stream */
+ got_backchannel_stream =
+ gst_rtsp_onvif_media_collect_backchannel (GST_RTSP_ONVIF_MEDIA (media));
+ /* FIXME: This should not happen! We checked for that before */
+ if (requires_backchannel (ctx->request) && !got_backchannel_stream) {
+ g_object_unref (media);
+ return NULL;
+ }
+
+ pipeline = klass->create_pipeline (factory, media);
+ if (pipeline == NULL)
+ goto no_pipeline;
+
+ gst_rtsp_onvif_media_set_backchannel_bandwidth (GST_RTSP_ONVIF_MEDIA (media),
+ GST_RTSP_ONVIF_MEDIA_FACTORY (factory)->priv->backchannel_bandwidth);
+
+ return media;
+
+ /* ERRORS */
+no_create:
+ {
+ g_critical ("no create_pipeline function");
+ return NULL;
+ }
+no_element:
+ {
+ g_critical ("could not create element");
+ return NULL;
+ }
+no_pipeline:
+ {
+ g_critical ("can't create pipeline");
+ g_object_unref (media);
+ return NULL;
+ }
+}
+
+static GstElement *
+gst_rtsp_onvif_media_factory_create_element (GstRTSPMediaFactory * factory,
+ const GstRTSPUrl * url)
+{
+ GstElement *element;
+ GError *error = NULL;
+ gchar *launch;
+ GstRTSPContext *ctx = gst_rtsp_context_get_current ();
+
+ /* Mostly a copy of the default implementation but with backchannel support below,
+ * unfortunately we can't re-use the default one because of how the virtual
+ * method is define */
+
+ launch = gst_rtsp_media_factory_get_launch (factory);
+
+ /* we need a parse syntax */
+ if (launch == NULL)
+ goto no_launch;
+
+ /* parse the user provided launch line */
+ element =
+ gst_parse_launch_full (launch, NULL, GST_PARSE_FLAG_PLACE_IN_BIN, &error);
+ if (element == NULL)
+ goto parse_error;
+
+ g_free (launch);
+
+ if (error != NULL) {
+ /* a recoverable error was encountered */
+ GST_WARNING ("recoverable parsing error: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* add backchannel pipeline part, if requested */
+ if (requires_backchannel (ctx->request)) {
+ GstRTSPOnvifMediaFactory *onvif_factory =
+ GST_RTSP_ONVIF_MEDIA_FACTORY (factory);
+ GstElement *backchannel_bin;
+ GstElement *backchannel_depay;
+ GstPad *depay_pad, *depay_ghostpad;
+
+ launch =
+ gst_rtsp_onvif_media_factory_get_backchannel_launch (onvif_factory);
+ if (launch == NULL)
+ goto no_launch_backchannel;
+
+ backchannel_bin =
+ gst_parse_bin_from_description_full (launch, FALSE, NULL,
+ GST_PARSE_FLAG_PLACE_IN_BIN, &error);
+ if (backchannel_bin == NULL)
+ goto parse_error_backchannel;
+
+ g_free (launch);
+
+ if (error != NULL) {
+ /* a recoverable error was encountered */
+ GST_WARNING ("recoverable parsing error: %s", error->message);
+ g_error_free (error);
+ }
+
+ gst_object_set_name (GST_OBJECT (backchannel_bin), "onvif-backchannel");
+
+ backchannel_depay =
+ gst_bin_get_by_name (GST_BIN (backchannel_bin), "depay_backchannel");
+ if (!backchannel_depay) {
+ gst_object_unref (backchannel_bin);
+ goto wrongly_formatted_backchannel_bin;
+ }
+
+ depay_pad = gst_element_get_static_pad (backchannel_depay, "sink");
+ if (!depay_pad) {
+ gst_object_unref (backchannel_depay);
+ gst_object_unref (backchannel_bin);
+ goto wrongly_formatted_backchannel_bin;
+ }
+
+ depay_ghostpad = gst_ghost_pad_new ("sink", depay_pad);
+ gst_element_add_pad (backchannel_bin, depay_ghostpad);
+
+ gst_bin_add (GST_BIN (element), backchannel_bin);
+ }
+
+ return element;
+
+ /* ERRORS */
+no_launch:
+ {
+ g_critical ("no launch line specified");
+ g_free (launch);
+ return NULL;
+ }
+parse_error:
+ {
+ g_critical ("could not parse launch syntax (%s): %s", launch,
+ (error ? error->message : "unknown reason"));
+ if (error)
+ g_error_free (error);
+ g_free (launch);
+ return NULL;
+ }
+no_launch_backchannel:
+ {
+ g_critical ("no backchannel launch line specified");
+ gst_object_unref (element);
+ return NULL;
+ }
+parse_error_backchannel:
+ {
+ g_critical ("could not parse backchannel launch syntax (%s): %s", launch,
+ (error ? error->message : "unknown reason"));
+ if (error)
+ g_error_free (error);
+ g_free (launch);
+ gst_object_unref (element);
+ return NULL;
+ }
+
+wrongly_formatted_backchannel_bin:
+ {
+ g_critical ("invalidly formatted backchannel bin");
+
+ gst_object_unref (element);
+ return NULL;
+ }
+}
+
+static gboolean
+ gst_rtsp_onvif_media_factory_has_backchannel_support_default
+ (GstRTSPOnvifMediaFactory * factory)
+{
+ /* No locking here, we just check if it's non-NULL */
+ return factory->priv->backchannel_launch != NULL;
+}
+
+static void
+gst_rtsp_onvif_media_factory_finalize (GObject * object)
+{
+ GstRTSPOnvifMediaFactory *factory = GST_RTSP_ONVIF_MEDIA_FACTORY (object);
+
+ g_free (factory->priv->backchannel_launch);
+ factory->priv->backchannel_launch = NULL;
+
+ g_mutex_clear (&factory->priv->lock);
+
+ return
+ G_OBJECT_CLASS (gst_rtsp_onvif_media_factory_parent_class)->finalize
+ (object);
+}
+
+static void
+gst_rtsp_onvif_media_factory_class_init (GstRTSPOnvifMediaFactoryClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstRTSPMediaFactoryClass *factory_klass = (GstRTSPMediaFactoryClass *) klass;
+
+ gobject_class->finalize = gst_rtsp_onvif_media_factory_finalize;
+
+ factory_klass->gen_key = gst_rtsp_onvif_media_factory_gen_key;
+ factory_klass->construct = gst_rtsp_onvif_media_factory_construct;
+ factory_klass->create_element = gst_rtsp_onvif_media_factory_create_element;
+
+ klass->has_backchannel_support =
+ gst_rtsp_onvif_media_factory_has_backchannel_support_default;
+}
+
+static void
+gst_rtsp_onvif_media_factory_init (GstRTSPOnvifMediaFactory * factory)
+{
+ factory->priv = gst_rtsp_onvif_media_factory_get_instance_private (factory);
+ g_mutex_init (&factory->priv->lock);
+}
+
+/**
+ * gst_rtsp_onvif_media_factory_set_backchannel_launch:
+ * @factory: a #GstRTSPMediaFactory
+ * @launch: the launch description
+ *
+ * The gst_parse_launch() line to use for constructing the ONVIF backchannel
+ * pipeline in the default prepare vmethod if requested by the client.
+ *
+ * The pipeline description should return a GstBin as the toplevel element
+ * which can be accomplished by enclosing the description with brackets '('
+ * ')'.
+ *
+ * The description should return a pipeline with a single depayloader named
+ * depay_backchannel. A caps query on the depayloader's sinkpad should return
+ * all possible, complete RTP caps that are going to be supported. At least
+ * the payload type, clock-rate and encoding-name need to be specified.
+ *
+ * Since: 1.14
+ */
+void
+gst_rtsp_onvif_media_factory_set_backchannel_launch (GstRTSPOnvifMediaFactory *
+ factory, const gchar * launch)
+{
+ g_return_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory));
+
+ g_mutex_lock (&factory->priv->lock);
+ g_free (factory->priv->backchannel_launch);
+ factory->priv->backchannel_launch = g_strdup (launch);
+ g_mutex_unlock (&factory->priv->lock);
+}
+
+/**
+ * gst_rtsp_onvif_media_factory_get_backchannel_launch:
+ * @factory: a #GstRTSPMediaFactory
+ *
+ * Get the gst_parse_launch() pipeline description that will be used in the
+ * default prepare vmethod for generating the ONVIF backchannel pipeline.
+ *
+ * Returns: (transfer full): the configured backchannel launch description. g_free() after
+ * usage.
+ *
+ * Since: 1.14
+ */
+gchar *
+gst_rtsp_onvif_media_factory_get_backchannel_launch (GstRTSPOnvifMediaFactory *
+ factory)
+{
+ gchar *launch;
+
+ g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), NULL);
+
+ g_mutex_lock (&factory->priv->lock);
+ launch = g_strdup (factory->priv->backchannel_launch);
+ g_mutex_unlock (&factory->priv->lock);
+
+ return launch;
+}
+
+/**
+ * gst_rtsp_onvif_media_factory_has_backchannel_support:
+ * @factory: a #GstRTSPMediaFactory
+ *
+ * Returns %TRUE if an ONVIF backchannel is supported by the media factory.
+ *
+ * Returns: %TRUE if an ONVIF backchannel is supported by the media factory.
+ *
+ * Since: 1.14
+ */
+gboolean
+gst_rtsp_onvif_media_factory_has_backchannel_support (GstRTSPOnvifMediaFactory *
+ factory)
+{
+ GstRTSPOnvifMediaFactoryClass *klass;
+
+ g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), FALSE);
+
+ klass = GST_RTSP_ONVIF_MEDIA_FACTORY_GET_CLASS (factory);
+
+ if (klass->has_backchannel_support)
+ return klass->has_backchannel_support (factory);
+
+ return FALSE;
+}
+
+/**
+ * gst_rtsp_onvif_media_factory_set_backchannel_bandwidth:
+ * @factory: a #GstRTSPMediaFactory
+ * @bandwidth: the bandwidth in bits per second
+ *
+ * Set the configured/supported bandwidth of the ONVIF backchannel pipeline in
+ * bits per second.
+ *
+ * Since: 1.14
+ */
+void
+gst_rtsp_onvif_media_factory_set_backchannel_bandwidth (GstRTSPOnvifMediaFactory
+ * factory, guint bandwidth)
+{
+ g_return_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory));
+
+ g_mutex_lock (&factory->priv->lock);
+ factory->priv->backchannel_bandwidth = bandwidth;
+ g_mutex_unlock (&factory->priv->lock);
+}
+
+/**
+ * gst_rtsp_onvif_media_factory_get_backchannel_bandwidth:
+ * @factory: a #GstRTSPMediaFactory
+ *
+ * Get the configured/supported bandwidth of the ONVIF backchannel pipeline in
+ * bits per second.
+ *
+ * Returns: the configured/supported backchannel bandwidth.
+ *
+ * Since: 1.14
+ */
+guint
+gst_rtsp_onvif_media_factory_get_backchannel_bandwidth (GstRTSPOnvifMediaFactory
+ * factory)
+{
+ guint bandwidth;
+
+ g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), 0);
+
+ g_mutex_lock (&factory->priv->lock);
+ bandwidth = factory->priv->backchannel_bandwidth;
+ g_mutex_unlock (&factory->priv->lock);
+
+ return bandwidth;
+}
+
+/**
+ * gst_rtsp_onvif_media_factory_new:
+ *
+ * Create a new #GstRTSPOnvifMediaFactory
+ *
+ * Returns: A new #GstRTSPOnvifMediaFactory
+ *
+ * Since: 1.14
+ */
+GstRTSPMediaFactory *
+gst_rtsp_onvif_media_factory_new (void)
+{
+ return g_object_new (GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, NULL);
+}