~ubuntu-branches/debian/sid/cheese/sid

« back to all changes in this revision

Viewing changes to libcheese/cheese-camera.c

  • Committer: Bazaar Package Importer
  • Author(s): Laurent Bigonville
  • Date: 2011-07-17 21:04:16 UTC
  • mfrom: (15.1.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20110717210416-nt5qi659qei7a2yy
Tags: 3.0.1-2
* debian/control.in:
  - Change gir1.2-cheese-3.0 Section to libs
  - Make library packages depend against cheese-common package
  - Make cheese package recommends against hicolor-icon-theme
  - Move gst Dependency to libcheese package
* debian/patches/0002-fix-linking.patch: Add missing library to fix linking
* debian/watch:
  - Switch to .bz2 tarballs.
  - Bump version to 3

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org>
3
3
 * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org>
4
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>
5
7
 *
6
8
 * Licensed under the GNU General Public License Version 2
7
9
 *
26
28
#include <glib-object.h>
27
29
#include <glib.h>
28
30
#include <glib/gi18n.h>
29
 
#include <gtk/gtk.h>
30
 
#include <gdk/gdkx.h>
 
31
#include <clutter/clutter.h>
 
32
#include <clutter-gst/clutter-gst.h>
31
33
#include <gst/gst.h>
32
 
#include <gdk-pixbuf/gdk-pixbuf.h>
33
34
#include <X11/Xlib.h>
34
35
 
35
36
#include "cheese-camera.h"
42
43
 
43
44
#define CHEESE_CAMERA_ERROR cheese_camera_error_quark ()
44
45
 
 
46
typedef enum
 
47
{
 
48
  MODE_IMAGE = 0,
 
49
  MODE_VIDEO
 
50
} GstCameraBinMode;
 
51
 
 
52
typedef enum {
 
53
  GST_CAMERABIN_FLAG_SOURCE_RESIZE               = (1 << 0),
 
54
  GST_CAMERABIN_FLAG_SOURCE_COLOR_CONVERSION     = (1 << 1),
 
55
  GST_CAMERABIN_FLAG_VIEWFINDER_COLOR_CONVERSION = (1 << 2),
 
56
  GST_CAMERABIN_FLAG_VIEWFINDER_SCALE            = (1 << 3),
 
57
  GST_CAMERABIN_FLAG_AUDIO_CONVERSION            = (1 << 4),
 
58
  GST_CAMERABIN_FLAG_DISABLE_AUDIO               = (1 << 5),
 
59
  GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION      = (1 << 6),
 
60
  GST_CAMERABIN_FLAG_VIDEO_COLOR_CONVERSION      = (1 << 7)
 
61
} GstCameraBinFlags;
 
62
 
 
63
 
45
64
typedef struct
46
65
{
47
 
  GtkWidget *video_window;
48
 
 
49
 
  GstElement *pipeline;
50
66
  GstBus *bus;
51
67
 
52
 
  /* We build the active pipeline by linking the appropriate pipelines listed below*/
53
 
  GstElement *camera_source_bin;
54
 
  GstElement *video_display_bin;
55
 
  GstElement *photo_save_bin;
56
 
  GstElement *video_save_bin;
 
68
  GstElement *camerabin;
 
69
  GstElement *video_filter_bin;
 
70
  GstElement *effects_preview_bin;
57
71
 
58
72
  GstElement *video_source;
59
 
  GstElement *capsfilter;
60
73
  GstElement *video_file_sink;
61
 
  GstElement *photo_sink;
62
74
  GstElement *audio_source;
63
75
  GstElement *audio_enc;
64
76
  GstElement *video_enc;
65
77
 
66
 
  GstElement *effect_filter, *csp_post_effect;
 
78
  ClutterTexture *video_texture;
 
79
 
 
80
  GstElement *effect_filter;
67
81
  GstElement *video_balance, *csp_post_balance;
 
82
  GstElement *camera_tee, *effects_tee;
 
83
  GstElement *effects_downscaler;
 
84
  GstElement *main_valve, *effects_valve;
 
85
 
 
86
  GstCaps *preview_caps;
68
87
 
69
88
  gulong photo_handler_signal_id;
70
89
 
88
107
enum
89
108
{
90
109
  PROP_0,
91
 
  PROP_VIDEO_WINDOW,
 
110
  PROP_VIDEO_TEXTURE,
92
111
  PROP_DEVICE_NAME,
93
112
  PROP_FORMAT,
94
113
};
98
117
  PHOTO_SAVED,
99
118
  PHOTO_TAKEN,
100
119
  VIDEO_SAVED,
 
120
  STATE_FLAGS_CHANGED,
101
121
  LAST_SIGNAL
102
122
};
103
123
 
104
124
static guint camera_signals[LAST_SIGNAL];
105
125
 
106
 
typedef enum
107
 
{
108
 
  RGB,
109
 
  YUV
110
 
} VideoColorSpace;
111
 
 
112
 
typedef struct
113
 
{
114
 
  CheeseCameraEffect effect;
115
 
  const char *pipeline_desc;
116
 
  VideoColorSpace colorspace; /* The color space the effect works in */
117
 
} EffectToPipelineDesc;
118
 
 
119
 
 
120
 
static const EffectToPipelineDesc EFFECT_TO_PIPELINE_DESC[] = {
121
 
  {CHEESE_CAMERA_EFFECT_NO_EFFECT,       "identity",                             RGB},
122
 
  {CHEESE_CAMERA_EFFECT_MAUVE,           "videobalance saturation=1.5 hue=+0.5", YUV},
123
 
  {CHEESE_CAMERA_EFFECT_NOIR_BLANC,      "videobalance saturation=0",            YUV},
124
 
  {CHEESE_CAMERA_EFFECT_SATURATION,      "videobalance saturation=2",            YUV},
125
 
  {CHEESE_CAMERA_EFFECT_HULK,            "videobalance saturation=1.5 hue=-0.5", YUV},
126
 
  {CHEESE_CAMERA_EFFECT_VERTICAL_FLIP,   "videoflip method=5",                   YUV},
127
 
  {CHEESE_CAMERA_EFFECT_HORIZONTAL_FLIP, "videoflip method=4",                   YUV},
128
 
  {CHEESE_CAMERA_EFFECT_SHAGADELIC,      "shagadelictv",                         RGB},
129
 
  {CHEESE_CAMERA_EFFECT_VERTIGO,         "vertigotv",                            RGB},
130
 
  {CHEESE_CAMERA_EFFECT_EDGE,            "edgetv",                               RGB},
131
 
  {CHEESE_CAMERA_EFFECT_DICE,            "dicetv",                               RGB},
132
 
  {CHEESE_CAMERA_EFFECT_WARP,            "warptv",                               RGB}
133
 
};
134
 
 
135
 
static const int NUM_EFFECTS = G_N_ELEMENTS (EFFECT_TO_PIPELINE_DESC);
136
 
 
137
126
GST_DEBUG_CATEGORY (cheese_camera_cat);
138
127
#define GST_CAT_DEFAULT cheese_camera_cat
139
128
 
143
132
  return g_quark_from_static_string ("cheese-camera-error-quark");
144
133
}
145
134
 
146
 
static GstBusSyncReply
147
 
cheese_camera_bus_sync_handler (GstBus *bus, GstMessage *message, CheeseCamera *camera)
148
 
{
149
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
150
 
  GstXOverlay         *overlay;
151
 
 
152
 
  if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
153
 
    return GST_BUS_PASS;
154
 
 
155
 
  if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
156
 
    return GST_BUS_PASS;
157
 
 
158
 
  overlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
159
 
 
160
 
  if (g_object_class_find_property (G_OBJECT_GET_CLASS (overlay),
161
 
                                    "force-aspect-ratio"))
162
 
    g_object_set (G_OBJECT (overlay), "force-aspect-ratio", TRUE, NULL);
163
 
 
164
 
  gst_x_overlay_set_xwindow_id (overlay,
165
 
                                GDK_WINDOW_XWINDOW (gtk_widget_get_window (priv->video_window)));
166
 
 
167
 
  gst_message_unref (message);
168
 
 
169
 
  return GST_BUS_DROP;
170
 
}
171
 
 
172
 
static void
173
 
cheese_camera_change_sink (CheeseCamera *camera, GstElement *src,
174
 
                           GstElement *new_sink, GstElement *old_sink)
175
 
{
176
 
  CheeseCameraPrivate *priv       = CHEESE_CAMERA_GET_PRIVATE (camera);
177
 
  gboolean             is_playing = priv->pipeline_is_playing;
178
 
 
179
 
  cheese_camera_stop (camera);
180
 
 
181
 
  gst_element_unlink (src, old_sink);
182
 
  gst_object_ref (old_sink);
183
 
  gst_bin_remove (GST_BIN (priv->pipeline), old_sink);
184
 
 
185
 
  gst_bin_add (GST_BIN (priv->pipeline), new_sink);
186
 
  gst_element_link (src, new_sink);
187
 
 
188
 
  if (is_playing)
189
 
    cheese_camera_play (camera);
190
 
}
191
 
 
192
 
static gboolean
193
 
cheese_camera_expose_cb (GtkWidget *widget, GdkEventExpose *event, CheeseCamera *camera)
194
 
