ed0dd5aaa3d73f23e868ec0ad23032553ce9495a
[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 //void  cb_dynparm_value_changed (GtkWidget *widget, gpointer user_data);
52 void    cb_dynparm_value_changed (GtkRange *range, GstDParam *dparam);
53
54 /* GStreamer helper functions go here */
55   
56 /* go through a bin, finding the one pad that is unconnected in the given
57  * direction, and return a ghost pad */
58 GstPad *
59 gst_bin_find_unconnected_pad (GstBin *bin, GstPadDirection direction, 
60                               gchar *name)
61 {
62   GstPad *pad = NULL;
63   GList *elements = NULL;
64   const GList *pads = NULL;
65   GstElement *element = NULL;
66
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)
71   {
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     {
77       /* check if the direction matches */
78       if (GST_PAD_DIRECTION (GST_PAD (pads->data)) == direction)
79       {
80         if (GST_PAD_PEER (GST_PAD (pads->data)) == NULL)
81         {
82           /* found it ! */
83           g_print ("DEBUG: found an unconnected pad !\n");
84           pad = GST_PAD (pads->data);
85         }
86       }
87       if (pad) break; /* found one already */
88       pads = g_list_next (pads);
89     }
90     elements = g_list_next (elements);
91   }
92
93   g_print ("DEBUG: find_unconnected stop\n");
94   if (pad == NULL) /* we didn't find it at all */
95     return NULL;
96   
97   pad = gst_ghost_pad_new (name, pad);
98   return pad;
99 }
100
101 void
102 ui_feedback_add_text (_filter_ui_t *ui, const gchar *text)
103 {
104   GtkTextIter iter;
105
106   gtk_text_buffer_get_end_iter (ui->fb_buffer, &iter);
107   gtk_text_buffer_insert (ui->fb_buffer, &iter, text, -1);
108 }
109
110 void
111 ui_feedback_add (_filter_ui_t *ui, const gchar *format, ...)
112 {
113   va_list args;
114   gchar *buffer = NULL;
115
116   va_start (args, format);
117   buffer = g_strdup_vprintf (format, args);
118   va_end (args);
119   ui_feedback_add_text (ui, buffer);
120   g_free (buffer);
121 }
122
123 void
124 ui_feedback_clear (_filter_ui_t *ui)
125 {
126   gtk_text_buffer_set_text (ui->fb_buffer, "", 0);
127 }
128
129 /* create the control widget using the element's dynparams 
130  * control is a vbox which we need to empty first */
131 void
132 ui_control_create (GstElement *element, GtkWidget *control, _filter_ui_t *ui)
133 {
134   GtkWidget *hbox = NULL;
135   GtkWidget *widget = NULL;
136   GstDParamManager *dpman = NULL;
137   GstDParam *dparam = NULL;
138   GParamSpec **specs = NULL;
139   int i = 0;
140   
141   g_assert (GTK_IS_VBOX (control));
142
143   /* empty control vbox */
144   g_print ("DEBUG: emptying control widget\n");
145   gtk_container_foreach (GTK_CONTAINER (control), cb_remove_and_destroy, 
146                          (gpointer) control);
147
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);
152   
153   if ((dpman = gst_dpman_get_manager (element)))
154   {
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)
158     {
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])) 
164       {
165         case G_TYPE_INT64:
166           widget = gtk_hscale_new_with_range (
167              (gdouble) (((GParamSpecInt64*)specs[i])->minimum),
168              (gdouble) (((GParamSpecInt64*)specs[i])->maximum),
169              1.0);
170           gtk_range_set_value (GTK_RANGE (widget),
171               (gdouble) ((GParamSpecInt64*)specs[i])->default_value);
172           break;
173
174         case G_TYPE_INT:
175           widget = gtk_hscale_new_with_range (
176              (gdouble) (((GParamSpecInt*)specs[i])->minimum),
177              (gdouble) (((GParamSpecInt*)specs[i])->maximum),
178              1.0);
179           gtk_range_set_value (GTK_RANGE (widget),
180               (gdouble) ((GParamSpecInt*)specs[i])->default_value);
181           break;
182         case G_TYPE_FLOAT:
183           widget = gtk_hscale_new_with_range (
184              (gdouble) (((GParamSpecFloat*)specs[i])->minimum),
185              (gdouble) (((GParamSpecFloat*)specs[i])->maximum),
186              0.00001);
187           gtk_range_set_value (GTK_RANGE (widget),
188               (gdouble) ((GParamSpecFloat*)specs[i])->default_value);
189           break;
190       }
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]), 
196                                          dparam));
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);
201                         
202       gtk_container_add (GTK_CONTAINER (hbox), widget);
203       gtk_widget_show (widget);      
204     }
205     gtk_container_add (GTK_CONTAINER (control), hbox);
206     gtk_widget_show (hbox);
207   }
208 }
209
210 /* all the pretty callbacks gather here please */
211 void
212 cb_remove_and_destroy (GtkWidget *widget, gpointer user_data)
213 {
214   GtkContainer *container = GTK_CONTAINER (user_data);
215
216   g_print ("DEBUG: trying to remove widget from a container.\n");
217   gtk_container_remove (container, widget);
218   gtk_widget_destroy (widget);
219 }
220
221 /* when the scale associated with a dparam changes, respond */
222 void
223 cb_dynparm_value_changed (GtkRange *range, GstDParam *dparam)
224 {
225   /*
226   GstDParam *dparam = GST_DPARAM (user_data);
227   GtkHScale *adj = GTK_HSCALE (widget);
228   */
229   gdouble value = 0.0;
230   g_assert (GST_IS_DPARAM (dparam));
231   g_assert (GTK_IS_RANGE (range));
232
233   value = gtk_range_get_value (range);
234
235   g_print ("DEBUG: setting value to %f\n", value); 
236
237   switch (G_PARAM_SPEC_VALUE_TYPE (GST_DPARAM_PARAM_SPEC (dparam))) 
238   {
239     case G_TYPE_INT64:
240       g_object_set (G_OBJECT (dparam), "value_int64",
241           (gint64) value, NULL);
242       break;
243
244     case G_TYPE_INT:
245       g_object_set (G_OBJECT (dparam), "value_int",
246           (gint) value, NULL);
247       break;
248
249     case G_TYPE_FLOAT:
250       g_object_set (G_OBJECT (dparam), "value_float",
251           (gfloat) value, NULL);
252       break;
253   }
254 }
255
256           
257 void
258 cb_entry_activate (GtkEntry *entry, gpointer user_data)
259 {
260   g_print ("DEBUG: oi ! you activated an entry !\n");
261   g_print ("DEBUG: pipeline: %s\n", gtk_entry_get_text (entry));
262 }
263
264 void
265 cb_play_clicked (GtkButton *button, gpointer *user_data)
266 {
267   _filter_data_t *fd = (_filter_data_t *) user_data;
268
269   g_return_if_fail (GST_IS_PIPELINE (fd->pipeline));
270   if (GST_STATE (fd->pipeline) == GST_STATE_PLAYING)
271   {
272     ui_feedback_add (fd->ui, "Pipeline is already playing !\n");
273     return;
274   }
275   gst_element_set_state (fd->pipeline, GST_STATE_PLAYING);
276 }
277
278 void
279 cb_stop_clicked (GtkButton *button, gpointer *user_data)
280 {
281   _filter_data_t *fd = (_filter_data_t *) user_data;
282
283   if (GST_STATE (fd->pipeline) != GST_STATE_PLAYING)
284   {
285     ui_feedback_add (fd->ui, "Pipeline is not playing !\n");
286     return;
287   }
288   gst_element_set_state (fd->pipeline, GST_STATE_NULL);
289 }
290
291 void
292 cb_parse_clicked (GtkButton *button, gpointer *user_data)
293 {
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;
298   
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));
314
315   /* try to create in and out bins */
316   if (strlen (fd->input_pipe) == 0)
317   {
318     ui_feedback_add (fd->ui, "Error : try setting an input pipe.\n");
319     return;
320   }
321   if (fd->input) gst_object_unref (GST_OBJECT (fd->input));
322   fd->input = GST_ELEMENT (gst_parse_launch (fd->input_pipe, &error));
323   if (error)
324   {
325     ui_feedback_add (fd->ui, "Error : parsing input pipeline : %s\n", 
326                      error->message);
327     g_error_free (error);
328     return;
329   }
330   
331   if (strlen (fd->output_pipe) == 0)
332   {
333     ui_feedback_add (fd->ui, "Error : try setting an output pipe.\n");
334     return;
335   }
336   if (fd->output) gst_object_unref (GST_OBJECT (fd->output));
337   fd->output = GST_ELEMENT (gst_parse_launch (fd->output_pipe, &error));
338   if (error)
339   {
340     ui_feedback_add (fd->ui, "Error : parsing output pipeline : %s\n", 
341                      error->message);
342     g_error_free (error);
343     return;
344   }
345
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)
350   {
351     ui_feedback_add (fd->ui, "Error : could not create element %s\n",
352                      fd->filter_element);
353     return;
354   }
355
356   /* set up dynparam controls for filter */
357   ui_control_create (fd->filter, fd->ui->control, fd->ui);
358
359   /* create toplevel bin */
360   fd->pipeline = gst_thread_new ("toplevel");
361
362   /* add the players to it */
363   gst_bin_add_many (GST_BIN (fd->pipeline), 
364                     fd->input, fd->filter, 
365                     fd->output, NULL);
366
367   /* connect filter to input and output bin */
368   src_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->input), GST_PAD_SRC, 
369                                           "source");
370   if (src_pad == NULL)
371   {
372     ui_feedback_add (fd->ui, 
373                      "Error : could not find an unconnected source pad !\n");
374     return;
375   }
376   sink_pad = gst_bin_find_unconnected_pad (GST_BIN (fd->output), GST_PAD_SINK, 
377                                           "sink");
378   if (sink_pad == NULL)
379   {
380     ui_feedback_add (fd->ui, 
381                      "Error : could not find an unconnected sink pad !\n");
382     return;
383   }
384   gst_element_add_pad (fd->input, src_pad);
385   gst_element_add_pad (fd->output, sink_pad);
386
387   gst_element_link_many (fd->input, fd->filter, fd->output, NULL);
388   
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);
394
395
396 }
397
398 /* find out the list of choices for GStreamer filters */
399 GList *
400 get_filter_choices (void)
401 {
402   GList *choices = NULL;
403
404   choices = g_list_append (choices, "volume");
405   choices = g_list_append (choices, "ladspa_lpf");
406   choices = g_list_append (choices, "ladspa_hpf");
407   
408   return choices;
409 }
410
411 void
412 init_data (_filter_data_t *fd)
413 {
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 ();
419
420   /* GStreamer stuff */
421   fd->input = NULL;
422   fd->output = NULL;
423   fd->filter = NULL;
424   fd->pipeline = NULL;
425   fd->playing = FALSE;
426 }
427
428 void
429 create_ui (_filter_ui_t *fui, _filter_data_t *fd)
430 {
431   GtkWidget *widget;            /* temporary widget */
432   GtkWidget *vbox;              /* temporary vbox */
433     
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");
439
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);
450
451   /* button widget */
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);
464
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);
470   
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);
481
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);
489
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);
499
500   /* control widget is dynamically generated */
501   /*
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);
505   */
506 }
507
508
509 int
510 main (int argc, char *argv[])
511 {
512   _filter_data_t filter_data;
513   _filter_ui_t filter_ui;
514   
515   
516   gtk_init (&argc, &argv);
517   gst_init (&argc, &argv);
518   gst_control_init (&argc, &argv);
519
520   init_data (&filter_data);
521   filter_data.ui = &filter_ui;
522
523   create_ui (&filter_ui, &filter_data);
524   gtk_widget_show_all (filter_ui.window);
525
526   gtk_main ();
527   
528   return 0;
529 }
530