cheese-camera: Use GstEncodingProfile to set image resolution
[gstreamer-omap:cheese.git] / libcheese / cheese-camera.c
1 /*
2  * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org>
3  * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org>
4  * Copyright © 2008 Ryan Zeigler <zeiglerr@gmail.com>
5  * Copyright © 2010 Yuvaraj Pandian T <yuvipanda@yuvi.in>
6  * Copyright © 2011 Luciana Fujii Pontello <luciana@fujii.eti.br>
7  * Copyright © 2011 Collabora Ltd
8  *  @author: Luciana Fujii Pontello <luciana.fujii@collabora.co.uk>
9  *
10  * Licensed under the GNU General Public License Version 2
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  */
25 #ifdef HAVE_CONFIG_H
26   #include <cheese-config.h>
27 #endif
28
29 #define GST_USE_UNSTABLE_API
30
31 #include <string.h>
32 #include <glib-object.h>
33 #include <glib.h>
34 #include <glib/gi18n.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
37 #include <gst/gst.h>
38 #include <gdk-pixbuf/gdk-pixbuf.h>
39 #include <X11/Xlib.h>
40 #include <gst/pbutils/encoding-profile.h>
41 #include <gst/interfaces/photography.h>
42 #include <gst/interfaces/colorbalance.h>
43
44 #include "cheese-camera.h"
45 #include "cheese-camera-device.h"
46 #include "cheese-camera-device-monitor.h"
47
48 G_DEFINE_TYPE (CheeseCamera, cheese_camera, G_TYPE_OBJECT)
49
50 #define CHEESE_CAMERA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHEESE_TYPE_CAMERA, CheeseCameraPrivate))
51
52 #define CHEESE_CAMERA_ERROR cheese_camera_error_quark ()
53
54 typedef enum
55 {
56   MODE_IMAGE = 1,
57   MODE_VIDEO
58 } GstCameraBinMode;
59
60 typedef enum {
61   GST_CAMERABIN_FLAG_SOURCE_RESIZE               = (1 << 0),
62   GST_CAMERABIN_FLAG_SOURCE_COLOR_CONVERSION     = (1 << 1),
63   GST_CAMERABIN_FLAG_VIEWFINDER_COLOR_CONVERSION = (1 << 2),
64   GST_CAMERABIN_FLAG_VIEWFINDER_SCALE            = (1 << 3),
65   GST_CAMERABIN_FLAG_AUDIO_CONVERSION            = (1 << 4),
66   GST_CAMERABIN_FLAG_DISABLE_AUDIO               = (1 << 5),
67   GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION      = (1 << 6),
68   GST_CAMERABIN_FLAG_VIDEO_COLOR_CONVERSION      = (1 << 7)
69 } GstCameraBinFlags;
70
71
72 typedef struct
73 {
74   GtkWidget *video_window;
75
76   GstBus *bus;
77
78   GstElement *camerabin;
79
80   GstElement *video_source;
81   GstElement *camera_src;
82   GstElement *photography;
83   GstElement *balance;
84
85   gint saturation;
86
87   GHashTable *balance_table;
88
89   gboolean is_recording;
90   gboolean pipeline_is_playing;
91   char *photo_filename;
92
93   int num_camera_devices;
94   char *device_name;
95
96   /* an array of CheeseCameraDevices */
97   GPtrArray *camera_devices;
98   int selected_device;
99   CheeseVideoFormat *photo_format;
100   CheeseVideoFormat *video_format;
101
102   CheeseMediaMode current_mode;
103
104   guint eos_timeout_id;
105 } CheeseCameraPrivate;
106
107 enum
108 {
109   PROP_0,
110   PROP_VIDEO_WINDOW,
111   PROP_DEVICE_NAME,
112   PROP_PHOTO_FORMAT,
113   PROP_VIDEO_FORMAT
114 };
115
116 enum
117 {
118   PHOTO_SAVED,
119   PHOTO_TAKEN,
120   VIDEO_SAVED,
121   LAST_SIGNAL
122 };
123
124 static guint camera_signals[LAST_SIGNAL];
125
126 typedef enum
127 {
128   RGB,
129   YUV
130 } VideoColorSpace;
131
132 typedef struct
133 {
134   CheeseCameraEffect effect;
135   const char *pipeline_desc;
136   VideoColorSpace colorspace; /* The color space the effect works in */
137 } EffectToPipelineDesc;
138
139
140 static const EffectToPipelineDesc EFFECT_TO_PIPELINE_DESC[] = {
141   {CHEESE_CAMERA_EFFECT_NO_EFFECT,       "identity",                               RGB},
142   {CHEESE_CAMERA_EFFECT_MAUVE,           "! videobalance saturation=1.5 hue=+0.5 ", YUV},
143   {CHEESE_CAMERA_EFFECT_HULK,            "! videobalance saturation=1.5 hue=-0.5 ", YUV},
144   {CHEESE_CAMERA_EFFECT_VERTICAL_FLIP,   "! videoflip method=5 ",                   YUV},
145   {CHEESE_CAMERA_EFFECT_NOIR_BLANC,      " ",                                      YUV},
146   {CHEESE_CAMERA_EFFECT_SATURATION,      " ",                                      YUV},
147   {CHEESE_CAMERA_EFFECT_HORIZONTAL_FLIP, "! videoflip method=4 ",                   YUV},
148   {CHEESE_CAMERA_EFFECT_SHAGADELIC,      "shagadelictv",                           RGB},
149   {CHEESE_CAMERA_EFFECT_VERTIGO,         "vertigotv",                              RGB},
150   {CHEESE_CAMERA_EFFECT_EDGE,            "edgetv",                                 RGB},
151   {CHEESE_CAMERA_EFFECT_DICE,            "dicetv",                                 RGB},
152   {CHEESE_CAMERA_EFFECT_WARP,            "warptv",                                 RGB}
153 };
154
155 static const int NUM_EFFECTS = G_N_ELEMENTS (EFFECT_TO_PIPELINE_DESC);
156
157 GST_DEBUG_CATEGORY (cheese_camera_cat);
158 #define GST_CAT_DEFAULT cheese_camera_cat
159
160 GQuark
161 cheese_camera_error_quark (void)
162 {
163   return g_quark_from_static_string ("cheese-camera-error-quark");
164 }
165
166 static GstBusSyncReply
167 cheese_camera_bus_sync_handler (GstBus *bus, GstMessage *message, CheeseCamera *camera)
168 {
169   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
170   GstXOverlay         *overlay;
171
172   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
173     return GST_BUS_PASS;
174
175   if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
176     return GST_BUS_PASS;
177
178   overlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
179
180   if (g_object_class_find_property (G_OBJECT_GET_CLASS (overlay),
181                                     "force-aspect-ratio"))
182     g_object_set (G_OBJECT (overlay), "force-aspect-ratio", TRUE, NULL);
183
184   gdk_threads_enter();
185   gst_x_overlay_set_xwindow_id (overlay,
186                                 GDK_WINDOW_XWINDOW (gtk_widget_get_window (priv->video_window)));
187   gdk_threads_leave();
188
189   gst_message_unref (message);
190
191   return GST_BUS_DROP;
192 }
193
194 static gboolean
195 cheese_camera_expose_cb (GtkWidget *widget, GdkEventExpose *event, CheeseCamera *camera)
196 {
197   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
198   GtkAllocation        allocation;
199   GstState             state;
200   GstXOverlay         *overlay = GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (priv->camerabin),
201                                                                           GST_TYPE_X_OVERLAY));
202
203   gst_element_get_state (priv->camerabin, &state, NULL, 0);
204
205   if ((state < GST_STATE_PLAYING) || (overlay == NULL))
206   {
207     gtk_widget_get_allocation (widget, &allocation);
208     gdk_draw_rectangle (gtk_widget_get_window (widget),
209                         gtk_widget_get_style (widget)->black_gc, TRUE,
210                         0, 0, allocation.width, allocation.height);
211   }
212   else
213   {
214     gst_x_overlay_expose (overlay);
215   }
216
217   return FALSE;
218 }
219
220 static void
221 cheese_camera_photo_data (CheeseCamera *camera, GstBuffer *buffer)
222 {
223   GstCaps            *caps;
224   const GstStructure *structure;
225   int                 width, height, stride;
226   GdkPixbuf          *pixbuf;
227   const int           bits_per_pixel = 8;
228   guchar             *data = NULL;
229   CheeseCameraPrivate *priv  = CHEESE_CAMERA_GET_PRIVATE (camera);
230
231   caps = gst_buffer_get_caps (buffer);
232   structure = gst_caps_get_structure (caps, 0);
233   gst_structure_get_int (structure, "width", &width);
234   gst_structure_get_int (structure, "height", &height);
235
236   stride = buffer->size / height;
237
238   data = g_memdup (GST_BUFFER_DATA (buffer), buffer->size);
239   pixbuf = gdk_pixbuf_new_from_data (data ? data : GST_BUFFER_DATA (buffer),
240                                      GDK_COLORSPACE_RGB,
241                                      FALSE, bits_per_pixel, width, height, stride,
242                                      data ? (GdkPixbufDestroyNotify) g_free : NULL, NULL);
243
244   g_object_set (G_OBJECT (priv->camerabin), "post-previews", FALSE, NULL);
245   g_signal_emit (camera, camera_signals[PHOTO_TAKEN], 0, pixbuf);
246   g_object_unref (pixbuf);
247 }
248
249 static void
250 cheese_camera_bus_message_cb (GstBus *bus, GstMessage *message, CheeseCamera *camera)
251 {
252   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
253
254   switch (GST_MESSAGE_TYPE (message))
255   {
256     case GST_MESSAGE_WARNING:
257     {
258       GError *err = NULL;
259       gchar *debug = NULL;
260       gst_message_parse_warning (message, &err, &debug);
261
262       if (err && err->message) {
263         g_warning ("%s: %s\n", err->message, debug);
264         g_error_free (err);
265       } else {
266         g_warning ("Unparsable GST_MESSAGE_WARNING message.\n");
267       }
268
269       g_free (debug);
270       break;
271     }
272     case GST_MESSAGE_ERROR:
273     {
274       GError *err = NULL;
275       gchar *debug = NULL;
276       gst_message_parse_error (message, &err, &debug);
277
278       if (err && err->message) {
279         g_warning ("%s: %s\n", err->message, debug);
280         g_error_free (err);
281       } else {
282         g_warning ("Unparsable GST_MESSAGE_ERROR message.\n");
283       }
284
285       g_free (debug);
286       break;
287     }
288     case GST_MESSAGE_STATE_CHANGED:
289     {
290       if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin2") == 0)
291       {
292         GstState old, new;
293         gst_message_parse_state_changed (message, &old, &new, NULL);
294       }
295       break;
296     }
297     case GST_MESSAGE_ELEMENT:
298     {
299       const GstStructure *structure;
300       GstBuffer *buffer;
301       const GValue *image;
302       const gchar *filename = NULL;
303
304       if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin2") == 0)
305       {
306         structure = gst_message_get_structure (message);
307         if (strcmp (gst_structure_get_name (structure), "preview-image") == 0)
308         {
309           if (gst_structure_has_field_typed (structure, "buffer", GST_TYPE_BUFFER))
310           {
311             image = gst_structure_get_value (structure, "buffer");
312             if (image)
313             {
314               buffer = gst_value_get_buffer (image);
315               cheese_camera_photo_data (camera, buffer);
316             }
317             else
318             {
319               g_warning ("Could not get buffer from bus message");
320             }
321           }
322         }
323         if (strcmp (gst_structure_get_name (structure), "image-done") == 0)
324         {
325           if (gst_structure_has_field_typed (structure, "filename", G_TYPE_STRING))
326           {
327             filename = gst_structure_get_string (structure, "filename");
328             if (filename != NULL && (strcmp (priv->photo_filename, filename) == 0))
329             {
330                 g_signal_emit_by_name (camera, "photo-saved", 0);
331             }
332           }
333
334         }
335       }
336       break;
337     }
338     default:
339     {
340       break;
341     }
342   }
343 }
344
345 static gboolean
346 cheese_camera_set_colorbalance (CheeseCamera *camera)
347 {
348   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
349   GstColorBalance *balance;
350   const GList *controls;
351   const GList *item;
352   GstColorBalanceChannel *channel;
353   GString *label;
354
355   balance = GST_COLOR_BALANCE (priv->balance);
356
357   if (NULL == balance)
358     return FALSE;
359
360   if (priv->balance_table != NULL)
361     g_hash_table_destroy (priv->balance_table);
362   priv->balance_table = g_hash_table_new ((GHashFunc) g_string_hash,
363                                           (GEqualFunc) g_string_equal);
364
365   controls = gst_color_balance_list_channels (balance);
366   for (item = controls; item; item = g_list_next (item))
367   {
368     channel = (GstColorBalanceChannel *)item->data;
369     label = g_string_new (channel->label);
370     g_hash_table_insert (priv->balance_table,
371                          g_string_ascii_down (label),
372                          channel);
373   }
374   gst_object_unref (balance);
375
376   return TRUE;
377 }
378
379 static void
380 cheese_camera_add_device (CheeseCameraDeviceMonitor *monitor,
381                           const gchar               *id,
382                           const gchar               *device_file,
383                           const gchar               *product_name,
384                           gint                       api_version,
385                           CheeseCamera              *camera)
386 {
387   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
388   GError *error = NULL;
389
390   CheeseCameraDevice *device = cheese_camera_device_new (id,
391                                                          device_file,
392                                                          product_name,
393                                                          api_version,
394                                                          &error);
395   if (device == NULL)
396     GST_WARNING ("Device initialization for %s failed: %s ",
397                  device_file,
398                  (error != NULL) ? error->message : "Unknown reason");
399   else  {
400     g_ptr_array_add (priv->camera_devices, device);
401     priv->num_camera_devices++;
402   }
403 }
404
405 static void
406 cheese_camera_detect_camera_devices (CheeseCamera *camera)
407 {
408   CheeseCameraPrivate       *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
409   CheeseCameraDeviceMonitor *monitor;
410
411   priv->num_camera_devices = 0;
412   priv->camera_devices     = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
413
414   monitor = cheese_camera_device_monitor_new ();
415   g_signal_connect (G_OBJECT (monitor), "added",
416                     G_CALLBACK (cheese_camera_add_device), camera);
417   cheese_camera_device_monitor_coldplug (monitor);
418   g_object_unref (monitor);
419 }
420
421 static void
422 cheese_camera_set_error_element_not_found (GError **error, const char *factoryname)
423 {
424   if (error == NULL)
425     return;
426
427   if (*error == NULL)
428   {
429     g_set_error (error, CHEESE_CAMERA_ERROR, CHEESE_CAMERA_ERROR_ELEMENT_NOT_FOUND, "%s.", factoryname);
430   }
431   else
432   {
433     /* Ensure that what is found is not a substring of an element; all strings
434      * should have a ' ' or nothing to the left and a '.' or ',' to the right */
435     gchar *found = g_strrstr ((*error)->message, factoryname);
436     gchar  prev  = 0;
437     gchar  next  = 0;
438
439     if (found != NULL)
440     {
441       prev = *(found - 1);
442       next = *(found + strlen (factoryname));
443     }
444
445     if (found == NULL ||
446         ((found != (*error)->message && prev != ' ') && /* Prefix check */
447          (next != ',' && next != '.'))) /* Postfix check */
448     {
449       g_prefix_error (error, "%s, ", factoryname);
450     }
451   }
452 }
453
454 static gboolean
455 cheese_camera_set_camera_source (CheeseCamera *camera, GError **err)
456 {
457   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
458
459   char *camera_input;
460   int i;
461   CheeseCameraDevice *selected_camera;
462
463   /* If we have a matching video device use that one, otherwise use the first */
464   priv->selected_device = 0;
465   selected_camera       = g_ptr_array_index (priv->camera_devices, 0);
466
467   for (i = 1; i < priv->num_camera_devices; i++)
468   {
469     CheeseCameraDevice *device = g_ptr_array_index (priv->camera_devices, i);
470     if (g_strcmp0 (cheese_camera_device_get_device_file (device),
471                    priv->device_name) == 0)
472     {
473       selected_camera       = device;
474       priv->selected_device = i;
475       break;
476     }
477   }
478
479   if (g_strcmp0 (cheese_camera_device_get_src (selected_camera), "omx_camera"))
480   {
481     /* Use wrappercamerabinsrc + v4l(2) camera */
482     if ((priv->camera_src = gst_element_factory_make ("wrappercamerabinsrc", "camerasrc")) == NULL)
483     {
484       cheese_camera_set_error_element_not_found (err, "wrappercamerabinsrc");
485     }
486     camera_input = g_strdup_printf (
487         "%s name=video_source device=%s",
488         cheese_camera_device_get_src (selected_camera),
489         cheese_camera_device_get_device_file (selected_camera));
490     if (priv->video_source != NULL)
491     {
492       g_object_unref (priv->video_source);
493       priv->video_source = NULL;
494     }
495     priv->video_source = gst_parse_bin_from_description (camera_input, TRUE, err);
496     g_free (camera_input);
497     if (priv->video_source == NULL)
498     {
499       return FALSE;
500     }
501     g_object_set (priv->camera_src, "video-src", priv->video_source, NULL);
502   }
503   else
504   {
505     /* Use omx_camera */
506     if ((priv->camera_src = gst_element_factory_make ("omxcamerabinsrc", "camerasrc")) == NULL)
507     {
508       cheese_camera_set_error_element_not_found (err, "omxcamerabinsrc");
509     }
510     gst_element_set_state (priv->camera_src, GST_STATE_READY);
511     if (priv->video_source != NULL)
512     {
513       g_object_unref (priv->video_source);
514       priv->video_source = NULL;
515     }
516   }
517
518   g_object_set (priv->camerabin, "camera-src", priv->camera_src, NULL);
519
520   if (priv->photography != NULL)
521   {
522     g_object_unref (priv->photography);
523     priv->photography = NULL;
524   }
525
526   priv->photography = gst_bin_get_by_interface (GST_BIN (priv->camera_src),
527                                                 GST_TYPE_PHOTOGRAPHY);
528   if (priv->photography == NULL)
529     g_warning ("Source doesn't implement GstPhotography");
530
531   if (priv->balance != NULL)
532   {
533     g_object_unref (priv->balance);
534     priv->balance = NULL;
535   }
536   priv->balance = gst_bin_get_by_interface (GST_BIN (priv->camera_src),
537                                             GST_TYPE_COLOR_BALANCE);
538   if (priv->balance == NULL)
539     g_warning ("Source doesn't implement GstColorBalance");
540
541   return TRUE;
542 }
543
544 static void
545 cheese_camera_set_video_recording (CheeseCamera *camera, GError **error)
546 {
547   GstEncodingContainerProfile *prof;
548   GstEncodingVideoProfile *video_prof;
549   GstCaps *caps;
550   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
551
552   caps = gst_caps_from_string ("video/quicktime, variant=apple");
553   prof = gst_encoding_container_profile_new ("mov", "mov+h264",
554       caps, NULL);
555   gst_caps_unref (caps);
556
557   caps = gst_caps_new_simple ("video/x-h264", NULL);
558   video_prof = gst_encoding_video_profile_new (caps, NULL, NULL, 1);
559   gst_encoding_video_profile_set_variableframerate (video_prof, TRUE);
560   if (!gst_encoding_container_profile_add_profile (prof,
561           (GstEncodingProfile *) video_prof)) {
562     GST_WARNING_OBJECT (camera, "Failed to create encoding profiles");
563   }
564   gst_caps_unref (caps);
565
566   g_object_set (priv->camerabin, "video-profile", prof, NULL);
567 }
568
569 int
570 cheese_camera_get_num_camera_devices (CheeseCamera *camera)
571 {
572   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
573
574   return priv->num_camera_devices;
575 }
576
577 CheeseCameraDevice *
578 cheese_camera_get_selected_device (CheeseCamera *camera)
579 {
580   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
581
582   if (cheese_camera_get_num_camera_devices (camera) > 0)
583     return CHEESE_CAMERA_DEVICE (
584              g_ptr_array_index (priv->camera_devices, priv->selected_device));
585   else
586     return NULL;
587 }
588
589 gboolean
590 cheese_camera_switch_camera_device (CheeseCamera *camera)
591 {
592   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
593
594   GError *error;
595   gboolean was_recording        = FALSE;
596   gboolean pipeline_was_playing = FALSE;
597   gboolean source_set           = FALSE;
598
599   if (priv->is_recording)
600   {
601     cheese_camera_stop_video_recording (camera);
602     was_recording = TRUE;
603   }
604
605   if (priv->pipeline_is_playing)
606   {
607     cheese_camera_stop (camera);
608     pipeline_was_playing = TRUE;
609   }
610
611   source_set = cheese_camera_set_camera_source (camera, &error);
612   if (!source_set)
613       return FALSE;
614
615   if (pipeline_was_playing)
616   {
617     cheese_camera_play (camera);
618   }
619
620   /* if (was_recording)
621    * {
622    * Restart recording... ?
623    * } */
624
625   return TRUE;
626 }
627
628 void
629 cheese_camera_set_photo_resolution (CheeseCamera *camera)
630 {
631   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
632   GstCaps *encoding_caps;
633   GstCaps *restriction_caps;
634   GstEncodingVideoProfile *image_profile;
635
636   encoding_caps = gst_caps_from_string ("image/jpeg");
637   restriction_caps = gst_caps_new_simple ("video/x-raw-yuv",
638       "width", G_TYPE_INT, priv->photo_format->width,
639       "height", G_TYPE_INT, priv->photo_format->height,
640       NULL);
641   image_profile = gst_encoding_video_profile_new (encoding_caps, NULL,
642                                                   restriction_caps, 1);
643   gst_caps_unref (encoding_caps);
644   gst_caps_unref (restriction_caps);
645
646   g_object_set (priv->camerabin, "image-profile", image_profile, NULL);
647 }
648
649 void
650 cheese_camera_play (CheeseCamera *camera)
651 {
652   CheeseCameraPrivate *priv   = CHEESE_CAMERA_GET_PRIVATE (camera);
653   CheeseCameraDevice  *device = g_ptr_array_index (priv->camera_devices, priv->selected_device);
654   GstCaps             *caps;
655
656   caps = cheese_camera_device_get_caps_for_format (device, priv->video_format);
657
658   if (gst_caps_is_empty (caps))
659   {
660     gst_caps_unref (caps);
661     g_boxed_free (CHEESE_TYPE_VIDEO_FORMAT, priv->video_format);
662     priv->video_format = cheese_camera_device_get_best_format (device);
663     g_object_notify (G_OBJECT (camera), "format");
664     caps = cheese_camera_device_get_caps_for_format (device, priv->video_format);
665   }
666
667   /* Viewfinder is set to video resolution */
668
669   if (!gst_caps_is_empty (caps))
670   {
671     g_object_set (priv->camerabin,
672         "viewfinder-caps", caps,
673         "video-capture-caps", caps, NULL);
674   }
675   gst_caps_unref (caps);
676
677   cheese_camera_set_photo_resolution (camera);
678
679   gst_element_set_state (priv->camerabin, GST_STATE_PLAYING);
680   priv->pipeline_is_playing = TRUE;
681 }
682
683 void
684 cheese_camera_stop (CheeseCamera *camera)
685 {
686   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
687
688   if (priv->camerabin != NULL)
689     gst_element_set_state (priv->camerabin, GST_STATE_NULL);
690   priv->pipeline_is_playing = FALSE;
691 }
692
693 static void
694 cheese_camera_change_effect_filter (CheeseCamera *camera,
695     GstElement *video_filter, GstElement *image_filter)
696 {
697   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
698   gboolean is_playing = priv->pipeline_is_playing;
699
700   cheese_camera_stop (camera);
701
702   g_object_set (G_OBJECT (priv->camera_src), "source-filter", video_filter,
703       "allocate-buffers", video_filter == NULL ? FALSE : TRUE,
704       NULL);
705   g_object_set (G_OBJECT (priv->camerabin), "image-filter", image_filter, NULL);
706
707   if (is_playing)
708     cheese_camera_play (camera);
709 }
710
711 void
712 cheese_camera_set_effect (CheeseCamera *camera, CheeseCameraEffect effect)
713 {
714   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
715   GString    *rgb_effects_str = g_string_new ("");
716   GString    *yuv_effects_str = g_string_new ("");
717   char       *effects_pipeline_desc = NULL;
718   int         i;
719   GstElement *video_filter = NULL, *image_filter = NULL;
720   GError     *err = NULL;
721
722   if (effect == CHEESE_CAMERA_EFFECT_NO_EFFECT)
723     goto done;
724
725   for (i = 0; i < NUM_EFFECTS; i++)
726   {
727     if (effect & EFFECT_TO_PIPELINE_DESC[i].effect)
728     {
729       if (EFFECT_TO_PIPELINE_DESC[i].colorspace == RGB)
730       {
731         g_string_append (rgb_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
732         g_string_append (rgb_effects_str, " ! ");
733       }
734       else
735       {
736         g_string_append (yuv_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
737       }
738     }
739   }
740
741   if (!*rgb_effects_str->str && !*yuv_effects_str->str)
742     goto done;
743
744   effects_pipeline_desc = g_strconcat ("capsfilter caps=video/x-raw-yuv-strided,rowstride=4096 ! "
745                                         "stridetransform ! capsfilter caps=video/x-raw-yuv ! "
746                                         "ffmpegcolorspace ! ",
747                                         rgb_effects_str->str,
748                                         " ffmpegcolorspace ",
749                                         yuv_effects_str->str,
750                                         " ! ffmpegcolorspace ! capsfilter caps=video/x-raw-yuv ! "
751                                         "stridetransform ! "
752                                         "capsfilter caps=video/x-raw-yuv-strided,rowstride=4096",
753                                         NULL);
754
755   video_filter = gst_parse_bin_from_description (effects_pipeline_desc, TRUE, &err);
756
757   if (!video_filter || (err != NULL))
758   {
759     g_error ("ERROR video_filter: %s\n", err->message);
760     g_error_free (err);
761   }
762
763   g_free (effects_pipeline_desc);
764   effects_pipeline_desc = g_strconcat ("jpegdec ! "
765                                         "ffmpegcolorspace ! ",
766                                         rgb_effects_str->str,
767                                         " ffmpegcolorspace ",
768                                         yuv_effects_str->str,
769                                         " ! ffmpegcolorspace ! capsfilter caps=video/x-raw-yuv ! "
770                                         "stridetransform ! "
771                                         "capsfilter caps=video/x-raw-yuv",
772                                         NULL);
773
774   image_filter = gst_parse_bin_from_description (effects_pipeline_desc, TRUE, &err);
775   g_free (effects_pipeline_desc);
776
777   if (!image_filter || (err != NULL))
778   {
779     g_error ("ERROR image_filter: %s\n", err->message);
780     g_error_free (err);
781   }
782
783 done:
784   cheese_camera_set_balance_property (camera, "saturation", priv->saturation);
785   cheese_camera_change_effect_filter (camera, video_filter, image_filter);
786
787   if (effect & CHEESE_CAMERA_EFFECT_NOIR_BLANC)
788     cheese_camera_set_balance_property (camera, "saturation", -100);
789   else if (effect & CHEESE_CAMERA_EFFECT_SATURATION)
790     cheese_camera_set_balance_property (camera, "saturation", 100);
791
792   g_string_free (rgb_effects_str, TRUE);
793   g_string_free (yuv_effects_str, TRUE);
794 }
795
796 void
797 cheese_camera_start_video_recording (CheeseCamera *camera, char *filename)
798 {
799   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
800
801   g_object_set (priv->camerabin, "mode", MODE_VIDEO, NULL);
802   g_object_set (priv->camerabin, "location", filename, NULL);
803   g_signal_emit_by_name (priv->camerabin, "start-capture", 0);
804   priv->is_recording = TRUE;
805 }
806
807 static gboolean
808 cheese_camera_force_stop_video_recording (gpointer data)
809 {
810   CheeseCamera        *camera = CHEESE_CAMERA (data);
811   CheeseCameraPrivate *priv   = CHEESE_CAMERA_GET_PRIVATE (camera);
812
813   if (priv->is_recording)
814   {
815     GST_WARNING ("Cannot cleanly shutdown recording pipeline, forcing");
816     g_signal_emit (camera, camera_signals[VIDEO_SAVED], 0);
817
818     cheese_camera_stop (camera);
819     g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
820     cheese_camera_play (camera);
821     priv->is_recording = FALSE;
822   }
823
824   return FALSE;
825 }
826
827 void
828 cheese_camera_stop_video_recording (CheeseCamera *camera)
829 {
830   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
831   GstState             state;
832
833   gst_element_get_state (priv->camerabin, &state, NULL, 0);
834
835   if (state == GST_STATE_PLAYING)
836   {
837     g_signal_emit_by_name (priv->camerabin, "stop-capture", 0);
838     g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
839     g_signal_emit (camera, camera_signals[VIDEO_SAVED], 0);
840     priv->is_recording = FALSE;
841   }
842   else
843   {
844     cheese_camera_force_stop_video_recording (camera);
845   }
846 }
847
848 gboolean
849 cheese_camera_take_photo (CheeseCamera *camera, char *filename)
850 {
851   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
852   gboolean ready;
853
854   g_object_get (priv->camera_src, "ready-for-capture", &ready, NULL);
855   if (!ready)
856   {
857     GST_WARNING ("Still waiting for previous photo data, ignoring new request");
858     return FALSE;
859   }
860
861   if (priv->photo_filename)
862     g_free (priv->photo_filename);
863   priv->photo_filename = g_strdup (filename);
864
865   /* Take the photo*/
866
867   /* Only copy the data if we're giving away a pixbuf,
868    * not if we're throwing everything away straight away */
869
870   if (priv->photo_filename != NULL)
871   {
872     g_object_set (priv->camerabin, "location", priv->photo_filename, NULL);
873     g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
874     g_signal_emit_by_name (priv->camerabin, "start-capture", 0);
875   }
876   else
877   {
878     return FALSE;
879   }
880
881   return TRUE;
882 }
883
884 gboolean
885 cheese_camera_take_photo_pixbuf (CheeseCamera *camera)
886 {
887   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
888   GstCaps             *caps;
889   gboolean ready;
890
891   g_object_get (priv->camera_src, "ready-for-capture", &ready, NULL);
892   if (!ready)
893   {
894     GST_WARNING ("Still waiting for previous photo data, ignoring new request");
895     return FALSE;
896   }
897   caps = gst_caps_new_simple ("video/x-raw-rgb",
898                               "bpp", G_TYPE_INT, 24,
899                               "depth", G_TYPE_INT, 24,
900                               NULL);
901   g_object_set (G_OBJECT (priv->camerabin), "post-previews", TRUE, NULL);
902   g_object_set (G_OBJECT (priv->camerabin), "preview-caps", caps, NULL);
903   gst_caps_unref (caps);
904
905   if (priv->photo_filename)
906     g_free (priv->photo_filename);
907   priv->photo_filename = NULL;
908
909   /* Take the photo */
910
911   g_object_set (priv->camerabin, "location", "/dev/null", NULL);
912   g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
913   g_signal_emit_by_name (priv->camerabin, "start-capture", 0);
914
915   return TRUE;
916 }
917
918 static void
919 cheese_camera_finalize (GObject *object)
920 {
921   CheeseCamera *camera;
922
923   camera = CHEESE_CAMERA (object);
924   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
925
926   cheese_camera_stop (camera);
927
928   if (priv->camerabin != NULL)
929     gst_object_unref (priv->camerabin);
930
931   if (priv->photo_filename)
932     g_free (priv->photo_filename);
933   g_free (priv->device_name);
934   g_boxed_free (CHEESE_TYPE_VIDEO_FORMAT, priv->photo_format);
935   g_boxed_free (CHEESE_TYPE_VIDEO_FORMAT, priv->video_format);
936
937   /* Free CheeseCameraDevice array */
938   g_ptr_array_free (priv->camera_devices, TRUE);
939
940   G_OBJECT_CLASS (cheese_camera_parent_class)->finalize (object);
941 }
942
943 static void
944 cheese_camera_get_property (GObject *object, guint prop_id, GValue *value,
945                             GParamSpec *pspec)
946 {
947   CheeseCamera *self;
948
949   self = CHEESE_CAMERA (object);
950   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (self);
951
952   switch (prop_id)
953   {
954     case PROP_VIDEO_WINDOW:
955       g_value_set_pointer (value, priv->video_window);
956       break;
957     case PROP_DEVICE_NAME:
958       g_value_set_string (value, priv->device_name);
959       break;
960     case PROP_PHOTO_FORMAT:
961       g_value_set_boxed (value, priv->photo_format);
962       break;
963     case PROP_VIDEO_FORMAT:
964       g_value_set_boxed (value, priv->video_format);
965       break;
966     default:
967       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
968       break;
969   }
970 }
971
972 static void
973 cheese_camera_set_property (GObject *object, guint prop_id, const GValue *value,
974                             GParamSpec *pspec)
975 {
976   CheeseCamera *self;
977
978   self = CHEESE_CAMERA (object);
979   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (self);
980
981   switch (prop_id)
982   {
983     case PROP_VIDEO_WINDOW:
984       priv->video_window = g_value_get_pointer (value);
985       g_signal_connect (priv->video_window, "expose-event",
986                         G_CALLBACK (cheese_camera_expose_cb), self);
987       break;
988     case PROP_DEVICE_NAME:
989       g_free (priv->device_name);
990       priv->device_name = g_value_dup_string (value);
991       break;
992     case PROP_PHOTO_FORMAT:
993       if (priv->photo_format != NULL)
994         g_boxed_free (CHEESE_TYPE_VIDEO_FORMAT, priv->photo_format);
995       priv->photo_format = g_value_dup_boxed (value);
996       break;
997     case PROP_VIDEO_FORMAT:
998       if (priv->video_format != NULL)
999         g_boxed_free (CHEESE_TYPE_VIDEO_FORMAT, priv->video_format);
1000       priv->video_format = g_value_dup_boxed (value);
1001       break;
1002     default:
1003       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1004       break;
1005   }
1006 }
1007
1008 static void
1009 cheese_camera_class_init (CheeseCameraClass *klass)
1010 {
1011   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1012
1013   if (cheese_camera_cat == NULL)
1014     GST_DEBUG_CATEGORY_INIT (cheese_camera_cat,
1015                              "cheese-camera",
1016                              0, "Cheese Camera");
1017
1018   object_class->finalize     = cheese_camera_finalize;
1019   object_class->get_property = cheese_camera_get_property;
1020   object_class->set_property = cheese_camera_set_property;
1021
1022   camera_signals[PHOTO_SAVED] = g_signal_new ("photo-saved", G_OBJECT_CLASS_TYPE (klass),
1023                                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1024                                               G_STRUCT_OFFSET (CheeseCameraClass, photo_saved),
1025                                               NULL, NULL,
1026                                               g_cclosure_marshal_VOID__VOID,
1027                                               G_TYPE_NONE, 0);
1028
1029   camera_signals[PHOTO_TAKEN] = g_signal_new ("photo-taken", G_OBJECT_CLASS_TYPE (klass),
1030                                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1031                                               G_STRUCT_OFFSET (CheeseCameraClass, photo_taken),
1032                                               NULL, NULL,
1033                                               g_cclosure_marshal_VOID__OBJECT,
1034                                               G_TYPE_NONE, 1, GDK_TYPE_PIXBUF);
1035
1036   camera_signals[VIDEO_SAVED] = g_signal_new ("video-saved", G_OBJECT_CLASS_TYPE (klass),
1037                                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1038                                               G_STRUCT_OFFSET (CheeseCameraClass, video_saved),
1039                                               NULL, NULL,
1040                                               g_cclosure_marshal_VOID__VOID,
1041                                               G_TYPE_NONE, 0);
1042
1043
1044   g_object_class_install_property (object_class, PROP_VIDEO_WINDOW,
1045                                    g_param_spec_pointer ("video-window",
1046                                                          NULL,
1047                                                          NULL,
1048                                                          G_PARAM_READWRITE));
1049
1050   g_object_class_install_property (object_class, PROP_DEVICE_NAME,
1051                                    g_param_spec_string ("device-name",
1052                                                         NULL,
1053                                                         NULL,
1054                                                         "",
1055                                                         G_PARAM_READWRITE));
1056
1057   g_object_class_install_property (object_class, PROP_VIDEO_FORMAT,
1058                                    g_param_spec_boxed ("video-format",
1059                                                        NULL,
1060                                                        NULL,
1061                                                        CHEESE_TYPE_VIDEO_FORMAT,
1062                                                        G_PARAM_READWRITE));
1063
1064   g_object_class_install_property (object_class, PROP_PHOTO_FORMAT,
1065                                    g_param_spec_boxed ("photo-format",
1066                                                        NULL,
1067                                                        NULL,
1068                                                        CHEESE_TYPE_VIDEO_FORMAT,
1069                                                        G_PARAM_READWRITE));
1070
1071   g_type_class_add_private (klass, sizeof (CheeseCameraPrivate));
1072 }
1073
1074 static void
1075 cheese_camera_init (CheeseCamera *camera)
1076 {
1077   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1078
1079   priv->is_recording            = FALSE;
1080   priv->pipeline_is_playing     = FALSE;
1081   priv->photo_filename          = NULL;
1082   priv->camera_devices          = NULL;
1083   priv->device_name             = NULL;
1084   priv->photo_format            = NULL;
1085   priv->video_format            = NULL;
1086   priv->current_mode            = CHEESE_MEDIA_MODE_PHOTO;
1087   priv->video_source            = NULL;
1088   priv->camera_src              = NULL;
1089   priv->balance_table           = NULL;
1090   priv->saturation              = 0;
1091 }
1092
1093 CheeseCamera *
1094 cheese_camera_new (GtkWidget *video_window, char *camera_device_name,
1095                    int photo_x_resolution, int photo_y_resolution,
1096                    int video_x_resolution, int video_y_resolution)
1097 {
1098   CheeseCamera      *camera;
1099   CheeseVideoFormat *photo_format = g_slice_new (CheeseVideoFormat);
1100   CheeseVideoFormat *video_format = g_slice_new (CheeseVideoFormat);
1101
1102   photo_format->width  = photo_x_resolution;
1103   photo_format->height = photo_y_resolution;
1104   video_format->width  = video_x_resolution;
1105   video_format->height = video_y_resolution;
1106
1107   if (camera_device_name)
1108   {
1109     camera = g_object_new (CHEESE_TYPE_CAMERA, "video-window", video_window,
1110                            "device_name", camera_device_name,
1111                            "photo-format", photo_format,
1112                            "video-format", video_format,
1113                            NULL);
1114   }
1115   else
1116   {
1117     camera = g_object_new (CHEESE_TYPE_CAMERA, "video-window", video_window,
1118                            "photo-format", photo_format,
1119                            "video-format", video_format,
1120                            NULL);
1121   }
1122
1123   return camera;
1124 }
1125
1126 void
1127 cheese_camera_setup (CheeseCamera *camera, char *id, GError **error)
1128 {
1129   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1130   GstElement *element;
1131   GError  *tmp_error = NULL;
1132   GstElement *video_sink;
1133
1134   cheese_camera_detect_camera_devices (camera);
1135
1136   if (priv->num_camera_devices < 1)
1137   {
1138     g_set_error (error, CHEESE_CAMERA_ERROR, CHEESE_CAMERA_ERROR_NO_DEVICE, _("No device found"));
1139     return;
1140   }
1141
1142   if (id != NULL)
1143   {
1144     cheese_camera_set_device_by_dev_udi (camera, id);
1145   }
1146
1147   if ((priv->camerabin = gst_element_factory_make ("camerabin2", "camerabin2")) == NULL)
1148   {
1149     cheese_camera_set_error_element_not_found (error, "camerabin2");
1150   }
1151   g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
1152
1153   /* Create a gconfvideosink and set it as camerabin sink*/
1154
1155   video_sink = gst_parse_bin_from_description ("stridetransform ! "
1156       "capsfilter caps=video/x-raw-yuv-strided,rowstride=4096 ! "
1157       "v4l2sink name=actual-sink", TRUE, &tmp_error);
1158   if (tmp_error != NULL)
1159   {
1160     g_propagate_prefixed_error (error, tmp_error,
1161                                 _("One or more needed GStreamer elements are missing: "));
1162     GST_WARNING ("%s", (*error)->message);
1163     return;
1164   }
1165
1166   element = gst_bin_get_by_name (GST_BIN (video_sink), "actual-sink");
1167   g_object_set (element, "sync", FALSE, NULL);
1168   gst_object_unref (element);
1169
1170   g_object_set (G_OBJECT (priv->camerabin), "viewfinder-sink", video_sink, NULL);
1171
1172   if (!cheese_camera_set_camera_source (camera, &tmp_error))
1173     GST_WARNING ("could not set input camera");
1174   cheese_camera_set_video_recording (camera, &tmp_error);
1175   cheese_camera_set_colorbalance (camera);
1176
1177   if (tmp_error != NULL || (error != NULL && *error != NULL))
1178   {
1179     g_propagate_prefixed_error (error, tmp_error,
1180                                 _("One or more needed GStreamer elements are missing: "));
1181     GST_WARNING ("%s", (*error)->message);
1182     return;
1183   }
1184
1185   priv->bus = gst_element_get_bus (priv->camerabin);
1186   gst_bus_add_signal_watch (priv->bus);
1187
1188   g_signal_connect (G_OBJECT (priv->bus), "message",
1189                     G_CALLBACK (cheese_camera_bus_message_cb), camera);
1190
1191   gst_bus_set_sync_handler (priv->bus, (GstBusSyncHandler) cheese_camera_bus_sync_handler, camera);
1192
1193 }
1194
1195 GPtrArray *
1196 cheese_camera_get_camera_devices (CheeseCamera *camera)
1197 {
1198   CheeseCameraPrivate *priv;
1199
1200   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), NULL);
1201
1202   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1203
1204   return g_ptr_array_ref (priv->camera_devices);
1205 }
1206
1207 void
1208 cheese_camera_set_device_by_dev_file (CheeseCamera *camera, char *file)
1209 {
1210   g_return_if_fail (CHEESE_IS_CAMERA (camera));
1211   g_object_set (camera, "device_name", file, NULL);
1212 }
1213
1214 void
1215 cheese_camera_set_device_by_dev_udi (CheeseCamera *camera, char *udi)
1216 {
1217   CheeseCameraPrivate *priv;
1218   int i;
1219
1220   g_return_if_fail (CHEESE_IS_CAMERA (camera));
1221
1222   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1223
1224   for (i = 0; i < priv->num_camera_devices; i++)
1225   {
1226     CheeseCameraDevice *device = g_ptr_array_index (priv->camera_devices, i);
1227     if (strcmp (cheese_camera_device_get_id (device), udi) == 0)
1228     {
1229       g_object_set (camera,
1230                     "device_name", cheese_camera_device_get_id (device),
1231                     NULL);
1232       break;
1233     }
1234   }
1235 }
1236
1237 GList *
1238 cheese_camera_get_video_formats (CheeseCamera *camera)
1239 {
1240   CheeseCameraDevice *device;
1241
1242   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), NULL);
1243
1244   device = cheese_camera_get_selected_device (camera);
1245
1246   if (device)
1247     return cheese_camera_device_get_video_format_list (device);
1248   else
1249     return NULL;
1250 }
1251
1252 GList *
1253 cheese_camera_get_photo_formats (CheeseCamera *camera)
1254 {
1255   CheeseCameraDevice *device;
1256
1257   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), NULL);
1258
1259   device = cheese_camera_get_selected_device (camera);
1260
1261   if (device)
1262     return cheese_camera_device_get_photo_format_list (device);
1263   else
1264     return NULL;
1265 }
1266
1267 gboolean
1268 cheese_camera_is_playing (CheeseCamera *camera)
1269 {
1270   CheeseCameraPrivate *priv;
1271
1272   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1273
1274   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1275
1276   return priv->pipeline_is_playing;
1277 }
1278
1279 void
1280 cheese_camera_set_video_format (CheeseCamera *camera,
1281                                 CheeseVideoFormat *format,
1282                                 CheeseMediaMode mode)
1283 {
1284   CheeseCameraPrivate *priv;
1285   g_return_if_fail (CHEESE_IS_CAMERA (camera));
1286   g_return_if_fail (format != NULL);
1287
1288   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1289
1290   switch (mode)
1291   {
1292     case CHEESE_MEDIA_MODE_VIDEO:
1293       if (!(priv->video_format->width == format->width &&
1294             priv->video_format->height == format->height))
1295       {
1296         g_object_set (G_OBJECT (camera), "video-format", format, NULL);
1297         if (cheese_camera_is_playing (camera))
1298         {
1299           cheese_camera_stop (camera);
1300           cheese_camera_play (camera);
1301         }
1302       }
1303       break;
1304     default: /* Photo of burst mode */
1305       if (!(priv->photo_format->width == format->width &&
1306             priv->photo_format->height == format->height))
1307       {
1308         g_object_set (G_OBJECT (camera), "photo-format", format, NULL);
1309         cheese_camera_set_photo_resolution (camera);
1310       }
1311       break;
1312   }
1313 }
1314
1315 const CheeseVideoFormat *
1316 cheese_camera_get_current_video_format (CheeseCamera *camera)
1317 {
1318   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1319   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), NULL);
1320
1321   if (priv->current_mode == CHEESE_MEDIA_MODE_VIDEO)
1322     return priv->video_format;
1323   else
1324     return priv->photo_format;
1325 }
1326
1327 GParamSpec *
1328 cheese_camera_get_photography_property_enum (CheeseCamera *camera,
1329                                              gchar *property)
1330 {
1331   CheeseCameraPrivate *priv;
1332   GParamSpec *pspec = NULL;
1333
1334   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), NULL);
1335   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1336
1337   if (!GST_IS_ELEMENT (priv->photography))
1338     return NULL;
1339
1340   pspec = g_object_class_find_property (
1341       G_OBJECT_GET_CLASS (G_OBJECT (priv->photography)), property);
1342   g_return_val_if_fail (G_IS_PARAM_SPEC_ENUM (pspec), NULL);
1343
1344   return pspec;
1345 }
1346
1347 gboolean
1348 cheese_camera_get_balance_property_range (CheeseCamera *camera,
1349                                           gchar *property,
1350                                           gint *min, gint *max, gint *def)
1351 {
1352   CheeseCameraPrivate *priv;
1353   GstColorBalanceChannel *channel;
1354   GString *key = g_string_new (property);
1355
1356   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1357   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1358
1359   g_return_val_if_fail (priv->balance_table != NULL, FALSE);
1360
1361   channel = g_hash_table_lookup (priv->balance_table,
1362                                  g_string_ascii_down (key));
1363   g_return_val_if_fail (channel != NULL, FALSE);
1364
1365   *min = channel->min_value;
1366   *max = channel->max_value;
1367   *def = (*min - *max)/2;
1368
1369   return TRUE;
1370 }
1371
1372 gboolean
1373 cheese_camera_get_property_range (CheeseCamera *camera,
1374                                   gchar *property,
1375                                   gdouble *min,
1376                                   gdouble *max,
1377                                   gdouble *def,
1378                                   gdouble *step)
1379
1380 {
1381   CheeseCameraPrivate *priv;
1382   GParamSpec *pspec;
1383
1384   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1385   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1386
1387   *min = -1;
1388   *max = 1;
1389   *def = 0;
1390   *step = 1;
1391
1392   if (!GST_IS_ELEMENT (priv->photography))
1393     return FALSE;
1394
1395   pspec = g_object_class_find_property (
1396       G_OBJECT_GET_CLASS (G_OBJECT (priv->photography)), property);
1397
1398   if (G_IS_PARAM_SPEC_INT (pspec))
1399   {
1400     *min = (double) G_PARAM_SPEC_INT (pspec)->minimum;
1401     *max = (double) G_PARAM_SPEC_INT (pspec)->maximum;
1402     *def = (double) G_PARAM_SPEC_INT (pspec)->default_value;
1403     *step = 1;
1404   }
1405   else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
1406   {
1407     *min = G_PARAM_SPEC_DOUBLE (pspec)->minimum;
1408     *max = G_PARAM_SPEC_DOUBLE (pspec)->maximum;
1409     *def = G_PARAM_SPEC_DOUBLE (pspec)->default_value;
1410     *step = 0.1;
1411   }
1412   else
1413   {
1414     *min = G_PARAM_SPEC_FLOAT (pspec)->minimum;
1415     *max = G_PARAM_SPEC_FLOAT (pspec)->maximum;
1416     *def = G_PARAM_SPEC_FLOAT (pspec)->default_value;
1417     *step = 0.1;
1418   }
1419
1420   return TRUE;
1421 }
1422
1423 gboolean
1424 cheese_camera_set_balance_property (CheeseCamera *camera,
1425                                     const gchar *property,
1426                                     gint value)
1427 {
1428   CheeseCameraPrivate *priv;
1429   GstColorBalance *balance;
1430   GstColorBalanceChannel *channel;
1431   GString *key = g_string_new (property);
1432
1433   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1434   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1435   balance = GST_COLOR_BALANCE (priv->balance);
1436
1437   g_return_val_if_fail (priv->balance_table != NULL, FALSE);
1438
1439   channel = g_hash_table_lookup (priv->balance_table,
1440                                  g_string_ascii_down (key));
1441   g_return_val_if_fail (channel != NULL, FALSE);
1442
1443   gst_color_balance_set_value (balance, channel, value);
1444   if (g_ascii_strcasecmp (key->str, "saturation"))
1445     priv->saturation = value;
1446   return TRUE;
1447 }
1448
1449 void
1450 cheese_camera_set_photography_property (CheeseCamera *camera,
1451                                         const gchar *property,
1452                                         gint value)
1453 {
1454   CheeseCameraPrivate *priv;
1455
1456   g_return_if_fail (CHEESE_IS_CAMERA (camera));
1457   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1458
1459   if (!GST_IS_ELEMENT (priv->photography))
1460     return;
1461
1462   if (NULL == g_object_class_find_property (
1463       G_OBJECT_GET_CLASS (G_OBJECT (priv->photography)), property))
1464     return;
1465
1466   g_object_set (G_OBJECT (priv->photography), property, value, NULL);
1467 }
1468
1469 gboolean
1470 cheese_camera_set_int_property (CheeseCamera *camera,
1471                                 const gchar *property, gint value)
1472 {
1473   CheeseCameraPrivate *priv;
1474
1475   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1476   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1477
1478   if (!GST_IS_ELEMENT (priv->photography))
1479     return FALSE;
1480
1481   if (NULL == g_object_class_find_property (
1482       G_OBJECT_GET_CLASS (G_OBJECT (priv->photography)), property))
1483     return FALSE;
1484   g_object_set (priv->photography, property, value, NULL);
1485   return TRUE;
1486 }
1487
1488 gboolean
1489 cheese_camera_set_boolean_property (CheeseCamera *camera,
1490                                     const gchar *property, gboolean value)
1491 {
1492   CheeseCameraPrivate *priv;
1493
1494   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1495   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1496
1497   if (!GST_IS_ELEMENT (priv->photography))
1498     return FALSE;
1499
1500   if (NULL == g_object_class_find_property (
1501       G_OBJECT_GET_CLASS (G_OBJECT (priv->photography)), property))
1502     return FALSE;
1503   g_object_set (priv->photography, property, value, NULL);
1504   return TRUE;
1505 }
1506
1507 gboolean
1508 cheese_camera_set_float_property (CheeseCamera *camera,
1509                                   const gchar *property,
1510                                   gfloat value)
1511 {
1512   CheeseCameraPrivate *priv;
1513
1514   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1515   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1516
1517   if (!GST_IS_ELEMENT (priv->photography))
1518     return FALSE;
1519
1520   if (NULL == g_object_class_find_property (
1521       G_OBJECT_GET_CLASS (G_OBJECT (priv->photography)), property))
1522     return FALSE;
1523   g_object_set (priv->photography, property, value, NULL);
1524   return TRUE;
1525 }
1526
1527 gboolean
1528 cheese_camera_set_autofocus (CheeseCamera *camera, gboolean autofocus)
1529 {
1530   CheeseCameraPrivate *priv;
1531   g_return_val_if_fail (CHEESE_IS_CAMERA (camera), FALSE);
1532   priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1533   gboolean was_playing = priv->pipeline_is_playing;
1534
1535   if (priv->photography == NULL)
1536     return FALSE;
1537
1538   if (was_playing)
1539     cheese_camera_stop (camera);
1540   gst_photography_set_autofocus (GST_PHOTOGRAPHY (priv->photography),
1541                                  autofocus);
1542   if (was_playing)
1543     cheese_camera_play (camera);
1544
1545   return TRUE;
1546 }