rtspsink: Add rtspclientsink element

Add an rtspclientsink element that accepts streams for which
there is a registered payloader and sends them to
an RTSP server using RECORD.

Sending is synchronised to the pipeline clock. Payload-types
are automatically selected. The 'new-payloader' signal is fired
for custom configuration of payloaders when they are created.

Can now stream a movie like this:

receiver:
  ./test-record "( decodebin name=depay0 ! videoconvert ! autovideosink \
       decodebin name=depay1 ! audioconvert ! autoaudiosink )"
sender:
  gst-launch-1.0 filesrc location=file-with-aac-and-h264.mp4 ! qtdemux name=d ! \
       queue ! aacparse ! rtspclientsink location=rtsp://127.0.0.1:8554/test name=s \

https://bugzilla.gnome.org/show_bug.cgi?id=758180
This commit is contained in:
Jan Schmidt 2015-11-17 01:12:28 +11:00
parent b6ca057c72
commit f54dd50203
9 changed files with 5428 additions and 4 deletions

1
.gitignore vendored
View file

@ -62,6 +62,7 @@ stamp-h.in
/tests/check/gst/stream
/tests/check/gst/threadpool
/tests/check/gst/token
/tests/check/gst/rtspclientsink
/tests/check/test-registry.reg
/tests/test-reuse

View file

