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