gst-indent
[platform/upstream/gst-plugins-good.git] / examples / dynparams / filter.c
1 /*
2  * filter.c
3  *
4  * demo application for filters using dparams
5  * dparams get their sliders which you can play with
6  *
7  * you can also enter an input and output part of a pipeline
8  */
9
10 #include <string.h>
11 #include <gtk/gtk.h>
12 #include <gst/gst.h>
13 #include <gst/control/control.h>
14
15 /* filter UI data */
16 struct _filter_ui
17 {
18   GtkWidget *window;            /* top-level interface window */
19
20   GtkWidget *buttons;           /* all of the control buttons */
21   GtkWidget *parse, *play, *stop;       /* control buttons */
22
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 */
27
28   GtkWidget *control;           /* the dynamically generated control UI */
29 };
30
31 typedef struct _filter_ui _filter_ui_t;
32
33 /* back-end data */
34 struct _filter_data
35 {
36   _filter_ui_t *ui;             /* the UI data */
37   gchar *input_pipe, *output_pipe, *filter_element;
38   gchar *pipe_string;
39   GList *filter_choices;
40
41   gboolean playing;
42   GstElement *input, *output;   /* these are in and out bins */
43   GstElement *pipeline;
44   GstElement *filter;
45 };
46
47 typedef struct _filter_data _filter_data_t;
48
49 /* internal prototypes when they can't be avoided */
50 void cb_remove_and_destroy (GtkWidget * widget, gpointer user_data);
51
52 //void  cb_dynparm_value_changed (GtkWidget *widget, gpointer user_data);
53 void cb_dynparm_value_changed (GtkRange * range, GstDParam * dparam);
54
55 /* GStreamer helper functions go here */
56
57 /* go through a bin, finding the one pad that is unconnected in the given
58  * direction, and return a ghost pad */
59 GstPad *
60 gst_bin_find_unconnected_pad (GstBin * bin, GstPadDirection direction,
61     gchar * name)
62 {
63   GstPad *pad = NULL;
64   GList *elements = NULL;
65   const GList *pads = NULL;
66   GstElement *element = NULL;
67
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);
75     while (pads) {
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) {
79           /* found it ! */
80           g_print ("DEBUG: found an unconnected pad !\n");
81           pad = GST_PAD (pads->data);
82         }
83       }
84       if (pad)
85         break;                  /* found one already */
86       pads = g_list_next (pads);
87     }
88     elements = g_list_next (elements);
89   }
90
91   g_print ("DEBUG: find_unconnected stop\n");
92   if (pad == NULL)              /* we didn't find it at all */
93     return NULL;
94
95   pad = gst_ghost_pad_new (name, pad);
96   return pad;
97 }
98
99 void
100 ui_feedback_add_text (_filter_ui_t * ui, const gchar * text)
101 {
102   GtkTextIter iter;
103
104   gtk_text_buffer_get_end_iter (ui->fb_buffer, &iter);
105   gtk_text_buffer_insert (ui->fb_buffer, &iter, text, -1);
106 }
107
108 void
109 ui_feedback_add (_filter_ui_t * ui, const gchar * format, ...)
110 {
111   va_list args;
112   gchar *buffer = NULL;
113
114   va_start (args, format);
115   buffer = g_strdup_vprintf (format, args);
116   va_end (args);
117   ui_feedback_add_text (ui, buffer);
118   g_free (buffer);
119 }
120
121 void
122 ui_feedback_clear (_filter_ui_t * ui)
123 {
124   gtk_text_buffer_set_text (ui->fb_buffer, "", 0);
125 }
126
127 /* create the control widget using the element's dynparams 
128  * control is a vbox which we need to empty first */
129 void
130 ui_control_create (GstElement * element, GtkWidget * control, _filter_ui_t * ui)
131 {
132   GtkWidget *hbox = NULL;
133   GtkWidget *widget = NULL;
134   GstDParamManager *dpman = NULL;
135   GstDParam *dparam = NULL;
136   GParamSpec **specs = NULL;
137   int i = 0;
138
139   g_assert (GTK_IS_VBOX (control));
140
141   /* empty control vbox */
142   g_print ("DEBUG: emptying control widget\n");
143   gtk_container_foreach (GTK_CONTAINER (control), cb_remove_and_destroy,
144       (gpointer) control);
145
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);
150
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])) {
160         case G_TYPE_INT64:
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);
166           break;
167
168         case G_TYPE_INT:
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);
174           break;
175         case G_TYPE_FLOAT:
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);
181           break;
182       }
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);
192
193       gtk_container_add (GTK_CONTAINER (hbox), widget);
194       gtk_widget_show (widget);
195     }
196     gtk_container_add (GTK_CONTAINER (control), hbox);
197     gtk_widget_show (hbox);
198   }
199 }
200
201 /* all the pretty callbacks gather here please */
202 void
203 cb_remove_and_destroy (GtkWidget * widget, gpointer user_data)
204 {
205   GtkContainer *container = GTK_CONTAINER (user_data);
206
207   g_print ("DEBUG: trying to remove widget from a container.\n");
208   gtk_container_remove (container, widget);
209   gtk_widget_destroy (widget);
210 }
211
212 /* when the scale associated with a dparam changes, respond */
213 void
214 cb_dynparm_value_changed (GtkRange * range, GstDParam * dparam)
215 {
216   /*
217      GstDParam *dparam = GST_DPARAM (user_data);
218      GtkHScale *adj = GTK_HSCALE (widget);
219    */
220   gdouble value = 0.0;
221
222   g_assert (GST_IS_DPARAM (dparam));
223   g_assert (GTK_IS_RANGE (range));
224
225   value = gtk_range_get_value (range);
226
227   g_print ("DEBUG: setting value to %f\n", value);
228
229   switch (G_PARAM_SPEC_VALUE_TYPE (GST_DPARAM_PARAM_SPEC (dparam))) {
230     case G_TYPE_INT64:
231       g_object_set (G_OBJECT (dparam), "value_int64", (gint64) value, NULL);
232       break;
233
234     case G_TYPE_INT:
235       g_object_set (G_OBJECT (dparam), "value_int", (gint) value, NULL);
236       break;
237
238     case G_TYPE_FLOAT:
239       g_object_set (G_OBJECT (dparam), "value_float", (gfloat) value, NULL);
240       break;
241   }
242 }
243
244
245 void
246 cb_entry_activate (GtkEntry * entry, gpointer user_data)
247 {
248   g_print ("DEBUG: oi ! you activated an entry !\n");
249   g_print ("DEBUG: pipeline: %s\n", gtk_entry_get_text (entry));
250 }
251
252 void
253 cb_play_clicked (GtkButton * button, gpointer * user_data)
254 {
255   _filter_data_t *fd = (_filter_data_t *) user_data;
256
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");
260     return;
261   }
262   gst_element_set_state (fd->pipeline, GST_STATE_PLAYING);
263 }
264
265 void
266 cb_stop_clicked (GtkButton * button, gpointer * user_data)
267 {
268   _filter_data_t *fd = (_filter_data_t *) user_data;
269
270   if (GST_STATE (fd->pipeline) != GST_STATE_PLAYING) {
271     ui_feedback_add (fd->ui, "Pipeline is not playing !\n");
272     return;
273   }
274   gst_element_set_state (fd->pipeline, GST_STATE_NULL);
275 }
276
277 void
278 cb_parse_clicked (GtkButton * button, gpointer * user_data)
279 {
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;
284
285   g_print ("DEBUG: you pressed parse.\n");
286   ui_feedback_clear (fd->ui);
287   ui_feedback_add (fd->ui, "Parsing pipeline ...\n");
288   if (fd->input_pipe)
289     g_free (fd->input_pipe);
290   if (fd->output_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 */
299   fd->filter_element =
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));
307
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");
311     return;
312   }
313   if (fd->input)
314     gst_object_unref (GST_OBJECT (fd->input));
315   fd->input = GST_ELEMENT (gst_parse_launch (fd->input_pipe, &error));
316   if (error) {
317     ui_feedback_add (fd->ui, "Error : parsing input pipeline : %s\n",
318         error->message);
319     g_error_free (error);
320     return;
321   }
322
323   if (strlen (fd->output_pipe) == 0) {
324     ui_feedback_add (fd->ui, "Error : try setting an output pipe.\n");
325     return;
326   }
327   if (fd->output)
328     gst_object_unref (GST_OBJECT (fd->output));
329   fd->output = GST_ELEMENT (gst_parse_launch (fd->output_pipe, &error));
330   if (error) {
331     ui_feedback_add (fd->ui, "Error : parsing output pipeline : %s\n",
332         error->message);
333     g_error_free (error);
334     return;
335   }
336
337   /* try to create filter */
338   if (fd->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",
343         fd->filter_element);
344     return;
345   }
346
347   /* set up dynparam controls for filter */
348   ui_control_create (fd->filter, fd->ui->control, fd->ui);
349
350   /* create toplevel bin */
351   fd->pipeline = gst_thread_new ("toplevel");
352
353   /* add the players to it */
354   gst_bin_add_many (GST_BIN (fd->pipeline),
355       fd->input, fd->filter, fd->output, NULL);
356
357   /* connect filter to input and output bin */
358   src_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->input), GST_PAD_SRC,
359       "source");
360   if (src_pad == NULL) {
361     ui_feedback_add (fd->ui,
362         "Error : could not find an unconnected source pad !\n");
363     return;
364   }
365   sink_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->output), GST_PAD_SINK,
366       "sink");
367   if (sink_pad == NULL) {
368     ui_feedback_add (fd->ui,
369         "Error : could not find an unconnected sink pad !\n");
370     return;
371   }
372   gst_element_add_pad (fd->input, src_pad);
373   gst_element_add_pad (fd->output, sink_pad);
374
375   gst_element_link_many (fd->input, fd->filter, fd->output, NULL);
376
377   if (fd->pipe_string)
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);
383
384
385 }
386
387 /* find out the list of choices for GStreamer filters */
388 GList *
389 get_filter_choices (void)
390 {
391   GList *choices = NULL;
392
393   choices = g_list_append (choices, "volume");
394   choices = g_list_append (choices, "ladspa_lpf");
395   choices = g_list_append (choices, "ladspa_hpf");
396
397   return choices;
398 }
399
400 void
401 init_data (_filter_data_t * fd)
402 {
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 ();
408
409   /* GStreamer stuff */
410   fd->input = NULL;
411   fd->output = NULL;
412   fd->filter = NULL;
413   fd->pipeline = NULL;
414   fd->playing = FALSE;
415 }
416
417 void
418 create_ui (_filter_ui_t * fui, _filter_data_t * fd)
419 {
420   GtkWidget *widget;            /* temporary widget */
421   GtkWidget *vbox;              /* temporary vbox */
422
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");
428
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);
439
440   /* button widget */
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);
453
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);
459
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);
470
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);
478
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);
488
489   /* control widget is dynamically generated */
490   /*
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);
494    */
495 }
496
497
498 int
499 main (int argc, char *argv[])
500 {
501   _filter_data_t filter_data;
502   _filter_ui_t filter_ui;
503
504
505   gtk_init (&argc, &argv);
506   gst_init (&argc, &argv);
507   gst_control_init (&argc, &argv);
508
509   init_data (&filter_data);
510   filter_data.ui = &filter_ui;
511
512   create_ui (&filter_ui, &filter_data);
513   gtk_widget_show_all (filter_ui.window);
514
515   gtk_main ();
516
517   return 0;
518 }