/*
* Clutter-GStreamer.
*
* GStreamer integration library for Clutter.
*
* clutter-gst-camera.c - a GStreamer pipeline to display/manipulate a
* camera stream.
*
* Authored By Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk>
*
* Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:clutter-gst-camera
* @short_description: A player of camera streams.
*
* #ClutterGstCamera implements the #ClutterGstPlayer interface and
* plays camera streams.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <glib.h>
#include <gio/gio.h>
#include <gst/base/gstbasesink.h>
#include <gst/video/video.h>
#include "clutter-gst-camera.h"
#include "clutter-gst-camera-manager.h"
#include "clutter-gst-debug.h"
#include "clutter-gst-enum-types.h"
#include "clutter-gst-marshal.h"
#include "clutter-gst-player.h"
#include "clutter-gst-private.h"
static const gchar *supported_media_types[] = {
"video/x-raw",
NULL
};
struct _ClutterGstCameraPrivate
{
ClutterGstCameraDevice *camera_device;
ClutterGstFrame *current_frame;
GstBus *bus;
GstElement *camerabin;
GstElement *camera_source;
ClutterGstVideoSink *video_sink;
/* video filter */
GstElement *video_filter_bin;
GstElement *identity;
GstElement *valve;
GstElement *custom_filter;
GstElement *gamma;
GstElement *pre_colorspace;
GstElement *color_balance;
GstElement *post_colorspace;
gboolean is_idle;
gboolean is_recording;
gchar *photo_filename;
};
enum
{
CAPTURE_MODE_IMAGE = 1,
CAPTURE_MODE_VIDEO
};
enum
{
PROP_0,
PROP_IDLE,
PROP_PLAYING,
PROP_AUDIO_VOLUME,
PROP_DEVICE,
};
enum
{
READY_FOR_CAPTURE,
PHOTO_SAVED,
PHOTO_TAKEN,
VIDEO_SAVED,
LAST_SIGNAL
};
static int camera_signals[LAST_SIGNAL] = { 0 };
static void player_iface_init (ClutterGstPlayerIface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterGstCamera, clutter_gst_camera, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CLUTTER_GST_TYPE_PLAYER, player_iface_init));
/*
* ClutterGstPlayer implementation
*/
static ClutterGstFrame *
clutter_gst_camera_get_frame (ClutterGstPlayer *self)
{
ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (self)->priv;
return priv->current_frame;
}
static GstElement *
clutter_gst_camera_get_pipeline (ClutterGstPlayer *player)
{
ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv;
return priv->camerabin;
}
static ClutterGstVideoSink *
clutter_gst_camera_get_video_sink (ClutterGstPlayer *player)
{
ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv;
return priv->video_sink;
}
static gboolean
clutter_gst_camera_get_idle (ClutterGstPlayer *player)
{
ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv;
return priv->is_idle;
}
static gdouble
clutter_gst_camera_get_audio_volume (ClutterGstPlayer *player)
{
return 0;
}
static void
clutter_gst_camera_set_audio_volume (ClutterGstPlayer *player,
gdouble volume)
{
}
static gboolean
clutter_gst_camera_get_playing (ClutterGstPlayer *player)
{
ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv;
GstState state, pending;
gboolean playing;
if (!priv->camerabin)
return FALSE;
gst_element_get_state (priv->camerabin, &state, &pending, 0);
if (pending)
playing = (pending == GST_STATE_PLAYING);
else
playing = (state == GST_STATE_PLAYING);
return playing;
}
static void
clutter_gst_camera_set_playing (ClutterGstPlayer *player,
gboolean playing)
{
ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv;
GstState target_state;
if (!priv->camerabin)
return;
target_state = playing ? GST_STATE_PLAYING : GST_STATE_NULL;
gst_element_set_state (priv->camerabin, target_state);
}
static void
player_iface_init (ClutterGstPlayerIface *iface)
{
iface->get_frame = clutter_gst_camera_get_frame;
iface->get_pipeline = clutter_gst_camera_get_pipeline;
iface->get_video_sink = clutter_gst_camera_get_video_sink;
iface->get_idle = clutter_gst_camera_get_idle;
iface->get_audio_volume = clutter_gst_camera_get_audio_volume;
iface->set_audio_volume = clutter_gst_camera_set_audio_volume;
iface->get_playing = clutter_gst_camera_get_playing;
iface->set_playing = clutter_gst_camera_set_playing;
}
/*
* GObject implementation
*/
static void
clutter_gst_camera_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_IDLE:
g_value_set_boolean (value,
clutter_gst_camera_get_idle (CLUTTER_GST_PLAYER (object)));
break;
case PROP_PLAYING:
g_value_set_boolean (value,
clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (object)));
break;
case PROP_AUDIO_VOLUME:
g_value_set_double (value,
clutter_gst_camera_get_audio_volume (CLUTTER_GST_PLAYER (object)));
break;
case PROP_DEVICE:
g_value_set_object (value,
clutter_gst_camera_get_camera_device (CLUTTER_GST_CAMERA (object)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
clutter_gst_camera_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_PLAYING:
clutter_gst_camera_set_playing (CLUTTER_GST_PLAYER (object),
g_value_get_boolean (value));
break;
case PROP_AUDIO_VOLUME:
clutter_gst_camera_set_audio_volume (CLUTTER_GST_PLAYER (object),
g_value_get_double (value));
break;
case PROP_DEVICE:
clutter_gst_camera_set_camera_device (CLUTTER_GST_CAMERA (object),
CLUTTER_GST_CAMERA_DEVICE (g_value_get_object (value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
clutter_gst_camera_dispose (GObject *object)
{
ClutterGstCamera *self = CLUTTER_GST_CAMERA (object);
ClutterGstCameraPrivate *priv = self->priv;
g_free (priv->photo_filename);
priv->photo_filename = NULL;
g_clear_object (&priv->camera_device);
if (priv->bus)
{
gst_object_unref (priv->bus);
priv->bus = NULL;
}
if (priv->camerabin)
{
gst_element_set_state (priv->camerabin, GST_STATE_NULL);
gst_object_unref (priv->camerabin);
priv->camerabin = NULL;
}
G_OBJECT_CLASS (clutter_gst_camera_parent_class)->dispose (object);
}
static void
clutter_gst_camera_class_init (ClutterGstCameraClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (ClutterGstCameraPrivate));
object_class->get_property = clutter_gst_camera_get_property;
object_class->set_property = clutter_gst_camera_set_property;
object_class->dispose = clutter_gst_camera_dispose;
g_object_class_override_property (object_class,
PROP_IDLE, "idle");
g_object_class_override_property (object_class,
PROP_PLAYING, "playing");
g_object_class_override_property (object_class,
PROP_AUDIO_VOLUME, "audio-volume");
/**
* ClutterGstCamera:camera-device:
*
* The camera device associated with the camera player.
*/
pspec = g_param_spec_object ("device",
"Device",
"Camera Device",
CLUTTER_GST_TYPE_CAMERA_DEVICE,
CLUTTER_GST_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_DEVICE, pspec);
/* Signals */
/**
* ClutterGstCamera::ready-for-capture:
* @self: the actor which received the signal
* @ready: whether the @self is ready for a new capture
*
* The ::ready-for-capture signal is emitted whenever the value of
* clutter_gst_camera_is_ready_for_capture changes.
*/
camera_signals[READY_FOR_CAPTURE] =
g_signal_new ("ready-for-capture",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterGstCameraClass, ready_for_capture),
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1,
G_TYPE_BOOLEAN);
/**
* ClutterGstCamera::photo-saved:
* @self: the actor which received the signal
*
* The ::photo-saved signal is emitted when a photo was saved to disk.
*/
camera_signals[PHOTO_SAVED] =
g_signal_new ("photo-saved",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (ClutterGstCameraClass, photo_saved),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* ClutterGstCamera::photo-taken:
* @self: the actor which received the signal
* @pixbuf: the photo taken as a #GdkPixbuf
*
* The ::photo-taken signal is emitted when a photo was taken.
*/
camera_signals[PHOTO_TAKEN] =
g_signal_new ("photo-taken",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (ClutterGstCameraClass, photo_taken),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GDK_TYPE_PIXBUF);
/**
* ClutterGstCamera::video-saved:
* @self: the actor which received the signal
*
* The ::video-saved signal is emitted when a video was saved to disk.
*/
camera_signals[VIDEO_SAVED] =
g_signal_new ("video-saved",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (ClutterGstCameraClass, video_saved),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
notify_ready_for_capture (GObject *object,
GParamSpec *pspec,
ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv = self->priv;
gboolean ready_for_capture;
g_object_get (priv->camera_source, "ready-for-capture",
&ready_for_capture, NULL);
g_signal_emit (self, camera_signals[READY_FOR_CAPTURE],
0, ready_for_capture);
}
static void
parse_photo_data (ClutterGstCamera *self,
GstSample *sample)
{
ClutterGstCameraPrivate *priv = self->priv;
GstBuffer *buffer;
GstCaps *caps;
const GstStructure *structure;
gint width, height, stride;
GdkPixbuf *pixbuf;
const gint bits_per_pixel = 8;
guchar *data = NULL;
GstMapInfo info;
buffer = gst_sample_get_buffer (sample);
caps = gst_sample_get_caps (sample);
gst_buffer_map (buffer, &info, GST_MAP_READ);
structure = gst_caps_get_structure (caps, 0);
gst_structure_get_int (structure, "width", &width);
gst_structure_get_int (structure, "height", &height);
stride = info.size / height;
data = g_memdup (info.data, info.size);
pixbuf = gdk_pixbuf_new_from_data (data,
GDK_COLORSPACE_RGB,
FALSE, bits_per_pixel, width, height, stride,
data ? (GdkPixbufDestroyNotify) g_free : NULL, NULL);
g_object_set (G_OBJECT (priv->camerabin), "post-previews", FALSE, NULL);
g_signal_emit (self, camera_signals[PHOTO_TAKEN], 0, pixbuf);
g_object_unref (pixbuf);
}
static void
bus_message_cb (GstBus *bus,
GstMessage *message,
ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv = self->priv;
switch (GST_MESSAGE_TYPE (message))
{
case GST_MESSAGE_ERROR:
{
GError *err = NULL;
gchar *debug = NULL;
gst_message_parse_error (message, &err, &debug);
if (err && err->message)
g_warning ("%s", err->message);
else
g_warning ("Unparsable GST_MESSAGE_ERROR message.");
if (err)
g_error_free (err);
g_free (debug);
priv->is_idle = TRUE;
g_object_notify (G_OBJECT (self), "idle");
break;
}
case GST_MESSAGE_STATE_CHANGED:
{
if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0)
{
GstState new;
gst_message_parse_state_changed (message, NULL, &new, NULL);
if (new == GST_STATE_PLAYING)
priv->is_idle = FALSE;
else
priv->is_idle = TRUE;
g_object_notify (G_OBJECT (self), "idle");
}
break;
}
case GST_MESSAGE_ELEMENT:
{
const GstStructure *structure;
const GValue *image;
if (strcmp (GST_MESSAGE_SRC_NAME (message), "camera_source") == 0)
{
structure = gst_message_get_structure (message);
if (strcmp (gst_structure_get_name (structure), "preview-image") == 0)
{
if (gst_structure_has_field_typed (structure, "sample", GST_TYPE_SAMPLE))
{
image = gst_structure_get_value (structure, "sample");
if (image)
{
GstSample *sample;
sample = gst_value_get_sample (image);
parse_photo_data (self, sample);
}
else
g_warning ("Could not get buffer from bus message");
}
}
}
else if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0)
{
structure = gst_message_get_structure (message);
if (strcmp (gst_structure_get_name (structure), "image-done") == 0)
{
const gchar *filename = gst_structure_get_string (structure, "filename");
if (priv->photo_filename != NULL && filename != NULL &&
(strcmp (priv->photo_filename, filename) == 0))
g_signal_emit (self, camera_signals[PHOTO_SAVED], 0);
}
else if (strcmp (gst_structure_get_name (structure), "video-done") == 0)
{
g_signal_emit (self, camera_signals[VIDEO_SAVED], 0);
priv->is_recording = FALSE;
}
}
break;
}
default:
break;
}
}
static void
set_video_profile (ClutterGstCamera *self)
{
GstEncodingContainerProfile *prof;
GstEncodingAudioProfile *audio_prof;
GstEncodingVideoProfile *video_prof;
GstCaps *caps;
caps = gst_caps_from_string ("application/ogg");
prof = gst_encoding_container_profile_new ("Ogg audio/video",
"Standard Ogg/Theora/Vorbis",
caps, NULL);
gst_caps_unref (caps);
caps = gst_caps_from_string ("video/x-theora");
video_prof = gst_encoding_video_profile_new (caps, NULL, NULL, 0);
gst_encoding_container_profile_add_profile (prof, (GstEncodingProfile*) video_prof);
gst_caps_unref (caps);
caps = gst_caps_from_string ("audio/x-vorbis");
audio_prof = gst_encoding_audio_profile_new (caps, NULL, NULL, 0);
gst_encoding_container_profile_add_profile (prof, (GstEncodingProfile*) audio_prof);
gst_caps_unref (caps);
clutter_gst_camera_set_video_profile (self,
(GstEncodingProfile *) prof);
gst_encoding_profile_unref (prof);
}
static GstElement *
setup_video_filter_bin (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv = self->priv;
GstElement *bin;
GstPad *pad;
if ((priv->identity = gst_element_factory_make ("identity", "identity")) == NULL)
goto error;
if ((priv->valve = gst_element_factory_make ("valve", "valve")) == NULL)
goto error;
if ((priv->gamma = gst_element_factory_make ("gamma", "gamma")) == NULL)
goto error;
if ((priv->pre_colorspace = gst_element_factory_make ("videoconvert", "pre_colorspace")) == NULL)
goto error;
if ((priv->color_balance = gst_element_factory_make ("videobalance", "color_balance")) == NULL)
goto error;
if ((priv->post_colorspace = gst_element_factory_make ("videoconvert", "post_colorspace")) == NULL)
goto error;
bin = gst_bin_new ("video_filter_bin");
gst_bin_add_many (GST_BIN (bin),
priv->identity, priv->valve, priv->gamma,
priv->pre_colorspace, priv->color_balance, priv->post_colorspace,
NULL);
if (!gst_element_link_many (priv->identity, priv->valve, priv->gamma,
priv->pre_colorspace, priv->color_balance, priv->post_colorspace,
NULL))
goto error_not_linked;
pad = gst_element_get_static_pad (priv->post_colorspace, "src");
gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
gst_object_unref (pad);
pad = gst_element_get_static_pad (priv->identity, "sink");
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (pad);
return bin;
error:
if (priv->identity)
gst_object_unref (priv->identity);
if (priv->valve)
gst_object_unref (priv->valve);
if (priv->gamma)
gst_object_unref (priv->gamma);
if (priv->pre_colorspace)
gst_object_unref (priv->pre_colorspace);
if (priv->color_balance)
gst_object_unref (priv->color_balance);
if (priv->post_colorspace)
gst_object_unref (priv->post_colorspace);
return NULL;
error_not_linked:
gst_object_unref (bin);
return NULL;
}
static GstCaps *
create_caps_for_formats (gint width,
gint height)
{
GstCaps *ret = NULL;
guint length;
guint i;
length = g_strv_length ((gchar **) supported_media_types);
for (i = 0; i < length; i++)
{
GstCaps *caps;
caps = gst_caps_new_simple (supported_media_types[i],
"width", G_TYPE_INT, width,
"height", G_TYPE_INT, height,
NULL);
if (!ret)
ret = caps;
else
gst_caps_append (ret, caps);
}
return ret;
}
static void
device_capture_resolution_changed (ClutterGstCameraDevice *camera_device,
gint width,
gint height,
ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv = self->priv;
GstCaps *caps;
if (priv->camera_device != camera_device)
return;
caps = create_caps_for_formats (width, height);
g_object_set (G_OBJECT (priv->camerabin), "video-capture-caps", caps, NULL);
g_object_set (G_OBJECT (priv->camerabin), "image-capture-caps", caps, NULL);
g_object_set (G_OBJECT (priv->camerabin), "viewfinder-caps", caps, NULL);
gst_caps_unref (caps);
}
static void
set_device_resolutions (ClutterGstCamera *self,
ClutterGstCameraDevice *device)
{
gint width;
gint height;
clutter_gst_camera_device_get_capture_resolution (device, &width, &height);
device_capture_resolution_changed (device, width, height, self);
}
static gboolean
setup_camera_source (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv = self->priv;
GstElement *camera_source;
if (priv->camera_source)
return TRUE;
camera_source = gst_element_factory_make ("wrappercamerabinsrc", "camera_source");
if (G_UNLIKELY (!camera_source))
{
g_critical ("Unable to create wrappercamerabinsrc element");
return FALSE;
}
priv->camera_source = camera_source;
g_object_set (priv->camerabin, "camera-source", camera_source, NULL);
g_signal_connect (camera_source, "notify::ready-for-capture",
G_CALLBACK (notify_ready_for_capture),
self);
if (priv->video_filter_bin)
{
g_object_set (G_OBJECT (camera_source),
"video-source-filter", priv->video_filter_bin,
NULL);
}
return TRUE;
}
static void
_new_frame_from_pipeline (ClutterGstVideoSink *sink, ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv = self->priv;
clutter_gst_player_update_frame (CLUTTER_GST_PLAYER (self),
&priv->current_frame,
clutter_gst_video_sink_get_frame (sink));
}
static void
_ready_from_pipeline (ClutterGstVideoSink *sink, ClutterGstCamera *self)
{
g_signal_emit_by_name (self, "ready");
}
static void
_pixel_aspect_ratio_changed (ClutterGstVideoSink *sink,
GParamSpec *spec,
ClutterGstCamera *self)
{
clutter_gst_frame_update_pixel_aspect_ratio (self->priv->current_frame, sink);
}
static gboolean
setup_pipeline (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv = self->priv;
const GPtrArray *camera_devices =
clutter_gst_camera_manager_get_camera_devices (clutter_gst_camera_manager_get_default ());
priv->camerabin = gst_element_factory_make ("camerabin", "camerabin");
if (G_UNLIKELY (!priv->camerabin))
{
g_critical ("Unable to create camerabin element");
return FALSE;
}
priv->video_filter_bin = setup_video_filter_bin (self);
if (!priv->video_filter_bin)
g_warning ("Unable to setup video filter, some features will be disabled");
if (G_UNLIKELY (!setup_camera_source (self)))
{
g_critical ("Unable to create camera source element");
gst_object_unref (priv->camerabin);
priv->camerabin = 0;
return FALSE;
}
if (camera_devices->len > 0 &&
!clutter_gst_camera_set_camera_device (self,
g_ptr_array_index (camera_devices, 0)))
{
g_critical ("Unable to select capture device");
gst_object_unref (priv->camerabin);
priv->camerabin = 0;
return FALSE;
}
priv->video_sink = clutter_gst_video_sink_new ();
g_signal_connect (priv->video_sink, "new-frame",
G_CALLBACK (_new_frame_from_pipeline), self);
g_signal_connect (priv->video_sink, "pipeline-ready",
G_CALLBACK (_ready_from_pipeline), self);
g_signal_connect (priv->video_sink, "notify::pixel-aspect-ratio",
G_CALLBACK (_pixel_aspect_ratio_changed), self);
g_object_set (priv->camerabin,
"viewfinder-sink", priv->video_sink,
NULL);
set_video_profile (self);
priv->bus = gst_element_get_bus (priv->camerabin);
gst_bus_add_signal_watch (priv->bus);
g_signal_connect (G_OBJECT (priv->bus), "message",
G_CALLBACK (bus_message_cb), self);
return TRUE;
}
static void
clutter_gst_camera_init (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv;
self->priv = priv =
G_TYPE_INSTANCE_GET_PRIVATE (self,
CLUTTER_GST_TYPE_CAMERA,
ClutterGstCameraPrivate);
if (!setup_pipeline (self))
{
g_warning ("Failed to initiate suitable elements for pipeline.");
return;
}
priv->current_frame = clutter_gst_create_blank_frame (NULL);
priv->is_idle = TRUE;
}
/*
* Public symbols
*/
/**
* clutter_gst_camera_new:
*
* Create a camera actor.
*
* <note>This function has to be called from Clutter's main thread. While
* GStreamer will spawn threads to do its work, we want all the GL calls to
* happen in the same thread. Clutter-gst knows which thread it is by
* assuming this constructor is called from the Clutter thread.</note>
*
* Return value: the newly created camera actor
*/
ClutterGstCamera *
clutter_gst_camera_new (void)
{
return g_object_new (CLUTTER_GST_TYPE_CAMERA,
NULL);
}
/* /\** */
/* * clutter_gst_camera_get_pipeline: */
/* * @self: a #ClutterGstCamera */
/* * */
/* * Retrieve the #GstPipeline used by the @self, for direct use with */
/* * GStreamer API. */
/* * */
/* * Return value: (transfer none): the pipeline element used by the camera actor */
/* *\/ */
/* GstElement * */
/* clutter_gst_camera_get_pipeline (ClutterGstCamera *self) */
/* { */
/* g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), NULL); */
/* return self->priv->camerabin; */
/* } */
/* /\** */
/* * clutter_gst_camera_get_camerabin: */
/* * @self: a #ClutterGstCamera */
/* * */
/* * Retrieve the camerabin element used by the @self, for direct use with */
/* * GStreamer API. */
/* * */
/* * Return value: (transfer none): the pipeline element used by the camera actor */
/* *\/ */
/* GstElement * */
/* clutter_gst_camera_get_camerabin (ClutterGstCamera *self) */
/* { */
/* g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), NULL); */
/* return self->priv->camerabin; */
/* } */
/**
* clutter_gst_camera_get_camera_device:
* @self: a #ClutterGstCamera
*
* Retrieve the current selected camera device.
*
* Return value: (transfer none): The currently selected camera device
*/
ClutterGstCameraDevice *
clutter_gst_camera_get_camera_device (ClutterGstCamera *self)
{
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), NULL);
return self->priv->camera_device;
}
/**
* clutter_gst_camera_set_camera_device:
* @self: a #ClutterGstCamera
* @device: a #ClutterGstCameraDevice
*
* Set the new active camera device.
*
* Return value: %TRUE on success, %FALSE otherwise
*/
gboolean
clutter_gst_camera_set_camera_device (ClutterGstCamera *self,
ClutterGstCameraDevice *device)
{
ClutterGstCameraPrivate *priv;
GstElementFactory *element_factory;
GstElement *src;
gchar *node;
gboolean was_playing = FALSE;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
g_return_val_if_fail (device != NULL, FALSE);
priv = self->priv;
if (!priv->camerabin)
return FALSE;
if (priv->is_recording)
clutter_gst_camera_stop_video_recording (self);
if (clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (self)))
{
gst_element_set_state (priv->camerabin, GST_STATE_NULL);
was_playing = TRUE;
}
g_object_get (device,
"element-factory", &element_factory,
"node", &node,
NULL);
src = gst_element_factory_create (element_factory, NULL);
if (!src)
{
g_warning ("Unable to create device source for "
"capture device %s (using factory %s)",
node, gst_object_get_name (GST_OBJECT (element_factory)));
return FALSE;
}
#if 0
g_print ("Setting active device to %s (using factory %s)\n",
node,
gst_object_get_name (GST_OBJECT (element_factory)));
#endif
gst_object_unref (element_factory);
if (priv->camera_device)
{
g_signal_handlers_disconnect_by_func (priv->camera_device,
device_capture_resolution_changed,
self);
g_clear_object (&priv->camera_device);
}
priv->camera_device = g_object_ref (device);
g_object_set (G_OBJECT (src), "device", node, NULL);
g_free (node);
g_object_set (G_OBJECT (priv->camera_source), "video-source", src, NULL);
g_signal_connect (device, "capture-resolution-changed",
G_CALLBACK (device_capture_resolution_changed),
self);
set_device_resolutions (self, device);
if (was_playing)
gst_element_set_state (priv->camerabin, GST_STATE_PLAYING);
return TRUE;
}
/**
* clutter_gst_camera_supports_gamma_correction:
* @self: a #ClutterGstCamera
*
* Check whether the @self supports gamma correction.
*
* Return value: %TRUE if @self supports gamma correction, %FALSE otherwise
*/
gboolean
clutter_gst_camera_supports_gamma_correction (ClutterGstCamera *self)
{
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
return (self->priv->gamma != NULL);
}
/**
* clutter_gst_camera_get_gamma_range:
* @self: a #ClutterGstCamera
* @min_value: Pointer to store the minimum gamma value, or %NULL
* @max_value: Pointer to store the maximum gamma value, or %NULL
* @default_value: Pointer to store the default gamma value, or %NULL
*
* Retrieve the minimum, maximum and default gamma values.
*
* This method will return FALSE if gamma correction is not
* supported on @self.
* See clutter_gst_camera_supports_gamma_correction().
*
* Return value: %TRUE if successful, %FALSE otherwise
*/
gboolean
clutter_gst_camera_get_gamma_range (ClutterGstCamera *self,
gdouble *min_value,
gdouble *max_value,
gdouble *default_value)
{
ClutterGstCameraPrivate *priv;
GParamSpec *pspec;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
if (!priv->gamma)
return FALSE;
pspec = g_object_class_find_property (
G_OBJECT_GET_CLASS (G_OBJECT (priv->gamma)), "gamma");
/* shouldn't happen */
g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE);
if (min_value)
*min_value = G_PARAM_SPEC_DOUBLE (pspec)->minimum;
if (max_value)
*max_value = G_PARAM_SPEC_DOUBLE (pspec)->maximum;
if (default_value)
*default_value = G_PARAM_SPEC_DOUBLE (pspec)->default_value;
return TRUE;
}
/**
* clutter_gst_camera_get_gamma:
* @self: a #ClutterGstCamera
* @cur_value: Pointer to store the current gamma value
*
* Retrieve the current gamma value.
*
* This method will return FALSE if gamma correction is not
* supported on @self.
* See clutter_gst_camera_supports_gamma_correction().
*
* Return value: %TRUE if successful, %FALSE otherwise
*/
gboolean
clutter_gst_camera_get_gamma (ClutterGstCamera *self,
gdouble *cur_value)
{
ClutterGstCameraPrivate *priv;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
g_return_val_if_fail (cur_value != NULL, FALSE);
priv = self->priv;
if (!priv->gamma)
return FALSE;
g_object_get (G_OBJECT (priv->gamma), "gamma", cur_value, NULL);
return TRUE;
}
/**
* clutter_gst_camera_set_gamma:
* @self: a #ClutterGstCamera
* @value: The value to set
*
* Set the gamma value.
* Allowed values can be retrieved with
* clutter_gst_camera_get_gamma_range().
*
* This method will return FALSE if gamma correction is not
* supported on @self.
* See clutter_gst_camera_supports_gamma_correction().
*
* Return value: %TRUE if successful, %FALSE otherwise
*/
gboolean
clutter_gst_camera_set_gamma (ClutterGstCamera *self,
gdouble value)
{
ClutterGstCameraPrivate *priv;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
if (!priv->gamma)
return FALSE;
g_object_set (G_OBJECT (priv->gamma), "gamma", value, NULL);
return TRUE;
}
/**
* clutter_gst_camera_supports_color_balance:
* @self: a #ClutterGstCamera
*
* Check whether the @self supports color balance.
*
* Return value: %TRUE if @self supports color balance, %FALSE otherwise
*/
gboolean
clutter_gst_camera_supports_color_balance (ClutterGstCamera *self)
{
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
return (self->priv->color_balance != NULL);
}
/**
* clutter_gst_camera_get_color_balance_property_range:
* @self: a #ClutterGstCamera
* @property: Property name
* @min_value: Pointer to store the minimum value of @property, or %NULL
* @max_value: Pointer to store the maximum value of @property, or %NULL
* @default_value: Pointer to store the default value of @property, or %NULL
*
* Retrieve the minimum, maximum and default values for the color balance property @property,
*
* This method will return FALSE if @property does not exist or color balance is not
* supported on @self.
* See clutter_gst_camera_supports_color_balance().
*
* Return value: %TRUE if successful, %FALSE otherwise
*/
gboolean
clutter_gst_camera_get_color_balance_property_range (ClutterGstCamera *self,
const gchar *property,
gdouble *min_value,
gdouble *max_value,
gdouble *default_value)
{
ClutterGstCameraPrivate *priv;
GParamSpec *pspec;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
if (!priv->color_balance)
return FALSE;
pspec = g_object_class_find_property (
G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property);
g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE);
if (min_value)
*min_value = G_PARAM_SPEC_DOUBLE (pspec)->minimum;
if (max_value)
*max_value = G_PARAM_SPEC_DOUBLE (pspec)->maximum;
if (default_value)
*default_value = G_PARAM_SPEC_DOUBLE (pspec)->default_value;
return TRUE;
}
/**
* clutter_gst_camera_get_color_balance_property:
* @self: a #ClutterGstCamera
* @property: Property name
* @cur_value: Pointer to store the current value of @property
*
* Retrieve the current value for the color balance property @property,
*
* This method will return FALSE if @property does not exist or color balance is not
* supported on @self.
* See clutter_gst_camera_supports_color_balance().
*
* Return value: %TRUE if successful, %FALSE otherwise
*/
gboolean
clutter_gst_camera_get_color_balance_property (ClutterGstCamera *self,
const gchar *property,
gdouble *cur_value)
{
ClutterGstCameraPrivate *priv;
GParamSpec *pspec;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
g_return_val_if_fail (cur_value != NULL, FALSE);
priv = self->priv;
if (!priv->color_balance)
return FALSE;
pspec = g_object_class_find_property (
G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property);
g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE);
g_object_get (G_OBJECT (priv->color_balance), property, cur_value, NULL);
return TRUE;
}
/**
* clutter_gst_camera_set_color_balance_property:
* @self: a #ClutterGstCamera
* @property: Property name
* @value: The value to set
*
* Set the value for the color balance property @property to @value.
* Allowed values can be retrieved with
* clutter_gst_camera_get_color_balance_property_range().
*
* This method will return FALSE if @property does not exist or color balance is not
* supported on @self.
* See clutter_gst_camera_supports_color_balance().
*
* Return value: %TRUE if successful, %FALSE otherwise
*/
gboolean
clutter_gst_camera_set_color_balance_property (ClutterGstCamera *self,
const gchar *property,
gdouble value)
{
ClutterGstCameraPrivate *priv;
GParamSpec *pspec;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
if (!priv->color_balance)
return FALSE;
pspec = g_object_class_find_property (
G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property);
g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE);
g_object_set (G_OBJECT (priv->color_balance), property, value, NULL);
return TRUE;
}
gboolean
clutter_gst_camera_get_brightness_range (ClutterGstCamera *self,
gdouble *min_value,
gdouble *max_value,
gdouble *default_value)
{
return clutter_gst_camera_get_color_balance_property_range (self,
"brightness", min_value, max_value, default_value);
}
gboolean
clutter_gst_camera_get_brightness (ClutterGstCamera *self,
gdouble *cur_value)
{
return clutter_gst_camera_get_color_balance_property (self,
"brightness", cur_value);
}
gboolean
clutter_gst_camera_set_brightness (ClutterGstCamera *self,
gdouble value)
{
return clutter_gst_camera_set_color_balance_property (self,
"brightness", value);
}
gboolean
clutter_gst_camera_get_contrast_range (ClutterGstCamera *self,
gdouble *min_value,
gdouble *max_value,
gdouble *default_value)
{
return clutter_gst_camera_get_color_balance_property_range (self,
"contrast", min_value, max_value, default_value);
}
gboolean
clutter_gst_camera_get_contrast (ClutterGstCamera *self,
gdouble *cur_value)
{
return clutter_gst_camera_get_color_balance_property (self,
"contrast", cur_value);
}
gboolean
clutter_gst_camera_set_contrast (ClutterGstCamera *self,
gdouble value)
{
return clutter_gst_camera_set_color_balance_property (self,
"contrast", value);
}
gboolean
clutter_gst_camera_get_saturation_range (ClutterGstCamera *self,
gdouble *min_value,
gdouble *max_value,
gdouble *default_value)
{
return clutter_gst_camera_get_color_balance_property_range (self,
"saturation", min_value, max_value, default_value);
}
gboolean
clutter_gst_camera_get_saturation (ClutterGstCamera *self,
gdouble *cur_value)
{
return clutter_gst_camera_get_color_balance_property (self,
"saturation", cur_value);
}
gboolean
clutter_gst_camera_set_saturation (ClutterGstCamera *self,
gdouble value)
{
return clutter_gst_camera_set_color_balance_property (self,
"saturation", value);
}
gboolean
clutter_gst_camera_get_hue_range (ClutterGstCamera *self,
gdouble *min_value,
gdouble *max_value,
gdouble *default_value)
{
return clutter_gst_camera_get_color_balance_property_range (self,
"hue", min_value, max_value, default_value);
}
gboolean
clutter_gst_camera_get_hue (ClutterGstCamera *self,
gdouble *cur_value)
{
return clutter_gst_camera_get_color_balance_property (self,
"hue", cur_value);
}
gboolean
clutter_gst_camera_set_hue (ClutterGstCamera *self,
gdouble value)
{
return clutter_gst_camera_set_color_balance_property (self,
"hue", value);
}
/**
* clutter_gst_camera_get_filter:
* @self: a #ClutterGstCamera
*
* Retrieve the current filter being used.
*
* Return value: (transfer none): The current filter or %NULL if none is set
*/
GstElement *
clutter_gst_camera_get_filter (ClutterGstCamera *self)
{
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
return self->priv->custom_filter;
}
static GstElement *
create_filter_bin (GstElement *filter)
{
GstElement *filter_bin = NULL;
GstElement *pre_filter_colorspace;
GstElement *post_filter_colorspace;
GstPad *pad;
if ((pre_filter_colorspace = gst_element_factory_make ("videoconvert", "pre_filter_colorspace")) == NULL)
goto err;
if ((post_filter_colorspace = gst_element_factory_make ("videoconvert", "post_filter_colorspace")) == NULL)
goto err;
filter_bin = gst_bin_new ("custom_filter_bin");
gst_bin_add_many (GST_BIN (filter_bin), pre_filter_colorspace,
filter, post_filter_colorspace, NULL);
if (!gst_element_link_many (pre_filter_colorspace,
filter, post_filter_colorspace, NULL))
goto err_not_linked;
pad = gst_element_get_static_pad (pre_filter_colorspace, "sink");
gst_element_add_pad (filter_bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (GST_OBJECT (pad));
pad = gst_element_get_static_pad (post_filter_colorspace, "src");
gst_element_add_pad (filter_bin, gst_ghost_pad_new ("src", pad));
gst_object_unref (GST_OBJECT (pad));
out:
return filter_bin;
err:
if (pre_filter_colorspace)
gst_object_unref (pre_filter_colorspace);
if (post_filter_colorspace)
gst_object_unref (post_filter_colorspace);
goto out;
err_not_linked:
gst_object_unref (filter_bin);
filter_bin = NULL;
goto out;
}
/**
* clutter_gst_camera_set_filter:
* @self: a #ClutterGstCamera
* @filter: a #GstElement for the filter
*
* Set the filter element to be used.
* Filters can be used for effects, image processing, etc.
*
* Return value: %TRUE on success, %FALSE otherwise
*/
gboolean
clutter_gst_camera_set_filter (ClutterGstCamera *self,
GstElement *filter)
{
ClutterGstCameraPrivate *priv;
gboolean ret = FALSE;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
if (!priv->custom_filter && !filter)
{
/* nothing to do here, we don't have a filter and NULL
* was passed as new filter */
return TRUE;
}
g_object_set (G_OBJECT (priv->valve), "drop", TRUE, NULL);
if (priv->custom_filter)
{
/* remove current filter if any */
gst_element_unlink_many (priv->valve, priv->custom_filter,
priv->gamma, NULL);
g_object_ref (priv->custom_filter);
gst_bin_remove (GST_BIN (priv->video_filter_bin), priv->custom_filter);
gst_element_set_state (priv->custom_filter, GST_STATE_NULL);
g_object_unref (priv->custom_filter);
priv->custom_filter = NULL;
}
else
{
/* we have no current filter,
* unlink valve and gamma to set the new filter */
gst_element_unlink (priv->valve, priv->gamma);
}
if (filter)
{
priv->custom_filter = create_filter_bin (filter);
if (!priv->custom_filter)
goto err_restore;
gst_bin_add (GST_BIN (priv->video_filter_bin), priv->custom_filter);
if (!gst_element_link_many (priv->valve, priv->custom_filter,
priv->gamma, NULL))
{
/* removing will also unref it */
gst_bin_remove (GST_BIN (priv->video_filter_bin),
priv->custom_filter);
priv->custom_filter = NULL;
goto err_restore;
}
if (clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (self)))
gst_element_set_state (priv->custom_filter, GST_STATE_PLAYING);
}
else
gst_element_link (priv->valve, priv->gamma);
ret = TRUE;
out:
g_object_set (G_OBJECT (priv->valve), "drop", FALSE, NULL);
return ret;
err_restore:
ret = FALSE;
/* restore default pipeline, should always work */
gst_element_link (priv->valve, priv->gamma);
goto out;
}
/**
* clutter_gst_camera_remove_filter:
* @self: a #ClutterGstCamera
*
* Remove the current filter, if any.
*
* Return value: %TRUE on success, %FALSE otherwise
*/
gboolean
clutter_gst_camera_remove_filter (ClutterGstCamera *self)
{
return clutter_gst_camera_set_filter (self, NULL);
}
/**
* clutter_gst_camera_is_ready_for_capture:
* @self: a #ClutterGstCamera
*
* Check whether the @self is ready for video/photo capture.
*
* Return value: %TRUE if @self is ready for capture, %FALSE otherwise
*/
gboolean
clutter_gst_camera_is_ready_for_capture (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv;
gboolean ready_for_capture;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
g_object_get (priv->camera_source, "ready-for-capture", &ready_for_capture, NULL);
return ready_for_capture;
}
/**
* clutter_gst_camera_is_recording_video:
* @self: a #ClutterGstCamera
*
* Check whether the @self is recording video.
*
* Return value: %TRUE if @self is recording video, %FALSE otherwise
*/
gboolean
clutter_gst_camera_is_recording_video (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
return priv->is_recording;
}
/**
* clutter_gst_camera_set_video_profile:
* @self: a #ClutterGstCamera
* @profile: A #GstEncodingProfile to be used for video recording.
*
* Set the encoding profile to be used for video recording.
* The default profile saves videos as Ogg/Theora videos.
*/
void
clutter_gst_camera_set_video_profile (ClutterGstCamera *self,
GstEncodingProfile *profile)
{
ClutterGstCameraPrivate *priv;
g_return_if_fail (CLUTTER_GST_IS_CAMERA (self));
priv = self->priv;
if (!priv->camerabin)
return;
g_object_set (priv->camerabin, "video-profile", profile, NULL);
}
/**
* clutter_gst_camera_start_video_recording:
* @self: a #ClutterGstCamera
* @filename: (type filename): the name of the video file to where the
* recording will be saved
*
* Start a video recording with the @self and save it to @filename.
* This method requires that @self is playing and ready for capture.
*
* The ::video-saved signal will be emitted when the video is saved.
*
* Return value: %TRUE if the video recording was successfully started, %FALSE otherwise
*/
gboolean
clutter_gst_camera_start_video_recording (ClutterGstCamera *self,
const gchar *filename)
{
ClutterGstCameraPrivate *priv;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
if (!priv->camerabin)
return FALSE;
if (priv->is_recording)
return TRUE;
if (!clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (self)))
return FALSE;
if (!clutter_gst_camera_is_ready_for_capture (self))
return FALSE;
g_object_set (priv->camerabin, "mode", CAPTURE_MODE_VIDEO, NULL);
g_object_set (priv->camerabin, "location", filename, NULL);
g_signal_emit_by_name (priv->camerabin, "start-capture");
priv->is_recording = TRUE;
return TRUE;
}
/**
* clutter_gst_camera_stop_video_recording:
* @self: a #ClutterGstCamera
*
* Stop recording video on the @self.
*/
void
clutter_gst_camera_stop_video_recording (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv;
GstState state;
g_return_if_fail (CLUTTER_GST_IS_CAMERA (self));
priv = self->priv;
if (!priv->camerabin)
return;
if (!priv->is_recording)
return;
if (!clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (self)))
return;
gst_element_get_state (priv->camerabin, &state, NULL, 0);
if (state == GST_STATE_PLAYING)
g_signal_emit_by_name (priv->camerabin, "stop-capture");
else if (priv->is_recording)
{
g_warning ("Cannot cleanly shutdown recording pipeline, forcing");
gst_element_set_state (priv->camerabin, GST_STATE_NULL);
gst_element_set_state (priv->camerabin, GST_STATE_PLAYING);
priv->is_recording = FALSE;
}
}
/**
* clutter_gst_camera_set_photo_profile:
* @self: a #ClutterGstCamera
* @profile: A #GstEncodingProfile to be used for photo captures.
*
* Set the encoding profile to be used for photo captures.
* The default profile saves photos as JPEG images.
*/
void
clutter_gst_camera_set_photo_profile (ClutterGstCamera *self,
GstEncodingProfile *profile)
{
ClutterGstCameraPrivate *priv;
g_return_if_fail (CLUTTER_GST_IS_CAMERA (self));
priv = self->priv;
if (!priv->camerabin)
return;
g_object_set (priv->camerabin, "image-profile", profile, NULL);
}
/**
* clutter_gst_camera_take_photo:
* @self: a #ClutterGstCamera
* @filename: (type filename): the name of the file to where the
* photo will be saved
*
* Take a photo with the @self and save it to @filename.
* This method requires that @self is playing and ready for capture.
*
* The ::photo-saved signal will be emitted when the video is saved.
*
* Return value: %TRUE if the photo was successfully captured, %FALSE otherwise
*/
gboolean
clutter_gst_camera_take_photo (ClutterGstCamera *self,
const gchar *filename)
{
ClutterGstCameraPrivate *priv;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
priv = self->priv;
if (!priv->camerabin)
return FALSE;
if (!clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (self)))
return FALSE;
if (!clutter_gst_camera_is_ready_for_capture (self))
return FALSE;
g_free (priv->photo_filename);
priv->photo_filename = g_strdup (filename);
/* Take the photo */
g_object_set (priv->camerabin, "location", filename, NULL);
g_object_set (priv->camerabin, "mode", CAPTURE_MODE_IMAGE, NULL);
g_signal_emit_by_name (priv->camerabin, "start-capture");
return TRUE;
}
/**
* clutter_gst_camera_take_photo_pixbuf:
* @self: a #ClutterGstCamera
*
* Take a photo with the @self and emit it in the ::photo-taken signal as a
* #GdkPixbuf.
* This method requires that @self is playing and ready for capture.
*
* Return value: %TRUE if the photo was successfully captured, %FALSE otherwise
*/
gboolean
clutter_gst_camera_take_photo_pixbuf (ClutterGstCamera *self)
{
ClutterGstCameraPrivate *priv;
GstCaps *caps;
g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE);
priv = self->priv;
if (!priv->camerabin)
return FALSE;
if (!clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (self)))
return FALSE;
if (!clutter_gst_camera_is_ready_for_capture (self))
return FALSE;
caps = gst_caps_new_simple ("video/x-raw",
"bpp", G_TYPE_INT, 24,
"depth", G_TYPE_INT, 24,
NULL);
g_object_set (G_OBJECT (priv->camerabin), "post-previews", TRUE, NULL);
g_object_set (G_OBJECT (priv->camerabin), "preview-caps", caps, NULL);
gst_caps_unref (caps);
g_free (priv->photo_filename);
priv->photo_filename = NULL;
/* Take the photo */
g_object_set (priv->camerabin, "location", NULL, NULL);
g_object_set (priv->camerabin, "mode", CAPTURE_MODE_IMAGE, NULL);
g_signal_emit_by_name (priv->camerabin, "start-capture");
return TRUE;
}