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);
52 //void cb_dynparm_value_changed (GtkWidget *widget, gpointer user_data);
53 void cb_dynparm_value_changed (GtkRange * range, GstDParam * dparam);
55 /* GStreamer helper functions go here */
57 /* go through a bin, finding the one pad that is unconnected in the given
58 * direction, and return a ghost pad */
60 gst_bin_find_unconnected_pad (GstBin * bin, GstPadDirection direction,
64 GList *elements = NULL;
65 const GList *pads = NULL;
66 GstElement *element = NULL;
68 g_print ("DEBUG: find_unconnected start\n");
69 elements = (GList *) gst_bin_get_list (bin);
70 /* traverse all elements looking for unconnected pads */
71 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);
76 /* check if the direction matches */
77 if (GST_PAD_DIRECTION (GST_PAD (pads->data)) == direction) {
78 if (GST_PAD_PEER (GST_PAD (pads->data)) == NULL) {
80 g_print ("DEBUG: found an unconnected pad !\n");
81 pad = GST_PAD (pads->data);
85 break; /* found one already */
86 pads = g_list_next (pads);
88 elements = g_list_next (elements);
91 g_print ("DEBUG: find_unconnected stop\n");
92 if (pad == NULL) /* we didn't find it at all */
95 pad = gst_ghost_pad_new (name, pad);
100 ui_feedback_add_text (_filter_ui_t * ui, const gchar * text)
104 gtk_text_buffer_get_end_iter (ui->fb_buffer, &iter);
105 gtk_text_buffer_insert (ui->fb_buffer, &iter, text, -1);
109 ui_feedback_add (_filter_ui_t * ui, const gchar * format, ...)
112 gchar *buffer = NULL;
114 va_start (args, format);
115 buffer = g_strdup_vprintf (format, args);
117 ui_feedback_add_text (ui, buffer);
122 ui_feedback_clear (_filter_ui_t * ui)
124 gtk_text_buffer_set_text (ui->fb_buffer, "", 0);
127 /* create the control widget using the element's dynparams
128 * control is a vbox which we need to empty first */
130 ui_control_create (GstElement * element, GtkWidget * control, _filter_ui_t * ui)
132 GtkWidget *hbox = NULL;
133 GtkWidget *widget = NULL;
134 GstDParamManager *dpman = NULL;
135 GstDParam *dparam = NULL;
136 GParamSpec **specs = NULL;
139 g_assert (GTK_IS_VBOX (control));
141 /* empty control vbox */
142 g_print ("DEBUG: emptying control widget\n");
143 gtk_container_foreach (GTK_CONTAINER (control), cb_remove_and_destroy,
146 g_print ("DEBUG: adding label to control widget\n");
147 widget = gtk_label_new ("Dynamic Parameters");
148 gtk_container_add (GTK_CONTAINER (control), widget);
149 gtk_widget_show (widget);
151 if ((dpman = gst_dpman_get_manager (element))) {
152 ui_feedback_add (ui, "Found Dynamic Parameters on filter element.\n");
153 specs = gst_dpman_list_dparam_specs (dpman);
154 for (i = 0; specs[i] != NULL; ++i) {
155 hbox = gtk_hbox_new (FALSE, 0);
156 widget = gtk_label_new (g_param_spec_get_name (specs[i]));
157 gtk_container_add (GTK_CONTAINER (hbox), widget);
158 gtk_widget_show (widget);
159 switch (G_PARAM_SPEC_VALUE_TYPE (specs[i])) {
161 widget = gtk_hscale_new_with_range (
162 (gdouble) (((GParamSpecInt64 *) specs[i])->minimum),
163 (gdouble) (((GParamSpecInt64 *) specs[i])->maximum), 1.0);
164 gtk_range_set_value (GTK_RANGE (widget),
165 (gdouble) ((GParamSpecInt64 *) specs[i])->default_value);
169 widget = gtk_hscale_new_with_range (
170 (gdouble) (((GParamSpecInt *) specs[i])->minimum),
171 (gdouble) (((GParamSpecInt *) specs[i])->maximum), 1.0);
172 gtk_range_set_value (GTK_RANGE (widget),
173 (gdouble) ((GParamSpecInt *) specs[i])->default_value);
176 widget = gtk_hscale_new_with_range (
177 (gdouble) (((GParamSpecFloat *) specs[i])->minimum),
178 (gdouble) (((GParamSpecFloat *) specs[i])->maximum), 0.00001);
179 gtk_range_set_value (GTK_RANGE (widget),
180 (gdouble) ((GParamSpecFloat *) specs[i])->default_value);
183 /* create the dparam object */
184 dparam = gst_dpsmooth_new (G_PARAM_SPEC_VALUE_TYPE (specs[i]));
185 g_object_set (G_OBJECT (dparam), "update_period", 2000000LL, NULL);
186 g_assert (gst_dpman_attach_dparam (dpman,
187 (gchar *) g_param_spec_get_name (specs[i]), dparam));
188 gst_dpman_set_mode (dpman, "asynchronous");
189 g_signal_connect (widget, "value-changed",
190 G_CALLBACK (cb_dynparm_value_changed), dparam);
191 cb_dynparm_value_changed (GTK_RANGE (widget), dparam);
193 gtk_container_add (GTK_CONTAINER (hbox), widget);
194 gtk_widget_show (widget);
196 gtk_container_add (GTK_CONTAINER (control), hbox);
197 gtk_widget_show (hbox);
201 /* all the pretty callbacks gather here please */
203 cb_remove_and_destroy (GtkWidget * widget, gpointer user_data)
205 GtkContainer *container = GTK_CONTAINER (user_data);
207 g_print ("DEBUG: trying to remove widget from a container.\n");
208 gtk_container_remove (container, widget);
209 gtk_widget_destroy (widget);
212 /* when the scale associated with a dparam changes, respond */
214 cb_dynparm_value_changed (GtkRange * range, GstDParam * dparam)
217 GstDParam *dparam = GST_DPARAM (user_data);
218 GtkHScale *adj = GTK_HSCALE (widget);
222 g_assert (GST_IS_DPARAM (dparam));
223 g_assert (GTK_IS_RANGE (range));
225 value = gtk_range_get_value (range);
227 g_print ("DEBUG: setting value to %f\n", value);
229 switch (G_PARAM_SPEC_VALUE_TYPE (GST_DPARAM_PARAM_SPEC (dparam))) {
231 g_object_set (G_OBJECT (dparam), "value_int64", (gint64) value, NULL);
235 g_object_set (G_OBJECT (dparam), "value_int", (gint) value, NULL);
239 g_object_set (G_OBJECT (dparam), "value_float", (gfloat) value, NULL);
246 cb_entry_activate (GtkEntry * entry, gpointer user_data)
248 g_print ("DEBUG: oi ! you activated an entry !\n");
249 g_print ("DEBUG: pipeline: %s\n", gtk_entry_get_text (entry));
253 cb_play_clicked (GtkButton * button, gpointer * user_data)
255 _filter_data_t *fd = (_filter_data_t *) user_data;
257 g_return_if_fail (GST_IS_PIPELINE (fd->pipeline));
258 if (GST_STATE (fd->pipeline) == GST_STATE_PLAYING) {
259 ui_feedback_add (fd->ui, "Pipeline is already playing !\n");
262 gst_element_set_state (fd->pipeline, GST_STATE_PLAYING);
266 cb_stop_clicked (GtkButton * button, gpointer * user_data)
268 _filter_data_t *fd = (_filter_data_t *) user_data;
270 if (GST_STATE (fd->pipeline) != GST_STATE_PLAYING) {
271 ui_feedback_add (fd->ui, "Pipeline is not playing !\n");
274 gst_element_set_state (fd->pipeline, GST_STATE_NULL);
278 cb_parse_clicked (GtkButton * button, gpointer * user_data)
280 _filter_data_t *fd = (_filter_data_t *) user_data;
281 GtkCombo *filter = GTK_COMBO (fd->ui->filter);
282 GError *error = NULL;
283 GstPad *src_pad, *sink_pad;
285 g_print ("DEBUG: you pressed parse.\n");
286 ui_feedback_clear (fd->ui);
287 ui_feedback_add (fd->ui, "Parsing pipeline ...\n");
289 g_free (fd->input_pipe);
291 g_free (fd->output_pipe);
292 if (fd->filter_element)
293 g_free (fd->filter_element);
294 fd->input_pipe = g_strdup_printf ("bin.( %s )",
295 gtk_entry_get_text (GTK_ENTRY (fd->ui->input)));
296 fd->output_pipe = g_strdup_printf ("bin.( %s )",
297 gtk_entry_get_text (GTK_ENTRY (fd->ui->output)));
298 /* gtkcombo.h says I can access the entry field directly */
300 g_strdup (gtk_entry_get_text (GTK_ENTRY (filter->entry)));
301 g_print ("Input pipeline :\t%s (%d)\n", fd->input_pipe,
302 (int) strlen (fd->input_pipe));
303 g_print ("Filter element :\t%s (%d)\n", fd->filter_element,
304 (int) strlen (fd->filter_element));
305 g_print ("Output pipeline :\t%s (%d)\n", fd->output_pipe,
306 (int) strlen (fd->output_pipe));
308 /* try to create in and out bins */
309 if (strlen (fd->input_pipe) == 0) {
310 ui_feedback_add (fd->ui, "Error : try setting an input pipe.\n");
314 gst_object_unref (GST_OBJECT (fd->input));
315 fd->input = GST_ELEMENT (gst_parse_launch (fd->input_pipe, &error));
317 ui_feedback_add (fd->ui, "Error : parsing input pipeline : %s\n",
319 g_error_free (error);
323 if (strlen (fd->output_pipe) == 0) {
324 ui_feedback_add (fd->ui, "Error : try setting an output pipe.\n");
328 gst_object_unref (GST_OBJECT (fd->output));
329 fd->output = GST_ELEMENT (gst_parse_launch (fd->output_pipe, &error));
331 ui_feedback_add (fd->ui, "Error : parsing output pipeline : %s\n",
333 g_error_free (error);
337 /* try to create filter */
339 gst_object_unref (GST_OBJECT (fd->filter));
340 fd->filter = gst_element_factory_make (fd->filter_element, "filter");
341 if (fd->filter == NULL) {
342 ui_feedback_add (fd->ui, "Error : could not create element %s\n",
347 /* set up dynparam controls for filter */
348 ui_control_create (fd->filter, fd->ui->control, fd->ui);
350 /* create toplevel bin */
351 fd->pipeline = gst_thread_new ("toplevel");
353 /* add the players to it */
354 gst_bin_add_many (GST_BIN (fd->pipeline),
355 fd->input, fd->filter, fd->output, NULL);
357 /* connect filter to input and output bin */
358 src_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->input), GST_PAD_SRC,
360 if (src_pad == NULL) {
361 ui_feedback_add (fd->ui,
362 "Error : could not find an unconnected source pad !\n");
365 sink_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->output), GST_PAD_SINK,
367 if (sink_pad == NULL) {
368 ui_feedback_add (fd->ui,
369 "Error : could not find an unconnected sink pad !\n");
372 gst_element_add_pad (fd->input, src_pad);
373 gst_element_add_pad (fd->output, sink_pad);
375 gst_element_link_many (fd->input, fd->filter, fd->output, NULL);
378 g_free (fd->pipe_string);
379 fd->pipe_string = g_strdup_printf ("%s ! %s ! %s", fd->input_pipe,
380 fd->filter_element, fd->output_pipe);
381 g_print ("Pipeline : %s\n", fd->pipe_string);
382 ui_feedback_add (fd->ui, "Complete parsed pipeline: %s\n", fd->pipe_string);
387 /* find out the list of choices for GStreamer filters */
389 get_filter_choices (void)
391 GList *choices = NULL;
393 choices = g_list_append (choices, "volume");
394 choices = g_list_append (choices, "ladspa_lpf");
395 choices = g_list_append (choices, "ladspa_hpf");
401 init_data (_filter_data_t * fd)
403 fd->input_pipe = NULL;
404 fd->output_pipe = NULL;
405 fd->filter_element = NULL;
406 fd->pipe_string = NULL;
407 fd->filter_choices = get_filter_choices ();
409 /* GStreamer stuff */
418 create_ui (_filter_ui_t * fui, _filter_data_t * fd)
420 GtkWidget *widget; /* temporary widget */
421 GtkWidget *vbox; /* temporary vbox */
423 g_print ("DEBUG: creating top-level window\n");
424 fui->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
425 widget = gtk_vbox_new (FALSE, 0);
426 gtk_container_add (GTK_CONTAINER (fui->window), widget);
427 gtk_window_set_title (GTK_WINDOW (fui->window), "Gee, I can set titles too");
429 /* top level window division */
430 g_print ("DEBUG: creating top-level window contents\n");
431 fui->buttons = gtk_hbox_new (FALSE, 0);
432 gtk_container_add (GTK_CONTAINER (widget), fui->buttons);
433 fui->feedback = gtk_text_view_new ();
434 gtk_container_add (GTK_CONTAINER (widget), fui->feedback);
435 fui->selection = gtk_hbox_new (FALSE, 0);
436 gtk_container_add (GTK_CONTAINER (widget), fui->selection);
437 fui->control = gtk_vbox_new (TRUE, 5);
438 gtk_container_add (GTK_CONTAINER (widget), fui->control);
441 fui->parse = gtk_button_new_with_label ("Parse");
442 gtk_container_add (GTK_CONTAINER (fui->buttons), fui->parse);
443 g_signal_connect (G_OBJECT (fui->parse), "clicked",
444 G_CALLBACK (cb_parse_clicked), fd);
445 fui->play = gtk_button_new_with_label ("Play");
446 gtk_container_add (GTK_CONTAINER (fui->buttons), fui->play);
447 g_signal_connect (G_OBJECT (fui->play), "clicked",
448 G_CALLBACK (cb_play_clicked), fd);
449 fui->stop = gtk_button_new_with_label ("Stop");
450 gtk_container_add (GTK_CONTAINER (fui->buttons), fui->stop);
451 g_signal_connect (G_OBJECT (fui->stop), "clicked",
452 G_CALLBACK (cb_stop_clicked), fd);
454 /* feedback widget */
455 fui->fb_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (fui->feedback));
456 gtk_text_buffer_set_text (fui->fb_buffer,
457 "Hello, and welcome to the GStreamer filter demo app !\n"
458 "I'll be your feedback window for today.\n", -1);
460 /* selection widget */
461 vbox = gtk_vbox_new (FALSE, 0);
462 widget = gtk_label_new ("Input Pipe");
463 gtk_container_add (GTK_CONTAINER (vbox), widget);
464 fui->input = gtk_entry_new ();
465 gtk_entry_set_text (GTK_ENTRY (fui->input), "sinesrc");
466 gtk_container_add (GTK_CONTAINER (vbox), fui->input);
467 gtk_container_add (GTK_CONTAINER (fui->selection), vbox);
468 g_signal_connect (G_OBJECT (fui->input), "activate",
469 G_CALLBACK (cb_entry_activate), NULL);
471 vbox = gtk_vbox_new (FALSE, 0);
472 widget = gtk_label_new ("Filter");
473 gtk_container_add (GTK_CONTAINER (vbox), widget);
474 fui->filter = gtk_combo_new ();
475 gtk_combo_set_popdown_strings (GTK_COMBO (fui->filter), fd->filter_choices);
476 gtk_container_add (GTK_CONTAINER (vbox), fui->filter);
477 gtk_container_add (GTK_CONTAINER (fui->selection), vbox);
479 vbox = gtk_vbox_new (FALSE, 0);
480 widget = gtk_label_new ("Output Pipe");
481 gtk_container_add (GTK_CONTAINER (vbox), widget);
482 fui->output = gtk_entry_new ();
483 gtk_entry_set_text (GTK_ENTRY (fui->output), "osssink fragment=1572872"); /* fixme: gconf default ? */
484 gtk_container_add (GTK_CONTAINER (vbox), fui->output);
485 gtk_container_add (GTK_CONTAINER (fui->selection), vbox);
486 g_signal_connect (G_OBJECT (fui->output), "activate",
487 G_CALLBACK (cb_entry_activate), NULL);
489 /* control widget is dynamically generated */
491 g_print ("DEBUG: labeling control area.\n");
492 widget = gtk_label_new ("This is the big control area.");
493 gtk_container_add (GTK_CONTAINER (fui->control), widget);
499 main (int argc, char *argv[])
501 _filter_data_t filter_data;
502 _filter_ui_t filter_ui;
505 gtk_init (&argc, &argv);
506 gst_init (&argc, &argv);
507 gst_control_init (&argc, &argv);
509 init_data (&filter_data);
510 filter_data.ui = &filter_ui;
512 create_ui (&filter_ui, &filter_data);
513 gtk_widget_show_all (filter_ui.window);