Add -Wold-style-definition
[platform/upstream/gstreamer.git] / tests / examples / camerabin / gst-camera.c
1 /*
2  * GStreamer
3  * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 /*
21  * This is a demo application to test the camerabin element.
22  * If you have question don't hesitate in contact me edgard.lima@indt.org.br
23  */
24
25 /*
26  * Includes
27  */
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #include "gst-camera.h"
33
34 #include <gst/gst.h>
35 #include <gst/interfaces/xoverlay.h>
36 #include <gst/interfaces/colorbalance.h>
37 #include <gst/interfaces/photography.h>
38 #include <gtk/gtk.h>
39 #include <gdk/gdkx.h>
40 #include <gdk/gdkkeysyms.h>
41
42 #include <string.h>
43
44 #include <sys/time.h>
45 #include <time.h>
46 #include <glib/gstdio.h>        // g_fopen()
47
48 /*
49  * enums, typedefs and defines
50  */
51
52 #ifdef USE_MP4
53 #define VID_FILE_EXT "mp4"
54 #else
55 #define VID_FILE_EXT "ogg"
56 #endif
57
58 #define PREVIEW_TIME_MS (2 * 1000)
59 #define N_BURST_IMAGES 10
60 #define DEFAULT_UI_FILE "gst-camera.ui"
61 #define SHARED_UI_FILE CAMERA_APPS_UIDIR"/"DEFAULT_UI_FILE
62
63 /* Names of default elements */
64 #define CAMERA_APP_VIDEOSRC "v4l2src"
65 #define CAMERA_APP_IMAGE_POSTPROC "dummy"
66
67 #ifdef HAVE_GST_PHOTO_IFACE_H
68 #define EV_COMP_MAX 3.0
69 #define EV_COMP_MIN -3.0
70 #define EV_COMP_STEP 0.5
71 #endif
72
73 #define DEFAULT_VF_CAPS \
74   "video/x-raw-yuv, width = (int) 320, height = (int) 240, framerate = (fraction) 1496/100;" \
75   "video/x-raw-yuv, width = (int) 640, height = (int) 480, framerate = (fraction) 1494/100;" \
76   "video/x-raw-yuv, width = (int) 800, height = (int) 480, framerate = (fraction) 2503/100;" \
77   "video/x-raw-yuv, width = (int) 800, height = (int) 480, framerate = (fraction) 2988/100;" \
78   "video/x-raw-yuv, width = (int) 800, height = (int) 480, framerate = (fraction) 1494/100;" \
79   "video/x-raw-yuv, width = (int) 720, height = (int) 480, framerate = (fraction) 1494/100"
80
81 #define PREVIEW_CAPS \
82   "video/x-raw-rgb, width = (int) 640, height = (int) 480"
83
84 /* states:
85  (image) <---> (video_stopped) <---> (video_recording)
86 */
87 typedef enum _tag_CaptureState
88 {
89   CAP_STATE_IMAGE,
90   CAP_STATE_VIDEO_STOPED,
91   CAP_STATE_VIDEO_PAUSED,
92   CAP_STATE_VIDEO_RECORDING,
93 } CaptureState;
94
95 /*
96  * Global Vars
97  */
98
99 static GtkBuilder *builder = NULL;
100 static GtkWidget *ui_main_window = NULL;
101 static GtkWidget *ui_drawing = NULL;
102 static GtkWidget *ui_drawing_frame = NULL;
103 static GtkWidget *ui_chk_continous = NULL;
104 static GtkButton *ui_bnt_shot = NULL;
105 static GtkButton *ui_bnt_pause = NULL;
106 static GtkWidget *ui_chk_mute = NULL;
107 static GtkWidget *ui_vbox_color_controls = NULL;
108 static GtkWidget *ui_chk_rawmsg = NULL;
109
110 static GtkWidget *ui_rdbntImageCapture = NULL;
111 static GtkWidget *ui_rdbntVideoCapture = NULL;
112 static GtkWidget *ui_menuitem_photography = NULL;
113 static GtkWidget *ui_menuitem_capture = NULL;
114
115 static GtkComboBox *ui_cbbox_resolution = NULL;
116 static guint ui_cbbox_resolution_count = 0;
117
118 static CaptureState capture_state = CAP_STATE_IMAGE;
119
120 static GstElement *gst_camera_bin = NULL;
121 static GstElement *gst_videosrc = NULL;
122
123 static GString *filename = NULL;
124 static guint32 num_pics = 0;
125 static guint32 num_pics_cont = 0;
126 static guint32 num_vids = 0;
127
128 static gint max_fr_n = 0;
129 static gint max_fr_d = 0;
130 static const gchar *video_post;
131 static const gchar *image_post;
132
133 static GList *video_caps_list = NULL;
134
135 static guint bus_handler_id = 0;
136
137 #ifdef HAVE_GST_PHOTO_IFACE_H
138 static gchar *iso_speed_labels[] = { "auto", "100", "200", "400" };
139
140 static struct
141 {
142   const gchar *label;
143   gint width;
144   gint height;
145 } image_resolution_label_map[] = {
146   {
147   "View finder resolution", 0, 0}, {
148   "VGA", 640, 480}, {
149   "1,3Mpix (1280x960)", 1280, 960}, {
150   "3Mpix (2048x1536)", 2048, 1536}, {
151   "3,7Mpix 16:9 (2592x1456)", 2592, 1456}, {
152   "5Mpix (2592x1968)", 2592, 1968}
153 };
154 #endif
155
156 /*
157  * functions prototypes
158  */
159 static gboolean me_gst_setup_pipeline (const gchar * imagepost,
160     const gchar * videopost);
161 static void me_gst_cleanup_element (void);
162
163 static gboolean capture_mode_set_state (CaptureState state);
164 static void capture_mode_config_gui (void);
165 static gboolean capture_mode_stop (void);
166
167 static void ui_connect_signals (void);
168 static gboolean ui_create (void);
169 static void destroy_color_controls (void);
170 static void create_color_controls (void);
171 static void init_view_finder_resolution_combobox (void);
172
173 #ifdef HAVE_GST_PHOTO_IFACE_H
174 static void menuitem_toggle_active (GtkWidget * widget, gpointer data);
175 static void sub_menu_initialize (GtkWidget * widget, gpointer data);
176 static void fill_photography_menu (GtkMenuItem * parent_item);
177 #endif
178
179 /*
180  * functions implementation
181  */
182
183 static void
184 set_filename (GString * name)
185 {
186   const gchar *datadir;
187
188   if (capture_state == CAP_STATE_IMAGE) {
189     g_string_printf (name, G_DIR_SEPARATOR_S "test_%04u.jpg", num_pics);
190     datadir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
191   } else {
192     g_string_printf (name, G_DIR_SEPARATOR_S "test_%04u.%s", num_vids,
193         VID_FILE_EXT);
194     datadir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
195   }
196
197   if (datadir == NULL) {
198     gchar *curdir = g_get_current_dir ();
199     g_string_prepend (name, curdir);
200     g_free (curdir);
201   } else {
202     g_string_prepend (name, datadir);
203   }
204   GST_INFO ("capture to %s", name->str);
205 }
206
207 /* Write raw image buffer to file if found from message */
208 static void
209 handle_element_message (GstMessage * msg)
210 {
211   const GstStructure *st;
212   const GValue *image;
213   GstBuffer *buf = NULL;
214   guint8 *data = NULL;
215   gchar *caps_string;
216   guint size = 0;
217   gchar *filename = NULL;
218   FILE *f = NULL;
219   size_t written;
220
221   st = gst_message_get_structure (msg);
222   if (g_str_equal (gst_structure_get_name (st), "autofocus-done")) {
223     gtk_button_set_label (ui_bnt_pause, "Focus");
224   } else if (gst_structure_has_field_typed (st, "buffer", GST_TYPE_BUFFER)) {
225     image = gst_structure_get_value (st, "buffer");
226     if (image) {
227       buf = gst_value_get_buffer (image);
228       data = GST_BUFFER_DATA (buf);
229       size = GST_BUFFER_SIZE (buf);
230       if (g_str_equal (gst_structure_get_name (st), "raw-image")) {
231         filename = g_strdup_printf ("test_%04u.raw", num_pics);
232       } else if (g_str_equal (gst_structure_get_name (st), "preview-image")) {
233         filename = g_strdup_printf ("test_%04u_vga.rgb", num_pics);
234       } else {
235         /* for future purposes */
236         g_print ("unknown buffer received\n");
237         return;
238       }
239       caps_string = gst_caps_to_string (GST_BUFFER_CAPS (buf));
240       g_print ("writing buffer to %s, buffer caps: %s\n",
241           filename, caps_string);
242       g_free (caps_string);
243       f = g_fopen (filename, "w");
244       if (f) {
245         written = fwrite (data, size, 1, f);
246         if (!written) {
247           g_print ("errro writing file\n");
248         }
249         fclose (f);
250       } else {
251         g_print ("error opening file for raw image writing\n");
252       }
253       g_free (filename);
254     }
255   } else if (g_str_equal (gst_structure_get_name (st), "photo-capture-start")) {
256     g_print ("=== CLICK ===\n");
257   }
258 }
259
260 static GstBusSyncReply
261 my_bus_sync_callback (GstBus * bus, GstMessage * message, gpointer data)
262 {
263   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
264     return GST_BUS_PASS;
265
266   if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
267     return GST_BUS_PASS;
268
269   /* FIXME: make sure to get XID in main thread */
270   gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (message->src),
271       GDK_WINDOW_XWINDOW (ui_drawing->window));
272
273   gst_message_unref (message);
274   return GST_BUS_DROP;
275 }
276
277 static void
278 print_error_message (GstMessage * msg)
279 {
280   GError *err = NULL;
281   gchar *dbg = NULL;
282
283   gst_message_parse_error (msg, &err, &dbg);
284
285   g_printerr ("Camerabin won't start up!\nError: %s\nDebug Info: %s\n",
286       err->message, (dbg) ? dbg : "None");
287
288   g_error_free (err);
289   g_free (dbg);
290 }
291
292 static gboolean
293 my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
294 {
295   switch (GST_MESSAGE_TYPE (message)) {
296     case GST_MESSAGE_WARNING:{
297       GError *err;
298       gchar *debug;
299
300       gst_message_parse_warning (message, &err, &debug);
301       g_print ("Warning: %s\n", err->message);
302       g_error_free (err);
303       g_free (debug);
304       break;
305     }
306     case GST_MESSAGE_ERROR:{
307       print_error_message (message);
308       me_gst_cleanup_element ();
309       gtk_main_quit ();
310       break;
311     }
312     case GST_MESSAGE_EOS:
313       /* end-of-stream */
314       gtk_main_quit ();
315       break;
316     case GST_MESSAGE_STATE_CHANGED:{
317       GstState old, new, pending;
318
319       gst_message_parse_state_changed (message, &old, &new, &pending);
320
321       GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message), "state-change %s -> %s",
322           gst_element_state_get_name (old), gst_element_state_get_name (new));
323
324       /* Create/destroy color controls according videosrc state */
325       if (GST_MESSAGE_SRC (message) == GST_OBJECT (gst_videosrc)) {
326         GST_INFO_OBJECT (GST_MESSAGE_SRC (message), "state-change %s -> %s",
327             gst_element_state_get_name (old), gst_element_state_get_name (new));
328
329         if (old == GST_STATE_READY && new == GST_STATE_NULL) {
330           destroy_color_controls ();
331         } else if (old == GST_STATE_NULL && new == GST_STATE_READY) {
332           create_color_controls ();
333         }
334       }
335
336       /* we only care about pipeline state change messages */
337       if (GST_IS_PIPELINE (GST_MESSAGE_SRC (message))) {
338         /* dump graph for pipeline state changes */
339         gchar *dump_name = g_strdup_printf ("camerabin.%s_%s",
340             gst_element_state_get_name (old),
341             gst_element_state_get_name (new));
342         GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (GST_MESSAGE_SRC (message)),
343             GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE |
344             GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS, dump_name);
345         g_free (dump_name);
346       }
347       break;
348     }
349     case GST_MESSAGE_ELEMENT:
350     {
351       handle_element_message (message);
352       break;
353     }
354     default:
355       /* unhandled message */
356       break;
357   }
358   return TRUE;
359 }
360
361 static void
362 me_set_next_cont_file_name (GString * filename)
363 {
364   /* FIXME: better file naming (possible with signal) */
365   if (G_UNLIKELY (num_pics_cont == 1)) {
366     gint i;
367     for (i = filename->len - 1; i > 0; --i) {
368       if (filename->str[i] == '.')
369         break;
370     }
371     g_string_insert (filename, i, "_0001");
372   } else {
373     gchar tmp[6];
374     gint i;
375     for (i = filename->len - 1; i > 0; --i) {
376       if (filename->str[i] == '_')
377         break;
378     }
379     snprintf (tmp, 6, "_%04d", num_pics_cont);
380     memcpy (filename->str + i, tmp, 5);
381   }
382 }
383
384 static gboolean
385 stop_image_preview (gpointer data)
386 {
387   g_return_val_if_fail (data != NULL, FALSE);
388
389   g_signal_emit_by_name (data, "capture-stop", 0);
390
391   return FALSE;
392 }
393
394 static gboolean
395 me_image_capture_done (GstElement * camera, const gchar * fname,
396     gpointer user_data)
397 {
398   gboolean cont =
399       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ui_chk_continous));
400   GString *filename = g_string_new (fname);
401
402   if (num_pics_cont < N_BURST_IMAGES && cont) {
403     num_pics_cont++;
404     me_set_next_cont_file_name (filename);
405     g_object_set (G_OBJECT (camera), "filename", filename->str, NULL);
406     g_string_free (filename, TRUE);
407   } else {
408     gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_shot), TRUE);
409     printf ("%u image(s) saved\n", num_pics_cont + 1);
410     fflush (stdout);
411     num_pics_cont = 0;
412
413     g_timeout_add (PREVIEW_TIME_MS, (GSourceFunc) stop_image_preview, camera);
414
415     cont = FALSE;
416   }
417   return cont;
418 }
419
420 static gboolean
421 me_gst_setup_pipeline_create_post_bin (const gchar * post, gboolean video)
422 {
423   GstElement *vpp = NULL;
424   GstElement *bin, *c1, *c2, *filter;
425   GstPad *pad;
426   GstCaps *caps;
427
428   /* this function uses a bin just because it needs ffmpegcolorspace. For
429    * performance reason one should provide an element without need for color
430    * convertion */
431
432   vpp = gst_element_factory_make (post, NULL);
433   if (NULL == vpp) {
434     fprintf (stderr, "cannot create \'%s\' element\n", post);
435     fflush (stderr);
436     goto done;
437   }
438   c1 = gst_element_factory_make ("ffmpegcolorspace", NULL);
439   c2 = gst_element_factory_make ("ffmpegcolorspace", NULL);
440   if (NULL == c1 || NULL == c2) {
441     fprintf (stderr, "cannot create \'ffmpegcolorspace\' element\n");
442     fflush (stderr);
443     goto done;
444   }
445   filter = gst_element_factory_make ("capsfilter", NULL);
446   if (NULL == filter) {
447     fprintf (stderr, "cannot create \'capsfilter\' element\n");
448     fflush (stderr);
449     goto done;
450   }
451   bin = gst_bin_new (video ? "vid_postproc_bin" : "img_postproc_bin");
452   if (NULL == bin) {
453     goto done;
454   }
455
456   caps = gst_caps_new_simple ("video/x-raw-yuv",
457       "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), NULL);
458   g_object_set (G_OBJECT (filter), "caps", caps, NULL);
459   gst_caps_unref (caps);
460
461   gst_bin_add_many (GST_BIN (bin), c1, vpp, c2, filter, NULL);
462   if (!gst_element_link_many (c1, vpp, c2, filter, NULL)) {
463     fprintf (stderr, "cannot link video post proc elements\n");
464     fflush (stderr);
465     goto done;
466   }
467
468   pad = gst_element_get_static_pad (c1, "sink");
469   gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
470   gst_object_unref (GST_OBJECT (pad));
471
472   pad = gst_element_get_static_pad (filter, "src");
473   gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
474   gst_object_unref (GST_OBJECT (pad));
475
476   g_object_set (gst_camera_bin,
477       (video ? "video-post-processing" : "image-post-processing"), bin, NULL);
478   return TRUE;
479 done:
480   return FALSE;
481 }
482
483 static void
484 me_gst_setup_pipeline_create_codecs (void)
485 {
486 #ifdef USE_MP4
487   g_object_set (gst_camera_bin, "video-encoder",
488       gst_element_factory_make ("omx_mpeg4enc", NULL), NULL);
489
490   g_object_set (gst_camera_bin, "audio-encoder",
491       gst_element_factory_make ("omx_aacenc", NULL), NULL);
492
493   g_object_set (gst_camera_bin, "video-muxer",
494       gst_element_factory_make ("hantromp4mux", NULL), NULL);
495 #else
496   /* using defaults theora, vorbis, ogg */
497 #endif
498 }
499
500 static gboolean
501 me_gst_setup_pipeline_create_img_post_bin (const gchar * imagepost)
502 {
503   return me_gst_setup_pipeline_create_post_bin (imagepost, FALSE);
504 }
505
506 static gboolean
507 me_gst_setup_pipeline_create_vid_post_bin (const gchar * videopost)
508 {
509   return me_gst_setup_pipeline_create_post_bin (videopost, TRUE);
510 }
511
512 static gboolean
513 me_gst_setup_pipeline (const gchar * imagepost, const gchar * videopost)
514 {
515   GstBus *bus;
516   GstCaps *preview_caps;
517
518   set_filename (filename);
519
520   me_gst_cleanup_element ();
521
522   gst_camera_bin = gst_element_factory_make ("camerabin", NULL);
523   if (NULL == gst_camera_bin) {
524     goto done;
525   }
526
527   g_signal_connect (gst_camera_bin, "image-done",
528       (GCallback) me_image_capture_done, NULL);
529
530   preview_caps = gst_caps_from_string (PREVIEW_CAPS);
531
532   bus = gst_pipeline_get_bus (GST_PIPELINE (gst_camera_bin));
533   bus_handler_id = gst_bus_add_watch (bus, my_bus_callback, NULL);
534   gst_bus_set_sync_handler (bus, my_bus_sync_callback, NULL);
535   gst_object_unref (bus);
536
537   /* set properties */
538   g_object_set (gst_camera_bin, "filename", filename->str, NULL);
539   g_object_set (gst_camera_bin, "preview-caps", preview_caps, NULL);
540   gst_caps_unref (preview_caps);
541
542   gst_videosrc = gst_element_factory_make (CAMERA_APP_VIDEOSRC, NULL);
543   if (gst_videosrc) {
544     g_object_set (G_OBJECT (gst_camera_bin), "video-source", gst_videosrc,
545         NULL);
546   }
547
548   if (imagepost) {
549     if (!me_gst_setup_pipeline_create_img_post_bin (imagepost))
550       goto done;
551   } else {
552     /* Use default image postprocessing element */
553     GstElement *ipp =
554         gst_element_factory_make (CAMERA_APP_IMAGE_POSTPROC, NULL);
555     if (ipp) {
556       g_object_set (G_OBJECT (gst_camera_bin), "image-post-processing", ipp,
557           NULL);
558     }
559   }
560
561   if (videopost) {
562     if (!me_gst_setup_pipeline_create_vid_post_bin (videopost))
563       goto done;
564   }
565
566   me_gst_setup_pipeline_create_codecs ();
567
568   if (GST_STATE_CHANGE_FAILURE ==
569       gst_element_set_state (gst_camera_bin, GST_STATE_READY)) {
570     goto done;
571   }
572
573   if (!gst_videosrc) {
574     g_object_get (G_OBJECT (gst_camera_bin), "video-source", &gst_videosrc,
575         NULL);
576   }
577
578   init_view_finder_resolution_combobox ();
579
580   gst_element_set_state (gst_camera_bin, GST_STATE_PLAYING);
581
582 #ifdef HAVE_GST_PHOTO_IFACE_H
583   /* Initialize menus to default settings */
584   GtkWidget *sub_menu =
585       gtk_menu_item_get_submenu (GTK_MENU_ITEM (ui_menuitem_capture));
586   gtk_container_foreach (GTK_CONTAINER (sub_menu), sub_menu_initialize, NULL);
587   sub_menu =
588       gtk_menu_item_get_submenu (GTK_MENU_ITEM (ui_menuitem_photography));
589   gtk_container_foreach (GTK_CONTAINER (sub_menu), sub_menu_initialize, NULL);
590 #endif
591
592   capture_state = CAP_STATE_IMAGE;
593   return TRUE;
594 done:
595   fprintf (stderr, "error to create pipeline\n");
596   fflush (stderr);
597   me_gst_cleanup_element ();
598   return FALSE;
599 }
600
601 static gboolean
602 me_gst_setup_default_pipeline (gpointer data)
603 {
604   if (!me_gst_setup_pipeline (NULL, NULL)) {
605     gtk_main_quit ();
606   }
607   return FALSE;
608 }
609
610 static void
611 me_gst_cleanup_element (void)
612 {
613   if (gst_camera_bin) {
614     GstBus *bus;
615
616     gst_element_set_state (gst_camera_bin, GST_STATE_NULL);
617     gst_element_get_state (gst_camera_bin, NULL, NULL, GST_CLOCK_TIME_NONE);
618
619     bus = gst_pipeline_get_bus (GST_PIPELINE (gst_camera_bin));
620     gst_bus_set_sync_handler (bus, NULL, NULL);
621     g_source_remove (bus_handler_id);
622
623     gst_object_unref (gst_camera_bin);
624     gst_camera_bin = NULL;
625
626     g_list_foreach (video_caps_list, (GFunc) gst_caps_unref, NULL);
627     g_list_free (video_caps_list);
628     video_caps_list = NULL;
629   }
630 }
631
632 static gboolean
633 capture_mode_stop (void)
634 {
635   if (capture_state == CAP_STATE_VIDEO_PAUSED
636       || capture_state == CAP_STATE_VIDEO_RECORDING) {
637     return capture_mode_set_state (CAP_STATE_VIDEO_STOPED);
638   } else {
639     return TRUE;
640   }
641 }
642
643 static void
644 capture_mode_config_gui (void)
645 {
646   switch (capture_state) {
647     case CAP_STATE_IMAGE:
648       gtk_button_set_label (ui_bnt_shot, "Shot");
649       gtk_button_set_label (ui_bnt_pause, "Focus");
650       gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_pause), TRUE);
651       gtk_widget_show (ui_chk_continous);
652       gtk_widget_show (ui_chk_rawmsg);
653       gtk_widget_hide (ui_chk_mute);
654       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui_rdbntImageCapture),
655           TRUE);
656       break;
657     case CAP_STATE_VIDEO_STOPED:
658       gtk_button_set_label (ui_bnt_shot, "Rec");
659       gtk_button_set_label (ui_bnt_pause, "Pause");
660       gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_pause), FALSE);
661       gtk_widget_show (GTK_WIDGET (ui_bnt_pause));
662       gtk_widget_show (ui_chk_mute);
663       gtk_widget_hide (ui_chk_continous);
664       gtk_widget_hide (ui_chk_rawmsg);
665       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui_rdbntVideoCapture),
666           TRUE);
667       break;
668     case CAP_STATE_VIDEO_PAUSED:
669       gtk_button_set_label (ui_bnt_pause, "Cont");
670       break;
671     case CAP_STATE_VIDEO_RECORDING:
672       gtk_button_set_label (ui_bnt_shot, "Stop");
673       gtk_button_set_label (ui_bnt_pause, "Pause");
674       gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_pause), TRUE);
675       break;
676     default:
677       break;
678   }
679 }
680
681 static gboolean
682 capture_mode_set_state (CaptureState state)
683 {
684   if (capture_state == state)
685     return TRUE;
686
687   switch (capture_state) {
688     case CAP_STATE_IMAGE:
689       if (state == CAP_STATE_VIDEO_PAUSED) {
690         goto done;
691       }
692       g_object_set (gst_camera_bin, "mode", 1, NULL);
693       capture_state = CAP_STATE_VIDEO_STOPED;
694       if (state == CAP_STATE_VIDEO_RECORDING)
695         capture_mode_set_state (state);
696       break;
697     case CAP_STATE_VIDEO_STOPED:
698       if (state == CAP_STATE_VIDEO_PAUSED) {
699         goto done;
700       }
701       capture_state = state;
702       if (state == CAP_STATE_IMAGE)
703         g_object_set (gst_camera_bin, "mode", 0, NULL);
704       else {                    /* state == CAP_STATE_VIDEO_RECORDING */
705         g_object_set (gst_camera_bin, "mode", 1, NULL);
706         g_signal_emit_by_name (gst_camera_bin, "capture-start", 0);
707       }
708       break;
709     case CAP_STATE_VIDEO_PAUSED:
710       if (state == CAP_STATE_VIDEO_RECORDING) {
711         g_signal_emit_by_name (gst_camera_bin, "capture-start", 0);
712         capture_state = CAP_STATE_VIDEO_RECORDING;
713       } else {
714         g_signal_emit_by_name (gst_camera_bin, "capture-stop", 0);
715         capture_state = CAP_STATE_VIDEO_STOPED;
716         if (state == CAP_STATE_IMAGE)
717           capture_mode_set_state (state);
718       }
719       break;
720     case CAP_STATE_VIDEO_RECORDING:
721       if (state == CAP_STATE_VIDEO_PAUSED) {
722         g_signal_emit_by_name (gst_camera_bin, "capture-pause", 0);
723         capture_state = CAP_STATE_VIDEO_PAUSED;
724       } else {
725         g_signal_emit_by_name (gst_camera_bin, "capture-stop", 0);
726         capture_state = CAP_STATE_VIDEO_STOPED;
727         if (state == CAP_STATE_IMAGE)
728           capture_mode_set_state (state);
729       }
730       break;
731   }
732   return TRUE;
733 done:
734   return FALSE;
735 }
736
737 void
738 on_windowMain_delete_event (GtkWidget * widget, GdkEvent * event, gpointer data)
739 {
740   capture_mode_set_state (CAP_STATE_IMAGE);
741   capture_mode_config_gui ();
742   me_gst_cleanup_element ();
743   gtk_main_quit ();
744 }
745
746 static void
747 set_metadata (void)
748 {
749   /* for more information about image metadata tags, see:
750    * http://webcvs.freedesktop.org/gstreamer/gst-plugins-bad/tests/icles/metadata_editor.c
751    * and for the mapping:
752    * http://webcvs.freedesktop.org/gstreamer/gst-plugins-bad/ext/metadata/metadata_mapping.htm?view=co 
753    */
754
755   GstTagSetter *setter = GST_TAG_SETTER (gst_camera_bin);
756   GTimeVal time = { 0, 0 };
757   gchar *date_str, *desc_str;
758
759   g_get_current_time (&time);
760   date_str = g_time_val_to_iso8601 (&time);     /* this is UTC */
761   desc_str = g_strdup_printf ("picture taken by %s", g_get_real_name ());
762
763   gst_tag_setter_add_tags (setter, GST_TAG_MERGE_REPLACE,
764       "date-time-original", date_str,
765       "date-time-modified", date_str,
766       "creator-tool", "camerabin-demo",
767       GST_TAG_DESCRIPTION, desc_str,
768       GST_TAG_TITLE, "My picture", GST_TAG_COPYRIGHT, "LGPL", NULL);
769
770   g_free (date_str);
771   g_free (desc_str);
772 }
773
774 void
775 on_buttonShot_clicked (GtkButton * button, gpointer user_data)
776 {
777   switch (capture_state) {
778     case CAP_STATE_IMAGE:
779     {
780       gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_shot), FALSE);
781       set_filename (filename);
782       num_pics++;
783       g_object_set (gst_camera_bin, "filename", filename->str, NULL);
784
785       set_metadata ();
786       g_signal_emit_by_name (gst_camera_bin, "capture-start", 0);
787     }
788       break;
789     case CAP_STATE_VIDEO_STOPED:
790       set_filename (filename);
791       num_vids++;
792       g_object_set (gst_camera_bin, "filename", filename->str, NULL);
793       capture_mode_set_state (CAP_STATE_VIDEO_RECORDING);
794       capture_mode_config_gui ();
795       break;
796     case CAP_STATE_VIDEO_PAUSED:
797       /* fall trough */
798     case CAP_STATE_VIDEO_RECORDING:
799       capture_mode_set_state (CAP_STATE_VIDEO_STOPED);
800       capture_mode_config_gui ();
801       break;
802     default:
803       break;
804   }
805 }
806
807 void
808 on_buttonPause_clicked (GtkButton * button, gpointer user_data)
809 {
810   switch (capture_state) {
811     case CAP_STATE_IMAGE:
812       if (g_str_equal (gtk_button_get_label (ui_bnt_pause), "Focus")) {
813         /* Start autofocus */
814         gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), TRUE);
815         gtk_button_set_label (ui_bnt_pause, "Cancel Focus");
816       } else {
817         /* Cancel autofocus */
818         gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), FALSE);
819         gtk_button_set_label (ui_bnt_pause, "Focus");
820       }
821       break;
822     case CAP_STATE_VIDEO_STOPED:
823       break;
824     case CAP_STATE_VIDEO_PAUSED:
825       capture_mode_set_state (CAP_STATE_VIDEO_RECORDING);
826       capture_mode_config_gui ();
827       break;
828     case CAP_STATE_VIDEO_RECORDING:
829       capture_mode_set_state (CAP_STATE_VIDEO_PAUSED);
830       capture_mode_config_gui ();
831       break;
832     default:
833       break;
834   }
835 }
836
837 void
838 on_drawingareaView_realize (GtkWidget * widget, gpointer data)
839 {
840 #if GTK_CHECK_VERSION (2, 18, 0)
841   gdk_window_ensure_native (widget->window);
842 #endif
843 }
844
845 gboolean
846 on_drawingareaView_configure_event (GtkWidget * widget,
847     GdkEventConfigure * event, gpointer data)
848 {
849   gdk_window_move_resize (widget->window,
850       widget->allocation.x, widget->allocation.y,
851       widget->allocation.width, widget->allocation.height);
852   gdk_display_sync (gtk_widget_get_display (widget));
853
854   return TRUE;
855 }
856
857 void
858 on_comboboxResolution_changed (GtkComboBox * widget, gpointer user_data)
859 {
860   GstStructure *st;
861   gint w = 0, h = 0;
862   GstCaps *video_caps =
863       g_list_nth_data (video_caps_list, gtk_combo_box_get_active (widget));
864
865   if (video_caps) {
866     GstState old;
867
868     gst_element_get_state (gst_camera_bin, &old, NULL, GST_CLOCK_TIME_NONE);
869     GST_DEBUG ("change resolution in %s", gst_element_state_get_name (old));
870
871     if (old != GST_STATE_NULL) {
872       gst_element_set_state (gst_camera_bin, GST_STATE_READY);
873       /* source need to be NULL, otherwise changing the mode fails with device
874        * busy:
875        * - if src goes from NULL->PLAYING it sets new mode anyway
876        * - if src goes form READY->PLAYIN new mode is activated via reverse caps
877        *   negotiation, but then the device is already streaming
878        */
879       gst_element_set_state (gst_videosrc, GST_STATE_NULL);
880     }
881
882     st = gst_caps_get_structure (video_caps, 0);
883
884     gst_structure_get_int (st, "width", &w);
885     gst_structure_get_int (st, "height", &h);
886
887     if (w && h) {
888       g_object_set (ui_drawing_frame, "ratio", (gfloat) w / (gfloat) h, NULL);
889     }
890
891     g_object_set (G_OBJECT (gst_camera_bin), "filter-caps", video_caps, NULL);
892
893     if (old != GST_STATE_NULL) {
894       gst_element_set_state (gst_camera_bin, old);
895     }
896   }
897 }
898
899 void
900 on_radiobuttonImageCapture_toggled (GtkToggleButton * togglebutton,
901     gpointer user_data)
902 {
903   if (gtk_toggle_button_get_active (togglebutton)) {
904     if (capture_state != CAP_STATE_IMAGE) {
905       capture_mode_set_state (CAP_STATE_IMAGE);
906       capture_mode_config_gui ();
907     }
908   }
909 }
910
911 void
912 on_radiobuttonVideoCapture_toggled (GtkToggleButton * togglebutton,
913     gpointer user_data)
914 {
915   if (gtk_toggle_button_get_active (togglebutton)) {
916     if (capture_state == CAP_STATE_IMAGE) {
917       capture_mode_set_state (CAP_STATE_VIDEO_STOPED);
918       capture_mode_config_gui ();
919     }
920   }
921 }
922
923 static void
924 on_rbBntVidEff_toggled (GtkToggleButton * togglebutton, const gchar * effect)
925 {
926   if (gtk_toggle_button_get_active (togglebutton)) {
927     /* lets also use those effects to image */
928     video_post = effect;
929     image_post = effect;
930     capture_mode_stop ();
931
932     me_gst_cleanup_element ();
933     if (!me_gst_setup_pipeline (image_post, video_post))
934       gtk_main_quit ();
935     capture_mode_config_gui ();
936   }
937 }
938
939 void
940 on_rbBntVidEffNone_toggled (GtkToggleButton * togglebutton, gpointer data)
941 {
942   on_rbBntVidEff_toggled (togglebutton, NULL);
943 }
944
945 void
946 on_rbBntVidEffEdge_toggled (GtkToggleButton * togglebutton, gpointer data)
947 {
948   on_rbBntVidEff_toggled (togglebutton, "edgetv");
949 }
950
951 void
952 on_rbBntVidEffAging_toggled (GtkToggleButton * togglebutton, gpointer user_data)
953 {
954   on_rbBntVidEff_toggled (togglebutton, "agingtv");
955 }
956
957 void
958 on_rbBntVidEffDice_toggled (GtkToggleButton * togglebutton, gpointer user_data)
959 {
960   on_rbBntVidEff_toggled (togglebutton, "dicetv");
961 }
962
963 void
964 on_rbBntVidEffWarp_toggled (GtkToggleButton * togglebutton, gpointer data)
965 {
966   on_rbBntVidEff_toggled (togglebutton, "warptv");
967 }
968
969 void
970 on_rbBntVidEffShagadelic_toggled (GtkToggleButton * togglebutton, gpointer data)
971 {
972   on_rbBntVidEff_toggled (togglebutton, "shagadelictv");
973 }
974
975 void
976 on_rbBntVidEffVertigo_toggled (GtkToggleButton * togglebutton, gpointer data)
977 {
978   on_rbBntVidEff_toggled (togglebutton, "vertigotv");
979 }
980
981 void
982 on_rbBntVidEffRev_toggled (GtkToggleButton * togglebutton, gpointer data)
983 {
984   on_rbBntVidEff_toggled (togglebutton, "revtv");
985 }
986
987 void
988 on_rbBntVidEffQuark_toggled (GtkToggleButton * togglebutton, gpointer data)
989 {
990   on_rbBntVidEff_toggled (togglebutton, "quarktv");
991 }
992
993 void
994 on_chkbntMute_toggled (GtkToggleButton * togglebutton, gpointer data)
995 {
996   g_object_set (gst_camera_bin, "mute",
997       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton)), NULL);
998 }
999
1000 void
1001 on_chkbtnRawMsg_toggled (GtkToggleButton * togglebutton, gpointer data)
1002 {
1003   const gchar *env_var = "CAMSRC_PUBLISH_RAW";
1004   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton))) {
1005     g_setenv (env_var, "1", TRUE);
1006   } else {
1007     g_unsetenv (env_var);
1008   }
1009 }
1010
1011 void
1012 on_hscaleZoom_value_changed (GtkRange * range, gpointer user_data)
1013 {
1014   gint zoom = gtk_range_get_value (range);
1015   g_object_set (gst_camera_bin, "zoom", zoom, NULL);
1016 }
1017
1018 void
1019 on_color_control_value_changed (GtkRange * range, gpointer user_data)
1020 {
1021   GstColorBalance *balance = GST_COLOR_BALANCE (gst_camera_bin);
1022   gint val = gtk_range_get_value (range);
1023   GstColorBalanceChannel *channel = (GstColorBalanceChannel *) user_data;
1024   gst_color_balance_set_value (balance, channel, val);
1025 }
1026
1027
1028 gboolean
1029 on_key_released (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
1030 {
1031   g_return_val_if_fail (event != NULL, FALSE);
1032
1033   switch (event->keyval) {
1034     case GDK_F11:
1035 #ifdef HAVE_GST_PHOTO_IFACE_H
1036       gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), FALSE);
1037 #endif
1038       break;
1039     default:
1040       break;
1041   }
1042
1043   return FALSE;
1044 }
1045
1046 gboolean
1047 on_key_pressed (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
1048 {
1049   g_return_val_if_fail (event != NULL, FALSE);
1050
1051   switch (event->keyval) {
1052     case GDK_F11:
1053 #ifdef HAVE_GST_PHOTO_IFACE_H
1054       gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), TRUE);
1055 #endif
1056       break;
1057     case 0x0:
1058       on_buttonShot_clicked (NULL, NULL);
1059       break;
1060     default:
1061       break;
1062   }
1063
1064   return FALSE;
1065 }
1066
1067 static void
1068 ui_connect_signals (void)
1069 {
1070   gtk_builder_connect_signals (builder, NULL);
1071
1072   g_signal_connect (ui_main_window, "key-press-event",
1073       (GCallback) on_key_pressed, NULL);
1074
1075   g_signal_connect (ui_main_window, "key-release-event",
1076       (GCallback) on_key_released, NULL);
1077 }
1078
1079 static gchar *
1080 format_value_callback (GtkScale * scale, gdouble value, gpointer user_data)
1081 {
1082   GstColorBalanceChannel *channel = (GstColorBalanceChannel *) user_data;
1083
1084   return g_strdup_printf ("%s: %d", channel->label, (gint) value);
1085 }
1086
1087 static gint
1088 create_menu_items_from_structure (GstStructure * structure)
1089 {
1090   const GValue *framerate_list = NULL;
1091   const gchar *structure_name;
1092   GString *item_str = NULL;
1093   guint j, num_items_created = 0, num_framerates = 1;
1094   gint w = 0, h = 0, n = 0, d = 1;
1095   guint32 fourcc = 0;
1096
1097   g_return_val_if_fail (structure != NULL, 0);
1098
1099   structure_name = gst_structure_get_name (structure);
1100
1101   /* lets filter yuv only */
1102   if (0 == strcmp (structure_name, "video/x-raw-yuv")) {
1103     item_str = g_string_new_len ("", 128);
1104
1105     if (gst_structure_has_field_typed (structure, "format", GST_TYPE_FOURCC)) {
1106       gst_structure_get_fourcc (structure, "format", &fourcc);
1107     }
1108
1109     if (gst_structure_has_field_typed (structure, "width", GST_TYPE_INT_RANGE)) {
1110       const GValue *wrange = gst_structure_get_value (structure, "width");
1111       /* If range found, use the maximum */
1112       w = gst_value_get_int_range_max (wrange);
1113     } else if (gst_structure_has_field_typed (structure, "width", G_TYPE_INT)) {
1114       gst_structure_get_int (structure, "width", &w);
1115     }
1116
1117     if (gst_structure_has_field_typed (structure, "height", GST_TYPE_INT_RANGE)) {
1118       const GValue *hrange = gst_structure_get_value (structure, "height");
1119       /* If range found, use the maximum */
1120       h = gst_value_get_int_range_max (hrange);
1121     } else if (gst_structure_has_field_typed (structure, "height", G_TYPE_INT)) {
1122       gst_structure_get_int (structure, "height", &h);
1123     }
1124
1125     if (gst_structure_has_field_typed (structure, "framerate",
1126             GST_TYPE_FRACTION)) {
1127       gst_structure_get_fraction (structure, "framerate", &n, &d);
1128     } else if (gst_structure_has_field_typed (structure, "framerate",
1129             GST_TYPE_LIST)) {
1130       framerate_list = gst_structure_get_value (structure, "framerate");
1131       num_framerates = gst_value_list_get_size (framerate_list);
1132     } else if (gst_structure_has_field_typed (structure, "framerate",
1133             GST_TYPE_FRACTION_RANGE)) {
1134       const GValue *fr = gst_structure_get_value (structure, "framerate");
1135       const GValue *frmax = gst_value_get_fraction_range_max (fr);
1136       max_fr_n = gst_value_get_fraction_numerator (frmax);
1137       max_fr_d = gst_value_get_fraction_denominator (frmax);
1138     }
1139
1140     if (max_fr_n || max_fr_d) {
1141       goto range_found;
1142     }
1143
1144     for (j = 0; j < num_framerates; j++) {
1145       GstCaps *video_caps;
1146
1147       if (framerate_list) {
1148         const GValue *item = gst_value_list_get_value (framerate_list, j);
1149         n = gst_value_get_fraction_numerator (item);
1150         d = gst_value_get_fraction_denominator (item);
1151       }
1152       g_string_assign (item_str, structure_name);
1153       g_string_append_printf (item_str, " (%" GST_FOURCC_FORMAT ")",
1154           GST_FOURCC_ARGS (fourcc));
1155       g_string_append_printf (item_str, ", %dx%d at %d/%d", w, h, n, d);
1156       gtk_combo_box_append_text (ui_cbbox_resolution, item_str->str);
1157
1158       video_caps =
1159           gst_caps_new_simple (structure_name, "format", GST_TYPE_FOURCC,
1160           fourcc,
1161           "width", G_TYPE_INT, w, "height", G_TYPE_INT, h,
1162           "framerate", GST_TYPE_FRACTION, n, d, NULL);
1163       video_caps_list = g_list_append (video_caps_list, video_caps);
1164       num_items_created++;
1165     }
1166   }
1167
1168 range_found:
1169
1170   if (item_str) {
1171     g_string_free (item_str, TRUE);
1172   }
1173
1174   return num_items_created;
1175 }
1176
1177 static void
1178 fill_resolution_combo (GstCaps * caps)
1179 {
1180   guint size, num_items, i;
1181   GstStructure *st;
1182
1183   max_fr_n = max_fr_d = 0;
1184
1185   /* Create new items */
1186   size = gst_caps_get_size (caps);
1187
1188   for (i = 0; i < size; i++) {
1189     st = gst_caps_get_structure (caps, i);
1190     num_items = create_menu_items_from_structure (st);
1191     ui_cbbox_resolution_count += num_items;
1192   }
1193 }
1194
1195 static GstCaps *
1196 create_default_caps (void)
1197 {
1198   GstCaps *default_caps;
1199
1200   default_caps = gst_caps_from_string (DEFAULT_VF_CAPS);
1201
1202   return default_caps;
1203 }
1204
1205 static void
1206 init_view_finder_resolution_combobox (void)
1207 {
1208   GstCaps *input_caps = NULL, *default_caps = NULL, *intersect = NULL;
1209
1210   g_object_get (gst_camera_bin, "video-source-caps", &input_caps, NULL);
1211   if (input_caps) {
1212     fill_resolution_combo (input_caps);
1213   }
1214
1215   /* Fill in default items if supported */
1216   default_caps = create_default_caps ();
1217   intersect = gst_caps_intersect (default_caps, input_caps);
1218   if (intersect) {
1219     fill_resolution_combo (intersect);
1220     gst_caps_unref (intersect);
1221   }
1222   gst_caps_unref (default_caps);
1223
1224   if (input_caps) {
1225     gst_caps_unref (input_caps);
1226   }
1227
1228   /* Set some item active */
1229   gtk_combo_box_set_active (ui_cbbox_resolution, ui_cbbox_resolution_count - 1);
1230 }
1231
1232 static void
1233 destroy_color_controls (void)
1234 {
1235   GList *widgets, *item;
1236   GtkWidget *widget = NULL;
1237   gpointer user_data = NULL;
1238
1239   widgets = gtk_container_get_children (GTK_CONTAINER (ui_vbox_color_controls));
1240   for (item = widgets; item; item = g_list_next (item)) {
1241     widget = GTK_WIDGET (item->data);
1242     user_data = g_object_get_data (G_OBJECT (widget), "channel");
1243     g_signal_handlers_disconnect_by_func (widget, (GFunc) format_value_callback,
1244         user_data);
1245     g_signal_handlers_disconnect_by_func (widget,
1246         (GFunc) on_color_control_value_changed, user_data);
1247     gtk_container_remove (GTK_CONTAINER (ui_vbox_color_controls), widget);
1248   }
1249   g_list_free (widgets);
1250 }
1251
1252 static void
1253 create_color_controls (void)
1254 {
1255   GstColorBalance *balance = NULL;
1256   const GList *controls, *item;
1257   GstColorBalanceChannel *channel;
1258   GtkWidget *hscale;
1259
1260   if (GST_IS_COLOR_BALANCE (gst_camera_bin)) {
1261     balance = GST_COLOR_BALANCE (gst_camera_bin);
1262   }
1263
1264   if (NULL == balance) {
1265     goto done;
1266   }
1267
1268   controls = gst_color_balance_list_channels (balance);
1269   for (item = controls; item; item = g_list_next (item)) {
1270     channel = item->data;
1271
1272     hscale = gtk_hscale_new ((GtkAdjustment *)
1273         gtk_adjustment_new (gst_color_balance_get_value (balance, channel),
1274             channel->min_value, channel->max_value, 1, 10, 10));
1275
1276     g_signal_connect (GTK_RANGE (hscale), "value-changed",
1277         (GCallback) on_color_control_value_changed, (gpointer) channel);
1278     g_signal_connect (GTK_SCALE (hscale), "format-value",
1279         (GCallback) format_value_callback, (gpointer) channel);
1280     g_object_set_data (G_OBJECT (hscale), "channel", (gpointer) channel);
1281
1282     gtk_box_pack_start (GTK_BOX (ui_vbox_color_controls), GTK_WIDGET (hscale),
1283         FALSE, TRUE, 0);
1284   }
1285
1286   gtk_widget_show_all (ui_vbox_color_controls);
1287 done:
1288   return;
1289 }
1290
1291 #ifdef HAVE_GST_PHOTO_IFACE_H
1292 static void
1293 menuitem_toggle_active (GtkWidget * widget, gpointer data)
1294 {
1295   gboolean active;
1296   g_object_get (G_OBJECT (widget), "active", &active, NULL);
1297   if (active) {
1298     gtk_check_menu_item_toggled (GTK_CHECK_MENU_ITEM (widget));
1299   }
1300 }
1301
1302 static void
1303 sub_menu_initialize (GtkWidget * widget, gpointer data)
1304 {
1305   GtkWidget *submenu;
1306   submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
1307   gtk_container_foreach (GTK_CONTAINER (submenu), menuitem_toggle_active, NULL);
1308 }
1309
1310 void
1311 photo_menuitem_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
1312 {
1313   gboolean active = FALSE, ret = FALSE;
1314   GEnumClass *eclass = (GEnumClass *) user_data;
1315   GType etype = G_ENUM_CLASS_TYPE (eclass);
1316   GEnumValue *val;
1317   gint set_value = -1;
1318
1319   /* Get value using menu item name */
1320   val =
1321       g_enum_get_value_by_nick (eclass,
1322       gtk_widget_get_name (GTK_WIDGET (menuitem)));
1323
1324   g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
1325   if (active) {
1326     if (etype == GST_TYPE_WHITE_BALANCE_MODE) {
1327       GstWhiteBalanceMode mode;
1328       ret =
1329           gst_photography_set_white_balance_mode (GST_PHOTOGRAPHY
1330           (gst_camera_bin), val->value);
1331       gst_photography_get_white_balance_mode (GST_PHOTOGRAPHY (gst_camera_bin),
1332           &mode);
1333       set_value = (gint) mode;
1334     } else if (etype == GST_TYPE_SCENE_MODE) {
1335       GstSceneMode mode;
1336       ret =
1337           gst_photography_set_scene_mode (GST_PHOTOGRAPHY (gst_camera_bin),
1338           val->value);
1339       gst_photography_get_scene_mode (GST_PHOTOGRAPHY (gst_camera_bin), &mode);
1340       set_value = (gint) mode;
1341     } else if (etype == GST_TYPE_COLOUR_TONE_MODE) {
1342       GstColourToneMode mode;
1343       ret =
1344           gst_photography_set_colour_tone_mode (GST_PHOTOGRAPHY
1345           (gst_camera_bin), val->value);
1346       gst_photography_get_colour_tone_mode (GST_PHOTOGRAPHY (gst_camera_bin),
1347           &mode);
1348       set_value = (gint) mode;
1349     } else if (etype == GST_TYPE_FLASH_MODE) {
1350       GstFlashMode mode;
1351       ret =
1352           gst_photography_set_flash_mode (GST_PHOTOGRAPHY (gst_camera_bin),
1353           val->value);
1354       gst_photography_get_flash_mode (GST_PHOTOGRAPHY (gst_camera_bin), &mode);
1355       set_value = (gint) mode;
1356     }
1357
1358     if (!ret) {
1359       g_print ("%s setting failed\n", val->value_name);
1360     } else if (val->value != set_value) {
1361       g_print ("%s setting failed, got %d\n", val->value_nick, set_value);
1362     }
1363   }
1364 }
1365
1366 void
1367 photo_iso_speed_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
1368 {
1369   gboolean active;
1370   const gchar *name;
1371   guint val = 0, set_val = G_MAXUINT;
1372
1373   g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
1374   if (active) {
1375     name = gtk_widget_get_name (GTK_WIDGET (menuitem));
1376     /* iso auto setting = 0 */
1377     /* FIXME: check what values other than 0 can be set */
1378     if (!g_str_equal (name, "auto")) {
1379       sscanf (name, "%d", &val);
1380     }
1381     if (!gst_photography_set_iso_speed (GST_PHOTOGRAPHY (gst_camera_bin), val)) {
1382       g_print ("ISO speed (%d) setting failed\n", val);
1383     } else {
1384       gst_photography_get_iso_speed (GST_PHOTOGRAPHY (gst_camera_bin),
1385           &set_val);
1386       if (val != set_val) {
1387         g_print ("ISO speed (%d) setting failed, got %d\n", val, set_val);
1388       }
1389     }
1390   }
1391 }
1392
1393 void
1394 photo_ev_comp_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
1395 {
1396   gboolean active;
1397   const gchar *name;
1398   gfloat val = 0.0, set_val = G_MAXFLOAT;
1399
1400   g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
1401   if (active) {
1402     name = gtk_widget_get_name (GTK_WIDGET (menuitem));
1403     sscanf (name, "%f", &val);
1404     if (!gst_photography_set_ev_compensation (GST_PHOTOGRAPHY (gst_camera_bin),
1405             val)) {
1406       g_print ("EV compensation (%.1f) setting failed\n", val);
1407     } else {
1408       gst_photography_get_ev_compensation (GST_PHOTOGRAPHY (gst_camera_bin),
1409           &set_val);
1410       if (val != set_val) {
1411         g_print ("EV compensation (%.1f) setting failed, got %.1f\n", val,
1412             set_val);
1413       }
1414     }
1415   }
1416 }
1417
1418 static void
1419 photo_add_submenu_from_enum (GtkMenuItem * parent_item, GType enum_type)
1420 {
1421   GTypeClass *tclass;
1422   GEnumClass *eclass;
1423   GtkWidget *new_item = NULL, *new_submenu = NULL;
1424   guint i;
1425   GEnumValue *val;
1426   GSList *group = NULL;
1427
1428   g_return_if_fail (parent_item && enum_type && G_TYPE_IS_CLASSED (enum_type));
1429
1430   tclass = g_type_class_ref (enum_type);
1431   eclass = G_ENUM_CLASS (tclass);
1432   new_submenu = gtk_menu_new ();
1433
1434   for (i = 0; i < eclass->n_values; i++) {
1435     val = g_enum_get_value (eclass, i);
1436     new_item = gtk_radio_menu_item_new_with_label (group, val->value_nick);
1437     /* Store enum nick as the menu item name */
1438     gtk_widget_set_name (new_item, val->value_nick);
1439     group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (new_item));
1440     g_signal_connect (new_item, "toggled",
1441         (GCallback) photo_menuitem_toggled_cb, eclass);
1442     gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), new_item);
1443     gtk_widget_show (new_item);
1444   }
1445
1446   gtk_menu_item_set_submenu (parent_item, new_submenu);
1447   g_type_class_unref (tclass);
1448 }
1449
1450 static void
1451 add_submenu_from_list (GtkMenuItem * parent_item, GList * labels,
1452     GCallback toggled_cb)
1453 {
1454   GtkWidget *new_item = NULL, *new_submenu = NULL;
1455   GSList *group = NULL;
1456   GList *l;
1457
1458   new_submenu = gtk_menu_new ();
1459
1460   for (l = labels; l != NULL; l = g_list_next (l)) {
1461     const gchar *label = l->data;
1462     new_item = gtk_radio_menu_item_new_with_label (group, label);
1463     if (g_str_equal (label, "0")) {
1464       /* Let's set zero as default */
1465       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_item), TRUE);
1466     }
1467     gtk_widget_set_name (new_item, label);
1468     group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (new_item));
1469     g_signal_connect (new_item, "toggled", toggled_cb, NULL);
1470     gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), new_item);
1471     gtk_widget_show (new_item);
1472   }
1473
1474   gtk_menu_item_set_submenu (parent_item, new_submenu);
1475 }
1476
1477 static GtkMenuItem *
1478 add_menuitem (GtkMenu * parent_menu, const gchar * item_name)
1479 {
1480   GtkWidget *new_item;
1481
1482   new_item = gtk_menu_item_new_with_label (item_name);
1483   gtk_menu_shell_append (GTK_MENU_SHELL (parent_menu), new_item);
1484   gtk_widget_show (new_item);
1485
1486   return GTK_MENU_ITEM (new_item);
1487 }
1488
1489 GList *
1490 create_iso_speed_labels (void)
1491 {
1492   GList *labels = NULL;
1493   gint i;
1494   for (i = 0; i < G_N_ELEMENTS (iso_speed_labels); i++) {
1495     labels = g_list_append (labels, iso_speed_labels[i]);
1496   }
1497   return labels;
1498 }
1499
1500 GList *
1501 create_ev_comp_labels (void)
1502 {
1503   GList *labels = NULL;
1504   gdouble comp;
1505   char buf[G_ASCII_DTOSTR_BUF_SIZE];
1506
1507   for (comp = EV_COMP_MIN; comp <= EV_COMP_MAX; comp += EV_COMP_STEP) {
1508     g_ascii_dtostr (buf, sizeof (buf), comp);
1509     labels = g_list_append (labels, g_strdup (buf));
1510   }
1511   return labels;
1512 }
1513
1514 static void
1515 fill_photography_menu (GtkMenuItem * parent_item)
1516 {
1517   GtkWidget *photo_menu = gtk_menu_new ();
1518   GtkMenuItem *item = NULL;
1519   GList *labels = NULL;
1520
1521   /* Add menu items and create and associate submenus to each item */
1522   item = add_menuitem (GTK_MENU (photo_menu), "AWB");
1523   photo_add_submenu_from_enum (item, GST_TYPE_WHITE_BALANCE_MODE);
1524
1525   item = add_menuitem (GTK_MENU (photo_menu), "Colour Tone");
1526   photo_add_submenu_from_enum (item, GST_TYPE_COLOUR_TONE_MODE);
1527
1528   item = add_menuitem (GTK_MENU (photo_menu), "Scene");
1529   photo_add_submenu_from_enum (item, GST_TYPE_SCENE_MODE);
1530
1531   item = add_menuitem (GTK_MENU (photo_menu), "Flash");
1532   photo_add_submenu_from_enum (item, GST_TYPE_FLASH_MODE);
1533
1534   item = add_menuitem (GTK_MENU (photo_menu), "ISO");
1535   labels = create_iso_speed_labels ();
1536   add_submenu_from_list (item, labels, (GCallback) photo_iso_speed_toggled_cb);
1537   g_list_free (labels);
1538
1539   item = add_menuitem (GTK_MENU (photo_menu), "EV comp");
1540   labels = create_ev_comp_labels ();
1541   add_submenu_from_list (item, labels, (GCallback) photo_ev_comp_toggled_cb);
1542   g_list_free (labels);
1543
1544   gtk_menu_item_set_submenu (parent_item, photo_menu);
1545 }
1546
1547 void
1548 capture_image_res_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
1549 {
1550   gboolean active;
1551   const gchar *label;
1552   gint i;
1553
1554   g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
1555   if (active) {
1556     label = gtk_widget_get_name (GTK_WIDGET (menuitem));
1557     /* Look for width and height corresponding to the label */
1558     for (i = 0; i < G_N_ELEMENTS (image_resolution_label_map); i++) {
1559       if (g_str_equal (label, image_resolution_label_map[i].label)) {
1560         /* set found values */
1561         g_signal_emit_by_name (gst_camera_bin, "set-image-resolution",
1562             image_resolution_label_map[i].width,
1563             image_resolution_label_map[i].height, 0);
1564         break;
1565       }
1566     }
1567   }
1568 }
1569
1570 GList *
1571 create_image_resolution_labels (void)
1572 {
1573   GList *labels = NULL;
1574   int i;
1575   for (i = 0; i < G_N_ELEMENTS (image_resolution_label_map); i++) {
1576     labels = g_list_append (labels, image_resolution_label_map[i].label);
1577   }
1578   return labels;
1579 }
1580
1581 static void
1582 fill_capture_menu (GtkMenuItem * parent_item)
1583 {
1584   GtkWidget *capture_menu = gtk_menu_new ();
1585   GtkMenuItem *item = NULL;
1586   GList *labels = NULL;
1587
1588   /* Add menu items and create and associate submenus to each item */
1589   item = add_menuitem (GTK_MENU (capture_menu), "Image resolution");
1590
1591   labels = create_image_resolution_labels ();
1592   add_submenu_from_list (item, labels,
1593       (GCallback) capture_image_res_toggled_cb);
1594   g_list_free (labels);
1595
1596   gtk_menu_item_set_submenu (parent_item, capture_menu);
1597 }
1598 #endif /* HAVE_GST_PHOTO_IFACE_H */
1599
1600 static gboolean
1601 ui_create (void)
1602 {
1603   GError *error = NULL;
1604   const gchar *uifile = DEFAULT_UI_FILE;
1605
1606   if (!g_file_test (uifile, G_FILE_TEST_EXISTS)) {
1607     uifile = SHARED_UI_FILE;
1608   }
1609
1610   builder = gtk_builder_new ();
1611   if (!gtk_builder_add_from_file (builder, uifile, &error)) {
1612     g_warning ("Couldn't load builder file: %s", error->message);
1613     g_error_free (error);
1614     goto done;
1615   }
1616
1617   ui_main_window = GTK_WIDGET (gtk_builder_get_object (builder, "windowMain"));
1618   ui_drawing = GTK_WIDGET (gtk_builder_get_object (builder, "drawingareaView"));
1619   ui_drawing_frame =
1620       GTK_WIDGET (gtk_builder_get_object (builder, "drawingareaFrame"));
1621   ui_chk_continous =
1622       GTK_WIDGET (gtk_builder_get_object (builder, "chkbntContinous"));
1623   ui_chk_rawmsg = GTK_WIDGET (gtk_builder_get_object (builder, "chkbtnRawMsg"));
1624   ui_bnt_shot = GTK_BUTTON (gtk_builder_get_object (builder, "buttonShot"));
1625   ui_bnt_pause = GTK_BUTTON (gtk_builder_get_object (builder, "buttonPause"));
1626   ui_cbbox_resolution =
1627       GTK_COMBO_BOX (gtk_builder_get_object (builder, "comboboxResolution"));
1628   ui_chk_mute = GTK_WIDGET (gtk_builder_get_object (builder, "chkbntMute"));
1629   ui_vbox_color_controls =
1630       GTK_WIDGET (gtk_builder_get_object (builder, "vboxColorControls"));
1631   ui_rdbntImageCapture =
1632       GTK_WIDGET (gtk_builder_get_object (builder, "radiobuttonImageCapture"));
1633   ui_rdbntVideoCapture =
1634       GTK_WIDGET (gtk_builder_get_object (builder, "radiobuttonVideoCapture"));
1635   ui_menuitem_photography =
1636       GTK_WIDGET (gtk_builder_get_object (builder, "menuitemPhotography"));
1637   ui_menuitem_capture =
1638       GTK_WIDGET (gtk_builder_get_object (builder, "menuitemCapture"));
1639
1640 #ifdef HAVE_GST_PHOTO_IFACE_H
1641   if (ui_menuitem_photography) {
1642     fill_photography_menu (GTK_MENU_ITEM (ui_menuitem_photography));
1643   }
1644
1645   if (ui_menuitem_capture) {
1646     fill_capture_menu (GTK_MENU_ITEM (ui_menuitem_capture));
1647   }
1648 #endif
1649   if (!(ui_main_window && ui_drawing && ui_chk_continous && ui_bnt_shot &&
1650           ui_bnt_pause && ui_cbbox_resolution && ui_chk_mute &&
1651           ui_vbox_color_controls && ui_rdbntImageCapture &&
1652           ui_rdbntVideoCapture && ui_chk_rawmsg && ui_menuitem_photography &&
1653           ui_menuitem_capture)) {
1654     fprintf (stderr, "Some widgets couldn't be created\n");
1655     fflush (stderr);
1656     goto done;
1657   }
1658
1659   gtk_widget_set_double_buffered (ui_drawing, FALSE);
1660   ui_connect_signals ();
1661   gtk_widget_show_all (ui_main_window);
1662   capture_mode_config_gui ();
1663   return TRUE;
1664 done:
1665   return FALSE;
1666 }
1667
1668 /*
1669  * main
1670  */
1671
1672 int
1673 main (int argc, char *argv[])
1674 {
1675   int ret = 0;
1676
1677   gst_init (&argc, &argv);
1678   gtk_init (&argc, &argv);
1679
1680   filename = g_string_new_len ("", 16);
1681
1682   /* create UI */
1683   if (!ui_create ()) {
1684     ret = -1;
1685     goto done;
1686   }
1687   /* create pipeline and run */
1688   g_idle_add (me_gst_setup_default_pipeline, NULL);
1689   gtk_main ();
1690
1691 done:
1692   me_gst_cleanup_element ();
1693   g_string_free (filename, TRUE);
1694   return ret;
1695 }