camerabin2: Adding v4l2camerasrc from robclark's branch
authorThiago Santos <thiago.sousa.santos@collabora.co.uk>
Thu, 25 Nov 2010 16:00:50 +0000 (13:00 -0300)
committerThiago Santos <thiago.sousa.santos@collabora.co.uk>
Wed, 8 Dec 2010 18:38:26 +0000 (15:38 -0300)
Adds 3-pad v4l2 camera source from Rob Clark's camerabin
branch on http://gitorious.org/robclark-gstreamer/gst-plugins-bad

gst/camerabin2/Makefile.am
gst/camerabin2/camerabingeneral.c [new file with mode: 0644]
gst/camerabin2/camerabingeneral.h [new file with mode: 0644]
gst/camerabin2/gstbasecamerasrc.c [new file with mode: 0644]
gst/camerabin2/gstbasecamerasrc.h [new file with mode: 0644]
gst/camerabin2/gstcamerabin-enum.c [new file with mode: 0644]
gst/camerabin2/gstcamerabin-enum.h [new file with mode: 0644]
gst/camerabin2/gstplugin.c
gst/camerabin2/gstv4l2camerasrc.c [new file with mode: 0644]
gst/camerabin2/gstv4l2camerasrc.h [new file with mode: 0644]

index 49d29aa..25b1e61 100644 (file)
@@ -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 (file)
index 0000000..2a50d9e
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
+ *
+ * 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 <string.h>
+
+#include "camerabingeneral.h"
+#include <glib.h>
+
+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 (file)
index 0000000..93520c5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
+ *
+ * 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 <gst/gst.h>
+
+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 (file)
index 0000000..aba756e
--- /dev/null
@@ -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 <config.h>
+#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 <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 ....
+
+  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 (file)
index 0000000..75ef022
--- /dev/null
@@ -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 <gst/gst.h>
+#include <gst/gstbin.h>
+#include <gst/interfaces/photography.h>
+#include <gst/interfaces/colorbalance.h>
+#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 (file)
index 0000000..8d1d0c9
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * GStreamer
+ * Copyright (C) 2009 Nokia Corporation <multimedia@maemo.org>
+ *
+ * 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 (file)
index 0000000..cd1707d
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * GStreamer
+ * Copyright (C) 2009 Nokia Corporation <multimedia@maemo.org>
+ *
+ * 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 <gst/gst.h>
+
+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__ */
index 1f1dd0e..c88ad8d 100644 (file)
@@ -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 (file)
index 0000000..1cc4ca8
--- /dev/null
@@ -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 <config.h>
+#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 <rob@ti.com>");
+}
+
+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 (file)
index 0000000..dd3ea4e
--- /dev/null
@@ -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 <gst/gst.h>
+#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__ */