{
195
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
196
 
  GtkAllocation        allocation;
197
 
  GstState             state;
198
 
  GstXOverlay         *overlay = GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (priv->pipeline),
199
 
                                                                          GST_TYPE_X_OVERLAY));
200
 
 
201
 
  gst_element_get_state (priv->pipeline, &state, NULL, 0);
202
 
 
203
 
  if ((state < GST_STATE_PLAYING) || (overlay == NULL))
204
 
  {
205
 
    gtk_widget_get_allocation (widget, &allocation);
206
 
    gdk_draw_rectangle (gtk_widget_get_window (widget),
207
 
                        gtk_widget_get_style (widget)->black_gc, TRUE,
208
 
                        0, 0, allocation.width, allocation.height);
209
 
  }
210
 
  else
211
 
  {
212
 
    gst_x_overlay_expose (overlay);
213
 
  }
214
 
 
215
 
  return FALSE;
216
 
}
217
 
 
218
 
static void
219
 
cheese_camera_photo_data_cb (GstElement *element, GstBuffer *buffer,
220
 
                             GstPad *pad, CheeseCamera *camera)
221
 
{
222
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
223
 
 
 
135
static void
 
136
cheese_camera_photo_data (CheeseCamera *camera, GstBuffer *buffer)
 
137
{
224
138
  GstCaps            *caps;
225
139
  const GstStructure *structure;
226
140
  int                 width, height, stride;
227
141
  GdkPixbuf          *pixbuf;
228
142
  const int           bits_per_pixel = 8;
229
 
  guchar             *data;
 
143
  guchar             *data = NULL;
 
144
  CheeseCameraPrivate *priv  = CHEESE_CAMERA_GET_PRIVATE (camera);
230
145
 
231
 
  caps      = gst_buffer_get_caps (buffer);
 
146
  caps = gst_buffer_get_caps (buffer);
232
147
  structure = gst_caps_get_structure (caps, 0);
233
148
  gst_structure_get_int (structure, "width", &width);
234
149
  gst_structure_get_int (structure, "height", &height);
235
150
 
236
151
  stride = buffer->size / height;
237
152
 
238
 
  /* Only copy the data if we're giving away a pixbuf,
239
 
   * not if we're throwing everything away straight away */
240
 
  if (priv->photo_filename != NULL)
241
 
    data = NULL;
242
 
  else
243
 
    data = g_memdup (GST_BUFFER_DATA (buffer), buffer->size);
 
153
  data = g_memdup (GST_BUFFER_DATA (buffer), buffer->size);
244
154
  pixbuf = gdk_pixbuf_new_from_data (data ? data : GST_BUFFER_DATA (buffer),
245
155
                                     GDK_COLORSPACE_RGB,
246
156
                                     FALSE, bits_per_pixel, width, height, stride,
247
157
                                     data ? (GdkPixbufDestroyNotify) g_free : NULL, NULL);
248
158
 
249
 
  g_signal_handler_disconnect (G_OBJECT (priv->photo_sink),
250
 
                               priv->photo_handler_signal_id);
251
 
  priv->photo_handler_signal_id = 0;
252
 
 
253
 
  if (priv->photo_filename != NULL)
254
 
  {
255
 
    gdk_pixbuf_save (pixbuf, priv->photo_filename, "jpeg", NULL, NULL);
256
 
    g_object_unref (G_OBJECT (pixbuf));
257
 
 
258
 
    g_free (priv->photo_filename);
259
 
    priv->photo_filename = NULL;
260
 
 
261
 
    g_signal_emit (camera, camera_signals[PHOTO_SAVED], 0);
262
 
  }
263
 
  else
264
 
  {
265
 
    g_signal_emit (camera, camera_signals[PHOTO_TAKEN], 0, pixbuf);
266
 
    g_object_unref (pixbuf);
267
 
  }
 
159
  g_object_set (G_OBJECT (priv->camerabin), "preview-caps", NULL, NULL);
 
160
  g_signal_emit (camera, camera_signals[PHOTO_TAKEN], 0, pixbuf);
 
161
  g_object_unref (pixbuf);
268
162
}
269
163
 
270
164
static void
271
165
cheese_camera_bus_message_cb (GstBus *bus, GstMessage *message, CheeseCamera *camera)
272
166
{
273
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
274
 
 
275
 
  if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS)
 
167
  switch (GST_MESSAGE_TYPE (message))
276
168
  {
277
 
    if (priv->is_recording)
278
 
    {
279
 
      GST_DEBUG ("Received EOS message");
280
 
 
281
 
      g_source_remove (priv->eos_timeout_id);
282
 
 
283
 
      g_signal_emit (camera, camera_signals[VIDEO_SAVED], 0);
284
 
 
285
 
      cheese_camera_change_sink (camera, priv->video_display_bin,
286
 
                                 priv->photo_save_bin, priv->video_save_bin);
287
 
      priv->is_recording = FALSE;
 
169
    case GST_MESSAGE_WARNING:
 
170
    {
 
171
      GError *err = NULL;
 
172
      gchar *debug = NULL;
 
173
      gst_message_parse_warning (message, &err, &debug);
 
174
 
 
175
      if (err && err->message) {
 
176
        g_warning ("%s\n", err->message);
 
177
        g_error_free (err);
 
178
      } else {
 
179
        g_warning ("Unparsable GST_MESSAGE_WARNING message.\n");
 
180
      }
 
181
 
 
182
      g_free (debug);
 
183
      break;
 
184
    }
 
185
    case GST_MESSAGE_ERROR:
 
186
    {
 
187
      GError *err = NULL;
 
188
      gchar *debug = NULL;
 
189
      gst_message_parse_error (message, &err, &debug);
 
190
 
 
191
      if (err && err->message) {
 
192
        g_warning ("%s\n", err->message);
 
193
        g_error_free (err);
 
194
      } else {
 
195
        g_warning ("Unparsable GST_MESSAGE_ERROR message.\n");
 
196
      }
 
197
 
 
198
      g_free (debug);
 
199
      break;
 
200
    }
 
201
    case GST_MESSAGE_STATE_CHANGED:
 
202
    {
 
203
      if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0)
 
204
      {
 
205
        GstState old, new;
 
206
        gst_message_parse_state_changed (message, &old, &new, NULL);
 
207
        if (new == GST_STATE_PLAYING)
 
208
          g_signal_emit (camera, camera_signals[STATE_FLAGS_CHANGED], 0, new);
 
209
      }
 
210
      break;
 
211
    }
 
212
    case GST_MESSAGE_ELEMENT:
 
213
    {
 
214
      const GstStructure *structure;
 
215
      GstBuffer *buffer;
 
216
      const GValue *image;
 
217
      if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0)
 
218
      {
 
219
        structure = gst_message_get_structure (message);
 
220
        if (strcmp (gst_structure_get_name (structure), "preview-image") == 0)
 
221
        {
 
222
          if (gst_structure_has_field_typed (structure, "buffer", GST_TYPE_BUFFER))
 
223
          {
 
224
            image = gst_structure_get_value (structure, "buffer");
 
225
            if (image)
 
226
            {
 
227
              buffer = gst_value_get_buffer (image);
 
228
              cheese_camera_photo_data (camera, buffer);
 
229
            }
 
230
            else
 
231
            {
 
232
              g_warning ("Could not get buffer from bus message");
 
233
            }
 
234
          }
 
235
        }
 
236
      }
 
237
      break;
 
238
    }
 
239
    default:
 
240
    {
 
241
      break;
288
242
    }
289
243
  }
290
244
}
297
251
                          gint                       api_version,
298
252
                          CheeseCamera              *camera)
299
253
{
300
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
301
 
  GError *error = NULL;
 
254
  CheeseCameraPrivate *priv  = CHEESE_CAMERA_GET_PRIVATE (camera);
 
255
  GError              *error = NULL;
302
256
 
303
257
  CheeseCameraDevice *device = cheese_camera_device_new (id,
304
258
                                                         device_file,
305
259
                                                         product_name,
306
260
                                                         api_version,
307
261
                                                         &error);
 
262
 
308
263
  if (device == NULL)
309
264
    GST_WARNING ("Device initialization for %s failed: %s ",
310
265
                 device_file,
311
266
                 (error != NULL) ? error->message : "Unknown reason");
312
 
  else  {
 
267
  else
 
268
  {
313
269
    g_ptr_array_add (priv->camera_devices, device);
314
270
    priv->num_camera_devices++;
315
271
  }
332
288
}
333
289
 
334
290
static gboolean
335
 
cheese_camera_create_camera_source_bin (CheeseCamera *camera)
 
291
cheese_camera_set_camera_source (CheeseCamera *camera)
336
292
{
337
293
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
338
294
 
359
315
  }
360
316
 
361
317
  camera_input = g_strdup_printf (
362
 
    "%s name=video_source device=%s ! capsfilter name=capsfilter ! identity",
 
318
    "%s name=video_source device=%s",
363
319
    cheese_camera_device_get_src (selected_camera),
364
320
    cheese_camera_device_get_device_file (selected_camera));
365
321
 
366
 
  priv->camera_source_bin = gst_parse_bin_from_description (camera_input,
367
 
                                                            TRUE, &err);
 
322
  priv->video_source = gst_parse_bin_from_description (camera_input, TRUE, &err);
368
323
  g_free (camera_input);
369
324
 
370
 
  if (priv->camera_source_bin == NULL)
 
325
  if (priv->video_source == NULL)
371
326
  {
372
 
    goto fallback;
 
327
    if (err != NULL)
 
328
    {
 
329
      g_error_free (err);
 
330
      err = NULL;
 
331
    }
 
332
    return FALSE;
373
333
  }
374
 
 
375
 
  priv->video_source = gst_bin_get_by_name (GST_BIN (priv->camera_source_bin), "video_source");
376
 
  priv->capsfilter   = gst_bin_get_by_name (GST_BIN (priv->camera_source_bin), "capsfilter");
 
334
  g_object_set (priv->camerabin, "video-source", priv->video_source, NULL);
377
335
 
378
336
  return TRUE;
379
 
 
380
 
fallback:
381
 
  if (err != NULL)
382
 
  {
383
 
    g_error_free (err);
384
 
    err = NULL;
385
 
  }
386
 
  return FALSE;
387
337
}
388
338
 
