1 /* GStreamer
2 *
3 * sprinkle.c: sample application to dynamically mix tones with adder
4 *
5 * Copyright (C) <2009> Wim Taymans <wim dot taymans at gmail dot com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 /*
24 * Produces a sweeping sprinkle of tones by dynamically adding and removing
25 * elements to adder.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <gst/gst.h>
33
34 static GstElement *pipeline, *adder;
35 static GMainLoop *loop;
36
37 typedef struct
38 {
39 GstElement *element;
40 GstPad *srcpad;
41 GstPad *sinkpad;
42 gdouble freq;
43 } SourceInfo;
44
45 /* dynamically add the source to the pipeline and link it to a new pad on
46 * adder */
47 static SourceInfo *
add_source(gdouble freq)48 add_source (gdouble freq)
49 {
50 SourceInfo *info;
51
52 info = g_new0 (SourceInfo, 1);
53 info->freq = freq;
54
55 /* make source with unique name */
56 info->element = gst_element_factory_make ("audiotestsrc", NULL);
57
58 g_object_set (info->element, "freq", freq, NULL);
59
60 /* add to the bin */
61 gst_bin_add (GST_BIN (pipeline), info->element);
62
63 /* get pad from the element */
64 info->srcpad = gst_element_get_static_pad (info->element, "src");
65
66 /* get new pad from adder, adder will now wait for data on this pad */
67 info->sinkpad = gst_element_get_request_pad (adder, "sink_%u");
68
69 /* link pad to adder */
70 gst_pad_link (info->srcpad, info->sinkpad);
71
72 /* and play the element */
73 gst_element_set_state (info->element, GST_STATE_PLAYING);
74
75 g_print ("added freq %f\n", info->freq);
76
77 return info;
78 }
79
80 /* remove the source from the pipeline after removing it from adder */
81 static void
remove_source(SourceInfo * info)82 remove_source (SourceInfo * info)
83 {
84 g_print ("remove freq %f\n", info->freq);
85
86 /* lock the state so that we can put it to NULL without the parent messing
87 * with our state */
88 gst_element_set_locked_state (info->element, TRUE);
89
90 /* first stop the source. Remember that this might block when in the PAUSED
91 * state. Alternatively one could send EOS to the source, install an event
92 * probe and schedule a state change/unlink/release from the mainthread.
93 * Note that changing the state of a source makes it emit an EOS, which can
94 * make adder go EOS. */
95 gst_element_set_state (info->element, GST_STATE_NULL);
96
97 /* unlink from adder */
98 gst_pad_unlink (info->srcpad, info->sinkpad);
99 gst_object_unref (info->srcpad);
100
101 /* remove from the bin */
102 gst_bin_remove (GST_BIN (pipeline), info->element);
103
104 /* give back the pad */
105 gst_element_release_request_pad (adder, info->sinkpad);
106 gst_object_unref (info->sinkpad);
107
108 g_free (info);
109 }
110
111 /* we'll keep the state of the sources in this structure. We keep 3 sources
112 * alive */
113 typedef struct
114 {
115 guint count;
116 SourceInfo *infos[3];
117 } SprinkleState;
118
119 static SprinkleState *
create_state(void)120 create_state (void)
121 {
122 SprinkleState *state;
123
124 state = g_new0 (SprinkleState, 1);
125
126 return state;
127 }
128
129 static void
free_state(SprinkleState * state)130 free_state (SprinkleState * state)
131 {
132 SourceInfo *info;
133 gint i;
134
135 for (i = 0; i < 3; i++) {
136 info = state->infos[i];
137 if (info)
138 remove_source (info);
139 }
140
141 g_free (state);
142 }
143
144 static gboolean
do_sprinkle(SprinkleState * state)145 do_sprinkle (SprinkleState * state)
146 {
147 SourceInfo *info;
148 gint i;
149
150 /* first remove the oldest info */
151 info = state->infos[2];
152
153 if (info)
154 remove_source (info);
155
156 /* move sources */
157 for (i = 2; i > 0; i--) {
158 state->infos[i] = state->infos[i - 1];
159 }
160
161 /* add new source, stop adding sources after 10 rounds. */
162 if (state->count < 10) {
163 state->infos[0] = add_source ((state->count * 100) + 200);
164 state->count++;
165 } else {
166 state->infos[0] = NULL;
167
168 /* if no more sources left, quit */
169 if (!state->infos[2])
170 g_main_loop_quit (loop);
171 }
172
173 return TRUE;
174 }
175
176 static void
message_received(GstBus * bus,GstMessage * message,GstPipeline * pipeline)177 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
178 {
179 const GstStructure *s;
180
181 s = gst_message_get_structure (message);
182 g_print ("message from \"%s\" (%s): ",
183 GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
184 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
185 if (s) {
186 gchar *sstr;
187
188 sstr = gst_structure_to_string (s);
189 g_print ("%s\n", sstr);
190 g_free (sstr);
191 } else {
192 g_print ("no message details\n");
193 }
194 }
195
196 static void
eos_message_received(GstBus * bus,GstMessage * message,GstPipeline * pipeline)197 eos_message_received (GstBus * bus, GstMessage * message,
198 GstPipeline * pipeline)
199 {
200 message_received (bus, message, pipeline);
201 g_main_loop_quit (loop);
202 }
203
204 int
main(int argc,char * argv[])205 main (int argc, char *argv[])
206 {
207 GstBus *bus;
208 GstElement *filter, *convert, *sink;
209 GstCaps *caps;
210 gboolean linked;
211 SprinkleState *state;
212
213 gst_init (&argc, &argv);
214
215 loop = g_main_loop_new (NULL, TRUE);
216
217 pipeline = gst_pipeline_new ("pipeline");
218
219 /* add the fixed part to the pipeline. Remember that we need a capsfilter
220 * after adder so that multiple sources are not racing to negotiate
221 * a format */
222 adder = gst_element_factory_make ("adder", "adder");
223 filter = gst_element_factory_make ("capsfilter", "filter");
224 convert = gst_element_factory_make ("audioconvert", "convert");
225 sink = gst_element_factory_make ("autoaudiosink", "sink");
226
227 caps = gst_caps_new_simple ("audio/x-raw",
228 "format", G_TYPE_STRING, "S16LE",
229 "channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, 44100, NULL);
230 g_object_set (filter, "caps", caps, NULL);
231 gst_caps_unref (caps);
232
233 gst_bin_add_many (GST_BIN (pipeline), adder, filter, convert, sink, NULL);
234
235 linked = gst_element_link_many (adder, filter, convert, sink, NULL);
236 g_assert (linked);
237
238 /* setup message handling */
239 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
240 gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
241 g_signal_connect (bus, "message::error", (GCallback) message_received,
242 pipeline);
243 g_signal_connect (bus, "message::warning", (GCallback) message_received,
244 pipeline);
245 g_signal_connect (bus, "message::eos", (GCallback) eos_message_received,
246 pipeline);
247
248 /* we set the pipeline to PLAYING, the pipeline will not yet preroll because
249 * there is no source providing data for it yet */
250 gst_element_set_state (pipeline, GST_STATE_PLAYING);
251
252 /* and add the function that modifies the pipeline every 100ms */
253 state = create_state ();
254 g_timeout_add (100, (GSourceFunc) do_sprinkle, state);
255
256 /* go to main loop */
257 g_main_loop_run (loop);
258
259 gst_element_set_state (pipeline, GST_STATE_NULL);
260
261 free_state (state);
262 gst_object_unref (bus);
263 gst_object_unref (pipeline);
264
265 return 0;
266 }
267