mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 05:12:09 +00:00
3d275b1345
They are wrong in the ONVIF streaming spec. The backchannel should be recvonly and the normal media should be sendonly: direction is always from the point of view of the SDP offerer (the server) according to RFC 3264.
330 lines
9.1 KiB
C
330 lines
9.1 KiB
C
/* 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, "recvonly", "");
|
|
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, "sendonly", "");
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|