Add simple exmple app to demonstrate starting and pausing live and non-live bins...
authorWim Taymans <wim.taymans@gmail.com>
Fri, 14 Sep 2007 16:56:16 +0000 (16:56 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Fri, 14 Sep 2007 16:56:16 +0000 (16:56 +0000)
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
configure.ac
tests/examples/Makefile.am
tests/examples/dynamic/.gitignore [new file with mode: 0644]
tests/examples/dynamic/Makefile.am [new file with mode: 0644]
tests/examples/dynamic/addstream.c [new file with mode: 0644]

index c7e02c9..f263067 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2007-09-14  Wim Taymans  <wim.taymans@gmail.com>
+
+       * 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  <julien@moutte.net>
 
        * gst/typefind/gsttypefindfunctions.c: (plugin_init): Add some
index 95bbeb0..3bca76e 100644 (file)
@@ -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
index a92de36..8502c38 100644 (file)
@@ -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 (file)
index 0000000..a22c853
--- /dev/null
@@ -0,0 +1 @@
+addstream
diff --git a/tests/examples/dynamic/Makefile.am b/tests/examples/dynamic/Makefile.am
new file mode 100644 (file)
index 0000000..4b5f353
--- /dev/null
@@ -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 (file)
index 0000000..dc7cad9
--- /dev/null
@@ -0,0 +1,255 @@
+/* GStreamer
+ *
+ * addstream.c: sample application to dynamically add streams to a running
+ * pipeline
+ *
+ * Copyright (C) <2007> Wim Taymans <wim dot taymans at gmail 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+
+#include <gst/gst.h>
+
+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;
+}