rtsp: Add support for ONVIF backchannel
authorSebastian Dröge <sebastian@centricular.com>
Mon, 25 Sep 2017 16:41:05 +0000 (19:41 +0300)
committerSebastian Dröge <sebastian@centricular.com>
Fri, 16 Feb 2018 09:04:53 +0000 (11:04 +0200)
This adds a new RTSP server, client, media-factory and media subclass
for handling the specifics of the backchannel. Ideally this later can be
extended with other ONVIF specific features.

17 files changed:
docs/libs/gst-rtsp-server-docs.sgml
docs/libs/gst-rtsp-server-sections.txt
examples/.gitignore
examples/Makefile.am
examples/test-onvif-backchannel.c [new file with mode: 0644]
gst/rtsp-server/Makefile.am
gst/rtsp-server/rtsp-media.h
gst/rtsp-server/rtsp-onvif-client.c [new file with mode: 0644]
gst/rtsp-server/rtsp-onvif-client.h [new file with mode: 0644]
gst/rtsp-server/rtsp-onvif-media-factory.c [new file with mode: 0644]
gst/rtsp-server/rtsp-onvif-media-factory.h [new file with mode: 0644]
gst/rtsp-server/rtsp-onvif-media.c [new file with mode: 0644]
gst/rtsp-server/rtsp-onvif-media.h [new file with mode: 0644]
gst/rtsp-server/rtsp-onvif-server.c [new file with mode: 0644]
gst/rtsp-server/rtsp-onvif-server.h [new file with mode: 0644]
gst/rtsp-server/rtsp-sdp.c
gst/rtsp-server/rtsp-sdp.h

index 06141f8..26c2a51 100644 (file)
     <xi:include href="xml/rtsp-token.xml"/>
     <xi:include href="xml/rtsp-permissions.xml"/>
     <xi:include href="xml/rtsp-params.xml"/>
+    <xi:include href="xml/rtsp-onvif-server.xml"/>
+    <xi:include href="xml/rtsp-onvif-client.xml"/>
+    <xi:include href="xml/rtsp-onvif-media-factory.xml"/>
+    <xi:include href="xml/rtsp-onvif-media.xml"/>
   </chapter>
 
   <chapter id="rtsp-server-hierarchy">
index 677ece1..86e3f25 100644 (file)
@@ -182,7 +182,9 @@ gst_rtsp_media_set_latency
 gst_rtsp_media_get_latency
 
 gst_rtsp_media_setup_sdp
+gst_rtsp_media_setup_sdp_full
 gst_rtsp_media_handle_sdp
+gst_rtsp_media_handle_sdp_full
 
 <SUBSECTION MediaPrepare>
 gst_rtsp_media_prepare
@@ -281,7 +283,9 @@ gst_rtsp_media_factory_set_media_gtype
 gst_rtsp_media_factory_get_media_gtype
 
 gst_rtsp_media_factory_construct
+gst_rtsp_media_factory_construct_full
 gst_rtsp_media_factory_create_element
+gst_rtsp_media_factory_create_element_full
 
 <SUBSECTION Standard>
 GST_RTSP_MEDIA_FACTORY_CAST
@@ -739,3 +743,106 @@ GST_TYPE_RTSP_TOKEN
 gst_rtsp_token_get_type
 </SECTION>
 
