• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * addstream.c: sample application to dynamically add streams to a running
4  * pipeline
5  *
6  * Copyright (C) <2007> Wim Taymans <wim dot taymans at gmail dot com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <gst/gst.h>
29 
30 static GstElement *pipeline;
31 static GstClock *theclock;
32 static GMainLoop *loop;
33 static GstElement *bin1, *bin2, *bin3, *bin4, *bin5;
34 
35 /* start a bin with the given description */
36 static GstElement *
create_stream(const gchar * descr)37 create_stream (const gchar * descr)
38 {
39   GstElement *bin;
40   GError *error = NULL;
41 
42   bin = gst_parse_launch (descr, &error);
43   if (error) {
44     g_print ("pipeline could not be constructed: %s\n", error->message);
45     g_error_free (error);
46     return NULL;
47   }
48 
49   /* add the bin to the pipeline now, this will set the current base_time of the
50    * pipeline on the new bin. */
51   gst_bin_add (GST_BIN_CAST (pipeline), bin);
52 
53   return bin;
54 }
55 
56 static void
pause_play_stream(GstElement * bin,gint seconds)57 pause_play_stream (GstElement * bin, gint seconds)
58 {
59   gboolean punch_in;
60   GstStateChangeReturn ret;
61   GstClockTime now, base_time, running_time;
62 
63   /* get current running time, we need this value to continue playback of
64    * non-live pipelines. */
65   now = gst_clock_get_time (theclock);
66   base_time = gst_element_get_base_time (bin);
67 
68   running_time = now - base_time;
69 
70   /* set the new bin to PAUSED, the parent bin will notice (because of the ASYNC
71    * message and will perform latency calculations again when going to PLAYING
72    * later. */
73   ret = gst_element_set_state (bin, GST_STATE_PAUSED);
74 
75   switch (ret) {
76     case GST_STATE_CHANGE_NO_PREROLL:
77       /* live source, timestamps are running_time of the pipeline clock. */
78       punch_in = FALSE;
79       break;
80     case GST_STATE_CHANGE_SUCCESS:
81       /* success, no async state changes, same as async, timestamps start
82        * from 0 */
83     case GST_STATE_CHANGE_ASYNC:
84       /* no live source, bin will preroll. We have to punch it in because in
85        * this situation timestamps start from 0.  */
86       punch_in = TRUE;
87       break;
88     case GST_STATE_CHANGE_FAILURE:
89       /* fall through to return */
90     default:
91       return;
92   }
93 
94   if (seconds)
95     g_usleep (seconds * G_USEC_PER_SEC);
96 
97   if (punch_in) {
98     /* new bin has to be aligned with previous running_time. We do this by taking
99      * the current absolute clock time and calculating the base time that would
100      * give the previous running_time. We set this base_time on the bin before
101      * setting it to PLAYING. */
102     now = gst_clock_get_time (theclock);
103     base_time = now - running_time;
104 
105     gst_element_set_base_time (bin, base_time);
106   }
107 
108   /* now set the pipeline to PLAYING */
109   gst_element_set_state (bin, GST_STATE_PLAYING);
110 }
111 
112 static void
message_received(GstBus * bus,GstMessage * message,GstPipeline * pipeline)113 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
114 {
115   const GstStructure *s;
116 
117   s = gst_message_get_structure (message);
118   g_print ("message from \"%s\" (%s): ",
119       GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
120       gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
121   if (s) {
122     gchar *sstr;
123 
124     sstr = gst_structure_to_string (s);
125     g_print ("%s\n", sstr);
126     g_free (sstr);
127   } else {
128     g_print ("no message details\n");
129   }
130 }
131 
132 static void
eos_message_received(GstBus * bus,GstMessage * message,GstPipeline * pipeline)133 eos_message_received (GstBus * bus, GstMessage * message,
134     GstPipeline * pipeline)
135 {
136   message_received (bus, message, pipeline);
137   g_main_loop_quit (loop);
138 }
139 
140 static gboolean
perform_step(gpointer pstep)141 perform_step (gpointer pstep)
142 {
143   gint step = GPOINTER_TO_INT (pstep);
144 
145   switch (step) {
146     case 0:
147       /* live stream locks on to running_time, pipeline configures latency. */
148       g_print ("creating bin1\n");
149       bin1 =
150           create_stream
151           ("( v4l2src ! videoconvert ! timeoverlay ! queue ! xvimagesink name=v4llive )");
152       pause_play_stream (bin1, 0);
153       g_timeout_add_seconds (1, (GSourceFunc) perform_step,
154           GINT_TO_POINTER (1));
155       break;
156     case 1:
157       /* live stream locks on to running_time, pipeline reconfigures latency
158        * together with the previously added bin so that they run synchronized. */
159       g_print ("creating bin2\n");
160       bin2 = create_stream ("( alsasrc ! queue ! alsasink name=alsalive )");
161       pause_play_stream (bin2, 0);
162       g_timeout_add_seconds (1, (GSourceFunc) perform_step,
163           GINT_TO_POINTER (2));
164       break;
165     case 2:
166       /* non-live stream, need base_time to align with current running live sources. */
167       g_print ("creating bin3\n");
168       bin3 = create_stream ("( audiotestsrc ! alsasink name=atnonlive )");
169       pause_play_stream (bin3, 0);
170       g_timeout_add_seconds (1, (GSourceFunc) perform_step,
171           GINT_TO_POINTER (3));
172       break;
173     case 3:
174       g_print ("creating bin4\n");
175       bin4 =
176           create_stream
177           ("( videotestsrc ! timeoverlay ! videoconvert ! ximagesink name=vtnonlive )");
178       pause_play_stream (bin4, 0);
179       g_timeout_add_seconds (1, (GSourceFunc) perform_step,
180           GINT_TO_POINTER (4));
181       break;
182     case 4:
183       /* live stream locks on to running_time */
184       g_print ("creating bin5\n");
185       bin5 =
186           create_stream
187           ("( videotestsrc is-live=1 ! timeoverlay ! videoconvert ! ximagesink name=vtlive )");
188       pause_play_stream (bin5, 0);
189       g_timeout_add_seconds (1, (GSourceFunc) perform_step,
190           GINT_TO_POINTER (5));
191       break;
192     case 5:
193       /* pause the fist live stream for 2 seconds */
194       g_print ("PAUSE bin1 for 2 seconds\n");
195       pause_play_stream (bin1, 2);
196       /* pause the non-live stream for 2 seconds */
197       g_print ("PAUSE bin4 for 2 seconds\n");
198       pause_play_stream (bin4, 2);
199       /* pause the pseudo live stream for 2 seconds */
200       g_print ("PAUSE bin5 for 2 seconds\n");
201       pause_play_stream (bin5, 2);
202       g_print ("Waiting 5 seconds\n");
203       g_timeout_add_seconds (5, (GSourceFunc) perform_step,
204           GINT_TO_POINTER (6));
205       break;
206     case 6:
207       g_print ("quitting\n");
208       g_main_loop_quit (loop);
209       break;
210     default:
211       break;
212   }
213   return FALSE;
214 }
215 
216 int
main(int argc,char * argv[])217 main (int argc, char *argv[])
218 {
219   GstBus *bus;
220 
221   gst_init (&argc, &argv);
222 
223   loop = g_main_loop_new (NULL, TRUE);
224 
225   pipeline = gst_pipeline_new ("pipeline");
226 
227   /* setup message handling */
228   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
229   gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
230   g_signal_connect (bus, "message::error", (GCallback) message_received,
231       pipeline);
232   g_signal_connect (bus, "message::warning", (GCallback) message_received,
233       pipeline);
234   g_signal_connect (bus, "message::eos", (GCallback) eos_message_received,
235       pipeline);
236 
237   /* we set the pipeline to PLAYING, this will distribute a default clock and
238    * start running. no preroll is needed */
239   gst_element_set_state (pipeline, GST_STATE_PLAYING);
240 
241   /* get the clock now. Since we never set the pipeline to PAUSED again, the
242    * clock will not change, even when we add new clock providers later.  */
243   theclock = gst_element_get_clock (pipeline);
244 
245   /* start our actions while we are in the mainloop so that we can catch errors
246    * and other messages. */
247   g_idle_add ((GSourceFunc) perform_step, GINT_TO_POINTER (0));
248   /* go to main loop */
249   g_main_loop_run (loop);
250 
251   gst_element_set_state (pipeline, GST_STATE_NULL);
252 
253   gst_object_unref (bus);
254   gst_object_unref (pipeline);
255   gst_object_unref (theclock);
256 
257   return 0;
258 }
259