389
339
static void
419
369
  }
420
370
}
421
371
 
422
 
static gboolean
423
 
cheese_camera_create_video_display_bin (CheeseCamera *camera, GError **error)
424
 
{
425
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
426
 
 
427
 
  GstElement *tee, *video_display_queue, *video_scale, *video_sink, *save_queue;
428
 
 
429
 
  gboolean ok;
430
 
  GstPad  *pad;
431
 
 
432
 
  priv->video_display_bin = gst_bin_new ("video_display_bin");
433
 
 
434
 
  cheese_camera_create_camera_source_bin (camera);
435
 
 
 
372
static void
 
373
cheese_camera_set_video_recording (CheeseCamera *camera, GError **error)
 
374
{
 
375
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
376
  GstElement          *video_enc;
 
377
  GstElement          *mux;
 
378
 
 
379
  /* Setup video-encoder explicitly to be able to set its properties*/
 
380
 
 
381
  if ((video_enc = gst_element_factory_make ("theoraenc", "theoraenc")) == NULL)
 
382
  {
 
383
    cheese_camera_set_error_element_not_found (error, "theoraenc");
 
384
    return;
 
385
  }
 
386
  g_object_set (priv->camerabin, "video-encoder", video_enc, NULL);
 
387
  g_object_set (G_OBJECT (video_enc), "speed-level", 2, NULL);
 
388
 
 
389
  if ((mux = gst_element_factory_make ("oggmux", "oggmux")) == NULL)
 
390
  {
 
391
    cheese_camera_set_error_element_not_found (error, "oggmux");
 
392
    return;
 
393
  }
 
394
  g_object_set (priv->camerabin, "video-muxer", mux, NULL);
 
395
  g_object_set (G_OBJECT (mux),
 
396
                "max-delay", (guint64) 10000000,
 
397
                "max-page-delay", (guint64) 10000000, NULL);
 
398
}
 
399
 
 
400
static gboolean
 
401
cheese_camera_create_effects_preview_bin (CheeseCamera *camera, GError **error)
 
402
{
 
403
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
404
 
 
405
  gboolean ok = TRUE;
 
406
  GstPad  *pad;
 
407
  GError  *err = NULL;
 
408
 
 
409
  priv->effects_preview_bin = gst_bin_new ("effects_preview_bin");
 
410
 
 
411
  if ((priv->effects_tee = gst_element_factory_make ("tee", "effects_tee")) == NULL)
 
412
  {
 
413
    cheese_camera_set_error_element_not_found (error, "tee");
 
414
  }
 
415
  if ((priv->effects_valve = gst_element_factory_make ("valve", "effects_valve")) == NULL)
 
416
  {
 
417
    cheese_camera_set_error_element_not_found (error, "effects_valve");
 
418
  }
 
419
  priv->effects_downscaler = gst_parse_bin_from_description (
 
420
    "videoscale ! video/x-raw-yuv,width=160,height=120 ! ffmpegcolorspace",
 
421
    TRUE,
 
422
    &err);
 
423
  if (priv->effects_downscaler == NULL || err != NULL)
 
424
  {
 
425
    cheese_camera_set_error_element_not_found (error, "effects_downscaler");
 
426
  }
 
427
 
 
428
  if (error != NULL && *error != NULL)
 
429
    return FALSE;
 
430
 
 
431
  gst_bin_add_many (GST_BIN (priv->effects_preview_bin),
 
432
                    priv->effects_downscaler, priv->effects_tee,
 
433
                    priv->effects_valve, NULL);
 
434
 
 
435
  ok &= gst_element_link_many (priv->effects_valve, priv->effects_downscaler,
 
436
                               priv->effects_tee, NULL);
 
437
 
 
438
  /* add ghostpads */
 
439
 
 
440
  pad = gst_element_get_static_pad (priv->effects_valve, "sink");
 
441
  gst_element_add_pad (priv->effects_preview_bin, gst_ghost_pad_new ("sink", pad));
 
442
  gst_object_unref (GST_OBJECT (pad));
 
443
 
 
444
  if (!ok)
 
445
    g_error ("Unable to create effects preview bin");
 
446
 
 
447
  return TRUE;
 
448
}
 
449
 
 
450
static gboolean
 
451
cheese_camera_create_video_filter_bin (CheeseCamera *camera, GError **error)
 
452
{
 
453
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
454
 
 
455
  gboolean ok = TRUE;
 
456
  GstPad  *pad;
 
457
 
 
458
  cheese_camera_create_effects_preview_bin (camera, error);
 
459
 
 
460
  priv->video_filter_bin = gst_bin_new ("video_filter_bin");
 
461
 
 
462
  if ((priv->camera_tee = gst_element_factory_make ("tee", "camera_tee")) == NULL)
 
463
  {
 
464
    cheese_camera_set_error_element_not_found (error, "tee");
 
465
  }
 
466
  if ((priv->main_valve = gst_element_factory_make ("valve", "main_valve")) == NULL)
 
467
  {
 
468
    cheese_camera_set_error_element_not_found (error, "main_valve");
 
469
  }
436
470
  if ((priv->effect_filter = gst_element_factory_make ("identity", "effect")) == NULL)
437
471
  {
438
472
    cheese_camera_set_error_element_not_found (error, "identity");
439
473
  }
440
 
  if ((priv->csp_post_effect = gst_element_factory_make ("ffmpegcolorspace", "csp_post_effect")) == NULL)
441
 
  {
442
 
    cheese_camera_set_error_element_not_found (error, "ffmpegcolorspace");
443
 
  }
444
474
  if ((priv->video_balance = gst_element_factory_make ("videobalance", "video_balance")) == NULL)
445
475
  {
446
476
    cheese_camera_set_error_element_not_found (error, "videobalance");
452
482
    return FALSE;
453
483
  }
454
484
 
455
 
  if ((tee = gst_element_factory_make ("tee", "tee")) == NULL)
456
 
  {
457
 
    cheese_camera_set_error_element_not_found (error, "tee");
458
 
  }
459
 
 
460
 
  if ((save_queue = gst_element_factory_make ("queue", "save_queue")) == NULL)
461
 
  {
462
 
    cheese_camera_set_error_element_not_found (error, "queue");
463
 
  }
464
 
 
465
 
  if ((video_display_queue = gst_element_factory_make ("queue", "video_display_queue")) == NULL)
466
 
  {
467
 
    cheese_camera_set_error_element_not_found (error, "queue");
468
 
  }
469
 
 
470
 
  if ((video_scale = gst_element_factory_make ("videoscale", "video_scale")) == NULL)
471
 
  {
472
 
    cheese_camera_set_error_element_not_found (error, "videoscale");
473
 
  }
474
 
  else
475
 
  {
476
 
    /* Use bilinear scaling */
477
 
    g_object_set (video_scale, "method", 1, NULL);
478
 
  }
479
 
 
480
 
  if ((video_sink = gst_element_factory_make ("gconfvideosink", "video_sink")) == NULL)
481
 
  {
482
 
    cheese_camera_set_error_element_not_found (error, "gconfvideosink");
483
 
  }
484
 
 
485
485
  if (error != NULL && *error != NULL)
486
486
    return FALSE;
487
487
 
488
 
  gst_bin_add_many (GST_BIN (priv->video_display_bin), priv->camera_source_bin,
489
 
                    priv->effect_filter, priv->csp_post_effect,
 
488
  gst_bin_add_many (GST_BIN (priv->video_filter_bin), priv->camera_tee,
 
489
                    priv->main_valve, priv->effect_filter,
490
490
                    priv->video_balance, priv->csp_post_balance,
491
 
                    tee, save_queue,
492
 
                    video_display_queue, video_scale, video_sink, NULL);
493
 
 
494
 
  ok = gst_element_link_many (priv->camera_source_bin, priv->effect_filter,
495
 
                              priv->csp_post_effect,
496
 
                              priv->video_balance, priv->csp_post_balance,
497
 
                              tee, NULL);
498
 
 
499
 
  ok &= gst_element_link_many (tee, save_queue, NULL);
500
 
  ok &= gst_element_link_many (tee, video_display_queue, video_scale, video_sink, NULL);
501
 
 
502
 
  /* add ghostpad */
503
 
  pad = gst_element_get_pad (save_queue, "src");
504
 
  gst_element_add_pad (priv->video_display_bin, gst_ghost_pad_new ("src", pad));
505
 
  gst_object_unref (GST_OBJECT (pad));
506
 
 
507
 
 
508
 
  if (!ok)
509
 
    g_error ("Unable to create display pipeline");
510
 
 
511
 
  return TRUE;
512
 
}
513
 
 
514
 
static gboolean
515
 
cheese_camera_create_photo_save_bin (CheeseCamera *camera, GError **error)
516
 
{
517
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
518
 
 
519
 
  GstElement *csp_photo_save_bin;
520
 
 
521
 
  gboolean ok;
522
 
  GstPad  *pad;
523
 
  GstCaps *caps;
524
 
 
525
 
  priv->photo_save_bin = gst_bin_new ("photo_save_bin");
526
 
 
527
 
  if ((csp_photo_save_bin = gst_element_factory_make ("ffmpegcolorspace", "csp_photo_save_bin")) == NULL)
528
 
  {
529
 
    cheese_camera_set_error_element_not_found (error, "ffmpegcolorspace");
530
 
  }
531
 
  if ((priv->photo_sink = gst_element_factory_make ("fakesink", "photo_sink")) == NULL)
532
 
  {
533
 
    cheese_camera_set_error_element_not_found (error, "fakesink");
534
 
  }
535
 
 
536
 
  if (error != NULL && *error != NULL)
537
 
    return FALSE;
538
 
 
539
 
  gst_bin_add_many (GST_BIN (priv->photo_save_bin), csp_photo_save_bin,
540
 
                    priv->photo_sink, NULL);
541
 
 
542
 
  /* add ghostpad */
543
 
  pad = gst_element_get_pad (csp_photo_save_bin, "sink");
544
 
  gst_element_add_pad (priv->photo_save_bin, gst_ghost_pad_new ("sink", pad));
545
 
  gst_object_unref (GST_OBJECT (pad));
546
 
 
547
 
  caps = gst_caps_new_simple ("video/x-raw-rgb",
548
 
                              "bpp", G_TYPE_INT, 24,
549
 
                              "depth", G_TYPE_INT, 24,
550
 
                              NULL);
551
 
  ok = gst_element_link_filtered (csp_photo_save_bin, priv->photo_sink, caps);
552
 
  gst_caps_unref (caps);
553
 
 
554
 
  g_object_set (G_OBJECT (priv->photo_sink), "signal-handoffs", TRUE, NULL);
555
 
 
556
 
  if (!ok)
557
 
    g_error ("Unable to create photo save pipeline");
558
 
 
559
 
  return TRUE;
560
 
}
561
 
 
562
 
static gboolean
563
 
cheese_camera_create_video_save_bin (CheeseCamera *camera, GError **error)
564
 
{
565
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
566
 
 
567
 
  GstElement *audio_queue, *audio_convert, *audio_enc;
568
 
  GstElement *video_save_csp, *video_save_rate, *video_save_scale, *video_enc;
569
 
  GstElement *mux;
570
 
  GstPad     *pad;
571
 
  gboolean    ok;
572
 
 
573
 
  priv->video_save_bin = gst_bin_new ("video_save_bin");
574
 
 
575
 
  if ((priv->audio_source = gst_element_factory_make ("gconfaudiosrc", "audio_source")) == NULL)
576
 
  {
577
 
    cheese_camera_set_error_element_not_found (error, "gconfaudiosrc");
578
 
  }
579
 
  if ((audio_queue = gst_element_factory_make ("queue", "audio_queue")) == NULL)
580
 
  {
581
 
    cheese_camera_set_error_element_not_found (error, "queue");
582
 
  }
583
 
  if ((audio_convert = gst_element_factory_make ("audioconvert", "audio_convert")) == NULL)
584
 
  {
585
 
    cheese_camera_set_error_element_not_found (error, "audioconvert");
586
 
  }
587
 
  if ((audio_enc = gst_element_factory_make ("vorbisenc", "audio_enc")) == NULL)
588
 
  {
589
 
    cheese_camera_set_error_element_not_found (error, "vorbisenc");
590
 
  }
591
 
 
592
 
  if ((video_save_csp = gst_element_factory_make ("ffmpegcolorspace", "video_save_csp")) == NULL)
593
 
  {
594
 
    cheese_camera_set_error_element_not_found (error, "ffmpegcolorspace");
595
 
  }
596
 
  if ((video_enc = gst_element_factory_make ("theoraenc", "video_enc")) == NULL)
597
 
  {
598
 
    cheese_camera_set_error_element_not_found (error, "theoraenc");
599
 
  }
600
 
  else
601
 
  {
602
 
    g_object_set (video_enc, "keyframe-force", 1, NULL);
603
 
  }
604
 
 
605
 
  if ((video_save_rate = gst_element_factory_make ("videorate", "video_save_rate")) == NULL)
606
 
  {
607
 
    cheese_camera_set_error_element_not_found (error, "videorate");
608
 
  }
609
 
  if ((video_save_scale = gst_element_factory_make ("videoscale", "video_save_scale")) == NULL)
610
 
  {
611
 
    cheese_camera_set_error_element_not_found (error, "videoscale");
612
 
  }
613
 
  else
614
 
  {
615
 
    /* Use bilinear scaling */
616
 
    g_object_set (video_save_scale, "method", 1, NULL);
617
 
  }
618
 
 
619
 
  if ((mux = gst_element_factory_make ("oggmux", "mux")) == NULL)
620
 
  {
621
 
    cheese_camera_set_error_element_not_found (error, "oggmux");
622
 
  }
623
 
  else
624
 
  {
625
 
    g_object_set (G_OBJECT (mux),
626
 
                  "max-delay", (guint64) 10000000,
627
 
                  "max-page-delay", (guint64) 10000000, NULL);
628
 
  }
629
 
 
630
 
  if ((priv->video_file_sink = gst_element_factory_make ("filesink", "video_file_sink")) == NULL)
631
 
  {
632
 
    cheese_camera_set_error_element_not_found (error, "filesink");
633
 
  }
634
 
 
635
 
  if (error != NULL && *error != NULL)
636
 
    return FALSE;
637
 
 
638
 
  gst_bin_add_many (GST_BIN (priv->video_save_bin), priv->audio_source, audio_queue,
639
 
                    audio_convert, audio_enc, video_save_csp, video_save_rate, video_save_scale, video_enc,
640
 
                    mux, priv->video_file_sink, NULL);
641
 
 
642
 
  /* add ghostpad */
643
 
  pad = gst_element_get_pad (video_save_csp, "sink");
644
 
  gst_element_add_pad (priv->video_save_bin, gst_ghost_pad_new ("sink", pad));
645
 
  gst_object_unref (GST_OBJECT (pad));
646
 
 
647
 
 
648
 
  ok = gst_element_link_many (priv->audio_source, audio_queue, audio_convert,
649
 
                              audio_enc, mux, priv->video_file_sink, NULL);
650
 
 
651
 
  ok &= gst_element_link_many (video_save_csp, video_save_rate, video_save_scale, video_enc,
652
 
                               NULL);
653
 
  ok &= gst_element_link (video_enc, mux);
654
 
 
655
 
  if (!ok)
656
 
    g_error ("Unable to create video save pipeline");
657
 
 
658
 
  return TRUE;
659
 
}
660
 
 
661
 
int
 
491
                    priv->effects_preview_bin, NULL);
 
492
 
 
493
  ok &= gst_element_link_many (priv->camera_tee, priv->main_valve,
 
494
                               priv->effect_filter, priv->video_balance,
 
495
                               priv->csp_post_balance, NULL);
 
496
  gst_pad_link (gst_element_get_request_pad (priv->camera_tee, "src%d"),
 
497
                gst_element_get_static_pad (priv->effects_preview_bin, "sink"));
 
498
 
 
499
  /* add ghostpads */
 
500
 
 
501
  pad = gst_element_get_static_pad (priv->csp_post_balance, "src");
 
502
  gst_element_add_pad (priv->video_filter_bin, gst_ghost_pad_new ("src", pad));
 
503
  gst_object_unref (GST_OBJECT (pad));
 
504
 
 
505
  pad = gst_element_get_static_pad (priv->camera_tee, "sink");
 
506
  gst_element_add_pad (priv->video_filter_bin, gst_ghost_pad_new ("sink", pad));
 
507
  gst_object_unref (GST_OBJECT (pad));
 
508
 
 
509
  if (!ok)
 
510
    g_error ("Unable to create filter bin");
 
511
 
 
512
  return TRUE;
 
513
}
 
