• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <gst/gst.h>
2 #include <gtk/gtk.h>
3 #include <gdk/gdkx.h>
4 #include <gst/video/video.h>
5 
6 #define WINDOW_GLADE "window.glade"
7 #define INT_PROPERTY_GLADE "int_property.glade"
8 #define ENUM_PROPERTY_GLADE "enum_property.glade"
9 #define BOOL_PROPERTY_GLADE "boolean_property.glade"
10 
11 #define PROPERTY_TO_VBOX                                                \
12   properties[i].dynamic ? GTK_BOX (dynamic_vbox) : GTK_BOX (static_vbox)
13 
14 #define GET_WIDGET(object, type, name)                          \
15   type (gtk_builder_get_object ((object)->builder, name))
16 
17 #define GET_PROP_WIDGET(type, name) GET_WIDGET (&(properties[i]), type, name)
18 
19 static guint h264_xid, preview_xid;
20 
21 typedef struct
22 {
23   GtkBuilder *builder;
24   GstElement *src;
25   enum
26   { NONE, INT, ENUM, BOOL } type;
27   const gchar *property_name;
28   gboolean readonly;
29   gboolean dynamic;
30 } Prop;
31 
32 typedef struct
33 {
34   GtkBuilder *builder;
35   GstElement *bin;
36   GstElement *src;
37   GstElement *identity;
38   GstElement *vid_capsfilter;
39   GstElement *vf_capsfilter;
40 } Main;
41 
42 Prop properties[] = {
43   {NULL, NULL, INT, "initial-bitrate", FALSE, FALSE},
44   {NULL, NULL, INT, "slice-units", FALSE, FALSE},
45   {NULL, NULL, ENUM, "slice-mode", FALSE, FALSE},
46   {NULL, NULL, INT, "iframe-period", FALSE, FALSE},
47   {NULL, NULL, ENUM, "usage-type", FALSE, FALSE},
48   {NULL, NULL, ENUM, "entropy", FALSE, FALSE},
49   {NULL, NULL, BOOL, "enable-sei", FALSE, FALSE},
50   {NULL, NULL, INT, "num-reorder-frames", FALSE, FALSE},
51   {NULL, NULL, BOOL, "preview-flipped", FALSE, FALSE},
52   {NULL, NULL, INT, "leaky-bucket-size", FALSE, FALSE},
53   {NULL, NULL, INT, "num-clock-samples", FALSE, TRUE},
54   {NULL, NULL, ENUM, "rate-control", FALSE, TRUE},
55   {NULL, NULL, BOOL, "fixed-framerate", FALSE, TRUE},
56   {NULL, NULL, INT, "max-mbps", TRUE, TRUE},
57   {NULL, NULL, INT, "level-idc", FALSE, TRUE},
58   {NULL, NULL, INT, "peak-bitrate", FALSE, TRUE},
59   {NULL, NULL, INT, "average-bitrate", FALSE, TRUE},
60   {NULL, NULL, INT, "min-iframe-qp", FALSE, TRUE},
61   {NULL, NULL, INT, "max-iframe-qp", FALSE, TRUE},
62   {NULL, NULL, INT, "min-pframe-qp", FALSE, TRUE},
63   {NULL, NULL, INT, "max-pframe-qp", FALSE, TRUE},
64   {NULL, NULL, INT, "min-bframe-qp", FALSE, TRUE},
65   {NULL, NULL, INT, "max-bframe-qp", FALSE, TRUE},
66   {NULL, NULL, INT, "ltr-buffer-size", FALSE, TRUE},
67   {NULL, NULL, INT, "ltr-encoder-control", FALSE, TRUE},
68 };
69 
70 static void set_drop_probability (Main * self);
71 static void get_all_properties (void);
72 static void probe_all_properties (gboolean playing);
73 
74 /* Callbacks */
75 void on_button_toggled (GtkToggleButton * button, gpointer user_data);
76 void on_get_button_clicked (GtkButton * button, gpointer user_data);
77 void on_set_button_clicked (GtkButton * button, gpointer user_data);
78 void on_button_ready_clicked (GtkButton * button, gpointer user_data);
79 void on_button_null_clicked (GtkButton * button, gpointer user_data);
80 void on_button_playing_clicked (GtkButton * button, gpointer user_data);
81 void on_iframe_button_clicked (GtkButton * button, gpointer user_data);
82 void on_renegotiate_button_clicked (GtkButton * button, gpointer user_data);
83 void on_start_capture_button_clicked (GtkButton * button, gpointer user_data);
84 void on_stop_capture_button_clicked (GtkButton * button, gpointer user_data);
85 void on_window_destroyed (GtkWindow * window, gpointer user_data);
86 
87 static GstEvent *
new_upstream_force_key_unit(GstClockTime running_time,gboolean all_headers,guint count)88 new_upstream_force_key_unit (GstClockTime running_time,
89     gboolean all_headers, guint count)
90 {
91   GstEvent *force_key_unit_event;
92   GstStructure *s;
93 
94   s = gst_structure_new ("GstForceKeyUnit",
95       "running-time", GST_TYPE_CLOCK_TIME, running_time,
96       "all-headers", G_TYPE_BOOLEAN, all_headers,
97       "count", G_TYPE_UINT, count, NULL);
98   force_key_unit_event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s);
99 
100   return force_key_unit_event;
101 }
102 
103 void
on_get_button_clicked(GtkButton * button,gpointer user_data)104 on_get_button_clicked (GtkButton * button, gpointer user_data)
105 {
106   Prop *property = user_data;
107 
108   switch (property->type) {
109     case INT:
110     {
111       gchar *val;
112       gint val_int;
113       g_object_get (property->src, property->property_name, &val_int, NULL);
114       val = g_strdup_printf ("%d", val_int);
115       gtk_entry_set_text (GET_WIDGET (property, GTK_ENTRY, "value"), val);
116       g_free (val);
117     }
118       break;
119     case ENUM:
120     {
121       GParamSpec *param;
122       gint val;
123 
124       g_object_get (property->src, property->property_name, &val, NULL);
125       param = g_object_class_find_property (G_OBJECT_GET_CLASS (property->src),
126           property->property_name);
127       if (G_IS_PARAM_SPEC_ENUM (param)) {
128         GEnumValue *values;
129         guint i = 0;
130 
131         values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values;
132 
133         while (values[i].value_name) {
134           if (values[i].value == val) {
135             gtk_combo_box_set_active (GET_WIDGET (property,
136                     (GtkComboBox *), "value"), i);
137             break;
138           }
139           i++;
140         }
141       }
142     }
143       break;
144     case BOOL:
145     {
146       gboolean val;
147 
148       g_object_get (property->src, property->property_name, &val, NULL);
149       gtk_toggle_button_set_active (GET_WIDGET (property,
150               (GtkToggleButton *), "value"), val);
151     }
152       break;
153     case NONE:
154     default:
155       break;
156   }
157 }
158 
159 void
on_set_button_clicked(GtkButton * button,gpointer user_data)160 on_set_button_clicked (GtkButton * button, gpointer user_data)
161 {
162   Prop *property = user_data;
163 
164   switch (property->type) {
165     case INT:
166     {
167       int val_int;
168       const gchar *val;
169 
170       val = gtk_entry_get_text (GET_WIDGET (property, GTK_ENTRY, "value"));
171       val_int = (int) g_ascii_strtoll (val, NULL, 0);
172       g_object_set (property->src, property->property_name, val_int, NULL);
173     }
174       break;
175     case ENUM:
176     {
177       GParamSpec *param;
178 
179       param = g_object_class_find_property (G_OBJECT_GET_CLASS (property->src),
180           property->property_name);
181       if (G_IS_PARAM_SPEC_ENUM (param)) {
182         GEnumValue *values;
183         guint val = 0;
184 
185         values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values;
186 
187         val = gtk_combo_box_get_active (GET_WIDGET (property,
188                 (GtkComboBox *), "value"));
189         g_object_set (property->src, property->property_name,
190             values[val].value, NULL);
191       }
192     }
193       break;
194     case BOOL:
195     {
196       gboolean val;
197 
198       val = gtk_toggle_button_get_active (GET_WIDGET (property,
199               (GtkToggleButton *), "value"));
200       g_object_set (property->src, property->property_name, val, NULL);
201     }
202       break;
203     case NONE:
204     default:
205       break;
206   }
207   get_all_properties ();
208 }
209 
210 void
on_button_toggled(GtkToggleButton * button,gpointer user_data)211 on_button_toggled (GtkToggleButton * button, gpointer user_data)
212 {
213   if (gtk_toggle_button_get_active (button))
214     gtk_button_set_label (GTK_BUTTON (button), "   Enabled   ");
215   else
216     gtk_button_set_label (GTK_BUTTON (button), "  Disabled   ");
217 }
218 
219 static gboolean
set_caps(Main * self,gboolean send_event)220 set_caps (Main * self, gboolean send_event)
221 {
222   const gchar *h264_filter;
223   const gchar *raw_filter;
224   GstCaps *h264_caps = NULL;
225   GstCaps *raw_caps = NULL;
226   gboolean ret = TRUE;
227 
228   h264_filter = gtk_entry_get_text (GET_WIDGET (self, GTK_ENTRY, "h264_caps"));
229   raw_filter =
230       gtk_entry_get_text (GET_WIDGET (self, GTK_ENTRY, "preview_caps"));
231   if (h264_filter)
232     h264_caps = gst_caps_from_string (h264_filter);
233   if (raw_filter)
234     raw_caps = gst_caps_from_string (raw_filter);
235 
236   g_debug ("H264 caps : %s", gst_caps_to_string (h264_caps));
237   g_debug ("Preview caps : %s", gst_caps_to_string (raw_caps));
238   if (!h264_caps || !raw_caps) {
239     g_debug ("Invalid caps");
240     ret = FALSE;
241     goto end;
242   }
243 
244   g_object_set (self->vid_capsfilter, "caps", h264_caps, NULL);
245   g_object_set (self->vf_capsfilter, "caps", raw_caps, NULL);
246 
247   if (send_event) {
248     gst_element_send_event (GST_ELEMENT (self->src),
249         gst_event_new_reconfigure ());
250   }
251 
252 end:
253   if (h264_caps)
254     gst_caps_unref (h264_caps);
255   if (raw_caps)
256     gst_caps_unref (raw_caps);
257 
258   return ret;
259 }
260 
261 void
on_button_ready_clicked(GtkButton * button,gpointer user_data)262 on_button_ready_clicked (GtkButton * button, gpointer user_data)
263 {
264   Main *self = user_data;
265 
266   set_caps (self, FALSE);
267   gst_element_set_state (self->bin, GST_STATE_READY);
268   probe_all_properties (FALSE);
269   get_all_properties ();
270 }
271 
272 void
on_button_null_clicked(GtkButton * button,gpointer user_data)273 on_button_null_clicked (GtkButton * button, gpointer user_data)
274 {
275   Main *self = user_data;
276 
277   gst_element_set_state (self->bin, GST_STATE_NULL);
278   probe_all_properties (FALSE);
279   get_all_properties ();
280 }
281 
282 void
on_button_playing_clicked(GtkButton * button,gpointer user_data)283 on_button_playing_clicked (GtkButton * button, gpointer user_data)
284 {
285   Main *self = user_data;
286 
287   if (gst_element_set_state (self->bin, GST_STATE_PLAYING) ==
288       GST_STATE_CHANGE_FAILURE) {
289     g_debug ("Unable to go to state PLAYING");
290   }
291   set_caps (self, FALSE);
292   probe_all_properties (TRUE);
293   get_all_properties ();
294 
295   set_drop_probability (self);
296 }
297 
298 void
on_iframe_button_clicked(GtkButton * button,gpointer user_data)299 on_iframe_button_clicked (GtkButton * button, gpointer user_data)
300 {
301   Main *self = user_data;
302   GstEvent *event;
303   gboolean pps_sps;
304 
305   set_drop_probability (self);
306   pps_sps = gtk_toggle_button_get_active (GET_WIDGET (self, (GtkToggleButton *),
307           "pps_sps"));
308 
309   event = new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, pps_sps, 0);
310   gst_element_send_event (GST_ELEMENT (self->src), event);
311 }
312 
313 void
on_renegotiate_button_clicked(GtkButton * button,gpointer user_data)314 on_renegotiate_button_clicked (GtkButton * button, gpointer user_data)
315 {
316   Main *self = user_data;
317 
318   set_caps (self, TRUE);
319   probe_all_properties (GST_STATE (self->bin) >= GST_STATE_PAUSED);
320   get_all_properties ();
321 }
322 
323 void
on_start_capture_button_clicked(GtkButton * button,gpointer user_data)324 on_start_capture_button_clicked (GtkButton * button, gpointer user_data)
325 {
326   Main *self = user_data;
327 
328   set_caps (self, FALSE);
329   g_signal_emit_by_name (G_OBJECT (self->src), "start-capture", NULL);
330   probe_all_properties (GST_STATE (self->bin) >= GST_STATE_PAUSED);
331   get_all_properties ();
332 }
333 
334 void
on_stop_capture_button_clicked(GtkButton * button,gpointer user_data)335 on_stop_capture_button_clicked (GtkButton * button, gpointer user_data)
336 {
337   Main *self = user_data;
338 
339   set_caps (self, FALSE);
340   g_signal_emit_by_name (G_OBJECT (self->src), "stop-capture", NULL);
341   probe_all_properties (GST_STATE (self->bin) >= GST_STATE_PAUSED);
342   get_all_properties ();
343 }
344 
345 void
on_window_destroyed(GtkWindow * window,gpointer user_data)346 on_window_destroyed (GtkWindow * window, gpointer user_data)
347 {
348   gtk_main_quit ();
349 }
350 
351 static gboolean
_bus_callback(GstBus * bus,GstMessage * message,gpointer user_data)352 _bus_callback (GstBus * bus, GstMessage * message, gpointer user_data)
353 {
354   const GstStructure *s = gst_message_get_structure (message);
355   GstObject *source = NULL;
356 
357   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
358       gst_structure_has_name (s, "prepare-window-handle")) {
359     source = GST_MESSAGE_SRC (message);
360     if (!g_strcmp0 (gst_object_get_name (source), "h264_sink"))
361       gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (source),
362           h264_xid);
363     else
364       gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (source),
365           preview_xid);
366   }
367 
368   return TRUE;
369 }
370 
371 static void
set_drop_probability(Main * self)372 set_drop_probability (Main * self)
373 {
374   const gchar *drop;
375   gdouble drop_probability = 0.0;
376 
377   drop = gtk_entry_get_text (GET_WIDGET (self, GTK_ENTRY, "drop"));
378   drop_probability = g_ascii_strtod (drop, NULL);
379   g_debug ("Setting drop probability to : %f", drop_probability);
380   g_object_set (self->identity, "drop-probability", drop_probability, NULL);
381 }
382 
383 static void
get_all_properties(void)384 get_all_properties (void)
385 {
386   int i;
387 
388   for (i = 0; i < G_N_ELEMENTS (properties); i++)
389     on_get_button_clicked (NULL, &properties[i]);
390 
391 }
392 
393 static void
probe_all_properties(gboolean playing)394 probe_all_properties (gboolean playing)
395 {
396   int i;
397 
398   for (i = 0; i < G_N_ELEMENTS (properties); i++) {
399     gboolean return_value, changeable, default_bool;
400     guint mask, minimum, maximum, default_int;
401     GParamSpec *param;
402 
403     /* When playing, ignore static controls */
404     if (playing && !properties[i].dynamic)
405       continue;
406 
407     switch (properties[i].type) {
408       case INT:
409         g_signal_emit_by_name (G_OBJECT (properties[i].src), "get-int-setting",
410             properties[i].property_name, &minimum, &default_int, &maximum,
411             &return_value, NULL);
412         if (return_value) {
413           gchar *min, *def, *max;
414 
415           min = g_strdup_printf ("%d", minimum);
416           def = g_strdup_printf ("%d", default_int);
417           max = g_strdup_printf ("%d", maximum);
418           gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "minimum"), min);
419           gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"), def);
420           gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "maximum"), max);
421           g_free (min);
422           g_free (def);
423           g_free (max);
424         } else {
425           gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "minimum"), "");
426           gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"), "");
427           gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "maximum"), "");
428         }
429         break;
430       case ENUM:
431         g_signal_emit_by_name (G_OBJECT (properties[i].src), "get-enum-setting",
432             properties[i].property_name, &mask, &default_int, &return_value,
433             NULL);
434         param =
435             g_object_class_find_property (G_OBJECT_GET_CLASS (properties
436                 [i].src), properties[i].property_name);
437         if (G_IS_PARAM_SPEC_ENUM (param)) {
438           GEnumValue *values;
439           guint j = 0;
440 
441           values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values;
442 
443           if (return_value) {
444             while (values[j].value_name) {
445               if (values[j].value == default_int) {
446                 gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"),
447                     values[j].value_name);
448                 break;
449               }
450               j++;
451             }
452           } else {
453             gtk_entry_set_text (GET_PROP_WIDGET (GTK_ENTRY, "default"), "");
454           }
455 
456           j = 0;
457           while (values[j].value_name) {
458 #if !GTK_CHECK_VERSION (2, 24, 0)
459             gtk_combo_box_remove_text (GET_PROP_WIDGET ((GtkComboBox *),
460                     "value"), 0);
461 #else
462             gtk_combo_box_text_remove (GET_PROP_WIDGET ((GtkComboBoxText *),
463                     "value"), 0);
464 #endif
465             j++;
466           }
467 
468           j = 0;
469           while (values[j].value_name) {
470             gchar *val;
471             if (return_value && (mask & (1 << values[j].value)) != 0)
472               val = g_strdup_printf ("**%s**", values[j].value_name);
473             else
474               val = g_strdup (values[j].value_name);
475 
476 #if !GTK_CHECK_VERSION (2, 24, 0)
477             gtk_combo_box_append_text (GET_PROP_WIDGET ((GtkComboBox *),
478                     "value"), val);
479 #else
480             gtk_combo_box_text_append_text (GET_PROP_WIDGET ((GtkComboBoxText
481                         *), "value"), val);
482 #endif
483             g_free (val);
484             j++;
485           }
486         }
487         break;
488       case BOOL:
489         g_signal_emit_by_name (G_OBJECT (properties[i].src),
490             "get-boolean-setting", properties[i].property_name,
491             &changeable, &default_bool, &return_value, NULL);
492         if (return_value) {
493           gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "value"),
494               changeable);
495           gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "get"),
496               changeable);
497           gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"),
498               changeable);
499           gtk_toggle_button_set_active (GET_PROP_WIDGET ((GtkToggleButton *),
500                   "default"), default_bool);
501         }
502         break;
503       case NONE:
504       default:
505         break;
506     }
507   }
508 }
509 
510 int
main(int argc,char * argv[])511 main (int argc, char *argv[])
512 {
513   Main self = { NULL, NULL, NULL, NULL };
514   GstBus *bus = NULL;
515   GtkWidget *window, *static_vbox, *dynamic_vbox, *da;
516   gchar *drop;
517   gdouble drop_probability;
518   GdkWindow *gdk_win = NULL;
519   const char *device = "/dev/video0";
520   GError *error = NULL;
521   int i;
522 
523   /* FIXME: add support for Gdk Wayland backend, code currently assumes X11 */
524   if (g_getenv ("GDK_BACKEND") == NULL)
525     g_setenv ("GDK_BACKEND", "x11", TRUE);
526 
527   gtk_init (&argc, &argv);
528   gst_init (&argc, &argv);
529 
530   if (argc > 1)
531     device = argv[1];
532   else
533     g_print ("Usage : %s [device]\nUsing default device : %s\n",
534         argv[0], device);
535 
536 
537   self.bin = gst_parse_launch ("uvch264src name=src src.vidsrc ! queue ! "
538       "capsfilter name=vid_cf ! identity name=identity ! decodebin ! "
539       "xvimagesink name=h264_sink async=false "
540       "src.vfsrc ! queue ! capsfilter name=vf_cf ! "
541       "xvimagesink name=preview_sink async=false", NULL);
542 
543   if (!self.bin)
544     return -1;
545 
546   /* Listen to the bus for messages */
547   bus = gst_element_get_bus (self.bin);
548   gst_bus_add_watch (bus, _bus_callback, self.bin);
549   gst_object_unref (bus);
550 
551   self.src = gst_bin_get_by_name (GST_BIN (self.bin), "src");
552   self.identity = gst_bin_get_by_name (GST_BIN (self.bin), "identity");
553   self.vid_capsfilter = gst_bin_get_by_name (GST_BIN (self.bin), "vid_cf");
554   self.vf_capsfilter = gst_bin_get_by_name (GST_BIN (self.bin), "vf_cf");
555 
556   self.builder = gtk_builder_new ();
557   gtk_builder_add_from_file (self.builder, WINDOW_GLADE, &error);
558   if (error) {
559     g_debug ("Unable to load glade file : %s", error->message);
560     goto end;
561   }
562   gtk_builder_connect_signals (self.builder, &self);
563 
564   g_object_get (self.identity, "drop-probability", &drop_probability, NULL);
565   drop = g_strdup_printf ("%f", drop_probability);
566   gtk_entry_set_text (GET_WIDGET (&self, GTK_ENTRY, "drop"), drop);
567   g_free (drop);
568   window = GET_WIDGET (&self, GTK_WIDGET, "window");
569   static_vbox = GET_WIDGET (&self, GTK_WIDGET, "static");
570   dynamic_vbox = GET_WIDGET (&self, GTK_WIDGET, "dynamic");
571   da = GET_WIDGET (&self, GTK_WIDGET, "h264");
572   gtk_widget_realize (da);
573   gdk_win = gtk_widget_get_window (da);
574   h264_xid = GDK_WINDOW_XID (gdk_win);
575   da = GET_WIDGET (&self, GTK_WIDGET, "preview");
576   gtk_widget_realize (da);
577   gdk_win = gtk_widget_get_window (da);
578   preview_xid = GDK_WINDOW_XID (gdk_win);
579 
580   set_caps (&self, FALSE);
581 
582   g_object_set (self.src, "device", device, NULL);
583   if (gst_element_set_state (self.bin, GST_STATE_READY) ==
584       GST_STATE_CHANGE_FAILURE) {
585     g_debug ("Unable to go to state READY");
586     goto end;
587   }
588 
589   for (i = 0; i < G_N_ELEMENTS (properties); i++) {
590     switch (properties[i].type) {
591       case INT:
592         properties[i].src = self.src;
593         properties[i].builder = gtk_builder_new ();
594         gtk_builder_add_from_file (properties[i].builder, INT_PROPERTY_GLADE,
595             NULL);
596         gtk_builder_connect_signals (properties[i].builder, &properties[i]);
597         gtk_box_pack_start (PROPERTY_TO_VBOX,
598             GET_PROP_WIDGET (GTK_WIDGET, "int-property"), TRUE, TRUE, 2);
599         gtk_label_set_label (GET_PROP_WIDGET (GTK_LABEL, "label"),
600             properties[i].property_name);
601         if (properties[i].readonly)
602           gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"), FALSE);
603         break;
604       case ENUM:
605         properties[i].src = self.src;
606         properties[i].builder = gtk_builder_new ();
607 #if !GTK_CHECK_VERSION (2, 24, 0)
608         gtk_builder_add_from_file (properties[i].builder,
609             "enum_property_gtk2.glade", NULL);
610 #else
611         gtk_builder_add_from_file (properties[i].builder, ENUM_PROPERTY_GLADE,
612             NULL);
613 #endif
614         gtk_builder_connect_signals (properties[i].builder, &properties[i]);
615         gtk_box_pack_start (PROPERTY_TO_VBOX,
616             GET_PROP_WIDGET (GTK_WIDGET, "enum-property"), TRUE, TRUE, 2);
617         gtk_label_set_label (GET_PROP_WIDGET (GTK_LABEL, "label"),
618             properties[i].property_name);
619 #if !GTK_CHECK_VERSION (2, 24, 0)
620         {
621           GtkComboBox *combo_box;
622           GtkCellRenderer *cell;
623           GtkListStore *store;
624 
625           combo_box = GET_PROP_WIDGET ((GtkComboBox *), "value");
626           store = gtk_list_store_new (1, G_TYPE_STRING);
627           gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
628           g_object_unref (store);
629 
630           cell = gtk_cell_renderer_text_new ();
631           gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
632           gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
633               "text", 0, NULL);
634         }
635 #endif
636         if (properties[i].readonly)
637           gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"), FALSE);
638         break;
639       case BOOL:
640         properties[i].src = self.src;
641         properties[i].builder = gtk_builder_new ();
642         gtk_builder_add_from_file (properties[i].builder, BOOL_PROPERTY_GLADE,
643             NULL);
644         gtk_builder_connect_signals (properties[i].builder, &properties[i]);
645         gtk_box_pack_start (PROPERTY_TO_VBOX,
646             GET_PROP_WIDGET (GTK_WIDGET, "boolean-property"), TRUE, TRUE, 2);
647         gtk_label_set_label (GET_PROP_WIDGET (GTK_LABEL, "label"),
648             properties[i].property_name);
649         if (properties[i].readonly)
650           gtk_widget_set_sensitive (GET_PROP_WIDGET (GTK_WIDGET, "set"), FALSE);
651         break;
652       case NONE:
653       default:
654         break;
655     }
656   }
657   probe_all_properties (FALSE);
658   get_all_properties ();
659 
660   gtk_widget_show (window);
661   gtk_main ();
662 
663 end:
664   g_object_unref (G_OBJECT (self.builder));
665   for (i = 0; i < G_N_ELEMENTS (properties); i++) {
666     if (properties[i].builder)
667       g_object_unref (G_OBJECT (properties[i].builder));
668   }
669   gst_element_set_state (self.bin, GST_STATE_NULL);
670   gst_object_unref (self.src);
671   gst_object_unref (self.identity);
672   gst_object_unref (self.vid_capsfilter);
673   gst_object_unref (self.vf_capsfilter);
674   gst_object_unref (self.bin);
675 
676   return 0;
677 }
678