+<SECTION>
+<FILE>rtsp-onvif-server</FILE>
+<TITLE>GstRTSPOnvifServer</TITLE>
+GstRTSPOnvifServer
+GstRTSPOnvifServerClass
+
+gst_rtsp_onvif_server_new
+
+GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT
+<SUBSECTION Standard>
+gst_rtsp_onvif_server_get_type
+GST_TYPE_RTSP_ONVIF_SERVER
+GST_RTSP_ONVIF_SERVER
+GST_RTSP_ONVIF_SERVER_CAST
+GST_RTSP_ONVIF_SERVER_CLASS
+GST_RTSP_ONVIF_SERVER_CLASS_CAST
+GST_RTSP_ONVIF_SERVER_GET_CLASS
+GST_IS_RTSP_ONVIF_SERVER
+GST_IS_RTSP_ONVIF_SERVER_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>rtsp-onvif-server</FILE>
+<TITLE>GstRTSPOnvifServer</TITLE>
+GstRTSPOnvifServer
+GstRTSPOnvifServerClass
+
+gst_rtsp_onvif_server_new
+<SUBSECTION Standard>
+gst_rtsp_onvif_server_get_type
+GST_TYPE_RTSP_ONVIF_SERVER
+GST_RTSP_ONVIF_SERVER
+GST_RTSP_ONVIF_SERVER_CAST
+GST_RTSP_ONVIF_SERVER_CLASS
+GST_RTSP_ONVIF_SERVER_CLASS_CAST
+GST_RTSP_ONVIF_SERVER_GET_CLASS
+GST_IS_RTSP_ONVIF_SERVER
+GST_IS_RTSP_ONVIF_SERVER_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>rtsp-onvif-client</FILE>
+<TITLE>GstRTSPOnvifClient</TITLE>
+GstRTSPOnvifClient
+GstRTSPOnvifClientClass
+<SUBSECTION Standard>
+gst_rtsp_onvif_client_get_type
+GST_TYPE_RTSP_ONVIF_CLIENT
+GST_RTSP_ONVIF_CLIENT
+GST_RTSP_ONVIF_CLIENT_CAST
+GST_RTSP_ONVIF_CLIENT_CLASS
+GST_RTSP_ONVIF_CLIENT_CLASS_CAST
+GST_RTSP_ONVIF_CLIENT_GET_CLASS
+GST_IS_RTSP_ONVIF_CLIENT
+GST_IS_RTSP_ONVIF_CLIENT_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>rtsp-onvif-media-factory</FILE>
+<TITLE>GstRTSPOnvifMediaFactory</TITLE>
+GstRTSPOnvifMediaFactory
+GstRTSPOnvifMediaFactoryClass
+
+gst_rtsp_onvif_media_factory_new
+gst_rtsp_onvif_media_factory_has_backchannel_support
+gst_rtsp_onvif_media_factory_set_backchannel_bandwidth
+gst_rtsp_onvif_media_factory_get_backchannel_bandwidth
+gst_rtsp_onvif_media_factory_set_backchannel_launch
+gst_rtsp_onvif_media_factory_get_backchannel_launch
+<SUBSECTION Standard>
+GstRTSPOnvifMediaFactoryPrivate
+gst_rtsp_onvif_media_factory_get_type
+GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY
+GST_RTSP_ONVIF_MEDIA_FACTORY
+GST_RTSP_ONVIF_MEDIA_FACTORY_CAST
+GST_RTSP_ONVIF_MEDIA_FACTORY_CLASS
+GST_RTSP_ONVIF_MEDIA_FACTORY_CLASS_CAST
+GST_RTSP_ONVIF_MEDIA_FACTORY_GET_CLASS
+GST_IS_RTSP_ONVIF_MEDIA_FACTORY
+GST_IS_RTSP_ONVIF_MEDIA_FACTORY_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>rtsp-onvif-media</FILE>
+<TITLE>GstRTSPOnvifMedia</TITLE>
+GstRTSPOnvifMedia
+GstRTSPOnvifMediaClass
+
+gst_rtsp_onvif_media_collect_backchannel
+gst_rtsp_onvif_media_get_backchannel_bandwidth
+gst_rtsp_onvif_media_set_backchannel_bandwidth
+<SUBSECTION Standard>
+GstRTSPOnvifMediaPrivate
+gst_rtsp_onvif_media_get_type
+GST_TYPE_RTSP_ONVIF_MEDIA
+GST_RTSP_ONVIF_MEDIA
+GST_RTSP_ONVIF_MEDIA_CAST
+GST_RTSP_ONVIF_MEDIA_CLASS
+GST_RTSP_ONVIF_MEDIA_CLASS_CAST
+GST_RTSP_ONVIF_MEDIA_GET_CLASS
+GST_IS_RTSP_ONVIF_MEDIA
+GST_IS_RTSP_ONVIF_MEDIA_CLASS
+</SECTION>
index 0907268..d9df57c 100644 (file)
@@ -13,3 +13,4 @@ test-uri
 test-auth
 test-netclock
 test-netclock-client
+test-onvif-backchannel
index a76f909..43f811a 100644 (file)
@@ -2,7 +2,8 @@ noinst_PROGRAMS = test-video test-ogg test-mp4 test-readme \
                  test-launch test-sdp test-uri test-auth test-auth-digest \
                  test-multicast test-multicast2 test-appsrc \
                  test-video-rtx test-record test-record-auth \
-                 test-netclock test-netclock-client
+                 test-netclock test-netclock-client \
+                 test-onvif-backchannel
 
 #INCLUDES = -I$(top_srcdir) -I$(srcdir)
 
@@ -28,3 +29,10 @@ test_netclock_client_LDADD = \
        $(LDADD) \
        $(GST_NET_LIBS)
 