514
 
 
515
static int
662
516
cheese_camera_get_num_camera_devices (CheeseCamera *camera)
663
517
{
664
518
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
666
520
  return priv->num_camera_devices;
667
521
}
668
522
 
 
523
/**
 
524
 * cheese_camera_get_selected_device:
 
525
 * @camera: a #CheeseCamera
 
526
 *
 
527
 * Returns: (transfer none): a #CheeseCameraDevice or NULL
 
528
 */
 
529
 
669
530
CheeseCameraDevice *
670
531
cheese_camera_get_selected_device (CheeseCamera *camera)
671
532
{
678
539
    return NULL;
679
540
}
680
541
 
681
 
gboolean
 
542
void
682
543
cheese_camera_switch_camera_device (CheeseCamera *camera)
683
544
{
684
545
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
685
546
 
686
547
  gboolean was_recording        = FALSE;
687
548
  gboolean pipeline_was_playing = FALSE;
688
 
  gboolean disp_bin_created     = FALSE;
689
 
  gboolean disp_bin_added       = FALSE;
690
 
  gboolean disp_bin_linked      = FALSE;
691
 
  GError  *error                = NULL;
692
549
 
693
550
  if (priv->is_recording)
694
551
  {
702
559
    pipeline_was_playing = TRUE;
703
560
  }
704
561
 
705
 
  gst_bin_remove (GST_BIN (priv->pipeline), priv->video_display_bin);
706
 
 
707
 
  disp_bin_created = cheese_camera_create_video_display_bin (camera, &error);
708
 
  if (!disp_bin_created)
709
 
  {
710
 
    return FALSE;
711
 
  }
712
 
  disp_bin_added = gst_bin_add (GST_BIN (priv->pipeline), priv->video_display_bin);
713
 
  if (!disp_bin_added)
714
 
  {
715
 
    gst_object_sink (priv->video_display_bin);
716
 
    return FALSE;
717
 
  }
718
 
 
719
 
  disp_bin_linked = gst_element_link (priv->video_display_bin, priv->photo_save_bin);
720
 
  if (!disp_bin_linked)
721
 
  {
722
 
    gst_bin_remove (GST_BIN (priv->pipeline), priv->video_display_bin);
723
 
    return FALSE;
724
 
  }
 
562
  cheese_camera_set_camera_source (camera);
725
563
 
726
564
  if (pipeline_was_playing)
727
565
  {
732
570
   * {
733
571
   * Restart recording... ?
734
572
   * } */
735
 
 
736
 
  return TRUE;
737
573
}
738
574
 
 
575
/**
 
576
 * cheese_camera_play:
 
577
 * @camera: a #CheeseCamera
 
578
 */
 
579
 
739
580
void
740
581
cheese_camera_play (CheeseCamera *camera)
741
582
{
752
593
    priv->current_format = cheese_camera_device_get_best_format (device);
753
594
    g_object_notify (G_OBJECT (camera), "format");
754
595
    caps = cheese_camera_device_get_caps_for_format (device, priv->current_format);
755
 
    if (G_UNLIKELY (gst_caps_is_empty (caps)))
756
 
    {
757
 
      gst_caps_unref (caps);
758
 
      caps = gst_caps_new_any ();
759
 
    }
760
596
  }
761
597
 
762
 
  g_object_set (priv->capsfilter, "caps", caps, NULL);
 
598
  if (!gst_caps_is_empty (caps))
 
599
  {
 
600
    g_signal_emit_by_name (priv->camerabin, "set-video-resolution-fps",
 
601
                           priv->current_format->width,
 
602
                           priv->current_format->height, 0, 1, 0);
 
603
  }
763
604
  gst_caps_unref (caps);
764
605
 
765
 
  gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
 
606
  gst_element_set_state (priv->camerabin, GST_STATE_PLAYING);
766
607
  priv->pipeline_is_playing = TRUE;
767
608
}
768
609
 
 
610
/**
 
611
 * cheese_camera_stop:
 
612
 * @camera: a #CheeseCamera
 
613
 */
 
