From 7eb8a5d24a8ccee2a6f1e5e762f5c4a64dbbceb1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 14 Sep 2007 16:56:16 +0000 Subject: [PATCH] Add simple exmple app to demonstrate starting and pausing live and non-live bins in a PLAYING pipeline. Original commit message from CVS: * configure.ac: * tests/examples/Makefile.am: * tests/examples/dynamic/.cvsignore: * tests/examples/dynamic/Makefile.am: * tests/examples/dynamic/addstream.c: (create_stream), (pause_play_stream), (message_received), (eos_message_received), (perform_step), (main): Add simple exmple app to demonstrate starting and pausing live and non-live bins in a PLAYING pipeline. --- ChangeLog | 12 ++ configure.ac | 1 + tests/examples/Makefile.am | 4 +- tests/examples/dynamic/.gitignore | 1 + tests/examples/dynamic/Makefile.am | 5 + tests/examples/dynamic/addstream.c | 255 +++++++++++++++++++++++++++++ 6 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 tests/examples/dynamic/.gitignore create mode 100644 tests/examples/dynamic/Makefile.am create mode 100644 tests/examples/dynamic/addstream.c diff --git a/ChangeLog b/ChangeLog index c7e02c967d..f263067807 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2007-09-14 Wim Taymans + + * configure.ac: + * tests/examples/Makefile.am: + * tests/examples/dynamic/.cvsignore: + * tests/examples/dynamic/Makefile.am: + * tests/examples/dynamic/addstream.c: (create_stream), + (pause_play_stream), (message_received), (eos_message_received), + (perform_step), (main): + Add simple exmple app to demonstrate starting and pausing live and + non-live bins in a PLAYING pipeline. + 2007-09-14 Julien MOUTTE * gst/typefind/gsttypefindfunctions.c: (plugin_init): Add some diff --git a/configure.ac b/configure.ac index 95bbeb0a59..3bca76e22c 100644 --- a/configure.ac +++ b/configure.ac @@ -696,6 +696,7 @@ pkgconfig/gstreamer-plugins-base-uninstalled.pc tests/Makefile tests/check/Makefile tests/examples/Makefile +tests/examples/dynamic/Makefile tests/examples/seek/Makefile tests/examples/volume/Makefile tests/icles/Makefile diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index a92de36b80..8502c38765 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -4,6 +4,6 @@ else FT2_SUBDIRS = endif -SUBDIRS = $(FT2_SUBDIRS) volume +SUBDIRS = $(FT2_SUBDIRS) volume dynamic -DIST_SUBDIRS = seek volume +DIST_SUBDIRS = seek volume dynamic diff --git a/tests/examples/dynamic/.gitignore b/tests/examples/dynamic/.gitignore new file mode 100644 index 0000000000..a22c853487 --- /dev/null +++ b/tests/examples/dynamic/.gitignore @@ -0,0 +1 @@ +addstream diff --git a/tests/examples/dynamic/Makefile.am b/tests/examples/dynamic/Makefile.am new file mode 100644 index 0000000000..4b5f353dae --- /dev/null +++ b/tests/examples/dynamic/Makefile.am @@ -0,0 +1,5 @@ +noinst_PROGRAMS = addstream + +addstream_SOURCES = addstream.c +addstream_CFLAGS = $(GST_CFLAGS) -D_GNU_SOURCE +addstream_LDFLAGS = $(GST_LIBS) diff --git a/tests/examples/dynamic/addstream.c b/tests/examples/dynamic/addstream.c new file mode 100644 index 0000000000..dc7cad99db --- /dev/null +++ b/tests/examples/dynamic/addstream.c @@ -0,0 +1,255 @@ +/* GStreamer + * + * addstream.c: sample application to dynamically add streams to a running + * pipeline + * + * Copyright (C) <2007> Wim Taymans + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +static GstElement *pipeline; +static GstClock *theclock; +static GMainLoop *loop; +static GstElement *bin1, *bin2, *bin3, *bin4, *bin5; + +/* start a bin with the given description */ +static GstElement * +create_stream (const gchar * descr) +{ + GstElement *bin; + GError *error = NULL; + + bin = gst_parse_launch (descr, &error); + if (error) { + g_print ("pipeline could not be constructed: %s\n", error->message); + g_error_free (error); + return NULL; + } + + /* add the bin to the pipeline now, this will set the current base_time of the + * pipeline on the new bin. */ + gst_bin_add (GST_BIN_CAST (pipeline), bin); + + return bin; +} + +static gboolean +pause_play_stream (GstElement * bin, gint seconds) +{ + gboolean punch_in; + GstStateChangeReturn ret; + GstClockTime now, base_time, running_time; + + /* get current running time, we need this value to continue playback of + * non-live pipelines. */ + now = gst_clock_get_time (theclock); + base_time = gst_element_get_base_time (bin); + + running_time = now - base_time; + + /* set the new bin to PAUSED, the parent bin will notice (because of the ASYNC + * message and will perform latency calculations again when going to PLAYING + * later. */ + ret = gst_element_set_state (bin, GST_STATE_PAUSED); + + switch (ret) { + case GST_STATE_CHANGE_NO_PREROLL: + /* live source, timestamps are running_time of the pipeline clock. */ + punch_in = FALSE; + break; + case GST_STATE_CHANGE_SUCCESS: + /* success, no async state changes, same as async, timestamps start + * from 0 */ + case GST_STATE_CHANGE_ASYNC: + /* no live source, bin will preroll. We have to punch it in because in + * this situation timestamps start from 0. */ + punch_in = TRUE; + break; + default: + case GST_STATE_CHANGE_FAILURE: + return FALSE; + } + + if (seconds) + sleep (seconds); + + if (punch_in) { + /* new bin has to be aligned with previous running_time. We do this by taking + * the current absolute clock time and calculating the base time that would + * give the previous running_time. We set this base_time on the bin before + * setting it to PLAYING. */ + now = gst_clock_get_time (theclock); + base_time = now - running_time; + + gst_element_set_base_time (bin, base_time); + } + + /* now set the pipeline to PLAYING */ + gst_element_set_state (bin, GST_STATE_PLAYING); + + return TRUE; +} + +static void +message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline) +{ + const GstStructure *s; + + s = gst_message_get_structure (message); + g_print ("message from \"%s\" (%s): ", + GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))), + gst_message_type_get_name (GST_MESSAGE_TYPE (message))); + if (s) { + gchar *sstr; + + sstr = gst_structure_to_string (s); + g_print ("%s\n", sstr); + g_free (sstr); + } else { + g_print ("no message details\n"); + } +} + +static void +eos_message_received (GstBus * bus, GstMessage * message, + GstPipeline * pipeline) +{ + message_received (bus, message, pipeline); + g_main_loop_quit (loop); +} + +static gboolean +perform_step (gpointer pstep) +{ + gint step = GPOINTER_TO_INT (pstep); + + switch (step) { + case 0: + /* live stream locks on to running_time, pipeline configures latency. */ + g_print ("creating bin1\n"); + bin1 = + create_stream + ("( v4l2src ! ffmpegcolorspace ! timeoverlay ! queue ! xvimagesink name=v4llive )"); + pause_play_stream (bin1, 0); + g_timeout_add (1000, (GSourceFunc) perform_step, GINT_TO_POINTER (1)); + break; + case 1: + /* live stream locks on to running_time, pipeline reconfigures latency + * together with the previously added bin so that they run synchronized. */ + g_print ("creating bin2\n"); + bin2 = create_stream ("( alsasrc ! queue ! alsasink name=alsalive )"); + pause_play_stream (bin2, 0); + g_timeout_add (1000, (GSourceFunc) perform_step, GINT_TO_POINTER (2)); + break; + case 2: + /* non-live stream, need base_time to align with current running live sources. */ + g_print ("creating bin3\n"); + bin3 = create_stream ("( audiotestsrc ! alsasink name=atnonlive )"); + pause_play_stream (bin3, 0); + g_timeout_add (1000, (GSourceFunc) perform_step, GINT_TO_POINTER (3)); + break; + case 3: + g_print ("creating bin4\n"); + bin4 = + create_stream + ("( videotestsrc ! timeoverlay ! ffmpegcolorspace ! ximagesink name=vtnonlive )"); + pause_play_stream (bin4, 0); + g_timeout_add (1000, (GSourceFunc) perform_step, GINT_TO_POINTER (4)); + break; + case 4: + /* live stream locks on to running_time */ + g_print ("creating bin5\n"); + bin5 = + create_stream + ("( videotestsrc is-live=1 ! timeoverlay ! ffmpegcolorspace ! ximagesink name=vtlive )"); + pause_play_stream (bin5, 0); + g_timeout_add (1000, (GSourceFunc) perform_step, GINT_TO_POINTER (5)); + break; + case 5: + /* pause the fist live stream for 2 seconds */ + g_print ("PAUSE bin1 for 2 seconds\n"); + pause_play_stream (bin1, 2); + /* pause the non-live stream for 2 seconds */ + g_print ("PAUSE bin4 for 2 seconds\n"); + pause_play_stream (bin4, 2); + /* pause the pseudo live stream for 2 seconds */ + g_print ("PAUSE bin5 for 2 seconds\n"); + pause_play_stream (bin5, 2); + g_print ("Waiting 5 seconds\n"); + g_timeout_add (5000, (GSourceFunc) perform_step, GINT_TO_POINTER (6)); + break; + case 6: + g_print ("quiting\n"); + g_main_loop_quit (loop); + break; + default: + break; + } + return FALSE; +} + +int +main (int argc, char *argv[]) +{ + GstBus *bus; + + gst_init (&argc, &argv); + + loop = g_main_loop_new (NULL, TRUE); + + pipeline = gst_pipeline_new ("pipeline"); + + /* setup message handling */ + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + g_signal_connect (bus, "message::error", (GCallback) message_received, + pipeline); + g_signal_connect (bus, "message::warning", (GCallback) message_received, + pipeline); + g_signal_connect (bus, "message::eos", (GCallback) eos_message_received, + pipeline); + + /* we set the pipeline to PLAYING, this will distribute a default clock and + * start running. no preroll is needed */ + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + /* get the clock now. Since we never set the pipeline to PAUSED again, the + * clock will not change, even when we add new clock providers later. */ + theclock = gst_element_get_clock (pipeline); + + /* start our actions while we are in the mainloop so that we can catch errors + * and other messages. */ + g_idle_add ((GSourceFunc) perform_step, GINT_TO_POINTER (0)), + /* go to main loop */ + g_main_loop_run (loop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + + gst_object_unref (bus); + gst_object_unref (pipeline); + gst_object_unref (theclock); + + return 0; +}