+test_onvif_backchannel_CFLAGS = \
+       $(AM_CFLAGS) \
+       $(GST_PLUGINS_BASE_CFLAGS)
+test_onvif_backchannel_LDADD = \
+       $(LDADD) \
+       $(GST_PLUGINS_BASE_LIBS) -lgstrtsp-1.0 -lgstsdp-1.0
+
diff --git a/examples/test-onvif-backchannel.c b/examples/test-onvif-backchannel.c
new file mode 100644 (file)
index 0000000..906c10b
--- /dev/null
@@ -0,0 +1,71 @@
+/* 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.
+ */
+
+#include <gst/gst.h>
+#include <gst/rtsp-server/rtsp-onvif-server.h>
+
+#include <string.h>
+
+int
+main (int argc, char *argv[])
+{
+  GMainLoop *loop;
+  GstRTSPServer *server;
+  GstRTSPMountPoints *mounts;
+  GstRTSPMediaFactory *factory;
+
+  gst_init (&argc, &argv);
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  /* create a server instance */
+  server = gst_rtsp_onvif_server_new ();
+
+  /* get the mount points for this server, every server has a default object
+   * that be used to map uri mount points to media factories */
+  mounts = gst_rtsp_server_get_mount_points (server);
+
+  /* make a media factory for a test stream. The default media factory can use
+   * gst-launch syntax to create pipelines.
+   * any launch line works as long as it contains elements named pay%d. Each
+   * element with pay%d names will be a stream */
+  factory = gst_rtsp_onvif_media_factory_new ();
+  gst_rtsp_media_factory_set_launch (factory,
+      "( videotestsrc is-live=true ! x264enc ! rtph264pay name=pay0 pt=96 audiotestsrc is-live=true ! mulawenc ! rtppcmupay name=pay1 )");
+  gst_rtsp_onvif_media_factory_set_backchannel_launch
+      (GST_RTSP_ONVIF_MEDIA_FACTORY (factory),
+      "( capsfilter caps=\"application/x-rtp, media=audio, payload=0, clock-rate=8000, encoding-name=PCMU\" name=depay_backchannel ! rtppcmudepay ! fakesink async=false  )");
+  gst_rtsp_media_factory_set_shared (factory, FALSE);
+  gst_rtsp_media_factory_set_media_gtype (factory, GST_TYPE_RTSP_ONVIF_MEDIA);
+
+  /* attach the test factory to the /test url */
+  gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
+
+  /* don't need the ref to the mapper anymore */
+  g_object_unref (mounts);
+
+  /* attach the server to the default maincontext */
+  gst_rtsp_server_attach (server, NULL);
+
+  /* start serving */
+  g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
+  g_main_loop_run (loop);
+
+  return 0;
+}
index 224b48b..95b239f 100644 (file)
@@ -17,7 +17,11 @@ public_headers = \
                rtsp-session-pool.h \
                rtsp-token.h \
                rtsp-client.h \
-               rtsp-server.h
+               rtsp-server.h \
+               rtsp-onvif-server.h \
+               rtsp-onvif-client.h \
+               rtsp-onvif-media-factory.h \
+               rtsp-onvif-media.h
 
 c_sources = \
        rtsp-auth.c \
@@ -38,7 +42,11 @@ c_sources = \
        rtsp-session-pool.c \
        rtsp-token.c \
        rtsp-client.c \
-       rtsp-server.c
+       rtsp-server.c \
+       rtsp-onvif-server.c \
+       rtsp-onvif-client.c \
+       rtsp-onvif-media-factory.c \
+       rtsp-onvif-media.c
 
 noinst_HEADERS = 
 
index e4c95f1..ae332ff 100644 (file)
@@ -332,7 +332,6 @@ gboolean              gst_rtsp_media_setup_sdp        (GstRTSPMedia * media, Gst
 GST_EXPORT
 gboolean              gst_rtsp_media_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp);
 
-
 /* creating streams */
 
 GST_EXPORT