614
 
769
615
void
770
616
cheese_camera_stop (CheeseCamera *camera)
771
617
{
772
618
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
773
619
 
774
 
  if (priv->pipeline != NULL)
775
 
    gst_element_set_state (priv->pipeline, GST_STATE_NULL);
 
620
  if (priv->camerabin != NULL)
 
621
    gst_element_set_state (priv->camerabin, GST_STATE_NULL);
776
622
  priv->pipeline_is_playing = FALSE;
777
623
}
778
624
 
780
626
cheese_camera_change_effect_filter (CheeseCamera *camera, GstElement *new_filter)
781
627
{
782
628
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
783
 
 
784
 
  gboolean is_playing = priv->pipeline_is_playing;
785
 
  gboolean ok;
786
 
 
787
 
  cheese_camera_stop (camera);
788
 
 
789
 
  gst_element_unlink_many (priv->camera_source_bin, priv->effect_filter,
790
 
                           priv->csp_post_effect, NULL);
791
 
 
792
 
  gst_bin_remove (GST_BIN (priv->video_display_bin), priv->effect_filter);
793
 
 
794
 
  gst_bin_add (GST_BIN (priv->video_display_bin), new_filter);
795
 
  ok = gst_element_link_many (priv->camera_source_bin, new_filter,
796
 
                              priv->csp_post_effect, NULL);
 
629
  gboolean             ok;
 
630
 
 
631
  g_object_set (G_OBJECT (priv->main_valve), "drop", TRUE, NULL);
 
632
 
 
633
  gst_element_unlink_many (priv->main_valve, priv->effect_filter,
 
634
                           priv->video_balance, NULL);
 
635
 
 
636
  g_object_ref (priv->effect_filter);
 
637
  gst_bin_remove (GST_BIN (priv->video_filter_bin), priv->effect_filter);
 
638
  gst_element_set_state (priv->effect_filter, GST_STATE_NULL);
 
639
  g_object_unref (priv->effect_filter);
 
640
 
 
641
  gst_bin_add (GST_BIN (priv->video_filter_bin), new_filter);
 
642
  ok = gst_element_link_many (priv->main_valve, new_filter,
 
643
                              priv->video_balance, NULL);
 
644
  gst_element_set_state (new_filter, GST_STATE_PAUSED);
 
645
 
797
646
  g_return_if_fail (ok);
798
647
 
799
 
  if (is_playing)
800
 
    cheese_camera_play (camera);
 
648
  g_object_set (G_OBJECT (priv->main_valve), "drop", FALSE, NULL);
801
649
 
802
650
  priv->effect_filter = new_filter;
803
651
}
804
652
 
805
 
void
806
 
cheese_camera_set_effect (CheeseCamera *camera, CheeseCameraEffect effect)
 
653
static GstElement *
 
