gstreamer/gst-libs/gst/basecamerabinsrc/gstbasecamerasrc.c
Lauri Lehtinen 76b3ff361f basecamerasrc: add virtual function to notify subclass of changing preview caps
Adds a virtual function to basecamerasrc in case subclasses want to be
notified of changing preview caps. This is useful if the subclass wants
to post the preview itself or if it wants to provide a preview buffer
as close to as possible to the user's requested resolution to the
preview generation pipeline.
2011-03-10 08:42:01 -03:00

611 lines
17 KiB
C

/*
* GStreamer
* Copyright (C) 2010 Texas Instruments, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library 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:element-basecamerasrc
*
* Base class for the camera source bin used by camerabin for capture.
* Sophisticated camera hardware can derive from this baseclass and map the
* features to this interface.
*
* The design mandates that the subclasses implement the following features and
* behaviour:
* <itemizedlist>
* <listitem><para>
* 3 pads: viewfinder, image capture, video capture
* </para></listitem>
* <listitem><para>
* </para></listitem>
* </itemizedlist>
*
* During construct_pipeline() vmethod a subclass can add several elements into
* the bin and expose 3 srcs pads as ghostpads implementing the 3 pad templates.
*
* It is also possible to add regular pads from the subclass and implement the
* dataflow methods on these pads. This way all functionality can be implemneted
* directly in the subclass without extra elements.
*
* The src will receive the capture mode from #GstCameraBin2 on the
* #GstBaseCameraSrc:mode property. Possible capture modes are defined in
* #GstCameraBinMode.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "gstbasecamerasrc.h"
enum
{
PROP_0,
PROP_MODE,
PROP_ZOOM,
PROP_READY_FOR_CAPTURE,
PROP_POST_PREVIEW,
PROP_PREVIEW_CAPS,
PROP_PREVIEW_FILTER
};
enum
{
/* action signals */
START_CAPTURE_SIGNAL,
STOP_CAPTURE_SIGNAL,
/* emit signals */
LAST_SIGNAL
};
#define DEFAULT_POST_PREVIEW TRUE
static guint basecamerasrc_signals[LAST_SIGNAL];
GST_DEBUG_CATEGORY (base_camera_src_debug);
#define GST_CAT_DEFAULT base_camera_src_debug
GST_BOILERPLATE (GstBaseCameraSrc, gst_base_camera_src, GstBin, GST_TYPE_BIN);
static GstStaticPadTemplate vfsrc_template =
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate imgsrc_template =
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate vidsrc_template =
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
/* NOTE: we could provide a vmethod for derived class to overload to provide
* it's own implementation of interface.. but in all cases I can think of at
* moment, either the camerasrc itself, or some element within the bin, will
* be implementing the interface..
*/
/**
* gst_base_camera_src_get_photography:
* @self: the camerasrc bin
*
* Get object implementing photography interface, if there is one. Otherwise
* returns NULL.
*/
GstPhotography *
gst_base_camera_src_get_photography (GstBaseCameraSrc * self)
{
GstElement *elem;
if (GST_IS_PHOTOGRAPHY (self)) {
elem = GST_ELEMENT (self);
} else {
elem = gst_bin_get_by_interface (GST_BIN (self), GST_TYPE_PHOTOGRAPHY);
}
if (elem) {
return GST_PHOTOGRAPHY (elem);
}
return NULL;
}
/**
* gst_base_camera_src_get_colorbalance:
* @self: the camerasrc bin
*
* Get object implementing colorbalance interface, if there is one. Otherwise
* returns NULL.
*/
GstColorBalance *
gst_base_camera_src_get_color_balance (GstBaseCameraSrc * self)
{
GstElement *elem;
if (GST_IS_COLOR_BALANCE (self)) {
elem = GST_ELEMENT (self);
} else {
elem = gst_bin_get_by_interface (GST_BIN (self), GST_TYPE_COLOR_BALANCE);
}
if (elem) {
return GST_COLOR_BALANCE (self);
}
return NULL;
}
/**
* gst_base_camera_src_set_mode:
* @self: the camerasrc bin
* @mode: the mode
*
* Set the chosen #GstCameraBinMode capture mode.
*/
gboolean
gst_base_camera_src_set_mode (GstBaseCameraSrc * self, GstCameraBinMode mode)
{
GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
g_return_val_if_fail (bclass->set_mode, FALSE);
if (bclass->set_mode (self, mode)) {
self->mode = mode;
return TRUE;
}
return FALSE;
}
/**
* gst_base_camera_src_setup_zoom:
* @self: camerasrc object
*
* Apply zoom configured to camerabin to capture.
*/
void
gst_base_camera_src_setup_zoom (GstBaseCameraSrc * self)
{
GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
gint zoom;
zoom = g_atomic_int_get (&self->zoom);
g_return_if_fail (zoom);
g_return_if_fail (bclass->set_zoom);
bclass->set_zoom (self, zoom);
}
/**
* gst_base_camera_src_setup_preview:
* @self: camerasrc bin
* @preview_caps: preview caps to set
*
* Apply preview caps to preview pipeline and to video source.
*/
void
gst_base_camera_src_setup_preview (GstBaseCameraSrc * self,
GstCaps * preview_caps)
{
GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
if (self->preview_pipeline) {
GST_DEBUG_OBJECT (self,
"Setting preview pipeline caps %" GST_PTR_FORMAT, self->preview_caps);
gst_camerabin_preview_set_caps (self->preview_pipeline, preview_caps);
}
if (bclass->set_preview)
bclass->set_preview (self, preview_caps);
}
/**
* gst_base_camera_src_get_allowed_input_caps:
* @self: the camerasrc bin
*
* Retrieve caps from videosrc describing formats it supports
*
* Returns: caps object from videosrc
*/
GstCaps *
gst_base_camera_src_get_allowed_input_caps (GstBaseCameraSrc * self)
{
GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
g_return_val_if_fail (bclass->get_allowed_input_caps, NULL);
return bclass->get_allowed_input_caps (self);
}
static void
gst_base_camera_src_start_capture (GstBaseCameraSrc * src)
{
GstBaseCameraSrcClass *klass = GST_BASE_CAMERA_SRC_GET_CLASS (src);
g_return_if_fail (klass->start_capture != NULL);
GST_DEBUG_OBJECT (src, "Starting capture");
g_mutex_lock (src->capturing_mutex);
if (src->capturing) {
GST_WARNING_OBJECT (src, "Capturing already ongoing");
g_mutex_unlock (src->capturing_mutex);
return;
}
src->capturing = TRUE;
g_object_notify (G_OBJECT (src), "ready-for-capture");
if (klass->start_capture (src)) {
GST_DEBUG_OBJECT (src, "Capture started");
} else {
src->capturing = FALSE;
g_object_notify (G_OBJECT (src), "ready-for-capture");
GST_WARNING_OBJECT (src, "Failed to start capture");
}
g_mutex_unlock (src->capturing_mutex);
}
static void
gst_base_camera_src_stop_capture (GstBaseCameraSrc * src)
{
GstBaseCameraSrcClass *klass = GST_BASE_CAMERA_SRC_GET_CLASS (src);
g_return_if_fail (klass->stop_capture != NULL);
g_mutex_lock (src->capturing_mutex);
if (!src->capturing) {
GST_DEBUG_OBJECT (src, "No ongoing capture");
g_mutex_unlock (src->capturing_mutex);
return;
}
klass->stop_capture (src);
g_mutex_unlock (src->capturing_mutex);
}
void
gst_base_camera_src_finish_capture (GstBaseCameraSrc * self)
{
GST_DEBUG_OBJECT (self, "Finishing capture");
g_return_if_fail (self->capturing);
self->capturing = FALSE;
g_object_notify (G_OBJECT (self), "ready-for-capture");
}
static void
gst_base_camera_src_dispose (GObject * object)
{
GstBaseCameraSrc *src = GST_BASE_CAMERA_SRC_CAST (object);
g_mutex_free (src->capturing_mutex);
if (src->preview_pipeline) {
gst_camerabin_destroy_preview_pipeline (src->preview_pipeline);
src->preview_pipeline = NULL;
}
if (src->preview_caps)
gst_caps_replace (&src->preview_caps, NULL);
if (src->preview_filter) {
gst_object_unref (src->preview_filter);
src->preview_filter = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_base_camera_src_finalize (GstBaseCameraSrc * self)
{
G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self));
}
static void
gst_base_camera_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstBaseCameraSrc *self = GST_BASE_CAMERA_SRC (object);
switch (prop_id) {
case PROP_MODE:
gst_base_camera_src_set_mode (GST_BASE_CAMERA_SRC (self),
g_value_get_enum (value));
break;
case PROP_ZOOM:{
g_atomic_int_set (&self->zoom, g_value_get_int (value));
/* does not set it if in NULL, the src is not created yet */
if (GST_STATE (self) != GST_STATE_NULL)
gst_base_camera_src_setup_zoom (self);
break;
}
case PROP_POST_PREVIEW:
self->post_preview = g_value_get_boolean (value);
break;
case PROP_PREVIEW_CAPS:
gst_caps_replace (&self->preview_caps,
(GstCaps *) gst_value_get_caps (value));
gst_base_camera_src_setup_preview (self,
(GstCaps *) gst_value_get_caps (value));
break;
case PROP_PREVIEW_FILTER:
if (self->preview_filter)
gst_object_unref (self->preview_filter);
self->preview_filter = g_value_dup_object (value);
self->preview_filter_changed = TRUE;
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break;
}
}
static void
gst_base_camera_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstBaseCameraSrc *self = GST_BASE_CAMERA_SRC (object);
switch (prop_id) {
case PROP_MODE:
g_value_set_enum (value, self->mode);
break;
case PROP_READY_FOR_CAPTURE:
g_value_set_boolean (value, !self->capturing);
break;
case PROP_ZOOM:
g_value_set_int (value, g_atomic_int_get (&self->zoom));
break;
case PROP_POST_PREVIEW:
g_value_set_boolean (value, self->post_preview);
break;
case PROP_PREVIEW_CAPS:
if (self->preview_caps)
gst_value_set_caps (value, self->preview_caps);
break;
case PROP_PREVIEW_FILTER:
if (self->preview_filter)
g_value_set_object (value, self->preview_filter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break;
}
}
static gboolean
construct_pipeline (GstBaseCameraSrc * self)
{
GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
if (bclass->construct_pipeline) {
if (!bclass->construct_pipeline (self)) {
GST_ERROR_OBJECT (self, "pipeline construction failed");
return FALSE;
}
}
return TRUE;
}
static gboolean
setup_pipeline (GstBaseCameraSrc * self)
{
GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self);
if (bclass->setup_pipeline)
return bclass->setup_pipeline (self);
return TRUE;
}
static GstStateChangeReturn
gst_base_camera_src_change_state (GstElement * element,
GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstBaseCameraSrc *self = GST_BASE_CAMERA_SRC (element);
GST_DEBUG_OBJECT (self, "%d -> %d",
GST_STATE_TRANSITION_CURRENT (transition),
GST_STATE_TRANSITION_NEXT (transition));
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
if (!construct_pipeline (self))
return GST_STATE_CHANGE_FAILURE;
/* recreate the preview pipeline */
if (self->preview_pipeline && self->preview_filter_changed) {
gst_camerabin_destroy_preview_pipeline (self->preview_pipeline);
}
if (self->preview_pipeline == NULL)
self->preview_pipeline =
gst_camerabin_create_preview_pipeline (GST_ELEMENT_CAST (self),
self->preview_filter);
g_assert (self->preview_pipeline != NULL);
self->preview_filter_changed = FALSE;
if (self->preview_caps) {
GST_DEBUG_OBJECT (self,
"Setting preview pipeline caps %" GST_PTR_FORMAT,
self->preview_caps);
gst_camerabin_preview_set_caps (self->preview_pipeline,
self->preview_caps);
}
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
if (!setup_pipeline (self))
return GST_STATE_CHANGE_FAILURE;
gst_element_set_state (self->preview_pipeline->pipeline,
GST_STATE_PLAYING);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL:
gst_element_set_state (self->preview_pipeline->pipeline, GST_STATE_NULL);
break;
default:
break;
}
return ret;
}
static void
gst_base_camera_src_base_init (gpointer g_class)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
GST_DEBUG_CATEGORY_INIT (base_camera_src_debug, "base_camera_src", 0,
"Base camera src");
gst_element_class_set_details_simple (gstelement_class,
"Base class for camerabin src bin", "Source/Video",
"Abstracts capture device for camerabin2", "Rob Clark <rob@ti.com>");
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&vfsrc_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&imgsrc_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&vidsrc_template));
}
static void
gst_base_camera_src_class_init (GstBaseCameraSrcClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gobject_class->dispose = gst_base_camera_src_dispose;
gobject_class->finalize = (GObjectFinalizeFunc) gst_base_camera_src_finalize;
gobject_class->set_property = gst_base_camera_src_set_property;
gobject_class->get_property = gst_base_camera_src_get_property;
g_object_class_install_property (gobject_class, PROP_MODE,
g_param_spec_enum ("mode", "Mode",
"The capture mode (still image capture or video recording)",
GST_TYPE_CAMERABIN_MODE, MODE_IMAGE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstBaseCameraSrc:post-previews:
*
* When %TRUE, preview images should be posted to the bus when
* captures are made
*/
g_object_class_install_property (gobject_class, PROP_POST_PREVIEW,
g_param_spec_boolean ("post-previews", "Post Previews",
"If capture preview images should be posted to the bus",
DEFAULT_POST_PREVIEW, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PREVIEW_CAPS,
g_param_spec_boxed ("preview-caps", "Preview caps",
"The caps of the preview image to be posted",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PREVIEW_FILTER,
g_param_spec_object ("preview-filter", "Preview filter",
"A custom preview filter to process preview image data",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstBaseCameraSrc:ready-for-capture:
*
* When TRUE new capture can be prepared. If FALSE capturing is ongoing
* and starting a new capture immediately is not possible.
*
* Note that calling start-capture from the notify callback of this property
* will cause a deadlock. If you need to react like this on the notify
* function, please schedule a new thread to do it. If you're using glib's
* mainloop you can use g_idle_add() for example.
*/
g_object_class_install_property (gobject_class, PROP_READY_FOR_CAPTURE,
g_param_spec_boolean ("ready-for-capture", "Ready for capture",
"Informs this element is ready for starting another capture",
TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/* Signals */
basecamerasrc_signals[START_CAPTURE_SIGNAL] =
g_signal_new ("start-capture",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GstBaseCameraSrcClass, private_start_capture),
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
basecamerasrc_signals[STOP_CAPTURE_SIGNAL] =
g_signal_new ("stop-capture",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GstBaseCameraSrcClass, private_stop_capture),
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
/* TODO these should be moved to a private struct
* that is allocated sequentially to the main struct as said at:
* http://library.gnome.org/devel/gobject/unstable/gobject-Type-Information.html#g-type-add-class-private
*/
klass->private_start_capture = gst_base_camera_src_start_capture;
klass->private_stop_capture = gst_base_camera_src_stop_capture;
gstelement_class->change_state = gst_base_camera_src_change_state;
}
static void
gst_base_camera_src_init (GstBaseCameraSrc * self,
GstBaseCameraSrcClass * klass)
{
self->width = DEFAULT_WIDTH;
self->height = DEFAULT_HEIGHT;
self->zoom = DEFAULT_ZOOM;
self->mode = MODE_IMAGE;
self->capturing = FALSE;
self->capturing_mutex = g_mutex_new ();
self->post_preview = DEFAULT_POST_PREVIEW;
}
void
gst_base_camera_src_post_preview (GstBaseCameraSrc * self, GstBuffer * buf)
{
if (self->post_preview) {
gst_camerabin_preview_pipeline_post (self->preview_pipeline, buf);
} else {
GST_DEBUG_OBJECT (self, "Previews not enabled, not posting");
}
}