diff --git a/gst/rtsp-server/rtsp-onvif-client.c b/gst/rtsp-server/rtsp-onvif-client.c
new file mode 100644 (file)
index 0000000..2fb50fa
--- /dev/null
@@ -0,0 +1,100 @@
+/* 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "rtsp-onvif-client.h"
+#include "rtsp-onvif-server.h"
+#include "rtsp-onvif-media-factory.h"
+
+G_DEFINE_TYPE (GstRTSPOnvifClient, gst_rtsp_onvif_client, GST_TYPE_RTSP_CLIENT);
+
+static gchar *
+gst_rtsp_onvif_client_check_requirements (GstRTSPClient * client,
+    GstRTSPContext * ctx, gchar ** requirements)
+{
+  GstRTSPMountPoints *mount_points = NULL;
+  GstRTSPMediaFactory *factory = NULL;
+  gchar *path = NULL;
+  gboolean has_backchannel = FALSE;
+  GString *unsupported = g_string_new ("");
+
+  while (*requirements) {
+    if (strcmp (*requirements, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT) == 0) {
+      has_backchannel = TRUE;
+    } else {
+      if (unsupported->len)
+        g_string_append (unsupported, ", ");
+      g_string_append (unsupported, *requirements);
+    }
+    requirements++;
+  }
+
+  if (unsupported->len)
+    goto out;
+
+  mount_points = gst_rtsp_client_get_mount_points (client);
+  if (!(path = gst_rtsp_mount_points_make_path (mount_points, ctx->uri)))
+    goto out;
+
+  if (!(factory = gst_rtsp_mount_points_match (mount_points, path, NULL)))
+    goto out;
+
+  if (has_backchannel && !GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory)) {
+    if (unsupported->len)
+      g_string_append (unsupported, ", ");
+    g_string_append (unsupported, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT);
+  } else if (has_backchannel) {
+    GstRTSPOnvifMediaFactory *onvif_factory =
+        GST_RTSP_ONVIF_MEDIA_FACTORY (factory);
+
+    if (!gst_rtsp_onvif_media_factory_has_backchannel_support (onvif_factory)) {
+      if (unsupported->len)
+        g_string_append (unsupported, ", ");
+      g_string_append (unsupported, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT);
+    }
+  }
+
+out:
+  if (path)
+    g_free (path);
+  if (factory)
+    g_object_unref (factory);
+  if (mount_points)
+    g_object_unref (mount_points);
+
+  return g_string_free (unsupported, FALSE);
+}
+
+static void
+gst_rtsp_onvif_client_class_init (GstRTSPOnvifClientClass * klass)
+{
+  GstRTSPClientClass *client_klass = (GstRTSPClientClass *) klass;
+
+  client_klass->check_requirements = gst_rtsp_onvif_client_check_requirements;
+}
+
+static void
+gst_rtsp_onvif_client_init (GstRTSPOnvifClient * client)
+{
+}
diff --git a/gst/rtsp-server/rtsp-onvif-client.h b/gst/rtsp-server/rtsp-onvif-client.h
new file mode 100644 (file)
index 0000000..0dc4e98
--- /dev/null
@@ -0,0 +1,57 @@
+/* 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.
+ */
+
+#ifndef __GST_RTSP_ONVIF_CLIENT_H__
+#define __GST_RTSP_ONVIF_CLIENT_H__
+
+#include <gst/gst.h>
+#include "rtsp-client.h"
+
+#define GST_TYPE_RTSP_ONVIF_CLIENT              (gst_rtsp_onvif_client_get_type ())
+#define GST_IS_RTSP_ONVIF_CLIENT(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_CLIENT))
+#define GST_IS_RTSP_ONVIF_CLIENT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_CLIENT))
+#define GST_RTSP_ONVIF_CLIENT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_CLIENT, GstRTSPOnvifClientClass))
+#define GST_RTSP_ONVIF_CLIENT(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_CLIENT, GstRTSPOnvifClient))
+#define GST_RTSP_ONVIF_CLIENT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_CLIENT, GstRTSPOnvifClientClass))
+#define GST_RTSP_ONVIF_CLIENT_CAST(obj)         ((GstRTSPOnvifClient*)(obj))
+#define GST_RTSP_ONVIF_CLIENT_CLASS_CAST(klass) ((GstRTSPOnvifClientClass*)(klass))
+
+typedef struct GstRTSPOnvifClientClass GstRTSPOnvifClientClass;
+typedef struct GstRTSPOnvifClient GstRTSPOnvifClient;
+
+struct GstRTSPOnvifClientClass
+{
+  GstRTSPClientClass parent;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING_LARGE];
+};
+
+struct GstRTSPOnvifClient
+{
+  GstRTSPClient parent;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GST_EXPORT
+GType gst_rtsp_onvif_client_get_type (void);
+
+#endif /* __GST_RTSP_ONVIF_CLIENT_H__ */
diff --git a/gst/rtsp-server/rtsp-onvif-media-factory.c b/gst/rtsp-server/rtsp-onvif-media-factory.c
new file mode 100644 (file)
index 0000000..a200f1d
--- /dev/null
@@ -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);
+}
diff --git a/gst/rtsp-server/rtsp-onvif-media-factory.h b/gst/rtsp-server/rtsp-onvif-media-factory.h
new file mode 100644 (file)
index 0000000..5468748
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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.
+ */
+
+#ifndef __GST_RTSP_ONVIF_MEDIA_FACTORY_H__
+#define __GST_RTSP_ONVIF_MEDIA_FACTORY_H__
+
+#include <gst/gst.h>
+#include "rtsp-media-factory.h"
+
+#define GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY              (gst_rtsp_onvif_media_factory_get_type ())
+#define GST_IS_RTSP_ONVIF_MEDIA_FACTORY(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY))
+#define GST_IS_RTSP_ONVIF_MEDIA_FACTORY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY))
+#define GST_RTSP_ONVIF_MEDIA_FACTORY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, GstRTSPOnvifMediaFactoryClass))
+#define GST_RTSP_ONVIF_MEDIA_FACTORY(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, GstRTSPOnvifMediaFactory))
+#define GST_RTSP_ONVIF_MEDIA_FACTORY_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, GstRTSPOnvifMediaFactoryClass))
+#define GST_RTSP_ONVIF_MEDIA_FACTORY_CAST(obj)         ((GstRTSPOnvifMediaFactory*)(obj))
+#define GST_RTSP_ONVIF_MEDIA_FACTORY_CLASS_CAST(klass) ((GstRTSPOnvifMediaFactoryClass*)(klass))
+
+typedef struct GstRTSPOnvifMediaFactoryClass GstRTSPOnvifMediaFactoryClass;
+typedef struct GstRTSPOnvifMediaFactory GstRTSPOnvifMediaFactory;
+typedef struct GstRTSPOnvifMediaFactoryPrivate GstRTSPOnvifMediaFactoryPrivate;
+
+struct GstRTSPOnvifMediaFactoryClass
+{
+  GstRTSPMediaFactoryClass parent;
+  gboolean (*has_backchannel_support) (GstRTSPOnvifMediaFactory * factory);
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING_LARGE];
+};
+
+struct GstRTSPOnvifMediaFactory
+{
+  GstRTSPMediaFactory parent;
+  GstRTSPOnvifMediaFactoryPrivate *priv;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GST_EXPORT
+GType gst_rtsp_onvif_media_factory_get_type (void);
+
+GST_EXPORT
+GstRTSPMediaFactory *gst_rtsp_onvif_media_factory_new (void);
+
+GST_EXPORT
+void gst_rtsp_onvif_media_factory_set_backchannel_launch (GstRTSPOnvifMediaFactory *
+    factory, const gchar * launch);
+GST_EXPORT
+gchar * gst_rtsp_onvif_media_factory_get_backchannel_launch (GstRTSPOnvifMediaFactory * factory);
+
+GST_EXPORT
+gboolean gst_rtsp_onvif_media_factory_has_backchannel_support (GstRTSPOnvifMediaFactory * factory);
+
+GST_EXPORT
+void gst_rtsp_onvif_media_factory_set_backchannel_bandwidth (GstRTSPOnvifMediaFactory * factory, guint bandwidth);
+GST_EXPORT
+guint gst_rtsp_onvif_media_factory_get_backchannel_bandwidth (GstRTSPOnvifMediaFactory * factory);
+
+#endif /* __GST_RTSP_ONVIF_MEDIA_FACTORY_H__ */
diff --git a/gst/rtsp-server/rtsp-onvif-media.c b/gst/rtsp-server/rtsp-onvif-media.c
new file mode 100644 (file)
index 0000000..8cd7261
--- /dev/null
@@ -0,0 +1,330 @@
+/* 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
+ * @short_description: The ONVIF media pipeline
+ * @see_also: #GstRTSPMedia, #GstRTSPOnvifMediaFactory, #GstRTSPStream, #GstRTSPSession,
+ *     #GstRTSPSessionMedia
+ *
+ * a #GstRTSPOnvifMedia contains the complete GStreamer pipeline to manage the
+ * streaming to the clients. The actual data transfer is done by the
+ * #GstRTSPStream objects that are created and exposed by the #GstRTSPMedia.
+ *
+ * On top of #GstRTSPMedia this subclass adds special ONVIF features.
+ * 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. To
+ * handle the ONVIF backchannel, a #GstRTSPOnvifMediaFactory and
+ * #GstRTSPOnvifServer has to be used.
+ *
+ * Since: 1.14
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "rtsp-onvif-media.h"
+
+struct GstRTSPOnvifMediaPrivate
+{
+  GMutex lock;
+  guint backchannel_bandwidth;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPOnvifMedia, gst_rtsp_onvif_media,
+    GST_TYPE_RTSP_MEDIA);
+
+static gboolean
+gst_rtsp_onvif_media_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
+    GstSDPInfo * info)
+{
+  guint i, n_streams;
+  gchar *rangestr;
+  gboolean res;
+
+  /* Mostly a copy of gst_rtsp_sdp_from_media() which handles the backchannel
+   * stream separately and adds sendonly/recvonly attributes to each media
+   */
+
+  n_streams = gst_rtsp_media_n_streams (media);
+
+  rangestr = gst_rtsp_media_get_range_string (media, FALSE, GST_RTSP_RANGE_NPT);
+  if (rangestr == NULL)
+    goto not_prepared;
+
+  gst_sdp_message_add_attribute (sdp, "range", rangestr);
+  g_free (rangestr);
+
+  res = TRUE;
+  for (i = 0; res && (i < n_streams); i++) {
+    GstRTSPStream *stream;
+    GstCaps *caps = NULL;
+    GstRTSPProfile profiles;
+    guint mask;
+    gboolean res;
+    GstPad *sinkpad = NULL;
+    guint n_caps, j;
+
+    /* Mostly a copy of gst_rtsp_sdp_from_stream() which handles the
+     * backchannel stream separately */
+
+    stream = gst_rtsp_media_get_stream (media, i);
+
+    if ((sinkpad = gst_rtsp_stream_get_sinkpad (stream))) {
+      caps = gst_pad_query_caps (sinkpad, NULL);
+    } else {
+      caps = gst_rtsp_stream_get_caps (stream);
+    }
+
+    if (caps == NULL) {
+      GST_ERROR ("stream %p has no caps", stream);
+      res = FALSE;
+      if (sinkpad)
+        gst_object_unref (sinkpad);
+      break;
+    } else if (!sinkpad && !gst_caps_is_fixed (caps)) {
+      GST_ERROR ("stream %p has unfixed caps", stream);
+      res = FALSE;
+      gst_caps_unref (caps);
+      break;
+    }
+
+    n_caps = gst_caps_get_size (caps);
+    for (j = 0; res && j < n_caps; j++) {
+      GstStructure *s = gst_caps_get_structure (caps, j);
+      GstCaps *media_caps = gst_caps_new_full (gst_structure_copy (s), NULL);
+
+      if (!gst_caps_is_fixed (media_caps)) {
+        GST_ERROR ("media caps for stream %p are not all fixed", stream);
+        res = FALSE;
+        gst_caps_unref (media_caps);
+        break;
+      }
+
+      /* make a new media for each profile */
+      profiles = gst_rtsp_stream_get_profiles (stream);
+      mask = 1;
+      res = TRUE;
+      while (res && (profiles >= mask)) {
+        GstRTSPProfile prof = profiles & mask;
+
+        if (prof) {
+          res = gst_rtsp_sdp_make_media (sdp, info, stream, media_caps, prof);
+          if (res) {
+            GstSDPMedia *smedia =
+                &g_array_index (sdp->medias, GstSDPMedia, sdp->medias->len - 1);
+
+            if (sinkpad) {
+              GstRTSPOnvifMedia *onvif_media = GST_RTSP_ONVIF_MEDIA (media);
+
+              gst_sdp_media_add_attribute (smedia, "sendonly", "");
+              if (onvif_media->priv->backchannel_bandwidth > 0)
+                gst_sdp_media_add_bandwidth (smedia, GST_SDP_BWTYPE_AS,
+                    onvif_media->priv->backchannel_bandwidth);
+            } else {
+              gst_sdp_media_add_attribute (smedia, "recvonly", "");
+            }
+          }
+        }
+
+        mask <<= 1;
+      }
+
+      if (sinkpad) {
+        GstStructure *s = gst_caps_get_structure (media_caps, 0);
+        gint pt = -1;
+
+        if (!gst_structure_get_int (s, "payload", &pt) || pt < 0) {
+          GST_ERROR ("stream %p has no payload type", stream);
+          res = FALSE;
+          gst_caps_unref (media_caps);
+          gst_object_unref (sinkpad);
+          break;
+        }
+
+        gst_rtsp_stream_set_pt_map (stream, pt, media_caps);
+      }
+
+      gst_caps_unref (media_caps);
+    }
+
+    gst_caps_unref (caps);
+    if (sinkpad)
+      gst_object_unref (sinkpad);
+  }
+
+  {
+    GstNetTimeProvider *provider;
+
+    if ((provider =
+            gst_rtsp_media_get_time_provider (media, info->server_ip, 0))) {
+      GstClock *clock;
+      gchar *address, *str;
+      gint port;
+
+      g_object_get (provider, "clock", &clock, "address", &address, "port",
+          &port, NULL);
+
+      str = g_strdup_printf ("GstNetTimeProvider %s %s:%d %" G_GUINT64_FORMAT,
+          g_type_name (G_TYPE_FROM_INSTANCE (clock)), address, port,
+          gst_clock_get_time (clock));
+
+      gst_sdp_message_add_attribute (sdp, "x-gst-clock", str);
+      g_free (str);
+      gst_object_unref (clock);
+      g_free (address);
+      gst_object_unref (provider);
+    }
+  }
+
+  return res;
+
+  /* ERRORS */
+not_prepared:
+  {
+    GST_ERROR ("media %p is not prepared", media);
+    return FALSE;
+  }
+}
+
+static void
+gst_rtsp_onvif_media_finalize (GObject * object)
+{
+  GstRTSPOnvifMedia *media = GST_RTSP_ONVIF_MEDIA (object);
+
+  g_mutex_clear (&media->priv->lock);
+
+  return G_OBJECT_CLASS (gst_rtsp_onvif_media_parent_class)->finalize (object);
+}
+
+static void
+gst_rtsp_onvif_media_class_init (GstRTSPOnvifMediaClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstRTSPMediaClass *media_class = (GstRTSPMediaClass *) klass;
+
+  gobject_class->finalize = gst_rtsp_onvif_media_finalize;
+
+  media_class->setup_sdp = gst_rtsp_onvif_media_setup_sdp;
+}
+
+static void
+gst_rtsp_onvif_media_init (GstRTSPOnvifMedia * media)
+{
+  media->priv = gst_rtsp_onvif_media_get_instance_private (media);
+  g_mutex_init (&media->priv->lock);
+}
+
+/**
+ * gst_rtsp_onvif_media_collect_backchannel:
+ * @media: a #GstRTSPOnvifMedia
+ *
+ * Find the ONVIF backchannel depayloader element. It should be named
+ * 'depay_backchannel', be placed in a bin called 'onvif-backchannel'
+ * and return all supported RTP caps on a caps query. Complete RTP caps with
+ * at least the payload type, clock-rate and encoding-name are required.
+ *
+ * A new #GstRTSPStream is created for the backchannel if found.
+ *
+ * Returns: %TRUE if a backchannel stream could be found and created
+ *
+ * Since: 1.14
+ */
+gboolean
+gst_rtsp_onvif_media_collect_backchannel (GstRTSPOnvifMedia * media)
+{
+  GstElement *element, *backchannel_bin = NULL;
+  GstPad *pad = NULL;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA (media), FALSE);
+
+  element = gst_rtsp_media_get_element (GST_RTSP_MEDIA (media));
+  if (!element)
+    return ret;
+
+  backchannel_bin =
+      gst_bin_get_by_name (GST_BIN (element), "onvif-backchannel");
+  if (!backchannel_bin)
+    goto out;
+
+  pad = gst_element_get_static_pad (backchannel_bin, "sink");
+  if (!pad)
+    goto out;
+
+  gst_rtsp_media_create_stream (GST_RTSP_MEDIA (media), backchannel_bin, pad);
+  ret = TRUE;
+
+out:
+  if (pad)
+    gst_object_unref (pad);
+  if (backchannel_bin)
+    gst_object_unref (backchannel_bin);
+  gst_object_unref (element);
+
+  return ret;
+}
+
+/**
+ * gst_rtsp_onvif_media_set_backchannel_bandwidth:
+ * @factory: a #GstRTSPMedia
+ * @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_set_backchannel_bandwidth (GstRTSPOnvifMedia * media,
+    guint bandwidth)
+{
+  g_return_if_fail (GST_IS_RTSP_ONVIF_MEDIA (media));
+
+  g_mutex_lock (&media->priv->lock);
+  media->priv->backchannel_bandwidth = bandwidth;
+  g_mutex_unlock (&media->priv->lock);
+}
+
+/**
+ * gst_rtsp_onvif_media_get_backchannel_bandwidth:
+ * @factory: a #GstRTSPMedia
+ *
+ * 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_get_backchannel_bandwidth (GstRTSPOnvifMedia * media)
+{
+  guint bandwidth;
+
+  g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA (media), 0);
+
+  g_mutex_lock (&media->priv->lock);
+  bandwidth = media->priv->backchannel_bandwidth;
+  g_mutex_unlock (&media->priv->lock);
+
+  return bandwidth;
+}
diff --git a/gst/rtsp-server/rtsp-onvif-media.h b/gst/rtsp-server/rtsp-onvif-media.h
new file mode 100644 (file)
index 0000000..a45c11e
--- /dev/null
@@ -0,0 +1,66 @@
+/* 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.
+ */
+
+#ifndef __GST_RTSP_ONVIF_MEDIA_H__
+#define __GST_RTSP_ONVIF_MEDIA_H__
+
+#include <gst/gst.h>
+#include "rtsp-media.h"
+
+#define GST_TYPE_RTSP_ONVIF_MEDIA              (gst_rtsp_onvif_media_get_type ())
+#define GST_IS_RTSP_ONVIF_MEDIA(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_MEDIA))
+#define GST_IS_RTSP_ONVIF_MEDIA_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_MEDIA))
+#define GST_RTSP_ONVIF_MEDIA_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_MEDIA, GstRTSPOnvifMediaClass))
+#define GST_RTSP_ONVIF_MEDIA(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_MEDIA, GstRTSPOnvifMedia))
+#define GST_RTSP_ONVIF_MEDIA_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_MEDIA, GstRTSPOnvifMediaClass))
+#define GST_RTSP_ONVIF_MEDIA_CAST(obj)         ((GstRTSPOnvifMedia*)(obj))
+#define GST_RTSP_ONVIF_MEDIA_CLASS_CAST(klass) ((GstRTSPOnvifMediaClass*)(klass))
+
+typedef struct GstRTSPOnvifMediaClass GstRTSPOnvifMediaClass;
+typedef struct GstRTSPOnvifMedia GstRTSPOnvifMedia;
+typedef struct GstRTSPOnvifMediaPrivate GstRTSPOnvifMediaPrivate;
+
+struct GstRTSPOnvifMediaClass
+{
+  GstRTSPMediaClass parent;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING_LARGE];
+};
+
+struct GstRTSPOnvifMedia
+{
+  GstRTSPMedia parent;
+  GstRTSPOnvifMediaPrivate *priv;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GST_EXPORT
+GType gst_rtsp_onvif_media_get_type (void);
+GST_EXPORT
+gboolean gst_rtsp_onvif_media_collect_backchannel (GstRTSPOnvifMedia * media);
+
+GST_EXPORT
+void gst_rtsp_onvif_media_set_backchannel_bandwidth (GstRTSPOnvifMedia * media, guint bandwidth);
+GST_EXPORT
+guint gst_rtsp_onvif_media_get_backchannel_bandwidth (GstRTSPOnvifMedia * media);
+
+#endif /* __GST_RTSP_ONVIF_MEDIA_H__ */
diff --git a/gst/rtsp-server/rtsp-onvif-server.c b/gst/rtsp-server/rtsp-onvif-server.c
new file mode 100644 (file)
index 0000000..3c3c6f4
--- /dev/null
@@ -0,0 +1,100 @@
+/* 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-server
+ * @short_description: The main server object
+ * @see_also: #GstRTSPOnvifMediaFactory, #GstRTSPClient
+ *
+ * The server object is the object listening for connections on a port and
+ * creating #GstRTSPOnvifClient objects to handle those connections.
+ *
+ * The only different to #GstRTSPServer is that #GstRTSPOnvifServer creates
+ * #GstRTSPOnvifClient that have special handling for ONVIF specific features,
+ * like a backchannel that allows clients to send back media to the server.
+ *
+ * Since: 1.14
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "rtsp-onvif-server.h"
+#include "rtsp-onvif-client.h"
+
+G_DEFINE_TYPE (GstRTSPOnvifServer, gst_rtsp_onvif_server, GST_TYPE_RTSP_SERVER);
+
+static GstRTSPClient *
+gst_rtsp_onvif_server_create_client (GstRTSPServer * server)
+{
+  GstRTSPClient *client;
+  GstRTSPSessionPool *session_pool;
+  GstRTSPMountPoints *mount_points;
+  GstRTSPAuth *auth;
+  GstRTSPThreadPool *thread_pool;
+
+  /* a new client connected, create a session to handle the client. */
+  client = g_object_new (GST_TYPE_RTSP_ONVIF_CLIENT, NULL);
+
+  /* set the session pool that this client should use */
+  session_pool = gst_rtsp_server_get_session_pool (server);
+  gst_rtsp_client_set_session_pool (client, session_pool);
+  g_object_unref (session_pool);
+  /* set the mount points that this client should use */
+  mount_points = gst_rtsp_server_get_mount_points (server);
+  gst_rtsp_client_set_mount_points (client, mount_points);
+  g_object_unref (mount_points);
+  /* set authentication manager */
+  auth = gst_rtsp_server_get_auth (server);
+  gst_rtsp_client_set_auth (client, auth);
+  if (auth)
+    g_object_unref (auth);
+  /* set threadpool */
+  thread_pool = gst_rtsp_server_get_thread_pool (server);
+  gst_rtsp_client_set_thread_pool (client, thread_pool);
+  g_object_unref (thread_pool);
+
+  return client;
+}
+
+/**
+ * gst_rtsp_onvif_server_new:
+ *
+ * Create a new #GstRTSPOnvifServer instance.
+ *
+ * Returns: (transfer full): a new #GstRTSPOnvifServer
+ */
+GstRTSPServer *
+gst_rtsp_onvif_server_new (void)
+{
+  return g_object_new (GST_TYPE_RTSP_ONVIF_SERVER, NULL);
+}
+
+static void
+gst_rtsp_onvif_server_class_init (GstRTSPOnvifServerClass * klass)
+{
+  GstRTSPServerClass *server_klass = (GstRTSPServerClass *) klass;
+
+  server_klass->create_client = gst_rtsp_onvif_server_create_client;
+}
+
+static void
+gst_rtsp_onvif_server_init (GstRTSPOnvifServer * server)
+{
+}
diff --git a/gst/rtsp-server/rtsp-onvif-server.h b/gst/rtsp-server/rtsp-onvif-server.h
new file mode 100644 (file)
index 0000000..56bd9dc
--- /dev/null
@@ -0,0 +1,65 @@
+/* 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.
+ */
+
+#ifndef __GST_RTSP_ONVIF_SERVER_H__
+#define __GST_RTSP_ONVIF_SERVER_H__
+
+#include <gst/gst.h>
+#include "rtsp-server.h"
+
+#define GST_TYPE_RTSP_ONVIF_SERVER              (gst_rtsp_onvif_server_get_type ())
+#define GST_IS_RTSP_ONVIF_SERVER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_SERVER))
+#define GST_IS_RTSP_ONVIF_SERVER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_SERVER))
+#define GST_RTSP_ONVIF_SERVER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_SERVER, GstRTSPOnvifServerClass))
+#define GST_RTSP_ONVIF_SERVER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_SERVER, GstRTSPOnvifServer))
+#define GST_RTSP_ONVIF_SERVER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_SERVER, GstRTSPOnvifServerClass))
+#define GST_RTSP_ONVIF_SERVER_CAST(obj)         ((GstRTSPOnvifServer*)(obj))
+#define GST_RTSP_ONVIF_SERVER_CLASS_CAST(klass) ((GstRTSPOnvifServerClass*)(klass))
+
+typedef struct GstRTSPOnvifServerClass GstRTSPOnvifServerClass;
+typedef struct GstRTSPOnvifServer GstRTSPOnvifServer;
+
+struct GstRTSPOnvifServerClass
+{
+  GstRTSPServerClass parent;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING_LARGE];
+};
+
+struct GstRTSPOnvifServer
+{
+  GstRTSPServer parent;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GST_EXPORT
+GType gst_rtsp_onvif_server_get_type (void);
+GST_EXPORT
+GstRTSPServer *gst_rtsp_onvif_server_new (void);
+
+#define GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT "www.onvif.org/ver20/backchannel"
+
+#include "rtsp-onvif-client.h"
+#include "rtsp-onvif-media-factory.h"
+#include "rtsp-onvif-media.h"
+
+#endif /* __GST_RTSP_ONVIF_SERVER_H__ */
index b037b28..ff55c02 100644 (file)
@@ -70,6 +70,8 @@ update_sdp_from_tags (GstRTSPStream * stream, GstSDPMedia * stream_media)
   GstPad *src_pad;
 
   src_pad = gst_rtsp_stream_get_srcpad (stream);
+  if (!src_pad)
+    return;
 
   gst_pad_sticky_events_foreach (src_pad, get_info_from_tags, stream_media);
 
@@ -179,8 +181,8 @@ cleanup:
   }
 }
 
