mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-11 19:06:33 +00:00
510 lines
14 KiB
C
510 lines
14 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-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);
|
|
|
|
/**
|
|
* gst_rtsp_onvif_media_factory_requires_backchannel:
|
|
* @factory: a #GstRTSPMediaFactory
|
|
*
|
|
* Checks whether the client request requires backchannel.
|
|
*
|
|
* Returns: %TRUE if the client request requires backchannel.
|
|
*
|
|
* Since: 1.14
|
|
*/
|
|
gboolean
|
|
gst_rtsp_onvif_media_factory_requires_backchannel (GstRTSPMediaFactory *
|
|
factory, GstRTSPContext * ctx)
|
|
{
|
|
GstRTSPMessage *msg = ctx->request;
|
|
GstRTSPResult res;
|
|
gint i;
|
|
gchar *reqs = NULL;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), FALSE);
|
|
|
|
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 ();
|
|
|
|
/* Only medias where no backchannel was requested can be shared */
|
|
if (gst_rtsp_onvif_media_factory_requires_backchannel (factory, ctx))
|
|
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 (gst_rtsp_onvif_media_factory_requires_backchannel (factory, ctx) &&
|
|
!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 (gst_rtsp_onvif_media_factory_requires_backchannel (factory, ctx)) {
|
|
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.
|
|
*
|
|
* Note: The pipeline part passed here must end in sinks that are not waiting
|
|
* until pre-rolling before reaching the PAUSED state, i.e. setting
|
|
* async=false on #GstBaseSink. Otherwise the whole media will not be able to
|
|
* prepare.
|
|
*
|
|
* 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);
|
|
}
|