summaryrefslogtreecommitdiff
path: root/gst/rtsp-server/rtsp-onvif-media.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/rtsp-server/rtsp-onvif-media.c')
-rw-r--r--gst/rtsp-server/rtsp-onvif-media.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/gst/rtsp-server/rtsp-onvif-media.c b/gst/rtsp-server/rtsp-onvif-media.c
new file mode 100644
index 0000000..8cd7261
--- /dev/null
+++ b/gst/rtsp-server/rtsp-onvif-media.c
@@ -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;
+}