-static gboolean
-make_media (GstSDPMessage * sdp, GstSDPInfo * info,
+gboolean
+gst_rtsp_sdp_make_media (GstSDPMessage * sdp, GstSDPInfo * info,
     GstRTSPStream * stream, GstCaps * caps, GstRTSPProfile profile)
 {
   GstSDPMedia *smedia;
@@ -557,7 +559,7 @@ gst_rtsp_sdp_from_stream (GstSDPMessage * sdp, GstSDPInfo * info,
     GstRTSPProfile prof = profiles & mask;
 
     if (prof)
-      res = make_media (sdp, info, stream, caps, prof);
+      res = gst_rtsp_sdp_make_media (sdp, info, stream, caps, prof);
 
     mask <<= 1;
   }
index 50cafe8..8683e32 100644 (file)
@@ -40,6 +40,10 @@ gboolean            gst_rtsp_sdp_from_media  (GstSDPMessage *sdp, GstSDPInfo *in
 GST_EXPORT
 gboolean            gst_rtsp_sdp_from_stream (GstSDPMessage * sdp, GstSDPInfo * info, GstRTSPStream *stream);
 
+GST_EXPORT
+gboolean
+gst_rtsp_sdp_make_media (GstSDPMessage * sdp, GstSDPInfo * info, GstRTSPStream * stream, GstCaps * caps, GstRTSPProfile profile);
+
 G_END_DECLS
 
 #endif /* __GST_RTSP_SDP_H__ */