654
cheese_camera_element_from_effect (CheeseCamera *camera, CheeseEffect *effect)
807
655
{
808
 
  GString    *rgb_effects_str = g_string_new ("");
809
 
  GString    *yuv_effects_str = g_string_new ("");
810
656
  char       *effects_pipeline_desc;
811
 
  int         i;
 
657
  char       *name;
812
658
  GstElement *effect_filter;
813
659
  GError     *err = NULL;
814
 
 
815
 
  for (i = 0; i < NUM_EFFECTS; i++)
816
 
  {
817
 
    if (effect & EFFECT_TO_PIPELINE_DESC[i].effect)
818
 
    {
819
 
      if (EFFECT_TO_PIPELINE_DESC[i].colorspace == RGB)
820
 
      {
821
 
        g_string_append (rgb_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
822
 
        g_string_append (rgb_effects_str, " ! ");
823
 
      }
824
 
      else
825
 
      {
826
 
        g_string_append (yuv_effects_str, " ! ");
827
 
        g_string_append (yuv_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
828
 
      }
829
 
    }
830
 
  }
831
 
  effects_pipeline_desc = g_strconcat ("ffmpegcolorspace ! ",
832
 
                                       rgb_effects_str->str,
833
 
                                       "ffmpegcolorspace",
834
 
                                       yuv_effects_str->str,
 
660
  char       *effect_desc;
 
661
  GstElement *colorspace1;
 
662
  GstElement *colorspace2;
 
663
  GstPad     *pad;
 
664
 
 
665
  g_object_get (G_OBJECT (effect),
 
666
                "pipeline_desc", &effect_desc,
 
667
                "name", &name, NULL);
 
668
 
 
669
  effects_pipeline_desc = g_strconcat ("ffmpegcolorspace name=colorspace1 ! ",
 
670
                                       effect_desc,
 
671
                                       " ! ffmpegcolorspace name=colorspace2",
835
672
                                       NULL);
836
 
 
837
 
  effect_filter = gst_parse_bin_from_description (effects_pipeline_desc, TRUE, &err);
 
673
  effect_filter = gst_parse_bin_from_description (effects_pipeline_desc, FALSE, &err);
838
674
  if (!effect_filter || (err != NULL))
839
675
  {
840
676
    g_error_free (err);
841
 
    g_error ("ERROR effect_filter\n");
 
677
    g_warning ("Error with effect filter %s. Ignored", name);
 
678
    return NULL;
842
679
  }
843
 
  cheese_camera_change_effect_filter (camera, effect_filter);
844
 
 
845
680
  g_free (effects_pipeline_desc);
846
 
  g_string_free (rgb_effects_str, TRUE);
847
 
  g_string_free (yuv_effects_str, TRUE);
848
 
}
849
 
 
850
 
void
851
 
cheese_camera_start_video_recording (CheeseCamera *camera, char *filename)
852
 
{
853
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
854
 
 
855
 
  g_object_set (CHEESE_CAMERA_GET_PRIVATE (camera)->video_file_sink, "location", filename, NULL);
856
 
  cheese_camera_change_sink (camera, priv->video_display_bin,
857
 
                             priv->video_save_bin, priv->photo_save_bin);
 
681
 
 
682
  /* Add ghost pads to effect_filter bin */
 
683
  colorspace1 = gst_bin_get_by_name (GST_BIN (effect_filter), "colorspace1");
 
684
  colorspace2 = gst_bin_get_by_name (GST_BIN (effect_filter), "colorspace2");
 
685
 
 
686
  pad = gst_element_get_static_pad (colorspace1, "sink");
 
687
  gst_element_add_pad (effect_filter, gst_ghost_pad_new ("sink", pad));
 
688
  gst_object_unref (GST_OBJECT (pad));
 
689
 
 
690
  pad = gst_element_get_static_pad (colorspace2, "src");
 
691
  gst_element_add_pad (effect_filter, gst_ghost_pad_new ("src", pad));
 
692
  gst_object_unref (GST_OBJECT (pad));
 
693
 
 
694
  return effect_filter;
 
695
}
 
696
 
 
697
/**
 
698
 * cheese_camera_set_effect:
 
699
 * @camera: a #CheeseCamera
 
700
 * @effect: a #CheeseEffect
 
701
 */
 
702
 
 
703
void
 
704
cheese_camera_set_effect (CheeseCamera *camera, CheeseEffect *effect)
 
705
{
 
706
  GstElement *effect_filter;
 
707
 
 
708
  effect_filter = cheese_camera_element_from_effect (camera, effect);
 
709
  if (effect_filter != NULL)
 
710
    cheese_camera_change_effect_filter (camera, effect_filter);
 
711
}
 
712
 
 
713
/**
 
714
 * cheese_camera_toggle_effects_pipeline:
 
715
 * @camera: a #CheeseCamera
 
716
 * @active: TRUE is effects pipeline is active, FALSE otherwise
 
717
 */
 
718
 
 
719
void
 
720
cheese_camera_toggle_effects_pipeline (CheeseCamera *camera, gboolean active)
 
721
{
 
722
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
723
 
 
724
  if (active)
 
725
  {
 
726
    g_object_set (G_OBJECT (priv->effects_valve), "drop", FALSE, NULL);
 
727
    g_object_set (G_OBJECT (priv->main_valve), "drop", TRUE, NULL);
 
728
  }
 
729
  else
 
730
  {
 
731
    g_object_set (G_OBJECT (priv->effects_valve), "drop", TRUE, NULL);
 
732
    g_object_set (G_OBJECT (priv->main_valve), "drop", FALSE, NULL);
 
733
  }
 
734
}
 
735
 
 
736
/**
 
737
 * cheese_camera_connect_effect_texture:
 
738
 * @camera: a #CheeseCamera
 
739
 * @effect: a #CheeseEffect
 
740
 * @texture: a #ClutterTexture
 
741
 */
 
742
 
 
743
void
 
744
cheese_camera_connect_effect_texture (CheeseCamera *camera, CheeseEffect *effect, ClutterTexture *texture)
 
745
{
 
746
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
747
 
 
748
  GstElement *effect_filter;
 
749
  GstElement *display_element;
 
750
  GstElement *display_queue;
 
751
  GstElement *control_valve;
 
752
  gboolean    ok = TRUE;
 
753
 
 
754
  g_object_set (G_OBJECT (priv->effects_valve), "drop", TRUE, NULL);
 
755
 
 
756
  control_valve = gst_element_factory_make ("valve", NULL);
 
757
  g_object_set (G_OBJECT (effect), "control_valve", control_valve, NULL);
 
758
 
 
759
  display_queue = gst_element_factory_make ("queue", NULL);
 
760
 
 
761
  effect_filter = cheese_camera_element_from_effect (camera, effect);
 
762
 
 
763
  display_element = clutter_gst_video_sink_new (texture);
 
764
  g_object_set (G_OBJECT (display_element), "async", FALSE, NULL);
 
765
 
 
766
  gst_bin_add_many (GST_BIN (priv->video_filter_bin), control_valve, effect_filter, display_queue, display_element, NULL);
 
767
 
 
768
  ok = gst_element_link_many (priv->effects_tee, control_valve, effect_filter, display_queue, display_element, NULL);
 
769
  g_return_if_fail (ok);
 
770
 
 
771
  /* HACK: I don't understand GStreamer enough to know why this works. */
 
772
  gst_element_set_state (control_valve, GST_STATE_PLAYING);
 
773
  gst_element_set_state (effect_filter, GST_STATE_PLAYING);
 
774
  gst_element_set_state (display_queue, GST_STATE_PLAYING);
 
775
  gst_element_set_state (display_element, GST_STATE_PLAYING);
 
776
 
 
777
  if (!ok)
 
778
      g_warning ("Could not create effects pipeline");
 
779
 
 
780
  g_object_set (G_OBJECT (priv->effects_valve), "drop", FALSE, NULL);
 
781
}
 
782
 
 
783
/**
 
784
 * cheese_camera_start_video_recording:
 
785
 * @camera: a #CheeseCamera
 
786
 * @filename: the name of the video file that should be recorded
 
787
 */
 
788
 
 
789
void
 
790
cheese_camera_start_video_recording (CheeseCamera *camera, const char *filename)
 
791
{
 
792
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
793
 
 
794
  g_object_set (priv->camerabin, "mode", MODE_VIDEO, NULL);
 
795
  gst_element_set_state (priv->camerabin, GST_STATE_READY);
 
796
  g_object_set (priv->camerabin, "filename", filename, NULL);
 
797
  g_signal_emit_by_name (priv->camerabin, "capture-start", 0);
 
798
  gst_element_set_state (priv->camerabin, GST_STATE_PLAYING);
858
799
  priv->is_recording = TRUE;
859
800
}
860
801
 
869
810
    GST_WARNING ("Cannot cleanly shutdown recording pipeline, forcing");
870
811
    g_signal_emit (camera, camera_signals[VIDEO_SAVED], 0);
871
812
 
872
 
    cheese_camera_change_sink (camera, priv->video_display_bin,
873
 
                               priv->photo_save_bin, priv->video_save_bin);
 
813
    cheese_camera_stop (camera);
 
814
    g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
874
815
    priv->is_recording = FALSE;
875
816
  }
876
817
 
877
818
  return FALSE;
878
819
}
879
820
 
 
821
/**
 
822
 * cheese_camera_stop_video_recording:
 
823
 * @camera: a #CheeseCamera
 
824
 */
 
825
 
880
826
void
881
827
cheese_camera_stop_video_recording (CheeseCamera *camera)
882
828
{
883
829
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
884
830
  GstState             state;
885
831
 
886
 
  gst_element_get_state (priv->pipeline, &state, NULL, 0);
 
832
  gst_element_get_state (priv->camerabin, &state, NULL, 0);
887
833
 
888
834
  if (state == GST_STATE_PLAYING)
889
835
  {
890
 
    /* Send EOS message down the pipeline by stopping video and audio source*/
891
 
    GST_DEBUG ("Sending EOS event down the recording pipeline");
892
 
    gst_element_send_event (priv->video_source, gst_event_new_eos ());
893
 
    gst_element_send_event (priv->audio_source, gst_event_new_eos ());
894
 
    priv->eos_timeout_id = g_timeout_add (3000, cheese_camera_force_stop_video_recording, camera);
 
836
    g_signal_emit_by_name (priv->camerabin, "capture-stop", 0);
 
837
    g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
 
838
    priv->is_recording = FALSE;
895
839
  }
896
840
  else
897
841
  {
899
843
  }
900
844
}
901
845
 
 
846
static void
 
847
cheese_camera_image_done_cb (GstElement *camerabin, gchar *filename,
 
848
                             CheeseCamera *camera)
 
849
{
 
850
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
851
  g_signal_handler_disconnect (G_OBJECT (priv->camerabin),
 
852
                               priv->photo_handler_signal_id);
 
853
  priv->photo_handler_signal_id = 0;
 
854
  if (priv->photo_filename != NULL)
 
855
    g_signal_emit (camera, camera_signals[PHOTO_SAVED], 0);
 
856
}
 
857
 
 
858
/**
 
859
 * cheese_camera_take_photo:
 
860
 * @camera: a #CheeseCamera
 
861
 * @filename: name of the file to save photo to
 
862
 *
 
863
 * Returns: TRUE on success, FALSE if an error occurred
 
864
 */
902
865
gboolean
903
 
cheese_camera_take_photo (CheeseCamera *camera, char *filename)
 
866
cheese_camera_take_photo (CheeseCamera *camera, const char *filename)
904
867
{
905
868
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
906
869
 
909
872
    GST_WARNING ("Still waiting for previous photo data, ignoring new request");
910
873
    return FALSE;
911
874
  }
 
875
  priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->camerabin),
 
876
                                                    "image-done",
 
877
                                                    G_CALLBACK (cheese_camera_image_done_cb),
 
878
                                                    camera);
912
879
 
913
880
  g_free (priv->photo_filename);
914
881
  priv->photo_filename = g_strdup (filename);
915
882
 
916
 
  /* Take the photo by connecting the handoff signal */
917
 
  priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->photo_sink),
918
 
                                                    "handoff",
919
 
                                                    G_CALLBACK (cheese_camera_photo_data_cb),
920
 
                                                    camera);
 
883
  /* Take the photo*/
 
884
 
 
885
  /* Only copy the data if we're giving away a pixbuf,
 
886
   * not if we're throwing everything away straight away */
 
887
 
 
888
  if (priv->photo_filename != NULL)
 
889
  {
 
890
    g_object_set (priv->camerabin, "filename", priv->photo_filename, NULL);
 
891
    g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
 
892
    g_signal_emit_by_name (priv->camerabin, "capture-start", 0);
 
893
  }
 
894
  else
 
