4 * demo application for filters using dparams
5 * dparams get their sliders which you can play with
7 * you can also enter an input and output part of a pipeline
13 #include <gst/control/control.h>
18 GtkWidget *window; /* top-level interface window */
20 GtkWidget *buttons; /* all of the control buttons */
21 GtkWidget *parse, *play, *stop; /* control buttons */
23 GtkWidget *feedback; /* here's where we'll tell you stuff */
24 GtkTextBuffer *fb_buffer; /* feedback buffer */
25 GtkWidget *selection; /* the place to input element stuff */
26 GtkWidget *input, *filter, *output; /* the selection widgets */
28 GtkWidget *control; /* the dynamically generated control UI */
31 typedef struct _filter_ui _filter_ui_t;
36 _filter_ui_t *ui; /* the UI data */
37 gchar *input_pipe, *output_pipe, *filter_element;
39 GList *filter_choices;
42 GstElement *input, *output; /* these are in and out bins */
47 typedef struct _filter_data _filter_data_t;
49 /* internal prototypes when they can't be avoided */
50 void cb_remove_and_destroy (GtkWidget *widget, gpointer user_data);
51 //void cb_dynparm_value_changed (GtkWidget *widget, gpointer user_data);
52 void cb_dynparm_value_changed (GtkRange *range, GstDParam *dparam);
54 /* GStreamer helper functions go here */
56 /* go through a bin, finding the one pad that is unconnected in the given
57 * direction, and return a ghost pad */
59 gst_bin_find_unconnected_pad (GstBin *bin, GstPadDirection direction,
63 GList *elements = NULL;
64 const GList *pads = NULL;
65 GstElement *element = NULL;
67 g_print ("DEBUG: find_unconnected start\n");
68 elements = (GList *) gst_bin_get_list (bin);
69 /* traverse all elements looking for unconnected pads */
70 while (elements && pad == NULL)
72 element = GST_ELEMENT (elements->data);
73 g_print ("DEBUG: looking in element %s\n", gst_element_get_name (element));
74 pads = gst_element_get_pad_list (element);
77 /* check if the direction matches */
78 if (GST_PAD_DIRECTION (GST_PAD (pads->data)) == direction)
80 if (GST_PAD_PEER (GST_PAD (pads->data)) == NULL)
83 g_print ("DEBUG: found an unconnected pad !\n");
84 pad = GST_PAD (pads->data);
87 if (pad) break; /* found one already */
88 pads = g_list_next (pads);
90 elements = g_list_next (elements);
93 g_print ("DEBUG: find_unconnected stop\n");
94 if (pad == NULL) /* we didn't find it at all */
97 pad = gst_ghost_pad_new (name, pad);
102 ui_feedback_add_text (_filter_ui_t *ui, const gchar *text)
106 gtk_text_buffer_get_end_iter (ui->fb_buffer, &iter);
107 gtk_text_buffer_insert (ui->fb_buffer, &iter, text, -1);
111 ui_feedback_add (_filter_ui_t *ui, const gchar *format, ...)
114 gchar *buffer = NULL;
116 va_start (args, format);
117 buffer = g_strdup_vprintf (format, args);
119 ui_feedback_add_text (ui, buffer);
124 ui_feedback_clear (_filter_ui_t *ui)
126 gtk_text_buffer_set_text (ui->fb_buffer, "", 0);
129 /* create the control widget using the element's dynparams
130 * control is a vbox which we need to empty first */
132 ui_control_create (GstElement *element, GtkWidget *control, _filter_ui_t *ui)
134 GtkWidget *hbox = NULL;
135 GtkWidget *widget = NULL;
136 GstDParamManager *dpman = NULL;
137 GstDParam *dparam = NULL;
138 GParamSpec **specs = NULL;
141 g_assert (GTK_IS_VBOX (control));
143 /* empty control vbox */
144 g_print ("DEBUG: emptying control widget\n");
145 gtk_container_foreach (GTK_CONTAINER (control), cb_remove_and_destroy,
148 g_print ("DEBUG: adding label to control widget\n");
149 widget = gtk_label_new ("Dynamic Parameters");
150 gtk_container_add (GTK_CONTAINER (control), widget);
151 gtk_widget_show (widget);
153 if ((dpman = gst_dpman_get_manager (element)))
155 ui_feedback_add (ui, "Found Dynamic Parameters on filter element.\n");
156 specs = gst_dpman_list_dparam_specs (dpman);
157 for (i = 0; specs[i] != NULL; ++i)
159 hbox = gtk_hbox_new (FALSE, 0);
160 widget = gtk_label_new (g_param_spec_get_name (specs[i]));
161 gtk_container_add (GTK_CONTAINER (hbox), widget);
162 gtk_widget_show (widget);
163 switch (G_PARAM_SPEC_VALUE_TYPE (specs[i]))
166 widget = gtk_hscale_new_with_range (
167 (gdouble) (((GParamSpecInt64*)specs[i])->minimum),
168 (gdouble) (((GParamSpecInt64*)specs[i])->maximum),
170 gtk_range_set_value (GTK_RANGE (widget),
171 (gdouble) ((GParamSpecInt64*)specs[i])->default_value);
175 widget = gtk_hscale_new_with_range (
176 (gdouble) (((GParamSpecInt*)specs[i])->minimum),
177 (gdouble) (((GParamSpecInt*)specs[i])->maximum),
179 gtk_range_set_value (GTK_RANGE (widget),
180 (gdouble) ((GParamSpecInt*)specs[i])->default_value);
183 widget = gtk_hscale_new_with_range (
184 (gdouble) (((GParamSpecFloat*)specs[i])->minimum),
185 (gdouble) (((GParamSpecFloat*)specs[i])->maximum),
187 gtk_range_set_value (GTK_RANGE (widget),
188 (gdouble) ((GParamSpecFloat*)specs[i])->default_value);
191 /* create the dparam object */
192 dparam = gst_dpsmooth_new (G_PARAM_SPEC_VALUE_TYPE (specs[i]));
193 g_object_set (G_OBJECT (dparam), "update_period", 2000000LL, NULL);
194 g_assert (gst_dpman_attach_dparam (dpman,
195 (gchar *) g_param_spec_get_name (specs[i]),
197 gst_dpman_set_mode(dpman, "asynchronous");
198 g_signal_connect (widget, "value-changed",
199 G_CALLBACK (cb_dynparm_value_changed), dparam);
200 cb_dynparm_value_changed (GTK_RANGE (widget), dparam);
202 gtk_container_add (GTK_CONTAINER (hbox), widget);
203 gtk_widget_show (widget);
205 gtk_container_add (GTK_CONTAINER (control), hbox);
206 gtk_widget_show (hbox);
210 /* all the pretty callbacks gather here please */
212 cb_remove_and_destroy (GtkWidget *widget, gpointer user_data)
214 GtkContainer *container = GTK_CONTAINER (user_data);
216 g_print ("DEBUG: trying to remove widget from a container.\n");
217 gtk_container_remove (container, widget);
218 gtk_widget_destroy (widget);
221 /* when the scale associated with a dparam changes, respond */
223 cb_dynparm_value_changed (GtkRange *range, GstDParam *dparam)
226 GstDParam *dparam = GST_DPARAM (user_data);
227 GtkHScale *adj = GTK_HSCALE (widget);
230 g_assert (GST_IS_DPARAM (dparam));
231 g_assert (GTK_IS_RANGE (range));
233 value = gtk_range_get_value (range);
235 g_print ("DEBUG: setting value to %f\n", value);
237 switch (G_PARAM_SPEC_VALUE_TYPE (GST_DPARAM_PARAM_SPEC (dparam)))
240 g_object_set (G_OBJECT (dparam), "value_int64",
241 (gint64) value, NULL);
245 g_object_set (G_OBJECT (dparam), "value_int",
250 g_object_set (G_OBJECT (dparam), "value_float",
251 (gfloat) value, NULL);
258 cb_entry_activate (GtkEntry *entry, gpointer user_data)
260 g_print ("DEBUG: oi ! you activated an entry !\n");
261 g_print ("DEBUG: pipeline: %s\n", gtk_entry_get_text (entry));
265 cb_play_clicked (GtkButton *button, gpointer *user_data)
267 _filter_data_t *fd = (_filter_data_t *) user_data;
269 g_return_if_fail (GST_IS_PIPELINE (fd->pipeline));
270 if (GST_STATE (fd->pipeline) == GST_STATE_PLAYING)
272 ui_feedback_add (fd->ui, "Pipeline is already playing !\n");
275 gst_element_set_state (fd->pipeline, GST_STATE_PLAYING);
279 cb_stop_clicked (GtkButton *button, gpointer *user_data)
281 _filter_data_t *fd = (_filter_data_t *) user_data;
283 if (GST_STATE (fd->pipeline) != GST_STATE_PLAYING)
285 ui_feedback_add (fd->ui, "Pipeline is not playing !\n");
288 gst_element_set_state (fd->pipeline, GST_STATE_NULL);
292 cb_parse_clicked (GtkButton *button, gpointer *user_data)
294 _filter_data_t *fd = (_filter_data_t *) user_data;
295 GtkCombo *filter = GTK_COMBO (fd->ui->filter);
296 GError *error = NULL;
297 GstPad *src_pad, *sink_pad;
299 g_print ("DEBUG: you pressed parse.\n");
300 ui_feedback_clear (fd->ui);
301 ui_feedback_add (fd->ui, "Parsing pipeline ...\n");
302 if (fd->input_pipe) g_free (fd->input_pipe);
303 if (fd->output_pipe) g_free (fd->output_pipe);
304 if (fd->filter_element) g_free (fd->filter_element);
305 fd->input_pipe = g_strdup_printf ("bin.( %s )",
306 gtk_entry_get_text (GTK_ENTRY (fd->ui->input)));
307 fd->output_pipe = g_strdup_printf ("bin.( %s )",
308 gtk_entry_get_text (GTK_ENTRY (fd->ui->output)));
309 /* gtkcombo.h says I can access the entry field directly */
310 fd->filter_element = g_strdup (gtk_entry_get_text (GTK_ENTRY (filter->entry)));
311 g_print ("Input pipeline :\t%s (%d)\n", fd->input_pipe, (int)strlen (fd->input_pipe));
312 g_print ("Filter element :\t%s (%d)\n", fd->filter_element, (int)strlen (fd->filter_element));
313 g_print ("Output pipeline :\t%s (%d)\n", fd->output_pipe, (int)strlen (fd->output_pipe));
315 /* try to create in and out bins */
316 if (strlen (fd->input_pipe) == 0)
318 ui_feedback_add (fd->ui, "Error : try setting an input pipe.\n");
321 if (fd->input) gst_object_unref (GST_OBJECT (fd->input));
322 fd->input = GST_ELEMENT (gst_parse_launch (fd->input_pipe, &error));
325 ui_feedback_add (fd->ui, "Error : parsing input pipeline : %s\n",
327 g_error_free (error);
331 if (strlen (fd->output_pipe) == 0)
333 ui_feedback_add (fd->ui, "Error : try setting an output pipe.\n");
336 if (fd->output) gst_object_unref (GST_OBJECT (fd->output));
337 fd->output = GST_ELEMENT (gst_parse_launch (fd->output_pipe, &error));
340 ui_feedback_add (fd->ui, "Error : parsing output pipeline : %s\n",
342 g_error_free (error);
346 /* try to create filter */
347 if (fd->filter) gst_object_unref (GST_OBJECT (fd->filter));
348 fd->filter = gst_element_factory_make (fd->filter_element, "filter");
349 if (fd->filter == NULL)
351 ui_feedback_add (fd->ui, "Error : could not create element %s\n",
356 /* set up dynparam controls for filter */
357 ui_control_create (fd->filter, fd->ui->control, fd->ui);
359 /* create toplevel bin */
360 fd->pipeline = gst_thread_new ("toplevel");
362 /* add the players to it */
363 gst_bin_add_many (GST_BIN (fd->pipeline),
364 fd->input, fd->filter,
367 /* connect filter to input and output bin */
368 src_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->input), GST_PAD_SRC,
372 ui_feedback_add (fd->ui,
373 "Error : could not find an unconnected source pad !\n");
376 sink_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->output), GST_PAD_SINK,
378 if (sink_pad == NULL)
380 ui_feedback_add (fd->ui,
381 "Error : could not find an unconnected sink pad !\n");
384 gst_element_add_pad (fd->input, src_pad);
385 gst_element_add_pad (fd->output, sink_pad);
387 gst_element_link_many (fd->input, fd->filter, fd->output, NULL);
389 if (fd->pipe_string) g_free (fd->pipe_string);
390 fd->pipe_string = g_strdup_printf ("%s ! %s ! %s", fd->input_pipe,
391 fd->filter_element, fd->output_pipe);
392 g_print ("Pipeline : %s\n", fd->pipe_string);
393 ui_feedback_add (fd->ui, "Complete parsed pipeline: %s\n", fd->pipe_string);
398 /* find out the list of choices for GStreamer filters */
400 get_filter_choices (void)
402 GList *choices = NULL;
404 choices = g_list_append (choices, "volume");
405 choices = g_list_append (choices, "ladspa_lpf");
406 choices = g_list_append (choices, "ladspa_hpf");
412 init_data (_filter_data_t *fd)
414 fd->input_pipe = NULL;
415 fd->output_pipe = NULL;
416 fd->filter_element = NULL;
417 fd->pipe_string = NULL;
418 fd->filter_choices = get_filter_choices ();
420 /* GStreamer stuff */
429 create_ui (_filter_ui_t *fui, _filter_data_t *fd)
431 GtkWidget *widget; /* temporary widget */
432 GtkWidget *vbox; /* temporary vbox */
434 g_print ("DEBUG: creating top-level window\n");
435 fui->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
436 widget = gtk_vbox_new (FALSE, 0);
437 gtk_container_add (GTK_CONTAINER (fui->window), widget);
438 gtk_window_set_title (GTK_WINDOW (fui->window), "Gee, I can set titles too");
440 /* top level window division */
441 g_print ("DEBUG: creating top-level window contents\n");
442 fui->buttons = gtk_hbox_new (FALSE, 0);
443 gtk_container_add (GTK_CONTAINER (widget), fui->buttons);
444 fui->feedback = gtk_text_view_new ();
445 gtk_container_add (GTK_CONTAINER (widget), fui->feedback);
446 fui->selection = gtk_hbox_new (FALSE, 0);
447 gtk_container_add (GTK_CONTAINER (widget), fui->selection);
448 fui->control = gtk_vbox_new (TRUE, 5);
449 gtk_container_add (GTK_CONTAINER (widget), fui->control);
452 fui->parse = gtk_button_new_with_label ("Parse");
453 gtk_container_add (GTK_CONTAINER (fui->buttons), fui->parse);
454 g_signal_connect (G_OBJECT (fui->parse), "clicked",
455 G_CALLBACK (cb_parse_clicked), fd);
456 fui->play = gtk_button_new_with_label ("Play");
457 gtk_container_add (GTK_CONTAINER (fui->buttons), fui->play);
458 g_signal_connect (G_OBJECT (fui->play), "clicked",
459 G_CALLBACK (cb_play_clicked), fd);
460 fui->stop = gtk_button_new_with_label ("Stop");
461 gtk_container_add (GTK_CONTAINER (fui->buttons), fui->stop);
462 g_signal_connect (G_OBJECT (fui->stop), "clicked",
463 G_CALLBACK (cb_stop_clicked), fd);
465 /* feedback widget */
466 fui->fb_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (fui->feedback));
467 gtk_text_buffer_set_text (fui->fb_buffer,
468 "Hello, and welcome to the GStreamer filter demo app !\n"\
469 "I'll be your feedback window for today.\n", -1);
471 /* selection widget */
472 vbox = gtk_vbox_new (FALSE, 0);
473 widget = gtk_label_new ("Input Pipe");
474 gtk_container_add (GTK_CONTAINER (vbox), widget);
475 fui->input = gtk_entry_new ();
476 gtk_entry_set_text (GTK_ENTRY (fui->input), "sinesrc");
477 gtk_container_add (GTK_CONTAINER (vbox), fui->input);
478 gtk_container_add (GTK_CONTAINER (fui->selection), vbox);
479 g_signal_connect (G_OBJECT (fui->input), "activate",
480 G_CALLBACK (cb_entry_activate), NULL);
482 vbox = gtk_vbox_new (FALSE, 0);
483 widget = gtk_label_new ("Filter");
484 gtk_container_add (GTK_CONTAINER (vbox), widget);
485 fui->filter = gtk_combo_new ();
486 gtk_combo_set_popdown_strings (GTK_COMBO (fui->filter), fd->filter_choices);
487 gtk_container_add (GTK_CONTAINER (vbox), fui->filter);
488 gtk_container_add (GTK_CONTAINER (fui->selection), vbox);
490 vbox = gtk_vbox_new (FALSE, 0);
491 widget = gtk_label_new ("Output Pipe");
492 gtk_container_add (GTK_CONTAINER (vbox), widget);
493 fui->output = gtk_entry_new ();
494 gtk_entry_set_text (GTK_ENTRY (fui->output), "osssink fragment=1572872"); /* fixme: gconf default ? */
495 gtk_container_add (GTK_CONTAINER (vbox), fui->output);
496 gtk_container_add (GTK_CONTAINER (fui->selection), vbox);
497 g_signal_connect (G_OBJECT (fui->output), "activate",
498 G_CALLBACK (cb_entry_activate), NULL);
500 /* control widget is dynamically generated */
502 g_print ("DEBUG: labeling control area.\n");
503 widget = gtk_label_new ("This is the big control area.");
504 gtk_container_add (GTK_CONTAINER (fui->control), widget);
510 main (int argc, char *argv[])
512 _filter_data_t filter_data;
513 _filter_ui_t filter_ui;
516 gtk_init (&argc, &argv);
517 gst_init (&argc, &argv);
518 gst_control_init (&argc, &argv);
520 init_data (&filter_data);
521 filter_data.ui = &filter_ui;
523 create_ui (&filter_ui, &filter_data);
524 gtk_widget_show_all (filter_ui.window);