From b04856f0cf7960ce8ed863bca7aa0ab5a233ff9b Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 3 Mar 2015 01:49:42 +1100 Subject: [PATCH] examples: Add a simple example of network synch for live streams. An example server and client that works for synchronising live streams only - as it can't support pause/play. --- examples/.gitignore | 2 + examples/Makefile.am | 11 ++- examples/test-netclock-client.c | 105 +++++++++++++++++++++++ examples/test-netclock.c | 181 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 examples/test-netclock-client.c create mode 100644 examples/test-netclock.c diff --git a/examples/.gitignore b/examples/.gitignore index 978258f..b8c175a 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -10,3 +10,5 @@ test-video test-video-rtx test-uri test-auth +test-netclock +test-netclock-client diff --git a/examples/Makefile.am b/examples/Makefile.am index ef33adf..0b1495d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,7 +1,8 @@ noinst_PROGRAMS = test-video test-ogg test-mp4 test-readme \ test-launch test-sdp test-uri test-auth \ test-multicast test-multicast2 test-appsrc \ - test-video-rtx test-record + test-video-rtx test-record \ + test-netclock test-netclock-client #INCLUDES = -I$(top_srcdir) -I$(srcdir) @@ -13,3 +14,11 @@ noinst_PROGRAMS += test-cgroups LDADD += $(LIBCGROUP_LIBS) endif +test_netclock_LDFLAGS = \ + $(GST_LIBS) \ + -lgstnet-@GST_API_VERSION@ \ + $(top_builddir)/gst/rtsp-server/libgstrtspserver-@GST_API_VERSION@.la + +test_netclock_client_LDFLAGS = \ + $(GST_LIBS) \ + -lgstnet-@GST_API_VERSION@ diff --git a/examples/test-netclock-client.c b/examples/test-netclock-client.c new file mode 100644 index 0000000..f91773e --- /dev/null +++ b/examples/test-netclock-client.c @@ -0,0 +1,105 @@ +/* GStreamer + * Copyright (C) 2008 Wim Taymans + * Copyright (C) 2014 Jan Schmidt + * + * 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 + +#include +#include + +#define PLAYBACK_DELAY_MS 40 + +static void +source_created (GstElement * pipe, GParamSpec * pspec) +{ + GstElement *source; + + g_object_get (pipe, "source", &source, NULL); + g_assert (source != NULL); + + g_object_set (source, "latency", PLAYBACK_DELAY_MS, + "use-pipeline-clock", TRUE, "buffer-mode", 1, NULL); + + gst_object_unref (source); +} + +int +main (int argc, char *argv[]) +{ + GstClock *net_clock; + gchar *server; + gint clock_port; + GstElement *pipe; + GstMessage *msg; + + gst_init (&argc, &argv); + + if (argc < 2) { + g_print ("usage: %s rtsp://URI clock-IP clock-PORT\n" + "example: %s rtsp://localhost:8554/test 127.0.0.1 8554\n", + argv[0], argv[0]); + return -1; + } + + server = argv[2]; + clock_port = atoi (argv[3]); + + net_clock = gst_net_client_clock_new ("net_clock", server, clock_port, 0); + if (net_clock == NULL) { + g_print ("Failed to create net clock client for %s:%d\n", + server, clock_port); + return 1; + } + + /* Wait 0.5 seconds for the clock to stabilise */ + g_usleep (G_USEC_PER_SEC / 2); + + pipe = gst_element_factory_make ("playbin", NULL); + g_object_set (pipe, "uri", argv[1], NULL); + g_signal_connect (pipe, "notify::source", (GCallback) source_created, NULL); + + gst_element_set_start_time (pipe, GST_CLOCK_TIME_NONE); + gst_element_set_base_time (pipe, 0); + gst_pipeline_use_clock (GST_PIPELINE (pipe), net_clock); + + if (gst_element_set_state (pipe, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + g_print ("Failed to set state to PLAYING\n"); + goto exit; + }; + + msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipe), + GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS | GST_MESSAGE_ERROR); + + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { + GError *err = NULL; + gchar *debug = NULL; + gst_message_parse_error (msg, &err, &debug); + g_print ("\nERROR: %s\n%s\n\n", err->message, debug); + g_error_free (err); + g_free (debug); + } + gst_message_unref (msg); + +exit: + gst_element_set_state (pipe, GST_STATE_NULL); + gst_object_unref (pipe); + + return 0; +} diff --git a/examples/test-netclock.c b/examples/test-netclock.c new file mode 100644 index 0000000..2e53635 --- /dev/null +++ b/examples/test-netclock.c @@ -0,0 +1,181 @@ +/* GStreamer + * Copyright (C) 2008 Wim Taymans + * Copyright (C) 2014 Jan Schmidt + * + * 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 + +#include +#include + +GstClock *global_clock; + +#define TEST_TYPE_RTSP_MEDIA_FACTORY (test_rtsp_media_factory_get_type ()) +#define TEST_TYPE_RTSP_MEDIA (test_rtsp_media_get_type ()) + +GType test_rtsp_media_factory_get_type (void); +GType test_rtsp_media_get_type (void); + +static GstRTSPMediaFactory *test_rtsp_media_factory_new (void); +static GstElement *create_pipeline (GstRTSPMediaFactory * factory, + GstRTSPMedia * media); + +typedef struct TestRTSPMediaFactoryClass TestRTSPMediaFactoryClass; +typedef struct TestRTSPMediaFactory TestRTSPMediaFactory; + +struct TestRTSPMediaFactoryClass +{ + GstRTSPMediaFactoryClass parent; +}; + +struct TestRTSPMediaFactory +{ + GstRTSPMediaFactory parent; +}; + +typedef struct TestRTSPMediaClass TestRTSPMediaClass; +typedef struct TestRTSPMedia TestRTSPMedia; + +struct TestRTSPMediaClass +{ + GstRTSPMediaClass parent; +}; + +struct TestRTSPMedia +{ + GstRTSPMedia parent; +}; + +GstRTSPMediaFactory * +test_rtsp_media_factory_new (void) +{ + GstRTSPMediaFactory *result; + + result = g_object_new (TEST_TYPE_RTSP_MEDIA_FACTORY, NULL); + + return result; +} + +G_DEFINE_TYPE (TestRTSPMediaFactory, test_rtsp_media_factory, + GST_TYPE_RTSP_MEDIA_FACTORY); + +static gboolean custom_setup_rtpbin (GstRTSPMedia * media, GstElement * rtpbin); + +static void +test_rtsp_media_factory_class_init (TestRTSPMediaFactoryClass * test_klass) +{ + GstRTSPMediaFactoryClass *mf_klass = + (GstRTSPMediaFactoryClass *) (test_klass); + mf_klass->create_pipeline = create_pipeline; +} + +static void +test_rtsp_media_factory_init (TestRTSPMediaFactory * factory) +{ +} + +static GstElement * +create_pipeline (GstRTSPMediaFactory * factory, GstRTSPMedia * media) +{ + GstElement *pipeline; + + pipeline = gst_pipeline_new ("media-pipeline"); + gst_pipeline_use_clock (GST_PIPELINE (pipeline), global_clock); + gst_element_set_base_time (pipeline, 0); + gst_element_set_start_time (pipeline, GST_CLOCK_TIME_NONE); + gst_rtsp_media_take_pipeline (media, GST_PIPELINE_CAST (pipeline)); + + return pipeline; +} + +G_DEFINE_TYPE (TestRTSPMedia, test_rtsp_media, GST_TYPE_RTSP_MEDIA); + +static void +test_rtsp_media_class_init (TestRTSPMediaClass * test_klass) +{ + GstRTSPMediaClass *klass = (GstRTSPMediaClass *) (test_klass); + klass->setup_rtpbin = custom_setup_rtpbin; +} + +static void +test_rtsp_media_init (TestRTSPMedia * media) +{ +} + +static gboolean +custom_setup_rtpbin (GstRTSPMedia * media, GstElement * rtpbin) +{ + g_object_set (rtpbin, "use-pipeline-clock", TRUE, NULL); + return TRUE; +} + +int +main (int argc, char *argv[]) +{ + GMainLoop *loop; + GstRTSPServer *server; + GstRTSPMountPoints *mounts; + GstRTSPMediaFactory *factory; + + gst_init (&argc, &argv); + + if (argc < 2) { + g_print ("usage: %s \n" + "example: %s \"( videotestsrc is-live=true ! x264enc ! rtph264pay name=pay0 pt=96 )\"\n" + "Pipeline must be live for synchronisation to work properly with this method!\n", + argv[0], argv[0]); + return -1; + } + + loop = g_main_loop_new (NULL, FALSE); + + global_clock = gst_system_clock_obtain (); + gst_net_time_provider_new (global_clock, "0.0.0.0", 8554); + + /* create a server instance */ + server = gst_rtsp_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 = test_rtsp_media_factory_new (); + gst_rtsp_media_factory_set_launch (factory, argv[1]); + gst_rtsp_media_factory_set_shared (GST_RTSP_MEDIA_FACTORY (factory), TRUE); + gst_rtsp_media_factory_set_media_gtype (GST_RTSP_MEDIA_FACTORY (factory), + TEST_TYPE_RTSP_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; +} -- 2.7.4