@ -170,6 +170,8 @@ AC_MSG_NOTICE(Using GStreamer Core Plugins in $GST_PLUGINS_DIR)
AG_GST_CHECK_GST_BASE($GST_API_VERSION, [$GST_REQ], [yes])
AG_GST_CHECK_GST_NET($GST_API_VERSION, [$GST_REQ], yes)
AG_GST_CHECK_GST_PLUGINS_BASE($GST_API_VERSION, [$GSTPB_REQ], [yes])
GSTPB_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-base-$GST_API_VERSION --variable pluginsdir`
AC_SUBST(GSTPB_PLUGINS_DIR)
@ -218,6 +220,31 @@ AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO([$PACKAGE_VERSION_NANO],
["${srcdir}/gst-rtsp-server.doap"],
[$PACKAGE_VERSION_MAJOR.$PACKAGE_VERSION_MINOR.$PACKAGE_VERSION_MICRO])
dnl build static plugins or not
AC_MSG_CHECKING([whether to build static plugins or not])
AC_ARG_ENABLE(
static-plugins,
AC_HELP_STRING(
[--enable-static-plugins],
[build static plugins @<:@default=no@:>@]),
[AS_CASE(
[$enableval], [no], [], [yes], [],
[AC_MSG_ERROR([bad value "$enableval" for --enable-static-plugins])])],
[enable_static_plugins=no])
AC_MSG_RESULT([$enable_static_plugins])
if test "x$enable_static_plugins" = xyes; then
AC_DEFINE(GST_PLUGIN_BUILD_STATIC, 1,
[Define if static plugins should be built])
GST_PLUGIN_LIBTOOLFLAGS=""
else
GST_PLUGIN_LIBTOOLFLAGS="--tag=disable-static"
fi
AC_SUBST(GST_PLUGIN_LIBTOOLFLAGS)
AM_CONDITIONAL(GST_PLUGIN_BUILD_STATIC, test "x$enable_static_plugins" = "xyes")
GST_PLUGIN_LDFLAGS="-module -avoid-version -export-symbols-regex '^[_]*gst_plugin_.*' $GST_ALL_LDFLAGS"
AC_SUBST(GST_PLUGIN_LDFLAGS)
# set by AG_GST_PARSE_SUBSYSTEM_DISABLES above
dnl make sure it doesn't complain about unused variables if debugging is disabled
NO_WARNINGS=""
@ -324,6 +351,7 @@ common/Makefile
common/m4/Makefile
gst/Makefile
gst/rtsp-server/Makefile
gst/rtsp-sink/Makefile
examples/Makefile
tests/Makefile
tests/check/Makefile

View file

@ -1 +1 @@
SUBDIRS = rtsp-server
SUBDIRS = rtsp-server rtsp-sink

18
gst/rtsp-sink/Makefile.am Normal file
View file

@ -0,0 +1,18 @@
plugin_LTLIBRARIES = libgstrtspclientsink.la
libgstrtspclientsink_la_SOURCES = gstrtspclientsink.c plugin.c
libgstrtspclientsink_la_CFLAGS = -I$(top_srcdir) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS)
# FIXME: Hack to avoid having to add GETTEXT_PACKAGE to gst-rtsp
libgstrtspclientsink_la_CFLAGS += -D"GETTEXT_PACKAGE=gst-rtsp-server-1.0"
libgstrtspclientsink_la_LIBADD = $(top_builddir)/gst/rtsp-server/libgstrtspserver-@GST_API_VERSION@.la \
$(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
-lgstrtp-@GST_API_VERSION@ -lgstrtsp-@GST_API_VERSION@ \
-lgstsdp-@GST_API_VERSION@ $(GST_NET_LIBS) $(GST_LIBS) \
$(GIO_LIBS)
libgstrtspclientsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstrtspclientsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
noinst_HEADERS = gstrtspclientsink.h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,244 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* <2006> Wim Taymans <wim@fluendo.com>
* <2015> Jan Schmidt <jan at centricular dot 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.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __GST_RTSP_CLIENT_SINK_H__
#define __GST_RTSP_CLIENT_SINK_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#include <gst/rtsp-server/rtsp-stream.h>
#include <gst/rtsp/rtsp.h>
#include <gio/gio.h>
#define GST_TYPE_RTSP_CLIENT_SINK \
(gst_rtsp_client_sink_get_type())
#define GST_RTSP_CLIENT_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTSP_CLIENT_SINK,GstRTSPClientSink))
#define GST_RTSP_CLIENT_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTSP_CLIENT_SINK,GstRTSPClientSinkClass))
#define GST_IS_RTSP_CLIENT_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTSP_CLIENT_SINK))
#define GST_IS_RTSP_CLIENT_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTSP_CLIENT_SINK))
#define GST_RTSP_CLIENT_SINK_CAST(obj) \
((GstRTSPClientSink *)(obj))
typedef struct _GstRTSPClientSink GstRTSPClientSink;
typedef struct _GstRTSPClientSinkClass GstRTSPClientSinkClass;
#define GST_RTSP_STATE_GET_LOCK(rtsp) (&GST_RTSP_CLIENT_SINK_CAST(rtsp)->state_rec_lock)
#define GST_RTSP_STATE_LOCK(rtsp) (g_rec_mutex_lock (GST_RTSP_STATE_GET_LOCK(rtsp)))
#define GST_RTSP_STATE_UNLOCK(rtsp) (g_rec_mutex_unlock (GST_RTSP_STATE_GET_LOCK(rtsp)))
#define GST_RTSP_STREAM_GET_LOCK(rtsp) (&GST_RTSP_CLIENT_SINK_CAST(rtsp)->stream_rec_lock)
#define GST_RTSP_STREAM_LOCK(rtsp) (g_rec_mutex_lock (GST_RTSP_STREAM_GET_LOCK(rtsp)))
#define GST_RTSP_STREAM_UNLOCK(rtsp) (g_rec_mutex_unlock (GST_RTSP_STREAM_GET_LOCK(rtsp)))
typedef struct _GstRTSPConnInfo GstRTSPConnInfo;
struct _GstRTSPConnInfo {
gchar *location;
GstRTSPUrl *url;
gchar *url_str;
GstRTSPConnection *connection;
gboolean connected;
gboolean flushing;
};
typedef struct _GstRTSPStreamInfo GstRTSPStreamInfo;
typedef struct _GstRTSPStreamContext GstRTSPStreamContext;
struct _GstRTSPStreamContext {
GstRTSPClientSink *parent;
guint index;
/* Index of the SDPMedia in the stored SDP */
guint sdp_index;
GstElement *payloader;
guint payloader_block_id;
gboolean prerolled;
/* Stream management object */
GstRTSPStream *stream;
gboolean joined;
/* Secure profile key mgmt */
GstCaps *srtcpparams;
/* per stream connection */
GstRTSPConnInfo conninfo;
/* For interleaved mode */
guint8 channel[2];
GstRTSPStreamTransport *stream_transport;
};
/**
* GstRTSPNatMethod:
* @GST_RTSP_NAT_NONE: none
* @GST_RTSP_NAT_DUMMY: send dummy packets
*
* Different methods for trying to traverse firewalls.
*/
typedef enum
{
GST_RTSP_NAT_NONE,
GST_RTSP_NAT_DUMMY
} GstRTSPNatMethod;
struct _GstRTSPClientSink {
GstBin parent;
/* task and mutex for interleaved mode */
gboolean interleaved;
GstTask *task;
GRecMutex stream_rec_lock;
GstSegment segment;
gint free_channel;
/* UDP mode loop */
gint pending_cmd;
gint busy_cmd;
gboolean ignore_timeout;
gboolean open_error;
/* mutex for protecting state changes */
GRecMutex state_rec_lock;
GstSDPMessage *uri_sdp;
gboolean from_sdp;
/* properties */
GstRTSPLowerTrans protocols;
gboolean debug;
guint retry;
guint64 udp_timeout;
GTimeVal tcp_timeout;
GTimeVal *ptcp_timeout;
guint latency;
gboolean do_rtsp_keep_alive;
gchar *proxy_host;
guint proxy_port;
gchar *proxy_user; /* from url or property */
gchar *proxy_passwd; /* from url or property */
gchar *prop_proxy_id; /* set via property */
gchar *prop_proxy_pw; /* set via property */
guint rtp_blocksize;
gchar *user_id;
gchar *user_pw;
GstRTSPRange client_port_range;
gint udp_buffer_size;
gboolean udp_reconnect;
gchar *multi_iface;
gboolean ntp_sync;
gboolean use_pipeline_clock;
GstStructure *sdes;
GTlsCertificateFlags tls_validation_flags;
GTlsDatabase *tls_database;
GTlsInteraction *tls_interaction;
gint ntp_time_source;
gchar *user_agent;
/* state */
GstRTSPState state;
gchar *content_base;
GstRTSPLowerTrans cur_protocols;
gboolean tried_url_auth;
gchar *addr;
gboolean need_redirect;
GstRTSPTimeRange *range;
gchar *control;
guint next_port_num;
GstClock *provided_clock;
/* supported methods */
gint methods;
/* session management */
GstRTSPConnInfo conninfo;
/* Everything goes in an internal
* locked-state bin */
GstBin *internal_bin;
/* Set to true when internal bin state
* >= PAUSED */
gboolean prerolled;
/* TRUE if we posted async-start */
gboolean in_async;
/* TRUE when stream info has been collected */
gboolean streams_collected;
guint next_pad_id;
gint next_dyn_pt;
GstElement *rtpbin;
GList *contexts;
GstSDPMessage cursdp;
GMutex send_lock;
GMutex preroll_lock;
GCond preroll_cond;
GstClockTime rtx_time;
GstRTSPProfile profiles;
gchar *server_ip;
};
struct _GstRTSPClientSinkClass {
GstBinClass parent_class;
};
GType gst_rtsp_client_sink_get_type(void);
G_END_DECLS
#endif /* __GST_RTSP_CLIENT_SINK_H__ */

26
gst/rtsp-sink/plugin.c Normal file
View file

@ -0,0 +1,26 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstrtspclientsink.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
#ifdef ENABLE_NLS
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */
if (!gst_element_register (plugin, "rtspclientsink", GST_RANK_NONE,
GST_TYPE_RTSP_CLIENT_SINK))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
rtspclientsink,
"RTSP client sink element",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -11,8 +11,8 @@ AM_TESTS_ENVIRONMENT = \
GST_STATE_IGNORE_ELEMENTS="$(STATE_IGNORE_ELEMENTS)" \
$(REGISTRY_ENVIRONMENT) \
GST_PLUGIN_SYSTEM_PATH_1_0= \
GST_PLUGIN_PATH_1_0=$(GST_PLUGINS_DIR):$(GSTPB_PLUGINS_DIR):$(GSTPG_PLUGINS_DIR):$(GSTPD_PLUGINS_DIR) \
GST_PLUGIN_LOADING_WHITELIST="gstreamer:gst-plugins-base:gst-plugins-good:gst-plugins-bad"
GST_PLUGIN_PATH_1_0=$(GST_PLUGINS_DIR):$(GSTPB_PLUGINS_DIR):$(GSTPG_PLUGINS_DIR):$(GSTPD_PLUGINS_DIR):$(top_builddir)/gst \
GST_PLUGIN_LOADING_WHITELIST="gstreamer:gst-plugins-base:gst-plugins-good:gst-plugins-bad:gst-rtsp-server"
# ths core dumps of some machines have PIDs appended
@ -37,7 +37,8 @@ check_PROGRAMS = \
gst/permissions \
gst/token \
gst/sessionmedia \
gst/sessionpool
gst/sessionpool \
gst/rtspclientsink
# these tests don't even pass
noinst_PROGRAMS =

View file

@ -0,0 +1,221 @@
/* GStreamer unit test for rtspclientsink
* Copyright (C) 2012 Axis Communications <dev-gstreamer at axis dot com>
* @author David Svensson Fors <davidsf at axis dot com>
* Copyright (C) 2015 Centricular Ltd
* @author Tim-Philipp Müller <tim@centricular.com>
* @author Jan Schmidt <jan@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/check/gstcheck.h>
#include <gst/sdp/gstsdpmessage.h>
#include <gst/rtp/gstrtpbuffer.h>
#include <gst/rtp/gstrtcpbuffer.h>
#include <stdio.h>
#include <netinet/in.h>
#include "rtsp-server.h"
#define TEST_MOUNT_POINT "/test"
/* tested rtsp server */
static GstRTSPServer *server = NULL;
/* tcp port that the test server listens for rtsp requests on */
static gint test_port = 0;
/* id of the server's source within the GMainContext */
static guint source_id;
/* iterate the default main context until there are no events to dispatch */
static void
iterate (void)
{
while (g_main_context_iteration (NULL, FALSE)) {
GST_DEBUG ("iteration");
}
}
/* start the testing rtsp server for RECORD mode */
static GstRTSPMediaFactory *
start_record_server (const gchar * launch_line)
{
GstRTSPMediaFactory *factory;
GstRTSPMountPoints *mounts;
gchar *service;
mounts = gst_rtsp_server_get_mount_points (server);
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_transport_mode (factory,
GST_RTSP_TRANSPORT_MODE_RECORD);
gst_rtsp_media_factory_set_launch (factory, launch_line);
gst_rtsp_mount_points_add_factory (mounts, TEST_MOUNT_POINT, factory);
g_object_unref (mounts);
/* set port to any */
gst_rtsp_server_set_service (server, "0");
/* attach to default main context */
source_id = gst_rtsp_server_attach (server, NULL);
fail_if (source_id == 0);
/* get port */
service = gst_rtsp_server_get_service (server);
test_port = atoi (service);
fail_unless (test_port != 0);
g_free (service);
GST_DEBUG ("rtsp server listening on port %d", test_port);
return factory;
}
/* stop the tested rtsp server */
static void
stop_server (void)
{
g_source_remove (source_id);
source_id = 0;
GST_DEBUG ("rtsp server stopped");
}
/* fixture setup function */
static void
setup (void)
{
server = gst_rtsp_server_new ();
}
/* fixture clean-up function */
static void
teardown (void)
{
if (server) {
g_object_unref (server);
server = NULL;
}
test_port = 0;
}
/* create an rtsp connection to the server on test_port */
static gchar *
get_server_uri (gint port, const gchar * mount_point)
{
gchar *address;
gchar *uri_string;
GstRTSPUrl *url = NULL;
address = gst_rtsp_server_get_address (server);
uri_string = g_strdup_printf ("rtsp://%s:%d%s", address, port, mount_point);
g_free (address);
fail_unless (gst_rtsp_url_parse (uri_string, &url) == GST_RTSP_OK);
gst_rtsp_url_free (url);
return uri_string;
}
static void
media_constructed_cb (GstRTSPMediaFactory * mfactory, GstRTSPMedia * media,
gpointer user_data)
{
GstElement **p_sink = user_data;
GstElement *bin;
bin = gst_rtsp_media_get_element (media);
*p_sink = gst_bin_get_by_name (GST_BIN (bin), "sink");
GST_INFO ("media constructed!: %" GST_PTR_FORMAT, *p_sink);
}
#define AUDIO_PIPELINE "audiotestsrc num-buffers=%d ! " \
"audio/x-raw,rate=8000 ! alawenc ! rtspclientsink name=sink location=%s"
#define RECORD_N_BUFS 10
GST_START_TEST (test_record)
{
GstRTSPMediaFactory *mfactory;
GstElement *server_sink = NULL;
gint i;
mfactory =
start_record_server ("( rtppcmadepay name=depay0 ! appsink name=sink )");
g_signal_connect (mfactory, "media-constructed",
G_CALLBACK (media_constructed_cb), &server_sink);
/* Create an rtspclientsink and send some data */
{
gchar *uri = get_server_uri (test_port, TEST_MOUNT_POINT);
gchar *pipe_str = g_strdup_printf (AUDIO_PIPELINE,
RECORD_N_BUFS, uri);
GstMessage *msg;
GstElement *pipeline;
GstBus *bus;
pipeline = gst_parse_launch (pipe_str, NULL);
fail_unless (pipeline != NULL);
bus = gst_element_get_bus (pipeline);
fail_if (bus == NULL);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
fail_if (GST_MESSAGE_TYPE (msg) != GST_MESSAGE_EOS);
gst_message_unref (msg);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
}
iterate ();
/* check received data (we assume every buffer created by audiotestsrc and
* subsequently encoded by mulawenc results in exactly one RTP packet) */
for (i = 0; i < RECORD_N_BUFS; ++i) {
GstSample *sample = NULL;
g_signal_emit_by_name (G_OBJECT (server_sink), "pull-sample", &sample);
GST_INFO ("%2d recv sample: %p", i, sample);
if (sample)
gst_sample_unref (sample);
}
/* clean up and iterate so the clean-up can finish */
stop_server ();
iterate ();
}
GST_END_TEST;
static Suite *
rtspclientsink_suite (void)
{
Suite *s = suite_create ("rtspclientsink");
TCase *tc = tcase_create ("general");
suite_add_tcase (s, tc);
tcase_add_checked_fixture (tc, setup, teardown);
tcase_set_timeout (tc, 120);
tcase_add_test (tc, test_record);
return s;
}
GST_CHECK_MAIN (rtspclientsink);