104
124
static guint camera_signals[LAST_SIGNAL];
114
CheeseCameraEffect effect;
115
const char *pipeline_desc;
116
VideoColorSpace colorspace; /* The color space the effect works in */
117
} EffectToPipelineDesc;
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}
135
static const int NUM_EFFECTS = G_N_ELEMENTS (EFFECT_TO_PIPELINE_DESC);
137
126
GST_DEBUG_CATEGORY (cheese_camera_cat);
138
127
#define GST_CAT_DEFAULT cheese_camera_cat
143
132
return g_quark_from_static_string ("cheese-camera-error-quark");
146
static GstBusSyncReply
147
cheese_camera_bus_sync_handler (GstBus *bus, GstMessage *message, CheeseCamera *camera)
149
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
150
GstXOverlay *overlay;
152
if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
155
if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
158
overlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
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);
164
gst_x_overlay_set_xwindow_id (overlay,
165
GDK_WINDOW_XWINDOW (gtk_widget_get_window (priv->video_window)));
167
gst_message_unref (message);
173
cheese_camera_change_sink (CheeseCamera *camera, GstElement *src,
174
GstElement *new_sink, GstElement *old_sink)
176
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
177
gboolean is_playing = priv->pipeline_is_playing;
179
cheese_camera_stop (camera);
181
gst_element_unlink (src, old_sink);
182
gst_object_ref (old_sink);
183
gst_bin_remove (GST_BIN (priv->pipeline), old_sink);
185
gst_bin_add (GST_BIN (priv->pipeline), new_sink);
186
gst_element_link (src, new_sink);
189
cheese_camera_play (camera);
193
cheese_camera_expose_cb (GtkWidget *widget, GdkEventExpose *event, CheeseCamera *camera)
195
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
196
GtkAllocation allocation;
198
GstXOverlay *overlay = GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (priv->pipeline),
199
GST_TYPE_X_OVERLAY));
201
gst_element_get_state (priv->pipeline, &state, NULL, 0);
203
if ((state < GST_STATE_PLAYING) || (overlay == NULL))
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);
212
gst_x_overlay_expose (overlay);
219
cheese_camera_photo_data_cb (GstElement *element, GstBuffer *buffer,
220
GstPad *pad, CheeseCamera *camera)
222
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
136
cheese_camera_photo_data (CheeseCamera *camera, GstBuffer *buffer)
225
139
const GstStructure *structure;
226
140
int width, height, stride;
227
141
GdkPixbuf *pixbuf;
228
142
const int bits_per_pixel = 8;
144
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
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);
236
151
stride = buffer->size / height;
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)
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);
249
g_signal_handler_disconnect (G_OBJECT (priv->photo_sink),
250
priv->photo_handler_signal_id);
251
priv->photo_handler_signal_id = 0;
253
if (priv->photo_filename != NULL)
255
gdk_pixbuf_save (pixbuf, priv->photo_filename, "jpeg", NULL, NULL);
256
g_object_unref (G_OBJECT (pixbuf));
258
g_free (priv->photo_filename);
259
priv->photo_filename = NULL;
261
g_signal_emit (camera, camera_signals[PHOTO_SAVED], 0);
265
g_signal_emit (camera, camera_signals[PHOTO_TAKEN], 0, pixbuf);
266
g_object_unref (pixbuf);
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);
271
165
cheese_camera_bus_message_cb (GstBus *bus, GstMessage *message, CheeseCamera *camera)
273
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
275
if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS)
167
switch (GST_MESSAGE_TYPE (message))
277
if (priv->is_recording)
279
GST_DEBUG ("Received EOS message");
281
g_source_remove (priv->eos_timeout_id);
283
g_signal_emit (camera, camera_signals[VIDEO_SAVED], 0);
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:
173
gst_message_parse_warning (message, &err, &debug);
175
if (err && err->message) {
176
g_warning ("%s\n", err->message);
179
g_warning ("Unparsable GST_MESSAGE_WARNING message.\n");
185
case GST_MESSAGE_ERROR:
189
gst_message_parse_error (message, &err, &debug);
191
if (err && err->message) {
192
g_warning ("%s\n", err->message);
195
g_warning ("Unparsable GST_MESSAGE_ERROR message.\n");
201
case GST_MESSAGE_STATE_CHANGED:
203
if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0)
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);
212
case GST_MESSAGE_ELEMENT:
214
const GstStructure *structure;
217
if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0)
219
structure = gst_message_get_structure (message);
220
if (strcmp (gst_structure_get_name (structure), "preview-image") == 0)
222
if (gst_structure_has_field_typed (structure, "buffer", GST_TYPE_BUFFER))
224
image = gst_structure_get_value (structure, "buffer");
227
buffer = gst_value_get_buffer (image);
228
cheese_camera_photo_data (camera, buffer);
232
g_warning ("Could not get buffer from bus message");
423
cheese_camera_create_video_display_bin (CheeseCamera *camera, GError **error)
425
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
427
GstElement *tee, *video_display_queue, *video_scale, *video_sink, *save_queue;
432
priv->video_display_bin = gst_bin_new ("video_display_bin");
434
cheese_camera_create_camera_source_bin (camera);
373
cheese_camera_set_video_recording (CheeseCamera *camera, GError **error)
375
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
376
GstElement *video_enc;
379
/* Setup video-encoder explicitly to be able to set its properties*/
381
if ((video_enc = gst_element_factory_make ("theoraenc", "theoraenc")) == NULL)
383
cheese_camera_set_error_element_not_found (error, "theoraenc");
386
g_object_set (priv->camerabin, "video-encoder", video_enc, NULL);
387
g_object_set (G_OBJECT (video_enc), "speed-level", 2, NULL);
389
if ((mux = gst_element_factory_make ("oggmux", "oggmux")) == NULL)
391
cheese_camera_set_error_element_not_found (error, "oggmux");
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);
401
cheese_camera_create_effects_preview_bin (CheeseCamera *camera, GError **error)
403
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
409
priv->effects_preview_bin = gst_bin_new ("effects_preview_bin");
411
if ((priv->effects_tee = gst_element_factory_make ("tee", "effects_tee")) == NULL)
413
cheese_camera_set_error_element_not_found (error, "tee");
415
if ((priv->effects_valve = gst_element_factory_make ("valve", "effects_valve")) == NULL)
417
cheese_camera_set_error_element_not_found (error, "effects_valve");
419
priv->effects_downscaler = gst_parse_bin_from_description (
420
"videoscale ! video/x-raw-yuv,width=160,height=120 ! ffmpegcolorspace",
423
if (priv->effects_downscaler == NULL || err != NULL)
425
cheese_camera_set_error_element_not_found (error, "effects_downscaler");
428
if (error != NULL && *error != NULL)
431
gst_bin_add_many (GST_BIN (priv->effects_preview_bin),
432
priv->effects_downscaler, priv->effects_tee,
433
priv->effects_valve, NULL);
435
ok &= gst_element_link_many (priv->effects_valve, priv->effects_downscaler,
436
priv->effects_tee, NULL);
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));
445
g_error ("Unable to create effects preview bin");
451
cheese_camera_create_video_filter_bin (CheeseCamera *camera, GError **error)
453
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
458
cheese_camera_create_effects_preview_bin (camera, error);
460
priv->video_filter_bin = gst_bin_new ("video_filter_bin");
462
if ((priv->camera_tee = gst_element_factory_make ("tee", "camera_tee")) == NULL)
464
cheese_camera_set_error_element_not_found (error, "tee");
466
if ((priv->main_valve = gst_element_factory_make ("valve", "main_valve")) == NULL)
468
cheese_camera_set_error_element_not_found (error, "main_valve");
436
470
if ((priv->effect_filter = gst_element_factory_make ("identity", "effect")) == NULL)
438
472
cheese_camera_set_error_element_not_found (error, "identity");
440
if ((priv->csp_post_effect = gst_element_factory_make ("ffmpegcolorspace", "csp_post_effect")) == NULL)
442
cheese_camera_set_error_element_not_found (error, "ffmpegcolorspace");
444
474
if ((priv->video_balance = gst_element_factory_make ("videobalance", "video_balance")) == NULL)
446
476
cheese_camera_set_error_element_not_found (error, "videobalance");
455
if ((tee = gst_element_factory_make ("tee", "tee")) == NULL)
457
cheese_camera_set_error_element_not_found (error, "tee");
460
if ((save_queue = gst_element_factory_make ("queue", "save_queue")) == NULL)
462
cheese_camera_set_error_element_not_found (error, "queue");
465
if ((video_display_queue = gst_element_factory_make ("queue", "video_display_queue")) == NULL)
467
cheese_camera_set_error_element_not_found (error, "queue");
470
if ((video_scale = gst_element_factory_make ("videoscale", "video_scale")) == NULL)
472
cheese_camera_set_error_element_not_found (error, "videoscale");
476
/* Use bilinear scaling */
477
g_object_set (video_scale, "method", 1, NULL);
480
if ((video_sink = gst_element_factory_make ("gconfvideosink", "video_sink")) == NULL)
482
cheese_camera_set_error_element_not_found (error, "gconfvideosink");
485
485
if (error != NULL && *error != NULL)
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,
492
video_display_queue, video_scale, video_sink, NULL);
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,
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);
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));
509
g_error ("Unable to create display pipeline");
515
cheese_camera_create_photo_save_bin (CheeseCamera *camera, GError **error)
517
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
519
GstElement *csp_photo_save_bin;
525
priv->photo_save_bin = gst_bin_new ("photo_save_bin");
527
if ((csp_photo_save_bin = gst_element_factory_make ("ffmpegcolorspace", "csp_photo_save_bin")) == NULL)
529
cheese_camera_set_error_element_not_found (error, "ffmpegcolorspace");
531
if ((priv->photo_sink = gst_element_factory_make ("fakesink", "photo_sink")) == NULL)
533
cheese_camera_set_error_element_not_found (error, "fakesink");
536
if (error != NULL && *error != NULL)
539
gst_bin_add_many (GST_BIN (priv->photo_save_bin), csp_photo_save_bin,
540
priv->photo_sink, NULL);
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));
547
caps = gst_caps_new_simple ("video/x-raw-rgb",
548
"bpp", G_TYPE_INT, 24,
549
"depth", G_TYPE_INT, 24,
551
ok = gst_element_link_filtered (csp_photo_save_bin, priv->photo_sink, caps);
552
gst_caps_unref (caps);
554
g_object_set (G_OBJECT (priv->photo_sink), "signal-handoffs", TRUE, NULL);
557
g_error ("Unable to create photo save pipeline");
563
cheese_camera_create_video_save_bin (CheeseCamera *camera, GError **error)
565
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
567
GstElement *audio_queue, *audio_convert, *audio_enc;
568
GstElement *video_save_csp, *video_save_rate, *video_save_scale, *video_enc;
573
priv->video_save_bin = gst_bin_new ("video_save_bin");
575
if ((priv->audio_source = gst_element_factory_make ("gconfaudiosrc", "audio_source")) == NULL)
577
cheese_camera_set_error_element_not_found (error, "gconfaudiosrc");
579
if ((audio_queue = gst_element_factory_make ("queue", "audio_queue")) == NULL)
581
cheese_camera_set_error_element_not_found (error, "queue");
583
if ((audio_convert = gst_element_factory_make ("audioconvert", "audio_convert")) == NULL)
585
cheese_camera_set_error_element_not_found (error, "audioconvert");
587
if ((audio_enc = gst_element_factory_make ("vorbisenc", "audio_enc")) == NULL)
589
cheese_camera_set_error_element_not_found (error, "vorbisenc");
592
if ((video_save_csp = gst_element_factory_make ("ffmpegcolorspace", "video_save_csp")) == NULL)
594
cheese_camera_set_error_element_not_found (error, "ffmpegcolorspace");
596
if ((video_enc = gst_element_factory_make ("theoraenc", "video_enc")) == NULL)
598
cheese_camera_set_error_element_not_found (error, "theoraenc");
602
g_object_set (video_enc, "keyframe-force", 1, NULL);
605
if ((video_save_rate = gst_element_factory_make ("videorate", "video_save_rate")) == NULL)
607
cheese_camera_set_error_element_not_found (error, "videorate");
609
if ((video_save_scale = gst_element_factory_make ("videoscale", "video_save_scale")) == NULL)
611
cheese_camera_set_error_element_not_found (error, "videoscale");
615
/* Use bilinear scaling */
616
g_object_set (video_save_scale, "method", 1, NULL);
619
if ((mux = gst_element_factory_make ("oggmux", "mux")) == NULL)
621
cheese_camera_set_error_element_not_found (error, "oggmux");
625
g_object_set (G_OBJECT (mux),
626
"max-delay", (guint64) 10000000,
627
"max-page-delay", (guint64) 10000000, NULL);
630
if ((priv->video_file_sink = gst_element_factory_make ("filesink", "video_file_sink")) == NULL)
632
cheese_camera_set_error_element_not_found (error, "filesink");
635
if (error != NULL && *error != NULL)
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);
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));
648
ok = gst_element_link_many (priv->audio_source, audio_queue, audio_convert,
649
audio_enc, mux, priv->video_file_sink, NULL);
651
ok &= gst_element_link_many (video_save_csp, video_save_rate, video_save_scale, video_enc,
653
ok &= gst_element_link (video_enc, mux);
656
g_error ("Unable to create video save pipeline");
491
priv->effects_preview_bin, NULL);
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"));
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));
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));
510
g_error ("Unable to create filter bin");
662
516
cheese_camera_get_num_camera_devices (CheeseCamera *camera)
664
518
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
780
626
cheese_camera_change_effect_filter (CheeseCamera *camera, GstElement *new_filter)
782
628
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
784
gboolean is_playing = priv->pipeline_is_playing;
787
cheese_camera_stop (camera);
789
gst_element_unlink_many (priv->camera_source_bin, priv->effect_filter,
790
priv->csp_post_effect, NULL);
792
gst_bin_remove (GST_BIN (priv->video_display_bin), priv->effect_filter);
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);
631
g_object_set (G_OBJECT (priv->main_valve), "drop", TRUE, NULL);
633
gst_element_unlink_many (priv->main_valve, priv->effect_filter,
634
priv->video_balance, NULL);
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);
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);
797
646
g_return_if_fail (ok);
800
cheese_camera_play (camera);
648
g_object_set (G_OBJECT (priv->main_valve), "drop", FALSE, NULL);
802
650
priv->effect_filter = new_filter;
806
cheese_camera_set_effect (CheeseCamera *camera, CheeseCameraEffect effect)
654
cheese_camera_element_from_effect (CheeseCamera *camera, CheeseEffect *effect)
808
GString *rgb_effects_str = g_string_new ("");
809
GString *yuv_effects_str = g_string_new ("");
810
656
char *effects_pipeline_desc;
812
658
GstElement *effect_filter;
813
659
GError *err = NULL;
815
for (i = 0; i < NUM_EFFECTS; i++)
817
if (effect & EFFECT_TO_PIPELINE_DESC[i].effect)
819
if (EFFECT_TO_PIPELINE_DESC[i].colorspace == RGB)
821
g_string_append (rgb_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
822
g_string_append (rgb_effects_str, " ! ");
826
g_string_append (yuv_effects_str, " ! ");
827
g_string_append (yuv_effects_str, EFFECT_TO_PIPELINE_DESC[i].pipeline_desc);
831
effects_pipeline_desc = g_strconcat ("ffmpegcolorspace ! ",
832
rgb_effects_str->str,
834
yuv_effects_str->str,
661
GstElement *colorspace1;
662
GstElement *colorspace2;
665
g_object_get (G_OBJECT (effect),
666
"pipeline_desc", &effect_desc,
667
"name", &name, NULL);
669
effects_pipeline_desc = g_strconcat ("ffmpegcolorspace name=colorspace1 ! ",
671
" ! ffmpegcolorspace name=colorspace2",
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))
840
676
g_error_free (err);
841
g_error ("ERROR effect_filter\n");
677
g_warning ("Error with effect filter %s. Ignored", name);
843
cheese_camera_change_effect_filter (camera, effect_filter);
845
680
g_free (effects_pipeline_desc);
846
g_string_free (rgb_effects_str, TRUE);
847
g_string_free (yuv_effects_str, TRUE);
851
cheese_camera_start_video_recording (CheeseCamera *camera, char *filename)
853
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
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);
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");
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));
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));
694
return effect_filter;
698
* cheese_camera_set_effect:
699
* @camera: a #CheeseCamera
700
* @effect: a #CheeseEffect
704
cheese_camera_set_effect (CheeseCamera *camera, CheeseEffect *effect)
706
GstElement *effect_filter;
708
effect_filter = cheese_camera_element_from_effect (camera, effect);
709
if (effect_filter != NULL)
710
cheese_camera_change_effect_filter (camera, effect_filter);
714
* cheese_camera_toggle_effects_pipeline:
715
* @camera: a #CheeseCamera
716
* @active: TRUE is effects pipeline is active, FALSE otherwise
720
cheese_camera_toggle_effects_pipeline (CheeseCamera *camera, gboolean active)
722
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
726
g_object_set (G_OBJECT (priv->effects_valve), "drop", FALSE, NULL);
727
g_object_set (G_OBJECT (priv->main_valve), "drop", TRUE, NULL);
731
g_object_set (G_OBJECT (priv->effects_valve), "drop", TRUE, NULL);
732
g_object_set (G_OBJECT (priv->main_valve), "drop", FALSE, NULL);
737
* cheese_camera_connect_effect_texture:
738
* @camera: a #CheeseCamera
739
* @effect: a #CheeseEffect
740
* @texture: a #ClutterTexture
744
cheese_camera_connect_effect_texture (CheeseCamera *camera, CheeseEffect *effect, ClutterTexture *texture)
746
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
748
GstElement *effect_filter;
749
GstElement *display_element;
750
GstElement *display_queue;
751
GstElement *control_valve;
754
g_object_set (G_OBJECT (priv->effects_valve), "drop", TRUE, NULL);
756
control_valve = gst_element_factory_make ("valve", NULL);
757
g_object_set (G_OBJECT (effect), "control_valve", control_valve, NULL);
759
display_queue = gst_element_factory_make ("queue", NULL);
761
effect_filter = cheese_camera_element_from_effect (camera, effect);
763
display_element = clutter_gst_video_sink_new (texture);
764
g_object_set (G_OBJECT (display_element), "async", FALSE, NULL);
766
gst_bin_add_many (GST_BIN (priv->video_filter_bin), control_valve, effect_filter, display_queue, display_element, NULL);
768
ok = gst_element_link_many (priv->effects_tee, control_valve, effect_filter, display_queue, display_element, NULL);
769
g_return_if_fail (ok);
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);
778
g_warning ("Could not create effects pipeline");
780
g_object_set (G_OBJECT (priv->effects_valve), "drop", FALSE, NULL);
784
* cheese_camera_start_video_recording:
785
* @camera: a #CheeseCamera
786
* @filename: the name of the video file that should be recorded
790
cheese_camera_start_video_recording (CheeseCamera *camera, const char *filename)
792
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
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;
869
810
GST_WARNING ("Cannot cleanly shutdown recording pipeline, forcing");
870
811
g_signal_emit (camera, camera_signals[VIDEO_SAVED], 0);
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;
822
* cheese_camera_stop_video_recording:
823
* @camera: a #CheeseCamera
881
827
cheese_camera_stop_video_recording (CheeseCamera *camera)
883
829
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
886
gst_element_get_state (priv->pipeline, &state, NULL, 0);
832
gst_element_get_state (priv->camerabin, &state, NULL, 0);
888
834
if (state == GST_STATE_PLAYING)
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;
909
872
GST_WARNING ("Still waiting for previous photo data, ignoring new request");
875
priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->camerabin),
877
G_CALLBACK (cheese_camera_image_done_cb),
913
880
g_free (priv->photo_filename);
914
881
priv->photo_filename = g_strdup (filename);
916
/* Take the photo by connecting the handoff signal */
917
priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->photo_sink),
919
G_CALLBACK (cheese_camera_photo_data_cb),
885
/* Only copy the data if we're giving away a pixbuf,
886
* not if we're throwing everything away straight away */
888
if (priv->photo_filename != NULL)
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);
896
g_signal_handler_disconnect (G_OBJECT (priv->camerabin),
897
priv->photo_handler_signal_id);
898
priv->photo_handler_signal_id = 0;
906
* cheese_camera_take_photo_pixbuf:
907
* @camera: a #CheeseCamera
925
911
cheese_camera_take_photo_pixbuf (CheeseCamera *camera)
927
913
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
929
916
if (priv->photo_handler_signal_id != 0)
931
918
GST_WARNING ("Still waiting for previous photo data, ignoring new request");
935
/* Take the photo by connecting the handoff signal */
936
priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->photo_sink),
938
G_CALLBACK (cheese_camera_photo_data_cb),
921
priv->photo_handler_signal_id = g_signal_connect (G_OBJECT (priv->camerabin),
923
G_CALLBACK (cheese_camera_image_done_cb),
925
caps = gst_caps_new_simple ("video/x-raw-rgb",
926
"bpp", G_TYPE_INT, 24,
927
"depth", G_TYPE_INT, 24,
929
g_object_set (G_OBJECT (priv->camerabin), "preview-caps", caps, NULL);
930
gst_caps_unref (caps);
932
if (priv->photo_filename)
933
g_free (priv->photo_filename);
934
priv->photo_filename = NULL;
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);
1110
1122
if (camera_device_name)
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);
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);
1126
cheese_camera_setup (CheeseCamera *camera, char *id, GError **error)
1128
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1138
* cheese_camera_set_device_by_dev_file:
1139
* @camera: a #CheeseCamera
1140
* @file: the filename
1144
cheese_camera_set_device_by_dev_file (CheeseCamera *camera, const gchar *file)
1146
g_return_if_fail (CHEESE_IS_CAMERA (camera));
1147
g_object_set (camera, "device_name", file, NULL);
1151
cheese_camera_set_device_by_dev_udi (CheeseCamera *camera, const gchar *udi)
1153
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1156
g_return_if_fail (CHEESE_IS_CAMERA (camera));
1159
for (i = 0; i < priv->num_camera_devices; i++)
1161
CheeseCameraDevice *device = g_ptr_array_index (priv->camera_devices, i);
1162
if (strcmp (cheese_camera_device_get_id (device), udi) == 0)
1164
g_object_set (camera,
1165
"device_name", cheese_camera_device_get_id (device),
1173
* cheese_camera_setup:
1174
* @camera: a #CheeseCamera
1175
* @id: (allow-none): the device id
1176
* @error: return location for a GError or NULL
1180
cheese_camera_setup (CheeseCamera *camera, const char *id, GError **error)
1182
CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
1131
1184
GError *tmp_error = NULL;
1185
GstElement *video_sink;
1133
1188
cheese_camera_detect_camera_devices (camera);
1143
1198
cheese_camera_set_device_by_dev_udi (camera, id);
1146
priv->pipeline = gst_pipeline_new ("pipeline");
1148
cheese_camera_create_video_display_bin (camera, &tmp_error);
1150
cheese_camera_create_photo_save_bin (camera, &tmp_error);
1152
cheese_camera_create_video_save_bin (camera, &tmp_error);
1153
if (tmp_error != NULL)
1202
if ((priv->camerabin = gst_element_factory_make ("camerabin", "camerabin")) == NULL)
1204
cheese_camera_set_error_element_not_found (error, "camerabin");
1206
g_object_set (priv->camerabin, "video-capture-height", 0,
1207
"video-capture-width", 0, NULL);
1209
/* Create a clutter-gst sink and set it as camerabin sink*/
1211
if ((video_sink = clutter_gst_video_sink_new (priv->video_texture)) == NULL)
1213
cheese_camera_set_error_element_not_found (error, "cluttervideosink");
1215
g_object_set (G_OBJECT (video_sink), "async", FALSE, NULL);
1216
g_object_set (G_OBJECT (priv->camerabin), "viewfinder-sink", video_sink, NULL);
1218
/* Set flags to enable conversions*/
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,
1229
/* Set caps to filter, so it doesn't defaults to I420 format*/
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);
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);
1239
if (tmp_error != NULL || (error != NULL && *error != NULL))
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);
1161
gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_display_bin,
1162
priv->photo_save_bin, NULL);
1164
ok = gst_element_link (priv->video_display_bin, priv->photo_save_bin);
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);
1248
priv->bus = gst_element_get_bus (priv->camerabin);
1167
1249
gst_bus_add_signal_watch (priv->bus);
1169
1251
g_signal_connect (G_OBJECT (priv->bus), "message",
1170
1252
G_CALLBACK (cheese_camera_bus_message_cb), camera);
1172
gst_bus_set_sync_handler (priv->bus, (GstBusSyncHandler) cheese_camera_bus_sync_handler, camera);
1175
g_error ("Unable link pipeline for photo");
1256
* cheese_camera_get_camera_devices:
1257
* @camera: a #CheeseCamera
1259
* Returns: (element-type Cheese.CameraDevice) (transfer container): Array of #CheeseCameraDevice
1179
1263
cheese_camera_get_camera_devices (CheeseCamera *camera)