895
  {
 
896
    g_signal_handler_disconnect (G_OBJECT (priv->camerabin),
 
897
                                 priv->photo_handler_signal_id);
 
898
    priv->photo_handler_signal_id = 0;
 
899
    return FALSE;
 
900
  }
 
901
 
921
902
  return TRUE;
922
903
}
923
904
 
 
905
/**
 
906
 * cheese_camera_take_photo_pixbuf:
 
907
 * @camera: a #CheeseCamera
 
908
 */
 
909
 
924
910
gboolean
925
911
cheese_camera_take_photo_pixbuf (CheeseCamera *camera)
926
912
{
927
913
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
914
  GstCaps             *caps;
928
915
 
929
916
  if (priv->photo_handler_signal_id != 0)
930
917
  {
931
918
    GST_WARNING ("Still waiting for previous photo data, ignoring new request");
932
919
    return FALSE;
933
920
  }
934
 
 
935
 
  /* Take the photo by connecting the handoff signal */
936
 
  priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->photo_sink),
937
 
                                                    "handoff",
938
 
                                                    G_CALLBACK (cheese_camera_photo_data_cb),
 
921
  priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->camerabin),
 
922
                                                    "image-done",
 
923
                                                    G_CALLBACK (cheese_camera_image_done_cb),
939
924
                                                    camera);
 
925
  caps = gst_caps_new_simple ("video/x-raw-rgb",
 
926
                              "bpp", G_TYPE_INT, 24,
 
927
                              "depth", G_TYPE_INT, 24,
 
928
                              NULL);
 
929
  g_object_set (G_OBJECT (priv->camerabin), "preview-caps", caps, NULL);
 
930
  gst_caps_unref (caps);
 
931
 
 
932
  if (priv->photo_filename)
 
933
    g_free (priv->photo_filename);
 
934
  priv->photo_filename = NULL;
 
935
 
 
936
  /* Take the photo */
 
937
 
 
938
  g_object_set (priv->camerabin, "filename", "/dev/null", NULL);
 
939
  g_object_set (priv->camerabin, "mode", MODE_IMAGE, NULL);
 
940
  g_signal_emit_by_name (priv->camerabin, "capture-start", 0);
 
941
 
940
942
  return TRUE;
941
943
}
942
944
 
949
951
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
950
952
 
951
953
  cheese_camera_stop (camera);
952
 
  if (priv->pipeline != NULL)
953
 
    gst_object_unref (priv->pipeline);
954
 
 
955
 
  if (priv->is_recording && priv->photo_save_bin != NULL)
956
 
    gst_object_unref (priv->photo_save_bin);
957
 
  else if (priv->video_save_bin != NULL)
958
 
    gst_object_unref (priv->video_save_bin);
959
 
 
960
 
  g_free (priv->photo_filename);
 
954
 
 
955
  if (priv->camerabin != NULL)
 
956
    gst_object_unref (priv->camerabin);
 
957
 
 
958
  if (priv->photo_filename)
 
959
    g_free (priv->photo_filename);
961
960
  g_free (priv->device_name);
962
961
  g_boxed_free (CHEESE_TYPE_VIDEO_FORMAT, priv->current_format);
963
962
 
978
977
 
979
978
  switch (prop_id)
