2
* Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org>
3
* Copyright © 2007,2008 daniel g. siegel <dgsiegel@gnome.org>
4
* Copyright © 2008 Ryan Zeigler <zeiglerr@gmail.com>
6
* Licensed under the GNU General Public License Version 2
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#include <cheese-config.h>
26
#include <glib-object.h>
28
#include <glib/gi18n.h>
32
#include <gdk-pixbuf/gdk-pixbuf.h>
39
#include <sys/ioctl.h>
40
#include <linux/videodev.h>
42
#include "cheese-webcam.h"
44
G_DEFINE_TYPE (CheeseWebcam, cheese_webcam, G_TYPE_OBJECT)
46
#define CHEESE_WEBCAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHEESE_TYPE_WEBCAM, CheeseWebcamPrivate))
48
#define CHEESE_WEBCAM_ERROR cheese_webcam_error_quark ()
50
#define MIN_DEFAULT_RATE 15.0
52
static void find_highest_framerate (CheeseVideoFormat *format);
54
enum CheeseWebcamError
56
CHEESE_WEBCAM_ERROR_UNKNOWN,
57
CHEESE_WEBCAM_ERROR_ELEMENT_NOT_FOUND
62
GtkWidget *video_window;
67
/* We build the active pipeline by linking the appropriate pipelines listed below*/
68
GstElement *webcam_source_bin;
69
GstElement *video_display_bin;
70
GstElement *photo_save_bin;
71
GstElement *video_save_bin;
73
GstElement *video_source;
74
GstElement *capsfilter;
75
GstElement *video_file_sink;
76
GstElement *photo_sink;
77
GstElement *audio_source;
78
GstElement *audio_enc;
79
GstElement *video_enc;
81
GstElement *effect_filter, *csp_post_effect;
82
GstElement *video_balance, *csp_post_balance;
84
gulong photo_handler_signal_id;
86
gboolean is_recording;
87
gboolean pipeline_is_playing;
90
int num_webcam_devices;
92
CheeseWebcamDevice *webcam_devices;
96
CheeseVideoFormat *current_format;
99
} CheeseWebcamPrivate;
117
static guint webcam_signals[LAST_SIGNAL];
127
CheeseWebcamEffect effect;
128
const char *pipeline_desc;
129
VideoColorSpace colorspace; /* The color space the effect works in */
130
} EffectToPipelineDesc;
133
static const EffectToPipelineDesc EFFECT_TO_PIPELINE_DESC[] = {
134
{CHEESE_WEBCAM_EFFECT_NO_EFFECT, "identity", RGB},
135
{CHEESE_WEBCAM_EFFECT_MAUVE, "videobalance saturation=1.5 hue=+0.5", YUV},
136
{CHEESE_WEBCAM_EFFECT_NOIR_BLANC, "videobalance saturation=0", YUV},
137
{CHEESE_WEBCAM_EFFECT_SATURATION, "videobalance saturation=2", YUV},
138
{CHEESE_WEBCAM_EFFECT_HULK, "videobalance saturation=1.5 hue=-0.5", YUV},
139
{CHEESE_WEBCAM_EFFECT_VERTICAL_FLIP, "videoflip method=5", YUV},
140
{CHEESE_WEBCAM_EFFECT_HORIZONTAL_FLIP, "videoflip method=4", YUV},
141
{CHEESE_WEBCAM_EFFECT_SHAGADELIC, "shagadelictv", RGB},
142
{CHEESE_WEBCAM_EFFECT_VERTIGO, "vertigotv", RGB},
143
{CHEESE_WEBCAM_EFFECT_EDGE, "edgetv", RGB},
144
{CHEESE_WEBCAM_EFFECT_DICE, "dicetv", RGB},
145
{CHEESE_WEBCAM_EFFECT_WARP, "warptv", RGB}
148
static const int NUM_EFFECTS = G_N_ELEMENTS (EFFECT_TO_PIPELINE_DESC);
151
cheese_webcam_error_quark (void)
153
return g_quark_from_static_string ("cheese-webcam-error-quark");
156
static GstBusSyncReply
157
cheese_webcam_bus_sync_handler (GstBus *bus, GstMessage *message, CheeseWebcam *webcam)
159
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
160
GstXOverlay *overlay;
162
if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
165
if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
168
overlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
170
if (g_object_class_find_property (G_OBJECT_GET_CLASS (overlay),
171
"force-aspect-ratio"))
172
g_object_set (G_OBJECT (overlay), "force-aspect-ratio", TRUE, NULL);
174
gst_x_overlay_set_xwindow_id (overlay,
175
GDK_WINDOW_XWINDOW (gtk_widget_get_window (priv->video_window)));
177
gst_message_unref (message);
183
cheese_webcam_change_sink (CheeseWebcam *webcam, GstElement *src,
184
GstElement *new_sink, GstElement *old_sink)
186
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
187
gboolean is_playing = priv->pipeline_is_playing;
189
cheese_webcam_stop (webcam);
191
gst_element_unlink (src, old_sink);
192
gst_object_ref (old_sink);
193
gst_bin_remove (GST_BIN (priv->pipeline), old_sink);
195
gst_bin_add (GST_BIN (priv->pipeline), new_sink);
196
gst_element_link (src, new_sink);
199
cheese_webcam_play (webcam);
203
cheese_webcam_expose_cb (GtkWidget *widget, GdkEventExpose *event, CheeseWebcam *webcam)
205
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
207
GstXOverlay *overlay = GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (priv->pipeline),
208
GST_TYPE_X_OVERLAY));
210
gst_element_get_state (priv->pipeline, &state, NULL, 0);
212
if ((state < GST_STATE_PLAYING) || (overlay == NULL))
214
gdk_draw_rectangle (widget->window, widget->style->black_gc, TRUE,
215
0, 0, widget->allocation.width, widget->allocation.height);
219
gst_x_overlay_expose (overlay);
226
cheese_webcam_photo_data_cb (GstElement *element, GstBuffer *buffer,
227
GstPad *pad, CheeseWebcam *webcam)
229
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
232
const GstStructure *structure;
233
int width, height, stride;
235
const int bits_per_pixel = 8;
237
caps = gst_buffer_get_caps (buffer);
238
structure = gst_caps_get_structure (caps, 0);
239
gst_structure_get_int (structure, "width", &width);
240
gst_structure_get_int (structure, "height", &height);
242
stride = buffer->size / height;
243
pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer), GDK_COLORSPACE_RGB,
244
FALSE, bits_per_pixel, width, height, stride,
247
gdk_pixbuf_save (pixbuf, priv->photo_filename, "jpeg", NULL, NULL);
248
g_object_unref (G_OBJECT (pixbuf));
250
g_signal_handler_disconnect (G_OBJECT (priv->photo_sink),
251
priv->photo_handler_signal_id);
252
priv->photo_handler_signal_id = 0;
254
g_signal_emit (webcam, webcam_signals[PHOTO_SAVED], 0);
258
cheese_webcam_bus_message_cb (GstBus *bus, GstMessage *message, CheeseWebcam *webcam)
260
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
262
if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS)
264
if (priv->is_recording)
266
g_print ("Received EOS message\n");
268
g_source_remove (priv->eos_timeout_id);
270
g_signal_emit (webcam, webcam_signals[VIDEO_SAVED], 0);
272
cheese_webcam_change_sink (webcam, priv->video_display_bin,
273
priv->photo_save_bin, priv->video_save_bin);
274
priv->is_recording = FALSE;
280
cheese_webcam_get_video_devices_from_hal (CheeseWebcam *webcam)
282
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
288
LibHalContext *hal_ctx;
290
priv->num_webcam_devices = 0;
292
g_print ("Probing devices with HAL...\n");
294
dbus_error_init (&error);
295
hal_ctx = libhal_ctx_new ();
298
g_warning ("Could not create libhal context");
299
dbus_error_free (&error);
303
if (!libhal_ctx_set_dbus_connection (hal_ctx, dbus_bus_get (DBUS_BUS_SYSTEM, &error)))
305
g_warning ("libhal_ctx_set_dbus_connection: %s: %s", error.name, error.message);
306
dbus_error_free (&error);
310
if (!libhal_ctx_init (hal_ctx, &error))
312
if (dbus_error_is_set (&error))
314
g_warning ("libhal_ctx_init: %s: %s", error.name, error.message);
315
dbus_error_free (&error);
317
g_warning ("Could not initialise connection to hald.\n"
318
"Normally this means the HAL daemon (hald) is not running or not ready");
322
udis = libhal_find_device_by_capability (hal_ctx, "video4linux", &num_udis, &error);
324
if (dbus_error_is_set (&error))
326
g_warning ("libhal_find_device_by_capability: %s: %s", error.name, error.message);
327
dbus_error_free (&error);
331
/* Initialize webcam structures */
332
priv->webcam_devices = g_new0 (CheeseWebcamDevice, num_udis);
334
for (i = 0; i < num_udis; i++)
337
char *parent_udi = NULL;
338
char *subsystem = NULL;
339
char *gstreamer_src, *product_name;
340
struct v4l2_capability v2cap;
341
struct video_capability v1cap;
344
gchar *property_name = NULL;
346
parent_udi = libhal_device_get_property_string (hal_ctx, udis[i], "info.parent", &error);
347
if (dbus_error_is_set (&error))
349
g_warning ("error getting parent for %s: %s: %s", udis[i], error.name, error.message);
350
dbus_error_free (&error);
353
if (parent_udi != NULL)
355
subsystem = libhal_device_get_property_string (hal_ctx, parent_udi, "info.subsystem", NULL);
356
if (subsystem == NULL) continue;
357
property_name = g_strjoin (".", subsystem, "vendor_id", NULL);
358
vendor_id = libhal_device_get_property_int (hal_ctx, parent_udi, property_name, &error);
359
if (dbus_error_is_set (&error))
361
g_warning ("error getting vendor id: %s: %s", error.name, error.message);
362
dbus_error_free (&error);
364
g_free (property_name);
366
property_name = g_strjoin (".", subsystem, "product_id", NULL);
367
product_id = libhal_device_get_property_int (hal_ctx, parent_udi, property_name, &error);
368
if (dbus_error_is_set (&error))
370
g_warning ("error getting product id: %s: %s", error.name, error.message);
371
dbus_error_free (&error);
373
g_free (property_name);
374
libhal_free_string (subsystem);
375
libhal_free_string (parent_udi);
378
g_print ("Found device %04x:%04x, getting capabilities...\n", vendor_id, product_id);
380
device = libhal_device_get_property_string (hal_ctx, udis[i], "video4linux.device", &error);
381
if (dbus_error_is_set (&error))
383
g_warning ("error getting V4L device for %s: %s: %s", udis[i], error.name, error.message);
384
dbus_error_free (&error);
388
/* vbi devices support capture capability too, but cannot be used,
389
* so detect them by device name */
390
if (strstr (device, "vbi"))
392
g_print ("Skipping vbi device: %s\n", device);
393
libhal_free_string (device);
397
if ((fd = open (device, O_RDONLY | O_NONBLOCK)) < 0)
399
g_warning ("Failed to open %s: %s", device, strerror (errno));
400
libhal_free_string (device);
403
ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
406
ok = ioctl (fd, VIDIOCGCAP, &v1cap);
409
g_warning ("Error while probing v4l capabilities for %s: %s",
410
device, strerror (errno));
411
libhal_free_string (device);
415
g_print ("Detected v4l device: %s\n", v1cap.name);
416
g_print ("Device type: %d\n", v1cap.type);
417
gstreamer_src = "v4lsrc";
418
product_name = v1cap.name;
422
guint cap = v2cap.capabilities;
423
g_print ("Detected v4l2 device: %s\n", v2cap.card);
424
g_print ("Driver: %s, version: %d\n", v2cap.driver, v2cap.version);
426
/* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */
427
g_print ("Capabilities: 0x%08X\n", v2cap.capabilities);
428
if (!(cap & V4L2_CAP_VIDEO_CAPTURE))
430
g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
431
"Removing it from device list.\n", device);
432
libhal_free_string (device);
436
gstreamer_src = "v4l2src";
437
product_name = (char *) v2cap.card;
442
priv->webcam_devices[priv->num_webcam_devices].hal_udi = g_strdup (udis[i]);
443
priv->webcam_devices[priv->num_webcam_devices].video_device = g_strdup (device);
444
priv->webcam_devices[priv->num_webcam_devices].gstreamer_src = g_strdup (gstreamer_src);
445
priv->webcam_devices[priv->num_webcam_devices].product_name = g_strdup (product_name);
446
priv->webcam_devices[priv->num_webcam_devices].num_video_formats = 0;
447
priv->webcam_devices[priv->num_webcam_devices].video_formats =
448
g_array_new (FALSE, FALSE, sizeof (CheeseVideoFormat));
449
priv->webcam_devices[priv->num_webcam_devices].supported_resolutions =
450
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
451
priv->num_webcam_devices++;
453
libhal_free_string (device);
456
libhal_free_string_array (udis);
458
if (priv->num_webcam_devices == 0)
460
/* Create a fake device so that resolution changing stil works even if the
461
* computer doesn't have a webcam. */
465
priv->webcam_devices = g_new0 (CheeseWebcamDevice, 1);
467
priv->webcam_devices[0].num_video_formats = 0;
468
priv->webcam_devices[0].video_formats =
469
g_array_new (FALSE, FALSE, sizeof (CheeseVideoFormat));
470
priv->webcam_devices[0].hal_udi = g_strdup ("cheese_fake_videodevice");
475
cheese_webcam_get_supported_framerates (CheeseVideoFormat *video_format, GstStructure *structure)
477
const GValue *framerates;
480
framerates = gst_structure_get_value (structure, "framerate");
481
if (GST_VALUE_HOLDS_FRACTION (framerates))
483
video_format->num_framerates = 1;
484
video_format->framerates = g_new0 (CheeseFramerate, video_format->num_framerates);
485
video_format->framerates[0].numerator = gst_value_get_fraction_numerator (framerates);
486
video_format->framerates[0].denominator = gst_value_get_fraction_denominator (framerates);
488
else if (GST_VALUE_HOLDS_LIST (framerates))
490
video_format->num_framerates = gst_value_list_get_size (framerates);
491
video_format->framerates = g_new0 (CheeseFramerate, video_format->num_framerates);
492
for (i = 0; i < video_format->num_framerates; i++)
495
value = gst_value_list_get_value (framerates, i);
496
video_format->framerates[i].numerator = gst_value_get_fraction_numerator (value);
497
video_format->framerates[i].denominator = gst_value_get_fraction_denominator (value);
500
else if (GST_VALUE_HOLDS_FRACTION_RANGE (framerates))
502
int numerator_min, denominator_min, numerator_max, denominator_max;
503
const GValue *fraction_range_min;
504
const GValue *fraction_range_max;
506
fraction_range_min = gst_value_get_fraction_range_min (framerates);
507
numerator_min = gst_value_get_fraction_numerator (fraction_range_min);
508
denominator_min = gst_value_get_fraction_denominator (fraction_range_min);
510
fraction_range_max = gst_value_get_fraction_range_max (framerates);
511
numerator_max = gst_value_get_fraction_numerator (fraction_range_max);
512
denominator_max = gst_value_get_fraction_denominator (fraction_range_max);
513
g_print ("FractionRange: %d/%d - %d/%d\n", numerator_min, denominator_min, numerator_max, denominator_max);
515
video_format->num_framerates = (numerator_max - numerator_min + 1) * (denominator_max - denominator_min + 1);
516
video_format->framerates = g_new0 (CheeseFramerate, video_format->num_framerates);
518
for (i = numerator_min; i <= numerator_max; i++)
520
for (j = denominator_min; j <= denominator_max; j++)
522
video_format->framerates[k].numerator = i;
523
video_format->framerates[k].denominator = j;
530
g_critical ("GValue type %s, cannot be handled for framerates", G_VALUE_TYPE_NAME (framerates));
535
cheese_webcam_add_video_format (CheeseWebcamDevice *webcam_device,
536
CheeseVideoFormat *video_format, GstStructure *format_structure)
541
cheese_webcam_get_supported_framerates (video_format, format_structure);
542
find_highest_framerate (video_format);
544
g_print ("%s %d x %d num_framerates %d\n", video_format->mimetype, video_format->width,
545
video_format->height, video_format->num_framerates);
546
for (i = 0; i < video_format->num_framerates; i++)
548
g_print ("%d/%d ", video_format->framerates[i].numerator,
549
video_format->framerates[i].denominator);
552
resolution = g_strdup_printf ("%ix%i", video_format->width,
553
video_format->height);
554
i = GPOINTER_TO_INT (g_hash_table_lookup (
555
webcam_device->supported_resolutions,
557
if (i) /* Resolution already added ? */
559
CheeseVideoFormat *curr_format = &g_array_index (
560
webcam_device->video_formats,
561
CheeseVideoFormat, i - 1);
562
float new_framerate = (float) video_format->highest_framerate.numerator /
563
video_format->highest_framerate.denominator;
564
float curr_framerate = (float) curr_format->highest_framerate.numerator /
565
curr_format->highest_framerate.denominator;
566
if (new_framerate > curr_framerate)
568
g_print ("higher framerate replacing existing format\n");
569
*curr_format = *video_format;
572
g_print ("already added, skipping\n");
578
g_array_append_val (webcam_device->video_formats, *video_format);
579
g_hash_table_insert (webcam_device->supported_resolutions, resolution,
580
GINT_TO_POINTER (webcam_device->num_video_formats + 1));
582
webcam_device->num_video_formats++;
586
cheese_resolution_compare (gconstpointer _a, gconstpointer _b)
588
const CheeseVideoFormat *a = _a;
589
const CheeseVideoFormat *b = _b;
591
if (a->width == b->width)
592
return a->height - b->height;
594
return a->width - b->width;
598
cheese_webcam_get_supported_video_formats (CheeseWebcamDevice *webcam_device, GstCaps *caps)
603
num_structures = gst_caps_get_size (caps);
604
for (i = 0; i < num_structures; i++)
606
GstStructure *structure;
607
const GValue *width, *height;
608
structure = gst_caps_get_structure (caps, i);
610
/* only interested in raw formats; we don't want to end up using image/jpeg
611
* (or whatever else the cam may produce) since we won't be able to link
612
* that to ffmpegcolorspace or the effect plugins, which makes it rather
613
* useless (although we could plug a decoder of course) */
614
if (!gst_structure_has_name (structure, "video/x-raw-yuv") &&
615
!gst_structure_has_name (structure, "video/x-raw-rgb"))
620
width = gst_structure_get_value (structure, "width");
621
height = gst_structure_get_value (structure, "height");
623
if (G_VALUE_HOLDS_INT (width))
625
CheeseVideoFormat video_format;
627
video_format.mimetype = g_strdup (gst_structure_get_name (structure));
628
gst_structure_get_int (structure, "width", &(video_format.width));
629
gst_structure_get_int (structure, "height", &(video_format.height));
630
cheese_webcam_add_video_format (webcam_device, &video_format, structure);
632
else if (GST_VALUE_HOLDS_INT_RANGE (width))
634
int min_width, max_width, min_height, max_height;
635
int cur_width, cur_height;
637
min_width = gst_value_get_int_range_min (width);
638
max_width = gst_value_get_int_range_max (width);
639
min_height = gst_value_get_int_range_min (height);
640
max_height = gst_value_get_int_range_max (height);
642
cur_width = min_width;
643
cur_height = min_height;
645
/* Gstreamer will sometimes give us a range with min_xxx == max_xxx,
646
* we use <= here (and not below) to make this work */
647
while (cur_width <= max_width && cur_height <= max_height)
649
CheeseVideoFormat video_format;
651
video_format.mimetype = g_strdup (gst_structure_get_name (structure));
652
video_format.width = cur_width;
653
video_format.height = cur_height;
654
cheese_webcam_add_video_format (webcam_device, &video_format, structure);
659
cur_width = max_width;
660
cur_height = max_height;
661
while (cur_width > min_width && cur_height > min_height)
663
CheeseVideoFormat video_format;
665
video_format.mimetype = g_strdup (gst_structure_get_name (structure));
666
video_format.width = cur_width;
667
video_format.height = cur_height;
668
cheese_webcam_add_video_format (webcam_device, &video_format, structure);
675
g_critical ("GValue type %s, cannot be handled for resolution width", G_VALUE_TYPE_NAME (width));
679
/* Sort the format array (so that it will show sorted in the resolution
680
* selection GUI), and rebuild the hashtable (as that will be invalid after
682
g_array_sort (webcam_device->video_formats, cheese_resolution_compare);
683
g_hash_table_remove_all (webcam_device->supported_resolutions);
684
for (i = 0; i < webcam_device->num_video_formats; i++)
686
CheeseVideoFormat *format = &g_array_index (webcam_device->video_formats,
687
CheeseVideoFormat, i);
688
g_hash_table_insert (webcam_device->supported_resolutions,
689
g_strdup_printf ("%ix%i", format->width,
691
GINT_TO_POINTER (i + 1));
696
cheese_webcam_get_webcam_device_data (CheeseWebcam *webcam,
697
CheeseWebcamDevice *webcam_device)
700
GstElement *pipeline;
702
GstStateChangeReturn ret;
707
pipeline_desc = g_strdup_printf ("%s name=source device=%s ! fakesink",
708
webcam_device->gstreamer_src,
709
webcam_device->video_device);
711
pipeline = gst_parse_launch (pipeline_desc, &err);
712
if ((pipeline != NULL) && (err == NULL))
714
/* Start the pipeline and wait for max. 10 seconds for it to start up */
715
gst_element_set_state (pipeline, GST_STATE_PLAYING);
716
ret = gst_element_get_state (pipeline, NULL, NULL, 10 * GST_SECOND);
718
/* Check if any error messages were posted on the bus */
719
bus = gst_element_get_bus (pipeline);
720
msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0);
721
gst_object_unref (bus);
723
if ((msg == NULL) && (ret == GST_STATE_CHANGE_SUCCESS))
730
gst_element_set_state (pipeline, GST_STATE_PAUSED);
732
src = gst_bin_get_by_name (GST_BIN (pipeline), "source");
734
g_object_get (G_OBJECT (src), "device-name", &name, NULL);
738
g_print ("Device: %s (%s)\n", name, webcam_device->video_device);
739
pad = gst_element_get_pad (src, "src");
740
caps = gst_pad_get_caps (pad);
741
gst_object_unref (pad);
742
cheese_webcam_get_supported_video_formats (webcam_device, caps);
743
gst_caps_unref (caps);
745
gst_element_set_state (pipeline, GST_STATE_NULL);
746
gst_object_unref (pipeline);
751
g_free (pipeline_desc);
756
cheese_webcam_create_fake_format (CheeseWebcam *webcam)
758
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
760
CheeseVideoFormat format;
762
/* Right now just emulate one format: video/x-raw-yuv, 320x240 @ 30 Hz */
764
format.mimetype = g_strdup ("video/x-raw-yuv");
767
format.num_framerates = 1;
768
format.framerates = g_new0 (CheeseFramerate, 1);
769
format.framerates[0].numerator = 30;
770
format.framerates[0].denominator = 1;
772
g_array_append_val (priv->webcam_devices[0].video_formats, format);
773
priv->current_format = &format;
777
cheese_webcam_detect_webcam_devices (CheeseWebcam *webcam)
779
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
783
cheese_webcam_get_video_devices_from_hal (webcam);
785
g_print ("Probing supported video formats...\n");
786
for (i = 0; i < priv->num_webcam_devices; i++)
788
cheese_webcam_get_webcam_device_data (webcam, &(priv->webcam_devices[i]));
792
if (priv->num_webcam_devices == 0)
793
cheese_webcam_create_fake_format (webcam);
797
find_highest_framerate (CheeseVideoFormat *format)
799
int framerate_numerator;
800
int framerate_denominator;
803
/* Select the highest framerate up to 30 Hz*/
804
framerate_numerator = 1;
805
framerate_denominator = 1;
806
for (i = 0; i < format->num_framerates; i++)
808
float framerate = format->framerates[i].numerator / format->framerates[i].denominator;
809
if (framerate > ((float) framerate_numerator / framerate_denominator)
812
framerate_numerator = format->framerates[i].numerator;
813
framerate_denominator = format->framerates[i].denominator;
817
format->highest_framerate.numerator = framerate_numerator;
818
format->highest_framerate.denominator = framerate_denominator;
822
cheese_webcam_create_webcam_source_bin (CheeseWebcam *webcam)
824
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
829
if (priv->num_webcam_devices == 0)
831
priv->webcam_source_bin = gst_parse_bin_from_description (
832
"videotestsrc name=video_source ! capsfilter name=capsfilter ! identity",
838
CheeseVideoFormat *format;
842
/* If we have a matching video device use that one, otherwise use the first */
843
priv->selected_device = 0;
845
for (i = 1; i < priv->num_webcam_devices; i++)
847
if (g_strcmp0 (priv->webcam_devices[i].video_device, priv->device_name) == 0)
848
priv->selected_device = i;
850
CheeseWebcamDevice *selected_webcam = &(priv->webcam_devices[priv->selected_device]);
852
resolution = g_strdup_printf ("%ix%i", priv->x_resolution,
855
/* Use the previously set resolution from gconf if it is set and the
856
* camera supports it. */
857
if (priv->x_resolution != 0 && priv->y_resolution != 0)
859
i = GPOINTER_TO_INT (g_hash_table_lookup (selected_webcam->supported_resolutions, resolution));
861
format = &g_array_index (selected_webcam->video_formats,
862
CheeseVideoFormat, i - 1);
867
/* Select the highest resolution */
868
format = &(g_array_index (selected_webcam->video_formats,
869
CheeseVideoFormat, 0));
870
for (i = 1; i < selected_webcam->num_video_formats; i++)
872
CheeseVideoFormat *new = &g_array_index (selected_webcam->video_formats,
873
CheeseVideoFormat, i);
874
gfloat newrate = new->highest_framerate.numerator /
875
new->highest_framerate.denominator;
876
if ((new->width + new->height) > (format->width + format->height) &&
877
(newrate >= MIN_DEFAULT_RATE))
884
priv->current_format = format;
890
webcam_input = g_strdup_printf (
891
"%s name=video_source device=%s ! capsfilter name=capsfilter caps=video/x-raw-rgb,width=%d,height=%d,framerate=%d/%d;video/x-raw-yuv,width=%d,height=%d,framerate=%d/%d ! identity",
892
selected_webcam->gstreamer_src,
893
selected_webcam->video_device,
896
format->highest_framerate.numerator,
897
format->highest_framerate.denominator,
900
format->highest_framerate.numerator,
901
format->highest_framerate.denominator);
902
g_print ("%s\n", webcam_input);
904
priv->webcam_source_bin = gst_parse_bin_from_description (webcam_input,
906
g_free (webcam_input);
908
if (priv->webcam_source_bin == NULL)
912
priv->video_source = gst_bin_get_by_name (GST_BIN (priv->webcam_source_bin), "video_source");
913
priv->capsfilter = gst_bin_get_by_name (GST_BIN (priv->webcam_source_bin), "capsfilter");
923
priv->webcam_source_bin = gst_parse_bin_from_description ("videotestsrc name=video_source",
925
priv->video_source = gst_bin_get_by_name (GST_BIN (priv->webcam_source_bin), "video_source");
931
priv->capsfilter = gst_bin_get_by_name (GST_BIN (priv->webcam_source_bin),
937
cheese_webcam_set_error_element_not_found (GError **error, const char *factoryname)
944
g_set_error (error, CHEESE_WEBCAM_ERROR, CHEESE_WEBCAM_ERROR_ELEMENT_NOT_FOUND, "%s.", factoryname);
948
/* Ensure that what is found is not a substring of an element; all strings
949
* should have a ' ' or nothing to the left and a '.' or ',' to the right */
950
gchar *found = g_strrstr ((*error)->message, factoryname);
957
next = *(found + strlen (factoryname));
961
((found != (*error)->message && prev != ' ') && /* Prefix check */
962
(next != ',' && next != '.'))) /* Postfix check */
964
g_prefix_error (error, "%s, ", factoryname);
970
cheese_webcam_create_video_display_bin (CheeseWebcam *webcam, GError **error)
972
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
974
GstElement *tee, *video_display_queue, *video_scale, *video_sink, *save_queue;
979
priv->video_display_bin = gst_bin_new ("video_display_bin");
981
cheese_webcam_create_webcam_source_bin (webcam);
983
if ((priv->effect_filter = gst_element_factory_make ("identity", "effect")) == NULL)
985
cheese_webcam_set_error_element_not_found (error, "identity");
987
if ((priv->csp_post_effect = gst_element_factory_make ("ffmpegcolorspace", "csp_post_effect")) == NULL)
989
cheese_webcam_set_error_element_not_found (error, "ffmpegcolorspace");
991
if ((priv->video_balance = gst_element_factory_make ("videobalance", "video_balance")) == NULL)
993
cheese_webcam_set_error_element_not_found (error, "videobalance");
996
if ((priv->csp_post_balance = gst_element_factory_make ("ffmpegcolorspace", "csp_post_balance")) == NULL)
998
cheese_webcam_set_error_element_not_found (error, "ffmpegcolorspace");
1002
if ((tee = gst_element_factory_make ("tee", "tee")) == NULL)
1004
cheese_webcam_set_error_element_not_found (error, "tee");
1007
if ((save_queue = gst_element_factory_make ("queue", "save_queue")) == NULL)
1009
cheese_webcam_set_error_element_not_found (error, "queue");
1012
if ((video_display_queue = gst_element_factory_make ("queue", "video_display_queue")) == NULL)
1014
cheese_webcam_set_error_element_not_found (error, "queue");
1017
if ((video_scale = gst_element_factory_make ("videoscale", "video_scale")) == NULL)
1019
cheese_webcam_set_error_element_not_found (error, "videoscale");
1023
/* Use bilinear scaling */
1024
g_object_set (video_scale, "method", 1, NULL);
1027
if ((video_sink = gst_element_factory_make ("gconfvideosink", "video_sink")) == NULL)
1029
cheese_webcam_set_error_element_not_found (error, "gconfvideosink");
1032
if (error != NULL && *error != NULL)
1035
gst_bin_add_many (GST_BIN (priv->video_display_bin), priv->webcam_source_bin,
1036
priv->effect_filter, priv->csp_post_effect,
1037
priv->video_balance, priv->csp_post_balance,
1039
video_display_queue, video_scale, video_sink, NULL);
1041
ok = gst_element_link_many (priv->webcam_source_bin, priv->effect_filter,
1042
priv->csp_post_effect,
1043
priv->video_balance, priv->csp_post_balance,
1046
ok &= gst_element_link_many (tee, save_queue, NULL);
1047
ok &= gst_element_link_many (tee, video_display_queue, video_scale, video_sink, NULL);
1050
pad = gst_element_get_pad (save_queue, "src");
1051
gst_element_add_pad (priv->video_display_bin, gst_ghost_pad_new ("src", pad));
1052
gst_object_unref (GST_OBJECT (pad));
1056
g_error ("Unable to create display pipeline");
1062
cheese_webcam_create_photo_save_bin (CheeseWebcam *webcam, GError **error)
1064
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1066
GstElement *csp_photo_save_bin;
1072
priv->photo_save_bin = gst_bin_new ("photo_save_bin");
1074
if ((csp_photo_save_bin = gst_element_factory_make ("ffmpegcolorspace", "csp_photo_save_bin")) == NULL)
1076
cheese_webcam_set_error_element_not_found (error, "ffmpegcolorspace");
1078
if ((priv->photo_sink = gst_element_factory_make ("fakesink", "photo_sink")) == NULL)
1080
cheese_webcam_set_error_element_not_found (error, "fakesink");
1083
if (error != NULL && *error != NULL)
1086
gst_bin_add_many (GST_BIN (priv->photo_save_bin), csp_photo_save_bin,
1087
priv->photo_sink, NULL);
1090
pad = gst_element_get_pad (csp_photo_save_bin, "sink");
1091
gst_element_add_pad (priv->photo_save_bin, gst_ghost_pad_new ("sink", pad));
1092
gst_object_unref (GST_OBJECT (pad));
1094
caps = gst_caps_new_simple ("video/x-raw-rgb",
1095
"bpp", G_TYPE_INT, 24,
1096
"depth", G_TYPE_INT, 24,
1098
ok = gst_element_link_filtered (csp_photo_save_bin, priv->photo_sink, caps);
1099
gst_caps_unref (caps);
1101
g_object_set (G_OBJECT (priv->photo_sink), "signal-handoffs", TRUE, NULL);
1104
g_error ("Unable to create photo save pipeline");
1110
cheese_webcam_create_video_save_bin (CheeseWebcam *webcam, GError **error)
1112
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1114
GstElement *audio_queue, *audio_convert, *audio_enc;
1115
GstElement *video_save_csp, *video_save_rate, *video_save_scale, *video_enc;
1120
priv->video_save_bin = gst_bin_new ("video_save_bin");
1122
if ((priv->audio_source = gst_element_factory_make ("gconfaudiosrc", "audio_source")) == NULL)
1124
cheese_webcam_set_error_element_not_found (error, "gconfaudiosrc");
1126
if ((audio_queue = gst_element_factory_make ("queue", "audio_queue")) == NULL)
1128
cheese_webcam_set_error_element_not_found (error, "queue");
1130
if ((audio_convert = gst_element_factory_make ("audioconvert", "audio_convert")) == NULL)
1132
cheese_webcam_set_error_element_not_found (error, "audioconvert");
1134
if ((audio_enc = gst_element_factory_make ("vorbisenc", "audio_enc")) == NULL)
1136
cheese_webcam_set_error_element_not_found (error, "vorbisenc");
1139
if ((video_save_csp = gst_element_factory_make ("ffmpegcolorspace", "video_save_csp")) == NULL)
1141
cheese_webcam_set_error_element_not_found (error, "ffmpegcolorspace");
1143
if ((video_enc = gst_element_factory_make ("theoraenc", "video_enc")) == NULL)
1145
cheese_webcam_set_error_element_not_found (error, "theoraenc");
1149
g_object_set (video_enc, "keyframe-force", 1, NULL);
1152
if ((video_save_rate = gst_element_factory_make ("videorate", "video_save_rate")) == NULL)
1154
cheese_webcam_set_error_element_not_found (error, "videorate");
1156
if ((video_save_scale = gst_element_factory_make ("videoscale", "video_save_scale")) == NULL)
1158
cheese_webcam_set_error_element_not_found (error, "videoscale");
1162
/* Use bilinear scaling */
1163
g_object_set (video_save_scale, "method", 1, NULL);
1166
if ((mux = gst_element_factory_make ("oggmux", "mux")) == NULL)
1168
cheese_webcam_set_error_element_not_found (error, "oggmux");
1172
g_object_set (G_OBJECT (mux),
1173
"max-delay", (guint64) 10000000,
1174
"max-page-delay", (guint64) 10000000, NULL);
1177
if ((priv->video_file_sink = gst_element_factory_make ("filesink", "video_file_sink")) == NULL)
1179
cheese_webcam_set_error_element_not_found (error, "filesink");
1182
if (error != NULL && *error != NULL)
1185
gst_bin_add_many (GST_BIN (priv->video_save_bin), priv->audio_source, audio_queue,
1186
audio_convert, audio_enc, video_save_csp, video_save_rate, video_save_scale, video_enc,
1187
mux, priv->video_file_sink, NULL);
1190
pad = gst_element_get_pad (video_save_csp, "sink");
1191
gst_element_add_pad (priv->video_save_bin, gst_ghost_pad_new ("sink", pad));
1192
gst_object_unref (GST_OBJECT (pad));
1195
ok = gst_element_link_many (priv->audio_source, audio_queue, audio_convert,
1196
audio_enc, mux, priv->video_file_sink, NULL);
1198
ok &= gst_element_link_many (video_save_csp, video_save_rate, video_save_scale, video_enc,
1200
ok &= gst_element_link (video_enc, mux);
1203
g_error ("Unable to create video save pipeline");
1209
cheese_webcam_get_num_webcam_devices (CheeseWebcam *webcam)
1211
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1213
return priv->num_webcam_devices;
1217
cheese_webcam_switch_webcam_device (CheeseWebcam *webcam)
1219
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1221
gboolean was_recording = FALSE;
1222
gboolean pipeline_was_playing = FALSE;
1223
gboolean disp_bin_created = FALSE;
1224
gboolean disp_bin_added = FALSE;
1225
gboolean disp_bin_linked = FALSE;
1226
GError *error = NULL;
1228
if (priv->is_recording)
1230
cheese_webcam_stop_video_recording (webcam);
1231
was_recording = TRUE;
1234
if (priv->pipeline_is_playing)
1236
cheese_webcam_stop (webcam);
1237
pipeline_was_playing = TRUE;
1240
gst_bin_remove (GST_BIN (priv->pipeline), priv->video_display_bin);
1242
disp_bin_created = cheese_webcam_create_video_display_bin (webcam, &error);
1243
if (!disp_bin_created)
1247
disp_bin_added = gst_bin_add (GST_BIN (priv->pipeline), priv->video_display_bin);
1248
if (!disp_bin_added)
1250
gst_object_sink (priv->video_display_bin);
1254
disp_bin_linked = gst_element_link (priv->video_display_bin, priv->photo_save_bin);
1255
if (!disp_bin_linked)
1257
gst_bin_remove (GST_BIN (priv->pipeline), priv->video_display_bin);
1261
if (pipeline_was_playing)
1263
cheese_webcam_play (webcam);
1266
/* if (was_recording)
1268
* Restart recording... ?
1275
cheese_webcam_play (CheeseWebcam *webcam)
1277
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1279
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1280
priv->pipeline_is_playing = TRUE;
1284
cheese_webcam_stop (CheeseWebcam *webcam)
1286
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1288
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1289
priv->pipeline_is_playing = FALSE;
1293
cheese_webcam_change_effect_filter (CheeseWebcam *webcam, GstElement *new_filter)
1295
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1297
gboolean is_playing = priv->pipeline_is_playing;
1300
cheese_webcam_stop (webcam);
1302
gst_element_unlink_many (priv->webcam_source_bin, priv->effect_filter,
1303
priv->csp_post_effect, NULL);
1305
gst_bin_remove (GST_BIN (priv->video_display_bin), priv->effect_filter);
1307
gst_bin_add (GST_BIN (priv->video_display_bin), new_filter);
1308
ok = gst_element_link_many (priv->webcam_source_bin, new_filter,
1309
priv->csp_post_effect, NULL);
1310
g_return_if_fail (ok);
1313
cheese_webcam_play (webcam);
1315
priv->effect_filter = new_filter;
1319
cheese_webcam_set_effect (CheeseWebcam *webcam, CheeseWebcamEffect effect)
1321
GString *rgb_effects_str = g_string_new ("");
1322
GString *yuv_effects_str = g_string_new ("");
1323
char *effects_pipeline_desc;
1325
GstElement *effect_filter;
1328
for (i = 0; i < NUM_EFFECTS; i++)
1330
if (effect & EFFECT_TO_PIPELINE_DESC[i].effect)
1332
if (EFFECT_TO_PIPELINE_DESC[i].colorspace == RGB)
1334
g_string_append (rgb_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
1335
g_string_append (rgb_effects_str, " ! ");
1339
g_string_append (yuv_effects_str, " ! ");
1340
g_string_append (yuv_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
1344
effects_pipeline_desc = g_strconcat ("ffmpegcolorspace ! ",
1345
rgb_effects_str->str,
1347
yuv_effects_str->str,
1350
effect_filter = gst_parse_bin_from_description (effects_pipeline_desc, TRUE, &err);
1351
if (!effect_filter || (err != NULL))
1354
g_error ("ERROR effect_filter\n");
1356
cheese_webcam_change_effect_filter (webcam, effect_filter);
1358
g_free (effects_pipeline_desc);
1359
g_string_free (rgb_effects_str, TRUE);
1360
g_string_free (yuv_effects_str, TRUE);
1364
cheese_webcam_start_video_recording (CheeseWebcam *webcam, char *filename)
1366
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1368
g_object_set (CHEESE_WEBCAM_GET_PRIVATE (webcam)->video_file_sink, "location", filename, NULL);
1369
cheese_webcam_change_sink (webcam, priv->video_display_bin,
1370
priv->video_save_bin, priv->photo_save_bin);
1371
priv->is_recording = TRUE;
1375
cheese_webcam_force_stop_video_recording (gpointer data)
1377
CheeseWebcam *webcam = CHEESE_WEBCAM (data);
1378
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1380
if (priv->is_recording)
1382
g_print ("Cannot cleanly shutdown recording pipeline, forcing\n");
1383
g_signal_emit (webcam, webcam_signals[VIDEO_SAVED], 0);
1385
cheese_webcam_change_sink (webcam, priv->video_display_bin,
1386
priv->photo_save_bin, priv->video_save_bin);
1387
priv->is_recording = FALSE;
1394
cheese_webcam_stop_video_recording (CheeseWebcam *webcam)
1396
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1399
gst_element_get_state (priv->pipeline, &state, NULL, 0);
1401
if (state == GST_STATE_PLAYING)
1403
/* Send EOS message down the pipeline by stopping video and audio source*/
1404
g_print ("Sending EOS event down the recording pipeline\n");
1405
gst_element_send_event (priv->video_source, gst_event_new_eos ());
1406
gst_element_send_event (priv->audio_source, gst_event_new_eos ());
1407
priv->eos_timeout_id = g_timeout_add (3000, cheese_webcam_force_stop_video_recording, webcam);
1411
cheese_webcam_force_stop_video_recording (webcam);
1416
cheese_webcam_take_photo (CheeseWebcam *webcam, char *filename)
1418
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1420
if (priv->photo_handler_signal_id != 0)
1422
g_print ("Still waiting for previous photo data, ignoring new request\n");
1426
g_free (priv->photo_filename);
1427
priv->photo_filename = g_strdup (filename);
1429
/* Take the photo by connecting the handoff signal */
1430
priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->photo_sink),
1432
G_CALLBACK (cheese_webcam_photo_data_cb),
1438
cheese_webcam_finalize (GObject *object)
1440
CheeseWebcam *webcam;
1443
webcam = CHEESE_WEBCAM (object);
1444
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1446
cheese_webcam_stop (webcam);
1447
gst_object_unref (priv->pipeline);
1449
if (priv->is_recording)
1450
gst_object_unref (priv->photo_save_bin);
1452
gst_object_unref (priv->video_save_bin);
1454
g_free (priv->photo_filename);
1455
g_free (priv->device_name);
1457
/* Free CheeseWebcamDevice array */
1458
for (i = 0; i < priv->num_webcam_devices; i++)
1460
for (j = 0; j < priv->webcam_devices[i].num_video_formats; j++)
1462
g_free (g_array_index (priv->webcam_devices[i].video_formats, CheeseVideoFormat, j).framerates);
1463
g_free (g_array_index (priv->webcam_devices[i].video_formats, CheeseVideoFormat, j).mimetype);
1465
g_free (priv->webcam_devices[i].video_device);
1466
g_free (priv->webcam_devices[i].hal_udi);
1467
g_free (priv->webcam_devices[i].gstreamer_src);
1468
g_free (priv->webcam_devices[i].product_name);
1469
g_array_free (priv->webcam_devices[i].video_formats, TRUE);
1470
g_hash_table_destroy (priv->webcam_devices[i].supported_resolutions);
1472
g_free (priv->webcam_devices);
1474
G_OBJECT_CLASS (cheese_webcam_parent_class)->finalize (object);
1478
cheese_webcam_get_property (GObject *object, guint prop_id, GValue *value,
1483
self = CHEESE_WEBCAM (object);
1484
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (self);
1488
case PROP_VIDEO_WINDOW:
1489
g_value_set_pointer (value, priv->video_window);
1491
case PROP_DEVICE_NAME:
1492
g_value_set_string (value, priv->device_name);
1494
case PROP_X_RESOLUTION:
1495
g_value_set_int (value, priv->x_resolution);
1497
case PROP_Y_RESOLUTION:
1498
g_value_set_int (value, priv->y_resolution);
1501
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1507
cheese_webcam_set_property (GObject *object, guint prop_id, const GValue *value,
1512
self = CHEESE_WEBCAM (object);
1513
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (self);
1517
case PROP_VIDEO_WINDOW:
1518
priv->video_window = g_value_get_pointer (value);
1519
g_signal_connect (priv->video_window, "expose-event",
1520
G_CALLBACK (cheese_webcam_expose_cb), self);
1522
case PROP_DEVICE_NAME:
1523
g_free (priv->device_name);
1524
priv->device_name = g_value_dup_string (value);
1526
case PROP_X_RESOLUTION:
1527
priv->x_resolution = g_value_get_int (value);
1529
case PROP_Y_RESOLUTION:
1530
priv->y_resolution = g_value_get_int (value);
1533
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1539
cheese_webcam_class_init (CheeseWebcamClass *klass)
1541
GObjectClass *object_class = G_OBJECT_CLASS (klass);
1543
object_class->finalize = cheese_webcam_finalize;
1544
object_class->get_property = cheese_webcam_get_property;
1545
object_class->set_property = cheese_webcam_set_property;
1547
webcam_signals[PHOTO_SAVED] = g_signal_new ("photo-saved", G_OBJECT_CLASS_TYPE (klass),
1548
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1549
G_STRUCT_OFFSET (CheeseWebcamClass, photo_saved),
1551
g_cclosure_marshal_VOID__VOID,
1554
webcam_signals[VIDEO_SAVED] = g_signal_new ("video-saved", G_OBJECT_CLASS_TYPE (klass),
1555
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1556
G_STRUCT_OFFSET (CheeseWebcamClass, video_saved),
1558
g_cclosure_marshal_VOID__VOID,
1562
g_object_class_install_property (object_class, PROP_VIDEO_WINDOW,
1563
g_param_spec_pointer ("video-window",
1566
G_PARAM_READWRITE));
1568
g_object_class_install_property (object_class, PROP_DEVICE_NAME,
1569
g_param_spec_string ("device-name",
1573
G_PARAM_READWRITE));
1575
g_object_class_install_property (object_class, PROP_X_RESOLUTION,
1576
g_param_spec_int ("x-resolution",
1583
G_PARAM_CONSTRUCT_ONLY));
1585
g_object_class_install_property (object_class, PROP_Y_RESOLUTION,
1586
g_param_spec_int ("y-resolution",
1593
G_PARAM_CONSTRUCT_ONLY));
1596
g_type_class_add_private (klass, sizeof (CheeseWebcamPrivate));
1600
cheese_webcam_init (CheeseWebcam *webcam)
1602
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1604
priv->is_recording = FALSE;
1605
priv->pipeline_is_playing = FALSE;
1606
priv->photo_filename = NULL;
1607
priv->webcam_devices = NULL;
1608
priv->device_name = NULL;
1609
priv->photo_handler_signal_id = 0;
1613
cheese_webcam_new (GtkWidget *video_window, char *webcam_device_name,
1614
int x_resolution, int y_resolution)
1616
CheeseWebcam *webcam;
1618
if (webcam_device_name)
1620
webcam = g_object_new (CHEESE_TYPE_WEBCAM, "video-window", video_window,
1621
"device_name", webcam_device_name,
1622
"x-resolution", x_resolution,
1623
"y-resolution", y_resolution, NULL);
1627
webcam = g_object_new (CHEESE_TYPE_WEBCAM, "video-window", video_window,
1628
"x-resolution", x_resolution,
1629
"y-resolution", y_resolution, NULL);
1636
cheese_webcam_setup (CheeseWebcam *webcam, char *hal_dev_udi, GError **error)
1638
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1641
GError *tmp_error = NULL;
1643
cheese_webcam_detect_webcam_devices (webcam);
1645
if (hal_dev_udi != NULL)
1647
cheese_webcam_set_device_by_dev_udi (webcam, hal_dev_udi);
1650
priv->pipeline = gst_pipeline_new ("pipeline");
1652
cheese_webcam_create_video_display_bin (webcam, &tmp_error);
1654
cheese_webcam_create_photo_save_bin (webcam, &tmp_error);
1656
cheese_webcam_create_video_save_bin (webcam, &tmp_error);
1657
if (tmp_error != NULL)
1659
g_propagate_error (error, tmp_error);
1660
g_prefix_error (error, _("One or more needed gstreamer elements are missing: "));
1664
gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_display_bin,
1665
priv->photo_save_bin, NULL);
1667
ok = gst_element_link (priv->video_display_bin, priv->photo_save_bin);
1669
priv->bus = gst_element_get_bus (priv->pipeline);
1670
gst_bus_add_signal_watch (priv->bus);
1672
g_signal_connect (G_OBJECT (priv->bus), "message",
1673
G_CALLBACK (cheese_webcam_bus_message_cb), webcam);
1675
gst_bus_set_sync_handler (priv->bus, (GstBusSyncHandler) cheese_webcam_bus_sync_handler, webcam);
1678
g_error ("Unable link pipeline for photo");
1682
cheese_webcam_get_selected_device_index (CheeseWebcam *webcam)
1684
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1686
return priv->selected_device;
1690
cheese_webcam_get_webcam_devices (CheeseWebcam *webcam)
1692
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1694
GArray *devices_arr;
1696
devices_arr = g_array_sized_new (FALSE,
1698
sizeof (CheeseWebcamDevice),
1699
priv->num_webcam_devices);
1700
devices_arr = g_array_append_vals (devices_arr,
1701
priv->webcam_devices,
1702
priv->num_webcam_devices);
1707
cheese_webcam_set_device_by_dev_file (CheeseWebcam *webcam, char *file)
1709
g_object_set (webcam, "device_name", file, NULL);
1713
cheese_webcam_set_device_by_dev_udi (CheeseWebcam *webcam, char *udi)
1715
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1719
for (i = 0; i < priv->num_webcam_devices; i++)
1721
if (strcmp (priv->webcam_devices[i].hal_udi, udi) == 0)
1723
g_object_set (webcam, "device_name", priv->webcam_devices[i].video_device, NULL);
1730
cheese_webcam_get_video_formats (CheeseWebcam *webcam)
1732
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1734
return priv->webcam_devices[priv->selected_device].video_formats;
1738
cheese_webcam_set_video_format (CheeseWebcam *webcam, CheeseVideoFormat *format)
1740
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1744
new_caps = gst_caps_new_simple ("video/x-raw-rgb",
1745
"width", G_TYPE_INT,
1747
"height", G_TYPE_INT,
1749
"framerate", GST_TYPE_FRACTION,
1750
format->highest_framerate.numerator,
1751
format->highest_framerate.denominator,
1754
gst_caps_append (new_caps, gst_caps_new_simple ("video/x-raw-yuv",
1755
"width", G_TYPE_INT,
1757
"height", G_TYPE_INT,
1759
"framerate", GST_TYPE_FRACTION,
1760
format->highest_framerate.numerator,
1761
format->highest_framerate.denominator,
1764
priv->current_format = format;
1766
cheese_webcam_stop (webcam);
1767
g_object_set (priv->capsfilter, "caps", new_caps, NULL);
1768
cheese_webcam_play (webcam);
1772
cheese_webcam_get_current_video_format (CheeseWebcam *webcam)
1774
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1776
return priv->current_format;
1780
cheese_webcam_get_balance_property_range (CheeseWebcam *webcam,
1782
gdouble *min, gdouble *max, gdouble *def)
1784
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1791
pspec = g_object_class_find_property (
1792
G_OBJECT_GET_CLASS (G_OBJECT (priv->video_balance)), property);
1794
g_return_if_fail (pspec != NULL);
1796
*min = G_PARAM_SPEC_DOUBLE (pspec)->minimum;
1797
*max = G_PARAM_SPEC_DOUBLE (pspec)->maximum;
1798
*def = G_PARAM_SPEC_DOUBLE (pspec)->default_value;
1802
cheese_webcam_set_balance_property (CheeseWebcam *webcam, gchar *property, gdouble value)
1804
CheeseWebcamPrivate *priv = CHEESE_WEBCAM_GET_PRIVATE (webcam);
1806
g_object_set (G_OBJECT (priv->video_balance), property, value, NULL);