diff --git a/gst/camerabin2/Makefile.am b/gst/camerabin2/Makefile.am index 49d29aa005..25b1e61ed6 100644 --- a/gst/camerabin2/Makefile.am +++ b/gst/camerabin2/Makefile.am @@ -2,17 +2,28 @@ plugin_LTLIBRARIES = libgstcamerabin2.la libgstcamerabin2_la_SOURCES = gstviewfinderbin.c \ gstimagecapturebin.c \ + camerabingeneral.c \ + gstbasecamerasrc.c \ + gstcamerabin-enum.c \ + gstv4l2camerasrc.c \ gstplugin.c libgstcamerabin2_la_CFLAGS = \ $(GST_PLUGINS_BAD_CFLAGS) \ - $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) + $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + -DGST_USE_UNSTABLE_API libgstcamerabin2_la_LIBADD = \ - $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) + $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-$(GST_MAJORMINOR).la \ + $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \ + -lgstinterfaces-$(GST_MAJORMINOR) -lgsttag-$(GST_MAJORMINOR) libgstcamerabin2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstcamerabin2_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gstviewfinderbin.h \ - gstimagecapturebin.h + gstimagecapturebin.h \ + camerabingeneral.h \ + gstbasecamerasrc.h \ + gstv4l2camerasrc.h \ + gstcamerabin-enum.h diff --git a/gst/camerabin2/camerabingeneral.c b/gst/camerabin2/camerabingeneral.c new file mode 100644 index 0000000000..2a50d9e126 --- /dev/null +++ b/gst/camerabin2/camerabingeneral.c @@ -0,0 +1,276 @@ +/* + * GStreamer + * Copyright (C) 2008 Nokia Corporation + * + * 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:camerabingeneral + * @short_description: helper functions for #GstCameraBin and it's modules + * + * Common helper functions for #GstCameraBin, #GstCameraBinImage and + * #GstCameraBinVideo. + * + */ +#include + +#include "camerabingeneral.h" +#include + +GST_DEBUG_CATEGORY (gst_camerabin_debug); + +/** + * gst_camerabin_add_element: + * @bin: add an element to this bin + * @new_elem: new element to be added + * + * Adds given element to given @bin. Looks for an unconnected src pad + * from the @bin and links the element to it. Raises an error if adding + * or linking failed. Unrefs the element in the case of an error. + * + * Returns: %TRUE if adding and linking succeeded, %FALSE otherwise. + */ +gboolean +gst_camerabin_add_element (GstBin * bin, GstElement * new_elem) +{ + return gst_camerabin_add_element_full (bin, NULL, new_elem, NULL); +} + +/** + * gst_camerabin_add_element_full: + * @bin: add an element to this bin + * @srcpad: src pad name, or NULL for any + * @new_elem: new element to be added + * @dstpad: dst pad name, or NULL for any + * + * Adds given element to given @bin. Looks for an unconnected src pad + * (with name @srcpad, if specified) from the @bin and links the element + * to it. Raises an error if adding or linking failed. Unrefs the element + * in the case of an error. + * + * Returns: %TRUE if adding and linking succeeded, %FALSE otherwise. + */ +gboolean +gst_camerabin_add_element_full (GstBin * bin, const gchar * srcpad, + GstElement * new_elem, const gchar * dstpad) +{ + gboolean ret; + + g_return_val_if_fail (bin, FALSE); + g_return_val_if_fail (new_elem, FALSE); + + ret = gst_camerabin_try_add_element (bin, srcpad, new_elem, dstpad); + + if (!ret) { + gchar *elem_name = gst_element_get_name (new_elem); + GST_ELEMENT_ERROR (bin, CORE, NEGOTIATION, (NULL), + ("linking %s failed", elem_name)); + g_free (elem_name); + gst_object_unref (new_elem); + } + + return ret; +} + +/** + * gst_camerabin_try_add_element: + * @bin: tries adding an element to this bin + * @srcpad: src pad name, or NULL for any + * @new_elem: new element to be added + * @dstpad: dst pad name, or NULL for any + * + * Adds given element to given @bin. Looks for an unconnected src pad + * (with name @srcpad, if specified) from the @bin and links the element to + * it. + * + * Returns: %TRUE if adding and linking succeeded, %FALSE otherwise. + */ +gboolean +gst_camerabin_try_add_element (GstBin * bin, const gchar * srcpad, + GstElement * new_elem, const gchar * dstpad) +{ + GstPad *bin_pad; + GstElement *bin_elem; + gboolean ret = TRUE; + + g_return_val_if_fail (bin, FALSE); + g_return_val_if_fail (new_elem, FALSE); + + /* Get pads for linking */ + bin_pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SRC); + /* Add to bin */ + gst_bin_add (GST_BIN (bin), new_elem); + /* Link, if unconnected pad was found, otherwise just add it to bin */ + if (bin_pad) { + GST_DEBUG_OBJECT (bin, "linking %s to %s:%s", GST_OBJECT_NAME (new_elem), + GST_DEBUG_PAD_NAME (bin_pad)); + bin_elem = gst_pad_get_parent_element (bin_pad); + gst_object_unref (bin_pad); + if (!gst_element_link_pads (bin_elem, srcpad, new_elem, dstpad)) { + gst_object_ref (new_elem); + gst_bin_remove (bin, new_elem); + ret = FALSE; + } + gst_object_unref (bin_elem); + } else { + GST_INFO_OBJECT (bin, "no unlinked source pad in bin"); + } + + return ret; +} + +/** + * gst_camerabin_create_and_add_element: + * @bin: tries adding an element to this bin + * @elem_name: name of the element to be created + * + * Creates an element according to given name and + * adds it to given @bin. Looks for an unconnected src pad + * from the @bin and links the element to it. + * + * Returns: pointer to the new element if successful, NULL otherwise. + */ +GstElement * +gst_camerabin_create_and_add_element (GstBin * bin, const gchar * elem_name) +{ + GstElement *new_elem; + + g_return_val_if_fail (bin, FALSE); + g_return_val_if_fail (elem_name, FALSE); + + new_elem = gst_element_factory_make (elem_name, NULL); + if (!new_elem) { + GST_ELEMENT_ERROR (bin, CORE, MISSING_PLUGIN, (NULL), + ("could not create \"%s\" element.", elem_name)); + } else if (!gst_camerabin_add_element (bin, new_elem)) { + new_elem = NULL; + } + + return new_elem; +} + +/* try to change the state of an element. This function returns the element when + * the state change could be performed. When this function returns NULL an error + * occured and the element is unreffed if @unref is TRUE. */ +static GstElement * +try_element (GstElement * bin, GstElement * element, gboolean unref) +{ + GstStateChangeReturn ret; + + if (element) { + ret = gst_element_set_state (element, GST_STATE_READY); + if (ret == GST_STATE_CHANGE_FAILURE) { + GST_DEBUG_OBJECT (bin, "failed state change.."); + gst_element_set_state (element, GST_STATE_NULL); + if (unref) + gst_object_unref (element); + element = NULL; + } + } + return element; +} + +GstElement * +gst_camerabin_setup_default_element (GstBin * bin, GstElement * user_elem, + const gchar * auto_elem_name, const gchar * default_elem_name) +{ + GstElement *elem; + + if (user_elem) { + GST_DEBUG_OBJECT (bin, "trying configured element"); + elem = try_element (GST_ELEMENT_CAST (bin), user_elem, FALSE); + } else { + /* only try fallback if no specific sink was chosen */ + GST_DEBUG_OBJECT (bin, "trying %s", auto_elem_name); + elem = gst_element_factory_make (auto_elem_name, NULL); + elem = try_element (GST_ELEMENT_CAST (bin), elem, TRUE); + if (elem == NULL) { + /* if default sink from config.h is different then try it too */ + if (strcmp (default_elem_name, auto_elem_name)) { + GST_DEBUG_OBJECT (bin, "trying %s", default_elem_name); + elem = gst_element_factory_make (default_elem_name, NULL); + elem = try_element (GST_ELEMENT_CAST (bin), elem, TRUE); + } + } + } + return elem; +} + +/** + * gst_camerabin_remove_elements_from_bin: + * @bin: removes all elements from this bin + * + * Removes all elements from this @bin. + */ +void +gst_camerabin_remove_elements_from_bin (GstBin * bin) +{ + GstIterator *iter = NULL; + gpointer data = NULL; + GstElement *elem = NULL; + gboolean done = FALSE; + + iter = gst_bin_iterate_elements (bin); + while (!done) { + switch (gst_iterator_next (iter, &data)) { + case GST_ITERATOR_OK: + elem = GST_ELEMENT (data); + gst_bin_remove (bin, elem); + gst_element_set_state (GST_ELEMENT (elem), GST_STATE_NULL); + /* Iterator increased the element refcount, so unref */ + gst_object_unref (elem); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (bin, "error in iterating elements"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (iter); +} + +/** + * gst_camerabin_drop_eos_probe: + * @pad: pad receiving the event + * @event: received event + * @u_data: not used + * + * Event probe that drop all eos events. + * + * Returns: FALSE to drop the event, TRUE otherwise + */ +gboolean +gst_camerabin_drop_eos_probe (GstPad * pad, GstEvent * event, gpointer u_data) +{ + gboolean ret = TRUE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + GST_DEBUG ("dropping eos in %s:%s", GST_DEBUG_PAD_NAME (pad)); + ret = FALSE; + break; + default: + break; + } + return ret; +} diff --git a/gst/camerabin2/camerabingeneral.h b/gst/camerabin2/camerabingeneral.h new file mode 100644 index 0000000000..93520c5a60 --- /dev/null +++ b/gst/camerabin2/camerabingeneral.h @@ -0,0 +1,42 @@ +/* + * GStreamer + * Copyright (C) 2008 Nokia Corporation + * + * 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. + */ + +#ifndef __CAMERABIN_GENERAL_H_ +#define __CAMERABIN_GENERAL_H_ + +#include + +gboolean gst_camerabin_try_add_element (GstBin * bin, const gchar * srcpad, GstElement * new_elem, const gchar * dstpad); +gboolean gst_camerabin_add_element (GstBin * bin, GstElement * new_elem); +gboolean gst_camerabin_add_element_full (GstBin * bin, const gchar * srcpad, GstElement * new_elem, const gchar * dstpad); + +GstElement *gst_camerabin_create_and_add_element (GstBin * bin, const gchar * elem_name); + +GstElement * gst_camerabin_setup_default_element (GstBin * bin, GstElement *user_elem, const gchar *auto_elem_name, const gchar *default_elem_name); + +void gst_camerabin_remove_elements_from_bin (GstBin * bin); + +gboolean gst_camerabin_drop_eos_probe (GstPad * pad, GstEvent * event, gpointer u_data); + +/* debug logging category */ +GST_DEBUG_CATEGORY_EXTERN (gst_camerabin_debug); +#define GST_CAT_DEFAULT gst_camerabin_debug + +#endif /* #ifndef __CAMERABIN_GENERAL_H_ */ diff --git a/gst/camerabin2/gstbasecamerasrc.c b/gst/camerabin2/gstbasecamerasrc.c new file mode 100644 index 0000000000..aba756e82b --- /dev/null +++ b/gst/camerabin2/gstbasecamerasrc.c @@ -0,0 +1,509 @@ +/* + * 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 src bin used by camerabin. Indented to be + * subclassed when plugging in more sophisticated cameras. + */ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstbasecamerasrc.h" + +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 ("vfsrc", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate imgsrc_template = GST_STATIC_PAD_TEMPLATE ("imgsrc", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate vidsrc_template = GST_STATIC_PAD_TEMPLATE ("vidsrc", + 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 (self); + } + + 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 + * + * XXX + */ +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); + + return bclass->set_mode (self, mode); +} + +/** + * 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_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); +} + +/** + * gst_base_camera_src_finish_image_capture: + * @self: camerasrc object + * + * Perform finishing operations after image capture is done and + * returning back to view finder mode. + */ +void +gst_base_camera_src_finish_image_capture (GstBaseCameraSrc * self) +{ + GstBaseCameraSrcClass *bclass = GST_BASE_CAMERA_SRC_GET_CLASS (self); + + if (bclass->finish_image_capture) { + bclass->finish_image_capture (self); + } +} + +/** + * gst_base_camera_src_find_better_framerate: + * @self: camerasrc object + * @st: structure that contains framerate candidates + * @orig_framerate: best framerate so far + * + * Looks for framerate better than @orig_framerate from @st structure. + * In night mode lowest framerate is considered best, otherwise highest is + * best. + * + * Returns: @orig_framerate or better if found + */ +const GValue * +gst_base_camera_src_find_better_framerate (GstBaseCameraSrc * self, + GstStructure * st, const GValue * orig_framerate) +{ + const GValue *framerate = NULL; + guint i, i_best, list_size; + gint res, comparison; + + if (self->night_mode) { + GST_LOG_OBJECT (self, "finding min framerate in %" GST_PTR_FORMAT, st); + comparison = GST_VALUE_LESS_THAN; + } else { + GST_LOG_OBJECT (self, "finding max framerate in %" GST_PTR_FORMAT, st); + comparison = GST_VALUE_GREATER_THAN; + } + + if (gst_structure_has_field (st, "framerate")) { + framerate = gst_structure_get_value (st, "framerate"); + /* Handle framerate lists */ + if (GST_VALUE_HOLDS_LIST (framerate)) { + list_size = gst_value_list_get_size (framerate); + GST_LOG_OBJECT (self, "finding framerate from list"); + for (i = 0, i_best = 0; i < list_size; i++) { + res = gst_value_compare (gst_value_list_get_value (framerate, i), + gst_value_list_get_value (framerate, i_best)); + if (comparison == res) { + i_best = i; + } + } + GST_LOG_OBJECT (self, "found best framerate from index %d", i_best); + framerate = gst_value_list_get_value (framerate, i_best); + } + /* Handle framerate ranges */ + if (GST_VALUE_HOLDS_FRACTION_RANGE (framerate)) { + if (self->night_mode) { + GST_LOG_OBJECT (self, "getting min framerate from range"); + framerate = gst_value_get_fraction_range_min (framerate); + } else { + GST_LOG_OBJECT (self, "getting max framerate from range"); + framerate = gst_value_get_fraction_range_max (framerate); + } + } + } + + /* Check if we found better framerate */ + if (orig_framerate && framerate) { + res = gst_value_compare (orig_framerate, framerate); + if (comparison == res) { + GST_LOG_OBJECT (self, "original framerate was the best"); + framerate = orig_framerate; + } + } + + return framerate; +} + +/** + * + */ +static void +gst_base_camera_src_dispose (GObject * object) +{ + 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 ARG_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 ARG_IMAGE_CAPTURE_WIDTH:{ + gint width = g_value_get_int (value); + + if (width != self->image_capture_width) { + self->image_capture_width = width; +//XXX self->image_capture_caps_update = TRUE; + } + break; + } + case ARG_IMAGE_CAPTURE_HEIGHT:{ + gint height = g_value_get_int (value); + + if (height != self->image_capture_height) { + self->image_capture_height = height; +//XXX self->image_capture_caps_update = TRUE; + } + break; + } + case ARG_VIDEO_CAPTURE_WIDTH:{ + gint width = g_value_get_int (value); + + if (width != self->width) { + self->width = width; +//XXX self->video_capture_caps_update = TRUE; + } + break; + } + case ARG_VIDEO_CAPTURE_HEIGHT:{ + gint height = g_value_get_int (value); + + if (height != self->height) { + self->height = height; +//XXX self->video_capture_caps_update = TRUE; + } + break; + } + case ARG_VIDEO_CAPTURE_FRAMERATE:{ + gint fps_n, fps_d; + + fps_n = gst_value_get_fraction_numerator (value); + fps_d = gst_value_get_fraction_denominator (value); + + if (fps_n != self->fps_n || fps_d != self->fps_d) { + self->fps_n = fps_n; + self->fps_d = fps_d; +//XXX self->video_capture_caps_update = 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 ARG_ZOOM: + g_value_set_int (value, g_atomic_int_get (&self->zoom)); + break; + case ARG_IMAGE_CAPTURE_WIDTH: + g_value_set_int (value, self->image_capture_width); + break; + case ARG_IMAGE_CAPTURE_HEIGHT: + g_value_set_int (value, self->image_capture_height); + break; + case ARG_VIDEO_CAPTURE_WIDTH: + g_value_set_int (value, self->width); + break; + case ARG_VIDEO_CAPTURE_HEIGHT: + g_value_set_int (value, self->height); + break; + case ARG_VIDEO_CAPTURE_FRAMERATE: + gst_value_set_fraction (value, self->fps_n, self->fps_d); + 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); + GstPad *vfsrc = NULL; + GstPad *imgsrc = NULL; + GstPad *vidsrc = NULL; + + g_return_val_if_fail (bclass->construct_pipeline, FALSE); + + if (!bclass->construct_pipeline (self, &vfsrc, &imgsrc, &vidsrc)) { + GST_ERROR_OBJECT (self, "pipeline construction failed"); + return FALSE; + } + + if (!vfsrc || !imgsrc || !vidsrc) { + GST_ERROR_OBJECT (self, "derived class must return src pads"); + return FALSE; + } + + GST_DEBUG_OBJECT (self, "vfsrc: %" GST_PTR_FORMAT, vfsrc); + GST_DEBUG_OBJECT (self, "imgsrc: %" GST_PTR_FORMAT, imgsrc); + GST_DEBUG_OBJECT (self, "vidsrc: %" GST_PTR_FORMAT, vidsrc); + + /* hook-up the ghostpads */ + gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vfsrc); + gst_ghost_pad_set_target (GST_GHOST_PAD (self->imgsrc), imgsrc); + gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), vidsrc); + + gst_pad_set_active (self->vfsrc, TRUE); + gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */ + gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */ + + 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; + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (!setup_pipeline (self)) + return GST_STATE_CHANGE_FAILURE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + 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 camerabin", "Rob Clark "); + + 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 .... + + gstelement_class->change_state = gst_base_camera_src_change_state; + +} + +static void +gst_base_camera_src_init (GstBaseCameraSrc * self, + GstBaseCameraSrcClass * klass) +{ + self->vfsrc = gst_ghost_pad_new_no_target ("vfsrc", GST_PAD_SRC); + gst_element_add_pad (GST_ELEMENT (self), self->vfsrc); + + self->imgsrc = gst_ghost_pad_new_no_target ("imgsrc", GST_PAD_SRC); + gst_element_add_pad (GST_ELEMENT (self), self->imgsrc); + + self->vidsrc = gst_ghost_pad_new_no_target ("vidsrc", GST_PAD_SRC); + gst_element_add_pad (GST_ELEMENT (self), self->vidsrc); + + self->width = DEFAULT_WIDTH; + self->height = DEFAULT_HEIGHT; + self->zoom = DEFAULT_ZOOM; + self->image_capture_width = 0; + self->image_capture_height = 0; + + self->night_mode = FALSE; + + self->fps_n = DEFAULT_FPS_N; + self->fps_d = DEFAULT_FPS_D; +} diff --git a/gst/camerabin2/gstbasecamerasrc.h b/gst/camerabin2/gstbasecamerasrc.h new file mode 100644 index 0000000000..75ef0222ac --- /dev/null +++ b/gst/camerabin2/gstbasecamerasrc.h @@ -0,0 +1,146 @@ +/* + * 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. + */ + + +#ifndef __GST_BASE_CAMERA_SRC_H__ +#define __GST_BASE_CAMERA_SRC_H__ + +#include +#include +#include +#include +#include "gstcamerabin-enum.h" + +G_BEGIN_DECLS +#define GST_TYPE_BASE_CAMERA_SRC \ + (gst_base_camera_src_get_type()) +#define GST_BASE_CAMERA_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_CAMERA_SRC,GstBaseCameraSrc)) +#define GST_BASE_CAMERA_SRC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASE_CAMERA_SRC, GstBaseCameraSrcClass)) +#define GST_BASE_CAMERA_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_CAMERA_SRC,GstBaseCameraSrcClass)) +#define GST_IS_BASE_CAMERA_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_CAMERA_SRC)) +#define GST_IS_BASE_CAMERA_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_CAMERA_SRC)) + GType gst_base_camera_src_get_type (void); + +typedef struct _GstBaseCameraSrc GstBaseCameraSrc; +typedef struct _GstBaseCameraSrcClass GstBaseCameraSrcClass; + + +/** + * GstBaseCameraSrc: + */ +struct _GstBaseCameraSrc +{ + GstBin parent; + + GstPad *vfsrc; + GstPad *imgsrc; + GstPad *vidsrc; + + /* XXX preview pads? */ + + /* Resolution of the buffers configured to camerabin */ + gint width; + gint height; + + /* The digital zoom (from 100% to 1000%) */ + gint zoom; + + /* Image capture resolution */ + gint image_capture_width; + gint image_capture_height; + + /* Frames per second configured to camerabin */ + gint fps_n; + gint fps_d; + + /* Night mode handling */ + gboolean night_mode; + gint pre_night_fps_n; + gint pre_night_fps_d; + + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + + +/** + * GstBaseCameraSrcClass: + * @construct_pipeline: construct pipeline must be implemented by derived + * class, and return by reference vfsrc, imgsrc, and vidsrc pads of the + * contained pipeline, which will be ghosted to the src pads of the + * camerasrc bin (and optionally the preview src pads?) + * @setup_pipeline: + * @set_zoom: set the zoom + * @set_mode: set the mode + */ +struct _GstBaseCameraSrcClass +{ + GstBinClass parent; + + /* construct pipeline must be implemented by derived class, and return by + * reference vfsrc, imgsrc, and vidsrc pads of the contained pipeline, which + * will be ghosted to the src pads of the camerasrc bin (and optionally the + * preview src pads?) */ + gboolean (*construct_pipeline) (GstBaseCameraSrc *self, + GstPad **vfsrc, GstPad **imgsrc, + GstPad **vidsrc); + + /* optional */ + gboolean (*setup_pipeline) (GstBaseCameraSrc *self); + + /* set the zoom */ + void (*set_zoom) (GstBaseCameraSrc *self, gint zoom); + + /* set the mode */ + gboolean (*set_mode) (GstBaseCameraSrc *self, + GstCameraBinMode mode); + + /* */ + GstCaps * (*get_allowed_input_caps) (GstBaseCameraSrc * self); + + /* optional */ + void (*finish_image_capture) (GstBaseCameraSrc * self); + + + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + + +#define MIN_ZOOM 100 +#define MAX_ZOOM 1000 +#define ZOOM_1X MIN_ZOOM + +GstPhotography * gst_base_camera_src_get_photography (GstBaseCameraSrc *self); +GstColorBalance * gst_base_camera_src_get_color_balance (GstBaseCameraSrc *self); + +gboolean gst_base_camera_src_set_mode (GstBaseCameraSrc *self, GstCameraBinMode mode); +void gst_base_camera_src_setup_zoom (GstBaseCameraSrc * self); +GstCaps * gst_base_camera_src_get_allowed_input_caps (GstBaseCameraSrc * self); +void gst_base_camera_src_finish_image_capture (GstBaseCameraSrc * self); +const GValue * gst_base_camera_src_find_better_framerate ( + GstBaseCameraSrc * self, GstStructure * st, const GValue * orig_framerate); + +// XXX add methods to get/set img capture and vid capture caps.. + +#endif /* __GST_BASE_CAMERA_SRC_H__ */ diff --git a/gst/camerabin2/gstcamerabin-enum.c b/gst/camerabin2/gstcamerabin-enum.c new file mode 100644 index 0000000000..8d1d0c92d3 --- /dev/null +++ b/gst/camerabin2/gstcamerabin-enum.c @@ -0,0 +1,78 @@ +/* + * GStreamer + * Copyright (C) 2009 Nokia Corporation + * + * 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. + */ + +#include "gstcamerabin-enum.h" + +#define C_FLAGS(v) ((guint) v) + +static void +register_gst_camerabin_flags (GType * id) +{ + static const GFlagsValue values[] = { + {C_FLAGS (GST_CAMERABIN_FLAG_SOURCE_RESIZE), + "Enable source crop and scale", "source-resize"}, + {C_FLAGS (GST_CAMERABIN_FLAG_SOURCE_COLOR_CONVERSION), + "Enable colorspace conversion for video source", + "source-colorspace-conversion"}, + {C_FLAGS (GST_CAMERABIN_FLAG_VIEWFINDER_COLOR_CONVERSION), + "Enable colorspace conversion for viewfinder", + "viewfinder-colorspace-conversion"}, + {C_FLAGS (GST_CAMERABIN_FLAG_VIEWFINDER_SCALE), + "Enable scale for viewfinder", "viewfinder-scale"}, + {C_FLAGS (GST_CAMERABIN_FLAG_AUDIO_CONVERSION), + "Enable audio conversion for video capture", "audio-conversion"}, + {C_FLAGS (GST_CAMERABIN_FLAG_DISABLE_AUDIO), + "Disable audio elements for video capture", "disable-audio"}, + {C_FLAGS (GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION), + "Enable colorspace conversion for still image", + "image-colorspace-conversion"}, + {0, NULL, NULL} + }; + *id = g_flags_register_static ("GstCameraBinFlags", values); +} + +GType +gst_camerabin_flags_get_type (void) +{ + static GType id; + static GOnce once = G_ONCE_INIT; + + g_once (&once, (GThreadFunc) register_gst_camerabin_flags, &id); + return id; +} + + +GType +gst_camerabin_mode_get_type (void) +{ + static GType gtype = 0; + + if (gtype == 0) { + static const GEnumValue values[] = { + {MODE_PREVIEW, "Preview mode (should be default?)", "mode-preview"}, + {MODE_IMAGE, "Still image capture (default)", "mode-image"}, + {MODE_VIDEO, "Video recording", "mode-video"}, + {0, NULL, NULL} + }; + + gtype = g_enum_register_static ("GstCameraBinMode", values); + } + return gtype; +} diff --git a/gst/camerabin2/gstcamerabin-enum.h b/gst/camerabin2/gstcamerabin-enum.h new file mode 100644 index 0000000000..cd1707dd2e --- /dev/null +++ b/gst/camerabin2/gstcamerabin-enum.h @@ -0,0 +1,140 @@ +/* + * GStreamer + * Copyright (C) 2009 Nokia Corporation + * + * 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. + */ + +#ifndef __GST_CAMERABIN_ENUM_H__ +#define __GST_CAMERABIN_ENUM_H__ + +#include + +G_BEGIN_DECLS + +/* XXX find better place for property related enum/defaults */ +enum +{ + ARG_0, + ARG_FILENAME, + ARG_MODE, + ARG_FLAGS, + ARG_MUTE, + ARG_ZOOM, + ARG_IMAGE_POST, + ARG_IMAGE_ENC, + ARG_VIDEO_POST, + ARG_VIDEO_ENC, + ARG_AUDIO_ENC, + ARG_VIDEO_MUX, + ARG_VF_SINK, + ARG_VIDEO_SRC, + ARG_AUDIO_SRC, + ARG_INPUT_CAPS, + ARG_FILTER_CAPS, + ARG_PREVIEW_CAPS, + ARG_WB_MODE, + ARG_COLOUR_TONE, + ARG_SCENE_MODE, + ARG_FLASH_MODE, + ARG_FOCUS_STATUS, + ARG_CAPABILITIES, + ARG_SHAKE_RISK, + ARG_EV_COMP, + ARG_ISO_SPEED, + ARG_APERTURE, + ARG_EXPOSURE, + ARG_VIDEO_SOURCE_FILTER, + ARG_IMAGE_CAPTURE_SUPPORTED_CAPS, + ARG_VIEWFINDER_FILTER, + ARG_FLICKER_MODE, + ARG_FOCUS_MODE, + ARG_BLOCK_VIEWFINDER, + ARG_IMAGE_CAPTURE_WIDTH, + ARG_IMAGE_CAPTURE_HEIGHT, + ARG_VIDEO_CAPTURE_WIDTH, + ARG_VIDEO_CAPTURE_HEIGHT, + ARG_VIDEO_CAPTURE_FRAMERATE +}; + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define DEFAULT_CAPTURE_WIDTH 800 +#define DEFAULT_CAPTURE_HEIGHT 600 +#define DEFAULT_FPS_N 0 /* makes it use the default */ +#define DEFAULT_FPS_D 1 +#define DEFAULT_ZOOM MIN_ZOOM + + +/** + * GstCameraBinFlags: + * @GST_CAMERABIN_FLAG_SOURCE_RESIZE: enable video crop and scale + * after capture + * @GST_CAMERABIN_FLAG_SOURCE_COLOR_CONVERSION: enable conversion + * of native video format by enabling ffmpegcolorspace + * @GST_CAMERABIN_FLAG_VIEWFINDER_COLOR_CONVERSION: enable color + * conversion for viewfinder element + * @GST_CAMERABIN_FLAG_VIEWFINDER_SCALE: enable scaling in + * viewfinder element retaining aspect ratio + * @GST_CAMERABIN_FLAG_AUDIO_CONVERSION: enable audioconvert and + * audioresample elements + * @GST_CAMERABIN_FLAG_DISABLE_AUDIO: disable audio elements + * @GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION: enable color + * conversion for image output element + * + * Extra flags to configure the behaviour of the sinks. + */ +typedef enum { + GST_CAMERABIN_FLAG_SOURCE_RESIZE = (1 << 0), + GST_CAMERABIN_FLAG_SOURCE_COLOR_CONVERSION = (1 << 1), + GST_CAMERABIN_FLAG_VIEWFINDER_COLOR_CONVERSION = (1 << 2), + GST_CAMERABIN_FLAG_VIEWFINDER_SCALE = (1 << 3), + GST_CAMERABIN_FLAG_AUDIO_CONVERSION = (1 << 4), + GST_CAMERABIN_FLAG_DISABLE_AUDIO = (1 << 5), + GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION = (1 << 6) +} GstCameraBinFlags; + +#define GST_TYPE_CAMERABIN_FLAGS (gst_camerabin_flags_get_type()) +GType gst_camerabin_flags_get_type (void); + + +/** + * GstCameraBinMode: + * @MODE_PREVIEW: preview only (no capture) mode + * @MODE_IMAGE: image capture + * @MODE_VIDEO: video capture + * + * Capture mode to use. + */ +typedef enum +{ + /* note: changed to align with 'capture-mode' property (even though + * I have no idea where this property comes from..) But it somehow + * seems more logical for preview to be mode==0 even if it is an ABI + * break.. + */ + MODE_PREVIEW = 0, + MODE_IMAGE = 1, + MODE_VIDEO = 2, +} GstCameraBinMode; + + +#define GST_TYPE_CAMERABIN_MODE (gst_camerabin_mode_get_type ()) +GType gst_camerabin_mode_get_type (void); + +G_END_DECLS + +#endif /* #ifndef __GST_CAMERABIN_ENUM_H__ */ diff --git a/gst/camerabin2/gstplugin.c b/gst/camerabin2/gstplugin.c index 1f1dd0ea7a..c88ad8d038 100644 --- a/gst/camerabin2/gstplugin.c +++ b/gst/camerabin2/gstplugin.c @@ -25,6 +25,7 @@ #include "gstviewfinderbin.h" #include "gstimagecapturebin.h" +#include "gstv4l2camerasrc.h" static gboolean plugin_init (GstPlugin * plugin) @@ -33,6 +34,8 @@ plugin_init (GstPlugin * plugin) return FALSE; if (!gst_image_capture_bin_plugin_init (plugin)) return FALSE; + if (!gst_v4l2_camera_src_plugin_init (plugin)) + return FALSE; return TRUE; } diff --git a/gst/camerabin2/gstv4l2camerasrc.c b/gst/camerabin2/gstv4l2camerasrc.c new file mode 100644 index 0000000000..1cc4ca81d3 --- /dev/null +++ b/gst/camerabin2/gstv4l2camerasrc.c @@ -0,0 +1,1077 @@ +/* + * 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-v4l2camerasrc + * + * A camera src element for camerabin.. currently uses v4l2 directly. + * It could be worthwhile to make this subclassable, so that other + * camera elements with a single src pad could re-use this.. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstv4l2camerasrc.h" +#include "camerabingeneral.h" +#include "gstcamerabin-enum.h" + + +#define CAMERABIN_DEFAULT_VF_CAPS "video/x-raw-yuv,format=(fourcc)I420" +//#define CAMERABIN_MAX_VF_WIDTH 848 +//#define CAMERABIN_MAX_VF_HEIGHT 848 + +/* Using "bilinear" as default zoom method */ +#define CAMERABIN_DEFAULT_ZOOM_METHOD 1 + +/* FIXME: this is v4l2camsrc specific */ +#define DEFAULT_V4L2CAMSRC_DRIVER_NAME "omap3cam" + +//GST_DEBUG_CATEGORY (v4l2_camera_src_debug); +//#define GST_CAT_DEFAULT v4l2_camera_src_debug + +GST_BOILERPLATE (GstV4l2CameraSrc, gst_v4l2_camera_src, GstBaseCameraSrc, + GST_TYPE_BASE_CAMERA_SRC); + +static void configure_format (GstV4l2CameraSrc * self, GstCaps * caps); +static void set_capsfilter_caps (GstV4l2CameraSrc * self, GstCaps * new_caps); + +static void +gst_v4l2_camera_src_dispose (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_v4l2_camera_src_finalize (GstV4l2CameraSrc * self) +{ + G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self)); +} + +static void +gst_v4l2_camera_src_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (object); + + switch (prop_id) { + case ARG_FILTER_CAPS: + GST_OBJECT_LOCK (self); + gst_caps_replace (&self->view_finder_caps, + (GstCaps *) gst_value_get_caps (value)); + GST_OBJECT_UNLOCK (self); + configure_format (self, self->view_finder_caps); + break; + case ARG_VIDEO_SOURCE_FILTER: + if (GST_STATE (self) != GST_STATE_NULL) { + GST_ELEMENT_ERROR (self, CORE, FAILED, + ("camerasrc must be in NULL state when setting the video filter element"), + (NULL)); + } else { + if (self->app_video_filter) + gst_object_unref (self->app_video_filter); + self->app_video_filter = g_value_dup_object (value); + } + break; + case ARG_VIDEO_SRC: + if (GST_STATE (self) != GST_STATE_NULL) { + GST_ELEMENT_ERROR (self, CORE, FAILED, + ("camerasrc must be in NULL state when setting the video source element"), + (NULL)); + } else { + if (self->app_vid_src) + gst_object_unref (self->app_vid_src); + self->app_vid_src = g_value_get_object (value); + gst_object_ref (self->app_vid_src); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + +static void +gst_v4l2_camera_src_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (object); + + switch (prop_id) { + case ARG_FILTER_CAPS: + gst_value_set_caps (value, self->view_finder_caps); + break; + case ARG_VIDEO_SOURCE_FILTER: + g_value_set_object (value, self->app_video_filter); + break; + case ARG_VIDEO_SRC: + if (self->src_vid_src) + g_value_set_object (value, self->src_vid_src); + else + g_value_set_object (value, self->app_vid_src); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + break; + } +} + +/** + * gst_v4l2_camera_src_imgsrc_probe: + * + * Buffer probe called before sending each buffer to image queue. + */ +static gboolean +gst_v4l2_camera_src_imgsrc_probe (GstPad * pad, GstBuffer * buffer, + gpointer data) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (data); + GST_DEBUG_OBJECT (self, "pass buffer: %d", self->mode == MODE_IMAGE); + return self->mode == MODE_IMAGE; +} + +/** + * gst_v4l2_camera_src_vidsrc_probe: + * + * Buffer probe called before sending each buffer to image queue. + */ +static gboolean +gst_v4l2_camera_src_vidsrc_probe (GstPad * pad, GstBuffer * buffer, + gpointer data) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (data); + GST_DEBUG_OBJECT (self, "pass buffer: %d", self->mode == MODE_VIDEO); + return self->mode == MODE_VIDEO; +} + +/** + * gst_v4l2_camera_src_construct_pipeline: + * @bcamsrc: camerasrc object + * @vfsrc: viewfinder src element (returned by reference) + * @imgsrc: image src element (returned by reference) + * @vidsrc: video src element (returned by reference) + * + * This function creates and links the elements of the camerasrc bin + * videosrc ! cspconv ! capsfilter ! crop ! scale ! capsfilter ! tee ! .. + * + * Returns: TRUE, if elements were successfully created, FALSE otherwise + */ +static gboolean +gst_v4l2_camera_src_construct_pipeline (GstBaseCameraSrc * bcamsrc, + GstPad ** vfsrc, GstPad ** imgsrc, GstPad ** vidsrc) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (bcamsrc); + GstBin *cbin = GST_BIN (bcamsrc); + GstElement *tee; + gboolean ret = FALSE; + + GST_DEBUG_OBJECT (self, "constructing pipeline"); + + /* Add application set or default video src element */ + if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin, + self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC))) { + self->src_vid_src = NULL; + goto done; + } else { + if (!gst_camerabin_add_element (cbin, self->src_vid_src)) { + goto done; + } + } + +#if 0 + /* XXX srcbin needs to know of some flags, perhaps?? */ + if (camera->flags & GST_CAMERABIN_FLAG_SOURCE_COLOR_CONVERSION) { +#else + if (1) { +#endif + if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace")) + goto done; + } + + if (!(self->src_filter = + gst_camerabin_create_and_add_element (cbin, "capsfilter"))) + goto done; + +#if 0 + /* XXX srcbin needs to know of some flags, perhaps?? */ + if (camera->flags & GST_CAMERABIN_FLAG_SOURCE_RESIZE) { +#else + if (1) { +#endif + if (!(self->src_zoom_crop = + gst_camerabin_create_and_add_element (cbin, "videocrop"))) + goto done; + if (!(self->src_zoom_scale = + gst_camerabin_create_and_add_element (cbin, "videoscale"))) + goto done; + if (!(self->src_zoom_filter = + gst_camerabin_create_and_add_element (cbin, "capsfilter"))) + goto done; + } + + if (self->app_video_filter) { + if (!gst_camerabin_add_element (cbin, self->app_video_filter)) { + goto done; + } + } + + if (!(tee = gst_camerabin_create_and_add_element (cbin, "tee"))) + goto done; + + self->tee_vf_srcpad = gst_element_get_request_pad (tee, "src%d"); + self->tee_image_srcpad = gst_element_get_request_pad (tee, "src%d"); + self->tee_video_srcpad = gst_element_get_request_pad (tee, "src%d"); + + gst_pad_add_buffer_probe (self->tee_image_srcpad, + G_CALLBACK (gst_v4l2_camera_src_imgsrc_probe), self); + gst_pad_add_buffer_probe (self->tee_video_srcpad, + G_CALLBACK (gst_v4l2_camera_src_vidsrc_probe), self); + + *vfsrc = self->tee_vf_srcpad; + *imgsrc = self->tee_image_srcpad; + *vidsrc = self->tee_video_srcpad; + +#if 0 + /* XXX another idea... put common parts in GstBaseCameraSrc.. perhaps + * derived class could use some flags, or something like this, to + * indicate which pads in needs vscale and queue on.. (but I think it + * doesn't hurt ot have on all..) + */ + /* XXX perhaps we should keep queues and vscale's in camerabin itself, + * because GstOmxCameraSrc would also probably need the queues.. and + * maybe some OMX camera implementations would want the vscale's (and + * at least the vscale's should become pass-through if OMX camera can + * negotiate the requested sizes.. + */ + queue = gst_element_factory_make ("queue", "viewfinder-queue"); + if (!gst_camerabin_add_element (cbin, queue)) { + goto error; + } + /* Set queue leaky, we don't want to block video encoder feed, but + * prefer leaking view finder buffers instead. */ + g_object_set (G_OBJECT (queue), "leaky", 2, "max-size-buffers", 1, NULL); +#endif + + /* Set default "driver-name" for v4l2camsrc if not set */ + /* FIXME: v4l2camsrc specific */ + { + gchar *driver_name = NULL; + if (g_object_class_find_property (G_OBJECT_GET_CLASS (self->src_vid_src), + "driver-name")) { + g_object_get (G_OBJECT (self->src_vid_src), "driver-name", + &driver_name, NULL); + if (!driver_name) { + g_object_set (G_OBJECT (self->src_vid_src), "driver-name", + DEFAULT_V4L2CAMSRC_DRIVER_NAME, NULL); + } + } + } + + ret = TRUE; +done: + return ret; +} + +/** + * get_srcpad_current_format: + * @element: element to get the format from + * + * Helper function to get the negotiated fourcc + * format from @element src pad. + * + * Returns: negotiated format (fourcc), 0 if not found + */ +static guint32 +get_srcpad_current_format (GstElement * element) +{ + GstPad *srcpad = NULL; + GstCaps *srccaps = NULL; + GstStructure *structure; + guint32 format = 0; + + g_return_val_if_fail (element != NULL, 0); + + if ((srcpad = gst_element_get_static_pad (element, "src")) == NULL) { + goto no_pad; + } + + if ((srccaps = gst_pad_get_negotiated_caps (srcpad)) == NULL) { + goto no_caps; + } + + GST_LOG ("negotiated caps %" GST_PTR_FORMAT, srccaps); + + structure = gst_caps_get_structure (srccaps, 0); + if (gst_structure_has_field (structure, "format")) { + gst_structure_get_fourcc (structure, "format", &format); + } + + gst_caps_unref (srccaps); +no_caps: + gst_object_unref (srcpad); +no_pad: + GST_DEBUG ("current format for %" GST_PTR_FORMAT ": %" GST_FOURCC_FORMAT, + element, GST_FOURCC_ARGS (format)); + return format; +} + +/** + * set_allowed_framerate: + * @self: camerasrc object + * @filter_caps: update allowed framerate to these caps + * + * Find allowed frame rate from video source that matches with + * resolution in @filter_caps. Set found frame rate to @filter_caps. + */ +static void +set_allowed_framerate (GstV4l2CameraSrc * self, GstCaps * filter_caps) +{ + GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self); + GstStructure *structure; + GstCaps *allowed_caps = NULL, *intersect = NULL, *tmp_caps = NULL; + const GValue *framerate = NULL; + guint caps_size, i; + guint32 format = 0; + + GST_INFO_OBJECT (self, "filter caps:%" GST_PTR_FORMAT, filter_caps); + + structure = gst_structure_copy (gst_caps_get_structure (filter_caps, 0)); + + /* Set fourcc format according to current videosrc format */ + format = get_srcpad_current_format (self->src_vid_src); + if (format) { + GST_DEBUG_OBJECT (self, + "using format %" GST_FOURCC_FORMAT " for matching", + GST_FOURCC_ARGS (format)); + gst_structure_set (structure, "format", GST_TYPE_FOURCC, format, NULL); + } else { + GST_DEBUG_OBJECT (self, "not matching against fourcc format"); + gst_structure_remove_field (structure, "format"); + } + + tmp_caps = gst_caps_new_full (structure, NULL); + + /* Get supported caps from video src that matches with new filter caps */ + allowed_caps = gst_base_camera_src_get_allowed_input_caps (bcamsrc); + intersect = gst_caps_intersect (allowed_caps, tmp_caps); + GST_INFO_OBJECT (self, "intersect caps:%" GST_PTR_FORMAT, intersect); + + /* Find the best framerate from the caps */ + caps_size = gst_caps_get_size (intersect); + for (i = 0; i < caps_size; i++) { + structure = gst_caps_get_structure (intersect, i); + framerate = gst_base_camera_src_find_better_framerate (bcamsrc, + structure, framerate); + } + + /* Set found frame rate to original caps */ + if (GST_VALUE_HOLDS_FRACTION (framerate)) { + gst_caps_set_simple (filter_caps, + "framerate", GST_TYPE_FRACTION, + gst_value_get_fraction_numerator (framerate), + gst_value_get_fraction_denominator (framerate), NULL); + } + + /* Unref helper caps */ + if (allowed_caps) { + gst_caps_unref (allowed_caps); + } + if (intersect) { + gst_caps_unref (intersect); + } + if (tmp_caps) { + gst_caps_unref (tmp_caps); + } +} + +/** + * gst_v4l2_camera_src_setup_pipeline: + * @bcamsrc: camerasrc object + * + * This function updates camerabin capsfilters according + * to fps, resolution and zoom that have been configured + * to camerabin. + */ +static gboolean +gst_v4l2_camera_src_setup_pipeline (GstBaseCameraSrc * bcamsrc) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (bcamsrc); + GstStructure *st; + GstCaps *new_caps; + gboolean detect_framerate = FALSE; + + /* clear video update status */ +//XXX self->video_capture_caps_update = FALSE; + + if (!self->view_finder_caps) { + st = gst_structure_from_string (CAMERABIN_DEFAULT_VF_CAPS, NULL); + } else { + st = gst_structure_copy (gst_caps_get_structure (self->view_finder_caps, + 0)); + } + + if (bcamsrc->width > 0 && bcamsrc->height > 0) { + gst_structure_set (st, + "width", G_TYPE_INT, bcamsrc->width, + "height", G_TYPE_INT, bcamsrc->height, NULL); + } + + if (bcamsrc->fps_n > 0 && bcamsrc->fps_d > 0) { + if (bcamsrc->night_mode) { + GST_INFO_OBJECT (self, "night mode, lowest allowed fps will be forced"); + bcamsrc->pre_night_fps_n = bcamsrc->fps_n; + bcamsrc->pre_night_fps_d = bcamsrc->fps_d; + detect_framerate = TRUE; + } else { + gst_structure_set (st, + "framerate", GST_TYPE_FRACTION, bcamsrc->fps_n, bcamsrc->fps_d, NULL); + new_caps = gst_caps_new_full (st, NULL); + } + } else { + GST_DEBUG_OBJECT (self, "no framerate specified"); + detect_framerate = TRUE; + } + + if (detect_framerate) { + GST_DEBUG_OBJECT (self, "detecting allowed framerate"); + /* Remove old framerate if any */ + if (gst_structure_has_field (st, "framerate")) { + gst_structure_remove_field (st, "framerate"); + } + new_caps = gst_caps_new_full (st, NULL); + + /* Set allowed framerate for the resolution */ + set_allowed_framerate (self, new_caps); + } + + /* Set default zoom method */ + if (self->src_zoom_scale) { + g_object_set (self->src_zoom_scale, "method", + CAMERABIN_DEFAULT_ZOOM_METHOD, NULL); + } + + /* we create new caps in any way and they take ownership of the structure st */ + gst_caps_replace (&self->view_finder_caps, new_caps); + gst_caps_unref (new_caps); + + /* Set caps for view finder mode */ + /* This also sets zoom */ + set_capsfilter_caps (self, self->view_finder_caps); + + return TRUE; +} + +static gboolean +copy_missing_fields (GQuark field_id, const GValue * value, gpointer user_data) +{ + GstStructure *st = (GstStructure *) user_data; + const GValue *val = gst_structure_id_get_value (st, field_id); + + if (G_UNLIKELY (val == NULL)) { + gst_structure_id_set_value (st, field_id, value); + } + + return TRUE; +} + +/** + * adapt_image_capture: + * @self: camerasrc object + * @in_caps: caps object that describes incoming image format + * + * Adjust capsfilters and crop according image capture caps if necessary. + * The captured image format from video source might be different from + * what application requested, so we can try to fix that in camerabin. + * + */ +static void +adapt_image_capture (GstV4l2CameraSrc * self, GstCaps * in_caps) +{ + GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self); + GstStructure *in_st, *new_st, *req_st; + gint in_width = 0, in_height = 0, req_width = 0, req_height = 0, crop = 0; + gdouble ratio_w, ratio_h; + GstCaps *filter_caps = NULL; + + GST_LOG_OBJECT (self, "in caps: %" GST_PTR_FORMAT, in_caps); + GST_LOG_OBJECT (self, "requested caps: %" GST_PTR_FORMAT, + self->image_capture_caps); + + in_st = gst_caps_get_structure (in_caps, 0); + gst_structure_get_int (in_st, "width", &in_width); + gst_structure_get_int (in_st, "height", &in_height); + + req_st = gst_caps_get_structure (self->image_capture_caps, 0); + gst_structure_get_int (req_st, "width", &req_width); + gst_structure_get_int (req_st, "height", &req_height); + + GST_INFO_OBJECT (self, "we requested %dx%d, and got %dx%d", req_width, + req_height, in_width, in_height); + + new_st = gst_structure_copy (req_st); + /* If new fields have been added, we need to copy them */ + gst_structure_foreach (in_st, copy_missing_fields, new_st); + +#if 0 + /* XXX srcbin needs to know of some flags, perhaps?? */ + if (!(camera->flags & GST_CAMERABIN_FLAG_SOURCE_RESIZE)) { +#else + if (1) { +#endif + GST_DEBUG_OBJECT (self, + "source-resize flag disabled, unable to adapt resolution"); + gst_structure_set (new_st, "width", G_TYPE_INT, in_width, "height", + G_TYPE_INT, in_height, NULL); + } + + GST_LOG_OBJECT (self, "new image capture caps: %" GST_PTR_FORMAT, new_st); + + /* Crop if requested aspect ratio differs from incoming frame aspect ratio */ + if (self->src_zoom_crop) { + + ratio_w = (gdouble) in_width / req_width; + ratio_h = (gdouble) in_height / req_height; + + if (ratio_w < ratio_h) { + crop = in_height - (req_height * ratio_w); + self->base_crop_top = crop / 2; + self->base_crop_bottom = crop / 2; + } else { + crop = in_width - (req_width * ratio_h); + self->base_crop_left = crop / 2; + self->base_crop_right += crop / 2; + } + + GST_INFO_OBJECT (self, + "setting base crop: left:%d, right:%d, top:%d, bottom:%d", + self->base_crop_left, self->base_crop_right, self->base_crop_top, + self->base_crop_bottom); + g_object_set (G_OBJECT (self->src_zoom_crop), + "top", self->base_crop_top, + "bottom", self->base_crop_bottom, + "left", self->base_crop_left, "right", self->base_crop_right, NULL); + } + + /* Update capsfilters */ + gst_caps_replace (&self->image_capture_caps, + gst_caps_new_full (new_st, NULL)); + set_capsfilter_caps (self, self->image_capture_caps); + + /* Adjust the capsfilter before crop and videoscale elements if necessary */ + if (in_width == bcamsrc->width && in_height == bcamsrc->height) { + GST_DEBUG_OBJECT (self, "no adaptation with resolution needed"); + } else { + GST_DEBUG_OBJECT (self, + "changing %" GST_PTR_FORMAT " from %dx%d to %dx%d", self->src_filter, + bcamsrc->width, bcamsrc->height, in_width, in_height); + /* Apply the width and height to filter caps */ + g_object_get (G_OBJECT (self->src_filter), "caps", &filter_caps, NULL); + filter_caps = gst_caps_make_writable (filter_caps); + gst_caps_set_simple (filter_caps, "width", G_TYPE_INT, in_width, "height", + G_TYPE_INT, in_height, NULL); + g_object_set (G_OBJECT (self->src_filter), "caps", filter_caps, NULL); + gst_caps_unref (filter_caps); + } +} + +/** + * img_capture_prepared: + * @data: camerasrc object + * @caps: caps describing the prepared image format + * + * Callback which is called after image capture has been prepared. + */ +static void +img_capture_prepared (gpointer data, GstCaps * caps) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (data); + + GST_INFO_OBJECT (self, "image capture prepared"); + + /* It is possible we are about to get something else that we requested */ + if (!gst_caps_is_equal (self->image_capture_caps, caps)) { + adapt_image_capture (self, caps); + } else { + set_capsfilter_caps (self, self->image_capture_caps); + } + +//XXX g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE, +//XXX "active-pad", camera->pad_src_img, NULL); +} + +static void +set_image_capture_caps (GstV4l2CameraSrc * self, gint width, gint height) +{ + GstStructure *structure; + GstCaps *new_caps = NULL; + + if (width && height && self->view_finder_caps) { + /* Use view finder mode caps as a basis */ + structure = gst_caps_get_structure (self->view_finder_caps, 0); + + /* Set new resolution for image capture */ + new_caps = gst_caps_new_simple (gst_structure_get_name (structure), + "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); + + /* Set allowed framerate for the resolution. */ + set_allowed_framerate (self, new_caps); + } + + GST_INFO_OBJECT (self, + "init filter caps for image capture %" GST_PTR_FORMAT, new_caps); + gst_caps_replace (&self->image_capture_caps, new_caps); + self->image_capture_caps_update = FALSE; +} + +/** + * + */ +static gboolean +start_image_capture (GstV4l2CameraSrc * self) +{ + GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self); + GstPhotography *photography = gst_base_camera_src_get_photography (bcamsrc); + gboolean wait_for_prepare = FALSE, ret = FALSE; + + if (photography) { + wait_for_prepare = TRUE; + + if (!self->image_capture_caps || self->image_capture_caps_update) { + if (bcamsrc->image_capture_width && bcamsrc->image_capture_height) { + /* Resolution is set, but it isn't in use yet */ + set_image_capture_caps (self, bcamsrc->image_capture_width, + bcamsrc->image_capture_height); + } else { + /* Capture resolution not set. Use viewfinder resolution */ + self->image_capture_caps = gst_caps_copy (self->view_finder_caps); + self->image_capture_caps_update = FALSE; + } + } + + /* Start preparations for image capture */ + GST_DEBUG_OBJECT (self, "prepare image capture caps %" GST_PTR_FORMAT, + self->image_capture_caps); + + ret = gst_photography_prepare_for_capture (photography, + (GstPhotoCapturePrepared) img_capture_prepared, + self->image_capture_caps, self); + + } else { +//XXX g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", TRUE, +//XXX "active-pad", camera->pad_src_img, NULL); + ret = TRUE; + } + + return ret; +} + +static gboolean +gst_v4l2_camera_src_set_mode (GstBaseCameraSrc * bcamsrc, GstCameraBinMode mode) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (bcamsrc); + GstPhotography *photography = gst_base_camera_src_get_photography (bcamsrc); + + if (photography) { + if (g_object_class_find_property (G_OBJECT_GET_CLASS (photography), + "capture-mode")) { + g_object_set (G_OBJECT (photography), "capture-mode", mode, NULL); + } + } + + self->mode = mode; + + switch (mode) { + case MODE_PREVIEW: + return TRUE; // XXX + case MODE_IMAGE: + return start_image_capture (GST_V4L2_CAMERA_SRC (bcamsrc)); + case MODE_VIDEO: + return TRUE; // XXX + } + + g_assert_not_reached (); + + return FALSE; +} + +static gboolean +set_videosrc_zoom (GstV4l2CameraSrc * self, gint zoom) +{ + gboolean ret = FALSE; + + if (g_object_class_find_property (G_OBJECT_GET_CLASS (self->src_vid_src), + "zoom")) { + g_object_set (G_OBJECT (self->src_vid_src), "zoom", + (gfloat) zoom / 100, NULL); + ret = TRUE; + } + return ret; +} + +static gboolean +set_element_zoom (GstV4l2CameraSrc * self, gint zoom) +{ + gboolean ret = FALSE; + GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self); + gint w2_crop = 0, h2_crop = 0; + GstPad *pad_zoom_sink = NULL; + gint left = self->base_crop_left; + gint right = self->base_crop_right; + gint top = self->base_crop_top; + gint bottom = self->base_crop_bottom; + + if (self->src_zoom_crop) { + /* Update capsfilters to apply the zoom */ + GST_INFO_OBJECT (self, "zoom: %d, orig size: %dx%d", zoom, + bcamsrc->width, bcamsrc->height); + + if (zoom != ZOOM_1X) { + w2_crop = (bcamsrc->width - (bcamsrc->width * ZOOM_1X / zoom)) / 2; + h2_crop = (bcamsrc->height - (bcamsrc->height * ZOOM_1X / zoom)) / 2; + + left += w2_crop; + right += w2_crop; + top += h2_crop; + bottom += h2_crop; + + /* force number of pixels cropped from left to be even, to avoid slow code + * path on videoscale */ + left &= 0xFFFE; + } + + pad_zoom_sink = gst_element_get_static_pad (self->src_zoom_crop, "sink"); + + GST_INFO_OBJECT (self, + "sw cropping: left:%d, right:%d, top:%d, bottom:%d", left, right, top, + bottom); + + GST_PAD_STREAM_LOCK (pad_zoom_sink); + g_object_set (self->src_zoom_crop, "left", left, "right", right, "top", + top, "bottom", bottom, NULL); + GST_PAD_STREAM_UNLOCK (pad_zoom_sink); + gst_object_unref (pad_zoom_sink); + ret = TRUE; + } + return ret; +} + +static void +gst_v4l2_camera_src_set_zoom (GstBaseCameraSrc * bcamsrc, gint zoom) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (bcamsrc); + + GST_INFO_OBJECT (self, "setting zoom %d", zoom); + + if (set_videosrc_zoom (self, zoom)) { + set_element_zoom (self, ZOOM_1X); + GST_INFO_OBJECT (self, "zoom set using videosrc"); + } else if (set_element_zoom (self, zoom)) { + GST_INFO_OBJECT (self, "zoom set using gst elements"); + } else { + GST_INFO_OBJECT (self, "setting zoom failed"); + } +} + +static GstCaps * +gst_v4l2_camera_src_get_allowed_input_caps (GstBaseCameraSrc * bcamsrc) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (bcamsrc); + GstCaps *caps = NULL; + GstPad *pad = NULL, *peer_pad = NULL; + GstState state; + GstElement *videosrc; + + videosrc = self->src_vid_src ? self->src_vid_src : self->app_vid_src; + + if (!videosrc) { + GST_WARNING_OBJECT (self, "no videosrc, can't get allowed caps"); + goto failed; + } + + if (self->allowed_caps) { + GST_DEBUG_OBJECT (self, "returning cached caps"); + goto done; + } + + pad = gst_element_get_static_pad (videosrc, "src"); + + if (!pad) { + GST_WARNING_OBJECT (self, "no srcpad in videosrc"); + goto failed; + } + + state = GST_STATE (videosrc); + + /* Make this function work also in NULL state */ + if (state == GST_STATE_NULL) { + GST_DEBUG_OBJECT (self, "setting videosrc to ready temporarily"); + peer_pad = gst_pad_get_peer (pad); + if (peer_pad) { + gst_pad_unlink (pad, peer_pad); + } + /* Set videosrc to READY to open video device */ + gst_element_set_locked_state (videosrc, TRUE); + gst_element_set_state (videosrc, GST_STATE_READY); + } + + self->allowed_caps = gst_pad_get_caps (pad); + + /* Restore state and re-link if necessary */ + if (state == GST_STATE_NULL) { + GST_DEBUG_OBJECT (self, "restoring videosrc state %d", state); + /* Reset videosrc to NULL state, some drivers seem to need this */ + gst_element_set_state (videosrc, GST_STATE_NULL); + if (peer_pad) { + gst_pad_link (pad, peer_pad); + gst_object_unref (peer_pad); + } + gst_element_set_locked_state (videosrc, FALSE); + } + + gst_object_unref (pad); + +done: + if (self->allowed_caps) { + caps = gst_caps_copy (self->allowed_caps); + } + GST_DEBUG_OBJECT (self, "allowed caps:%" GST_PTR_FORMAT, caps); +failed: + return caps; +} + +/** + * configure_format: + * @self: camerasrc object + * @caps: caps describing new format + * + * Configure internal video format for camerabin. + */ +static void +configure_format (GstV4l2CameraSrc * self, GstCaps * caps) +{ + GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self); + GstStructure *st; + gint width, height; + + st = gst_caps_get_structure (caps, 0); + + gst_structure_get_int (st, "width", &width); + gst_structure_get_int (st, "height", &height); + + g_object_set (self, "width", width, "height", height, NULL); + + if (gst_structure_has_field_typed (st, "framerate", GST_TYPE_FRACTION)) { + gst_structure_get_fraction (st, "framerate", &bcamsrc->fps_n, + &bcamsrc->fps_d); + } +} + + +/** + * update_aspect_filter: + * @self: camerasrc object + * @new_caps: new caps of next buffers arriving to view finder sink element + * + * Updates aspect ratio capsfilter to maintain aspect ratio, if we need to + * scale frames for showing them in view finder. + */ +static void +update_aspect_filter (GstV4l2CameraSrc * self, GstCaps * new_caps) +{ + // XXX why not instead add a preserve-aspect-ratio property to videoscale? +#if 0 + if (camera->flags & GST_CAMERABIN_FLAG_VIEWFINDER_SCALE) { + GstCaps *sink_caps, *ar_caps; + GstStructure *st; + gint in_w = 0, in_h = 0, sink_w = 0, sink_h = 0, target_w = 0, target_h = 0; + gdouble ratio_w, ratio_h; + GstPad *sink_pad; + const GValue *range; + + sink_pad = gst_element_get_static_pad (camera->view_sink, "sink"); + + if (sink_pad) { + sink_caps = gst_pad_get_caps (sink_pad); + gst_object_unref (sink_pad); + if (sink_caps) { + if (!gst_caps_is_any (sink_caps)) { + GST_DEBUG_OBJECT (camera, "sink element caps %" GST_PTR_FORMAT, + sink_caps); + /* Get maximum resolution that view finder sink accepts */ + st = gst_caps_get_structure (sink_caps, 0); + if (gst_structure_has_field_typed (st, "width", GST_TYPE_INT_RANGE)) { + range = gst_structure_get_value (st, "width"); + sink_w = gst_value_get_int_range_max (range); + } + if (gst_structure_has_field_typed (st, "height", GST_TYPE_INT_RANGE)) { + range = gst_structure_get_value (st, "height"); + sink_h = gst_value_get_int_range_max (range); + } + GST_DEBUG_OBJECT (camera, "sink element accepts max %dx%d", sink_w, + sink_h); + + /* Get incoming frames' resolution */ + if (sink_h && sink_w) { + st = gst_caps_get_structure (new_caps, 0); + gst_structure_get_int (st, "width", &in_w); + gst_structure_get_int (st, "height", &in_h); + GST_DEBUG_OBJECT (camera, "new caps with %dx%d", in_w, in_h); + } + } + gst_caps_unref (sink_caps); + } + } + + /* If we get bigger frames than view finder sink accepts, then we scale. + If we scale we need to adjust aspect ratio capsfilter caps in order + to maintain aspect ratio while scaling. */ + if (in_w && in_h && (in_w > sink_w || in_h > sink_h)) { + ratio_w = (gdouble) sink_w / in_w; + ratio_h = (gdouble) sink_h / in_h; + + if (ratio_w < ratio_h) { + target_w = sink_w; + target_h = (gint) (ratio_w * in_h); + } else { + target_w = (gint) (ratio_h * in_w); + target_h = sink_h; + } + + GST_DEBUG_OBJECT (camera, "setting %dx%d filter to maintain aspect ratio", + target_w, target_h); + ar_caps = gst_caps_copy (new_caps); + gst_caps_set_simple (ar_caps, "width", G_TYPE_INT, target_w, "height", + G_TYPE_INT, target_h, NULL); + } else { + GST_DEBUG_OBJECT (camera, "no scaling"); + ar_caps = new_caps; + } + + GST_DEBUG_OBJECT (camera, "aspect ratio filter caps %" GST_PTR_FORMAT, + ar_caps); + g_object_set (G_OBJECT (camera->aspect_filter), "caps", ar_caps, NULL); + if (ar_caps != new_caps) + gst_caps_unref (ar_caps); + } +#endif +} + + +/** + * set_capsfilter_caps: + * @self: camerasrc object + * @new_caps: pointer to caps object to set + * + * Set given caps to camerabin capsfilters. + */ +static void +set_capsfilter_caps (GstV4l2CameraSrc * self, GstCaps * new_caps) +{ + GST_INFO_OBJECT (self, "new_caps:%" GST_PTR_FORMAT, new_caps); + + configure_format (self, new_caps); + + /* Update zoom */ + gst_base_camera_src_setup_zoom (GST_BASE_CAMERA_SRC (self)); + + /* Update capsfilters */ + g_object_set (G_OBJECT (self->src_filter), "caps", new_caps, NULL); + if (self->src_zoom_filter) + g_object_set (G_OBJECT (self->src_zoom_filter), "caps", new_caps, NULL); + update_aspect_filter (self, new_caps); + GST_INFO_OBJECT (self, "udpated"); +} + +static void +gst_v4l2_camera_src_finish_image_capture (GstBaseCameraSrc * bcamsrc) +{ + GstV4l2CameraSrc *self = GST_V4L2_CAMERA_SRC (bcamsrc); + + if (self->image_capture_caps) { + /* If we used specific caps for image capture we need to + restore the caps and zoom/crop for view finder mode */ + if (self->src_zoom_crop) { + GST_DEBUG_OBJECT (self, "resetting crop in camerabin"); + g_object_set (self->src_zoom_crop, "left", 0, "right", 0, + "top", 0, "bottom", 0, NULL); + } + self->base_crop_left = 0; + self->base_crop_right = 0; + self->base_crop_top = 0; + self->base_crop_bottom = 0; + set_capsfilter_caps (self, self->view_finder_caps); + } +} + +static void +gst_v4l2_camera_src_base_init (gpointer g_class) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + +// GST_DEBUG_CATEGORY_INIT (v4l2_camera_src_debug, "v4l2_camera_src", 0, +// "V4l2 camera src"); + + gst_element_class_set_details_simple (gstelement_class, + "V4l2 camera src element for camerabin", "Source/Video", + "V4l2 camera src element for camerabin", "Rob Clark "); +} + +static void +gst_v4l2_camera_src_class_init (GstV4l2CameraSrcClass * klass) +{ + GObjectClass *gobject_class; + GstBaseCameraSrcClass *gstbasecamerasrc_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstbasecamerasrc_class = GST_BASE_CAMERA_SRC_CLASS (klass); + + gobject_class->dispose = gst_v4l2_camera_src_dispose; + gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2_camera_src_finalize; + gobject_class->set_property = gst_v4l2_camera_src_set_property; + gobject_class->get_property = gst_v4l2_camera_src_get_property; + + // g_object_class_install_property .... + + gstbasecamerasrc_class->construct_pipeline = + gst_v4l2_camera_src_construct_pipeline; + gstbasecamerasrc_class->setup_pipeline = gst_v4l2_camera_src_setup_pipeline; + gstbasecamerasrc_class->set_zoom = gst_v4l2_camera_src_set_zoom; + gstbasecamerasrc_class->set_mode = gst_v4l2_camera_src_set_mode; + gstbasecamerasrc_class->get_allowed_input_caps = + gst_v4l2_camera_src_get_allowed_input_caps; + gstbasecamerasrc_class->finish_image_capture = + gst_v4l2_camera_src_finish_image_capture; +} + +static void +gst_v4l2_camera_src_init (GstV4l2CameraSrc * self, + GstV4l2CameraSrcClass * klass) +{ + self->mode = MODE_PREVIEW; +} + +gboolean +gst_v4l2_camera_src_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "v4l2camerasrc", GST_RANK_NONE, + gst_v4l2_camera_src_get_type ()); +} diff --git a/gst/camerabin2/gstv4l2camerasrc.h b/gst/camerabin2/gstv4l2camerasrc.h new file mode 100644 index 0000000000..dd3ea4ea49 --- /dev/null +++ b/gst/camerabin2/gstv4l2camerasrc.h @@ -0,0 +1,112 @@ +/* + * 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. + */ + + +#ifndef __GST_V4L2_CAMERA_SRC_H__ +#define __GST_V4L2_CAMERA_SRC_H__ + +#include +#include "gstbasecamerasrc.h" + +G_BEGIN_DECLS +#define GST_TYPE_V4L2_CAMERA_SRC \ + (gst_v4l2_camera_src_get_type()) +#define GST_V4L2_CAMERA_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_CAMERA_SRC,GstV4l2CameraSrc)) +#define GST_V4L2_CAMERA_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_CAMERA_SRC,GstV4l2CameraSrcClass)) +#define GST_IS_V4L2_CAMERA_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_CAMERA_SRC)) +#define GST_IS_V4L2_CAMERA_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_CAMERA_SRC)) + GType gst_v4l2_camera_src_get_type (void); + +typedef struct _GstV4l2CameraSrc GstV4l2CameraSrc; +typedef struct _GstV4l2CameraSrcClass GstV4l2CameraSrcClass; + + +/** + * GstV4l2CameraSrc: + * + */ +struct _GstV4l2CameraSrc +{ + GstBaseCameraSrc parent; + + GstCameraBinMode mode; + + /* source elements */ + GstElement *src_vid_src; + GstElement *src_filter; + GstElement *src_zoom_crop; + GstElement *src_zoom_scale; + GstElement *src_zoom_filter; + GstElement *src_out_sel; + + /* srcpads of tee */ + GstPad *tee_vf_srcpad; + GstPad *tee_image_srcpad; + GstPad *tee_video_srcpad; + + /* Application configurable elements */ + GstElement *app_vid_src; + GstElement *app_video_filter; + + /* Caps that videosrc supports */ + GstCaps *allowed_caps; + + /* Optional base crop for frames. Used to crop frames e.g. + due to wrong aspect ratio, before the crop related to zooming. */ + gint base_crop_top; + gint base_crop_bottom; + gint base_crop_left; + gint base_crop_right; + + /* Caps applied to capsfilters when in view finder mode */ + GstCaps *view_finder_caps; + + /* Caps applied to capsfilters when taking still image */ + GstCaps *image_capture_caps; + gboolean image_capture_caps_update; // XXX where does this get set.. + + /* if GstV4l2CameraSrc is moved into camerabin plugin, then this isn't + * needed: + */ + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + + +/** + * GstV4l2CameraSrcClass: + * + */ +struct _GstV4l2CameraSrcClass +{ + GstBaseCameraSrcClass parent; + + /* if GstV4l2CameraSrc is moved into camerabin plugin, then this isn't + * needed: + */ + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + +gboolean gst_v4l2_camera_src_plugin_init (GstPlugin * plugin); + +#endif /* __GST_V4L2_CAMERA_SRC_H__ */