980
979
  {
981
 
    case PROP_VIDEO_WINDOW:
982
 
      g_value_set_pointer (value, priv->video_window);
 
980
    case PROP_VIDEO_TEXTURE:
 
981
      g_value_set_pointer (value, priv->video_texture);
983
982
      break;
984
983
    case PROP_DEVICE_NAME:
985
984
      g_value_set_string (value, priv->device_name);
1004
1003
 
1005
1004
  switch (prop_id)
1006
1005
  {
1007
 
    case PROP_VIDEO_WINDOW:
1008
 
      priv->video_window = g_value_get_pointer (value);
1009
 
      g_signal_connect (priv->video_window, "expose-event",
1010
 
                        G_CALLBACK (cheese_camera_expose_cb), self);
 
1006
    case PROP_VIDEO_TEXTURE:
 
1007
      priv->video_texture = g_value_get_pointer (value);
1011
1008
      break;
1012
1009
    case PROP_DEVICE_NAME:
1013
1010
      g_free (priv->device_name);
1059
1056
                                              g_cclosure_marshal_VOID__VOID,
1060
1057
                                              G_TYPE_NONE, 0);
1061
1058
 
1062
 
 
1063
 
  g_object_class_install_property (object_class, PROP_VIDEO_WINDOW,
1064
 
                                   g_param_spec_pointer ("video-window",
 
1059
  camera_signals[STATE_FLAGS_CHANGED] = g_signal_new ("state-flags-changed", G_OBJECT_CLASS_TYPE (klass),
 
1060
                                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 
1061
                                                G_STRUCT_OFFSET (CheeseCameraClass, state_flags_changed),
 
1062
                                                NULL, NULL,
 
1063
                                                g_cclosure_marshal_VOID__INT,
 
1064
                                                G_TYPE_NONE, 1, G_TYPE_INT);
 
1065
 
 
1066
 
 
1067
  g_object_class_install_property (object_class, PROP_VIDEO_TEXTURE,
 
1068
                                   g_param_spec_pointer ("video-texture",
1065
1069
                                                         NULL,
1066
1070
                                                         NULL,
1067
1071
                                                         G_PARAM_READWRITE));
1097
1101
  priv->current_format          = NULL;
1098
1102
}
1099
1103
 
 
1104
/**
 
1105
 * cheese_camera_new:
 
1106
 * @video_texture: a #ClutterTexture
 
1107
 * @camera_device_name: (allow-none): the device name
 
1108
 * @x_resolution: the resolution width
 
1109
 * @y_resolution: the resolution height
 
1110
 */
 
1111
 
1100
1112
CheeseCamera *
1101
 
cheese_camera_new (GtkWidget *video_window, char *camera_device_name,
 
1113
cheese_camera_new (ClutterTexture *video_texture, char *camera_device_name,
1102
1114
                   int x_resolution, int y_resolution)
1103
1115
{
1104
1116
  CheeseCamera      *camera;
1109
1121
 
1110
1122
  if (camera_device_name)
1111
1123
  {
1112
 
    camera = g_object_new (CHEESE_TYPE_CAMERA, "video-window", video_window,
 
1124
    camera = g_object_new (CHEESE_TYPE_CAMERA, "video-texture", video_texture,
1113
1125
                           "device_name", camera_device_name,
1114
1126
                           "format", format, NULL);
1115
1127
  }
1116
1128
  else
1117
1129
  {
1118
 
    camera = g_object_new (CHEESE_TYPE_CAMERA, "video-window", video_window,
 
1130
    camera = g_object_new (CHEESE_TYPE_CAMERA, "video-texture", video_texture,
1119
1131
                           "format", format, NULL);
1120
1132
  }
1121
1133
 
1122
1134
  return camera;
1123
1135
}
1124
1136
 
1125
 
void
1126
 
cheese_camera_setup (CheeseCamera *camera, char *id, GError **error)
1127
 
{
1128
 
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1129
 
 
1130
 
  gboolean ok        = TRUE;
 
1137
/**
 
1138
 * cheese_camera_set_device_by_dev_file:
 
1139
 * @camera: a #CheeseCamera
 
1140
 * @file: the filename
 
1141
 */
 
1142
 
 
1143
void
 
1144
cheese_camera_set_device_by_dev_file (CheeseCamera *camera, const gchar *file)
 
1145
{
 
1146
  g_return_if_fail (CHEESE_IS_CAMERA (camera));
 
1147
  g_object_set (camera, "device_name", file, NULL);
 
1148
}
 
1149
 
 
1150
static void
 
1151
cheese_camera_set_device_by_dev_udi (CheeseCamera *camera, const gchar *udi)
 
1152
{
 
1153
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
1154
  int                  i;
 
1155
 
 
1156
  g_return_if_fail (CHEESE_IS_CAMERA (camera));
 
1157
 
 
1158
 
 
1159
  for (i = 0; i < priv->num_camera_devices; i++)
 
1160
  {
 
1161
    CheeseCameraDevice *device = g_ptr_array_index (priv->camera_devices, i);
 
1162
    if (strcmp (cheese_camera_device_get_id (device), udi) == 0)
 
1163
    {
 
1164
      g_object_set (camera,
 
1165
                    "device_name", cheese_camera_device_get_id (device),
 
1166
                    NULL);
 
1167
      break;
 
1168
    }
 
1169
  }
 
1170
}
 
1171
 
 
1172
/**
 
1173
 * cheese_camera_setup:
 
1174
 * @camera: a #CheeseCamera
 
1175
 * @id: (allow-none): the device id
 
1176
 * @error: return location for a GError or NULL
 
1177
 */
 
1178
 
 
1179
void
 
1180
cheese_camera_setup (CheeseCamera *camera, const char *id, GError **error)
 
1181
{
 
1182
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
1183
 
1131
1184
  GError  *tmp_error = NULL;
 
1185
  GstElement *video_sink;
 
1186
  GstCaps *caps;
1132
1187
 
1133
1188
  cheese_camera_detect_camera_devices (camera);
1134
1189
 
1143
1198
    cheese_camera_set_device_by_dev_udi (camera, id);
1144
1199
  }
1145
1200
 
1146
 
  priv->pipeline = gst_pipeline_new ("pipeline");
1147
 
 
1148
 
  cheese_camera_create_video_display_bin (camera, &tmp_error);
1149
 
 
1150
 
  cheese_camera_create_photo_save_bin (camera, &tmp_error);
1151
 
 
1152
 
  cheese_camera_create_video_save_bin (camera, &tmp_error);
1153
 
  if (tmp_error != NULL)
 
1201
 
 
1202
  if ((priv->camerabin = gst_element_factory_make ("camerabin", "camerabin")) == NULL)
 
1203
  {
 
1204
    cheese_camera_set_error_element_not_found (error, "camerabin");
 
1205
  }
 
1206
  g_object_set (priv->camerabin, "video-capture-height", 0,
 
1207
                "video-capture-width", 0, NULL);
 
1208
 
 
1209
  /* Create a clutter-gst sink and set it as camerabin sink*/
 
1210
 
 
1211
  if ((video_sink = clutter_gst_video_sink_new (priv->video_texture)) == NULL)
 
1212
  {
 
1213
    cheese_camera_set_error_element_not_found (error, "cluttervideosink");
 
1214
  }
 
1215
  g_object_set (G_OBJECT (video_sink), "async", FALSE, NULL);
 
1216
  g_object_set (G_OBJECT (priv->camerabin), "viewfinder-sink", video_sink, NULL);
 
1217
 
 
1218
  /* Set flags to enable conversions*/
 
1219
 
 
1220
  g_object_set (G_OBJECT (priv->camerabin), "flags",
 
1221
                GST_CAMERABIN_FLAG_SOURCE_RESIZE |
 
1222
                GST_CAMERABIN_FLAG_SOURCE_COLOR_CONVERSION |
 
1223
                GST_CAMERABIN_FLAG_VIEWFINDER_SCALE |
 
1224
                GST_CAMERABIN_FLAG_AUDIO_CONVERSION |
 
1225
                GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION |
 
1226
                GST_CAMERABIN_FLAG_VIDEO_COLOR_CONVERSION,
 
1227
                NULL);
 
1228
 
 
1229
  /* Set caps to filter, so it doesn't defaults to I420 format*/
 
1230
 
 
1231
  caps = gst_caps_from_string ("video/x-raw-yuv; video/x-raw-rgb");
 
1232
  g_object_set (G_OBJECT (priv->camerabin), "filter-caps", caps, NULL);
 
1233
  gst_caps_unref (caps);
 
1234
 
 
1235
  cheese_camera_set_camera_source (camera);
 
1236
  cheese_camera_set_video_recording (camera, &tmp_error);
 
1237
  cheese_camera_create_video_filter_bin (camera, &tmp_error);
 
1238
 
 
1239
  if (tmp_error != NULL || (error != NULL && *error != NULL))
1154
1240
  {
1155
1241
    g_propagate_prefixed_error (error, tmp_error,
1156
1242
                                _("One or more needed GStreamer elements are missing: "));
1157
1243
    GST_WARNING ("%s", (*error)->message);
1158
1244
    return;
1159
1245
  }
1160
 
 
1161
 
  gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_display_bin,
1162
 
                    priv->photo_save_bin, NULL);
1163
 
 
1164
 
  ok = gst_element_link (priv->video_display_bin, priv->photo_save_bin);
1165
 
 
1166
 
  priv->bus = gst_element_get_bus (priv->pipeline);
 
1246
  g_object_set (G_OBJECT (priv->camerabin), "video-source-filter", priv->video_filter_bin, NULL);
 
1247
 
 
1248
  priv->bus = gst_element_get_bus (priv->camerabin);
1167
1249
  gst_bus_add_signal_watch (priv->bus);
1168
1250
 
1169
1251
  g_signal_connect (G_OBJECT (priv->bus), "message",
1170
1252
                    G_CALLBACK (cheese_camera_bus_message_cb), camera);
1171
 
 
1172
 
  gst_bus_set_sync_handler (priv->bus, (GstBusSyncHandler) cheese_camera_bus_sync_handler, camera);
1173
 
 
1174
 
  if (!ok)
1175
 
    g_error ("Unable link pipeline for photo");
1176
1253
}
1177
1254
 
 
1255
/**
 
1256
 * cheese_camera_get_camera_devices:
 
1257
 * @camera: a #CheeseCamera
 
1258
 *
 
1259
 * Returns: (element-type Cheese.CameraDevice) (transfer container): Array of #CheeseCameraDevice
 
1260
 */
 
1261
 
1178
1262
GPtrArray *
1179
1263
cheese_camera_get_camera_devices (CheeseCamera *camera)
1180
1264
{
1187
1271
  return g_ptr_array_ref (priv->camera_devices);
1188
1272
}
1189
1273
 
1190
 
void
1191
 
cheese_camera_set_device_by_dev_file (CheeseCamera *camera, char *file)
1192
 
{
1193
 
  g_return_if_fail (CHEESE_IS_CAMERA (camera));
1194
 
  g_object_set (camera, "device_name", file, NULL);
1195
 
}
1196
 
 
1197
 
void
1198
 
cheese_camera_set_device_by_dev_udi (CheeseCamera *camera, char *udi)
1199
 
{
1200
 
  CheeseCameraPrivate *priv;
1201
 
  int i;
1202
 
 
1203
 
  g_return_if_fail (CHEESE_IS_CAMERA (camera));
1204
 
 
1205
 
  priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1206
 
 
1207
 
  for (i = 0; i < priv->num_camera_devices; i++)
1208
 
  {
1209
 
    CheeseCameraDevice *device = g_ptr_array_index (priv->camera_devices, i);
1210
 
    if (strcmp (cheese_camera_device_get_id (device), udi) == 0)
1211
 
    {
1212
 
      g_object_set (camera,
1213
 
                    "device_name", cheese_camera_device_get_id (device),
1214
 
                    NULL);
1215
 
      break;
1216
 
    }
1217
 
  }
1218
 
}
 
1274
/**
 
1275
 * cheese_camera_get_video_formats:
 
1276
 * @camera: a #CheeseCamera
 
1277
 *
 
1278
 * Returns: (element-type Cheese.VideoFormat) (transfer container): List of #CheeseVideoFormat
 
1279
 */
1219
1280
 
1220
1281
GList *
1221
1282
cheese_camera_get_video_formats (CheeseCamera *camera)
1232
1293
    return NULL;
1233
1294
}
1234
1295
 
1235
 
gboolean
 
1296
static gboolean
1236
1297
cheese_camera_is_playing (CheeseCamera *camera)
1237
1298
{
1238
1299
  CheeseCameraPrivate *priv;
1244
1305
  return priv->pipeline_is_playing;
1245
1306
}
1246
1307
 
 
1308
/**
 
1309
 * cheese_camera_set_video_format:
 
1310
 * @camera: a #CheeseCamera
 
1311
 * format: a #CheeseVideoFormat
 
1312
 */
 
1313
 
1247
1314
void
1248
1315
cheese_camera_set_video_format (CheeseCamera *camera, CheeseVideoFormat *format)
1249
1316
{
1250
1317
  CheeseCameraPrivate *priv;
 
1318
 
1251
1319
  g_return_if_fail (CHEESE_IS_CAMERA (camera));
1252
1320
  g_return_if_fail (format != NULL);
1253
1321
 
1265
1333
  }
1266
1334
}
1267
1335
 
 
1336
/**
 
1337
 * cheese_camera_get_current_video_format:
 
1338
 * @camera: a #CheeseCamera
 
1339
 *
 
1340
 * Returns: (transfer none): a #CheeseVideoFormat
 
1341
 */
 
1342
 
1268
1343
const CheeseVideoFormat *
1269
1344
cheese_camera_get_current_video_format (CheeseCamera *camera)
1270
1345
{
1271
1346
  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
1347
 
1272
1348
  g_return_val_if_fail (CHEESE_IS_CAMERA (camera), NULL);
1273
1349
 
1274
1350
  return priv->current_format;
1275
1351
}
1276
1352
 
 
1353
/**
 
1354
 * cheese_camera_get_balance_property_range:
 
1355
 * @camera: a #CheeseCamera
 
1356
 * @property: name of the balance property
 
1357
 * @min: (out): minimun value
 
1358
 * @max: (out): maximum value
 
1359
 * @def: (out): default value
 
1360
 */
 
1361
 
1277
1362
gboolean
1278
1363
cheese_camera_get_balance_property_range (CheeseCamera *camera,
1279
 
                                          gchar *property,
 
1364
                                          const gchar *property,
1280
1365
                                          gdouble *min, gdouble *max, gdouble *def)
1281
1366
{
1282
1367
  CheeseCameraPrivate *priv;
1304
1389
  return TRUE;
1305
1390
}
1306
1391
 
 
1392
/**
 
1393
 * cheese_camera_set_balance_property:
 
1394
 * @camera: A #CheeseCamera
 
1395
 * @property: name of the balance property
 
1396
 * @value: value to be set
 
1397
 */
 
1398
 
1307
1399
void
1308
 
cheese_camera_set_balance_property (CheeseCamera *camera, gchar *property, gdouble value)
 
1400
cheese_camera_set_balance_property (CheeseCamera *camera, const gchar *property, gdouble value)
1309
1401
{
1310
1402
  CheeseCameraPrivate *priv;
1311
1403