Add camerabin element.
authorNokia Corporation <multimedia@maemo.org>
Thu, 5 Feb 2009 13:48:32 +0000 (15:48 +0200)
committerRené Stadler <rene.stadler@nokia.com>
Mon, 9 Feb 2009 08:22:09 +0000 (10:22 +0200)
17 files changed:
configure.ac
gst/camerabin/Makefile.am [new file with mode: 0644]
gst/camerabin/camerabingeneral.c [new file with mode: 0644]
gst/camerabin/camerabingeneral.h [new file with mode: 0644]
gst/camerabin/camerabinimage.c [new file with mode: 0644]
gst/camerabin/camerabinimage.h [new file with mode: 0644]
gst/camerabin/camerabinvideo.c [new file with mode: 0644]
gst/camerabin/camerabinvideo.h [new file with mode: 0644]
gst/camerabin/gstcamerabin-marshal.list [new file with mode: 0644]
gst/camerabin/gstcamerabin.c [new file with mode: 0644]
gst/camerabin/gstcamerabin.h [new file with mode: 0644]
gst/camerabin/gstcamerabincolorbalance.c [new file with mode: 0644]
gst/camerabin/gstcamerabincolorbalance.h [new file with mode: 0644]
gst/camerabin/gstcamerabinphotography.c [new file with mode: 0644]
gst/camerabin/gstcamerabinphotography.h [new file with mode: 0644]
gst/camerabin/gstcamerabinxoverlay.c [new file with mode: 0644]
gst/camerabin/gstcamerabinxoverlay.h [new file with mode: 0644]

index 7ec7cea5c278202352c32d1b3cb6f6a0aa6d8a3c..3cc51ab80e3df7b8166dc55ae9289871c0a55a73 100644 (file)
@@ -239,6 +239,7 @@ dnl these are all the gst plug-ins, compilable without additional libs
 AG_GST_CHECK_PLUGIN(aacparse)
 AG_GST_CHECK_PLUGIN(aiffparse)
 AG_GST_CHECK_PLUGIN(amrparse)
+AG_GST_CHECK_PLUGIN(camerabin)
 AG_GST_CHECK_PLUGIN(legacyresample)
 AG_GST_CHECK_PLUGIN(bayer)
 AG_GST_CHECK_PLUGIN(cdxaparse)
@@ -1412,6 +1413,7 @@ gst/aiffparse/Makefile
 gst/amrparse/Makefile
 gst/legacyresample/Makefile
 gst/bayer/Makefile
+gst/camerabin/Makefile
 gst/cdxaparse/Makefile
 gst/dccp/Makefile
 gst/deinterlace/Makefile
diff --git a/gst/camerabin/Makefile.am b/gst/camerabin/Makefile.am
new file mode 100644 (file)
index 0000000..8a5cc38
--- /dev/null
@@ -0,0 +1,46 @@
+glib_enum_prefix = gst_camerabin
+
+include $(top_srcdir)/common/glib-gen.mak
+
+built_sources = gstcamerabin-marshal.c
+built_headers = gstcamerabin-marshal.h
+
+BUILT_SOURCES = $(built_sources) $(built_headers)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = gstcamerabin-marshal.list
+
+plugin_LTLIBRARIES = libgstcamerabin.la
+
+libgstcamerabin_la_SOURCES = gstcamerabin.c             \
+                         gstcamerabinxoverlay.c     \
+                         gstcamerabincolorbalance.c \
+                         camerabinimage.c           \
+                         camerabinvideo.c           \
+                         camerabingeneral.c         \
+                         gstcamerabinphotography.c
+
+nodist_libgstcamerabin_la_SOURCES = $(built_sources)
+
+# libcamerabin_ladir = $(includedir)/gstreamer-@GST_MAJORMINOR@/camerabin
+
+# libcamerabin_la_HEADERS = gstcamerabin.h
+
+libgstcamerabin_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS)
+
+libgstcamerabin_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)      \
+                        -lgstinterfaces-$(GST_MAJORMINOR)    \
+                        $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-$(GST_MAJORMINOR).la
+
+libgstcamerabin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstcamerabin__la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = gstcamerabin.h             \
+                gstcamerabinxoverlay.h     \
+                gstcamerabincolorbalance.h \
+                camerabinimage.h           \
+                camerabinvideo.h           \
+                camerabingeneral.h         \
+                gstcamerabinphotography.h  \
+                $(built_headers)
diff --git a/gst/camerabin/camerabingeneral.c b/gst/camerabin/camerabingeneral.c
new file mode 100644 (file)
index 0000000..627e032
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * 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 "camerabingeneral.h"
+#include <glib.h>
+
+GST_DEBUG_CATEGORY (gst_camerabin_debug);
+
+static gboolean
+camerabin_general_dbg_have_event (GstPad * pad, GstEvent * event,
+    gpointer u_data)
+{
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+    {
+      GstElement *elem = (GstElement *) u_data;
+      gchar *elem_name = gst_element_get_name (elem);
+      gchar *pad_name = gst_pad_get_name (pad);
+
+      gboolean update;
+      gdouble rate;
+      GstFormat format;
+      gint64 start, stop, pos;
+      gst_event_parse_new_segment (event, &update, &rate, &format, &start,
+          &stop, &pos);
+
+      GST_DEBUG ("element %s, pad %s, new_seg_start =%" GST_TIME_FORMAT
+          ", new_seg_stop =%" GST_TIME_FORMAT
+          ", new_seg_pos =%" GST_TIME_FORMAT "\n", elem_name, pad_name,
+          GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (pos));
+
+      g_free (pad_name);
+      g_free (elem_name);
+    }
+      break;
+    default:
+      break;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+camerabin_general_dbg_have_buffer (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data)
+{
+  GstElement *elem = (GstElement *) u_data;
+  gchar *elem_name = gst_element_get_name (elem);
+  gchar *pad_name = gst_pad_get_name (pad);
+
+  GST_DEBUG ("element %s, pad %s, buf_ts =%" GST_TIME_FORMAT "\n", elem_name,
+      pad_name, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+
+  g_free (pad_name);
+  g_free (elem_name);
+
+  return TRUE;
+
+}
+
+void
+camerabin_general_dbg_set_probe (GstElement * elem, gchar * pad_name,
+    gboolean buf, gboolean evt)
+{
+  GstPad *pad = gst_element_get_static_pad (elem, pad_name);
+
+  if (buf)
+    gst_pad_add_buffer_probe (pad,
+        G_CALLBACK (camerabin_general_dbg_have_buffer), elem);
+  if (evt)
+    gst_pad_add_event_probe (pad,
+        G_CALLBACK (camerabin_general_dbg_have_event), elem);
+
+  gst_object_unref (pad);
+}
+
+/**
+ * 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.
+ *
+ * Returns: %TRUE if adding and linking succeeded, %FALSE otherwise.
+ */
+gboolean
+gst_camerabin_add_element (GstBin * bin, GstElement * new_elem)
+{
+  gboolean ret = FALSE;
+
+  ret = gst_camerabin_try_add_element (bin, new_elem);
+
+  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);
+  }
+
+  return ret;
+}
+
+/**
+ * gst_camerabin_try_add_element:
+ * @bin: tries adding 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.
+ *
+ * Returns: %TRUE if adding and linking succeeded, %FALSE otherwise.
+ */
+gboolean
+gst_camerabin_try_add_element (GstBin * bin, GstElement * new_elem)
+{
+  GstPad *bin_pad;
+  GstElement *bin_elem;
+  gboolean ret = TRUE;
+
+  if (!bin || !new_elem) {
+    return FALSE;
+  }
+
+  /* Get pads for linking */
+  GST_DEBUG ("finding unconnected src pad");
+  bin_pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SRC);
+  GST_DEBUG ("unconnected pad %s:%s", GST_DEBUG_PAD_NAME (bin_pad));
+  /* 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) {
+    bin_elem = gst_pad_get_parent_element (bin_pad);
+    gst_object_unref (bin_pad);
+    if (!gst_element_link (bin_elem, new_elem)) {
+      gst_bin_remove (bin, new_elem);
+      ret = FALSE;
+    }
+    gst_object_unref (bin_elem);
+  }
+
+  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 = NULL;
+
+  GST_DEBUG ("adding %s", elem_name);
+  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;
+}
+
+/**
+ * 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);
+        /* 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/camerabin/camerabingeneral.h b/gst/camerabin/camerabingeneral.h
new file mode 100644 (file)
index 0000000..e05bb1f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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 <sys/time.h>
+#include <time.h>
+
+#include <gst/gst.h>
+
+
+typedef struct timeval TIME_TYPE;
+#define GET_TIME(t) do { gettimeofday(&(t), NULL); } while(0)
+#define DIFF_TIME(t2,t1,d) do { d = ((t2).tv_sec - (t1).tv_sec) * 1000000 + \
+      (t2).tv_usec - (t1).tv_usec; } while(0)
+
+#define _INIT_TIMER_BLOCK TIME_TYPE t1, t2; guint32 d; do {;}while (0)
+
+#define _OPEN_TIMER_BLOCK { GET_TIME(t1); do {;}while (0)
+#define _CLOSE_TIMER_BLOCK GET_TIME(t2); DIFF_TIME(t2,t1,d); \
+  GST_DEBUG("elapsed time = %u\n", d); \
+  } do {;}while (0)
+
+
+extern void
+camerabin_general_dbg_set_probe (GstElement * elem, gchar * pad_name,
+    gboolean buf, gboolean evt);
+
+gboolean gst_camerabin_try_add_element (GstBin * bin, GstElement * new_elem);
+
+gboolean gst_camerabin_add_element (GstBin * bin, GstElement * new_elem);
+
+GstElement *gst_camerabin_create_and_add_element (GstBin * bin,
+    const gchar * elem_name);
+
+void gst_camerabin_remove_elements_from_bin (GstBin * bin);
+
+gboolean
+gst_camerabin_drop_eos_probe (GstPad * pad, GstEvent * event, gpointer u_data);
+
+GST_DEBUG_CATEGORY_EXTERN (gst_camerabin_debug);
+#define GST_CAT_DEFAULT gst_camerabin_debug
+
+#endif /* #ifndef __CAMERABIN_GENERAL_H_ */
diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c
new file mode 100644 (file)
index 0000000..e4ff6b8
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * 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:camerabinimage
+ * @short_description: image capturing module of #GstCameraBin
+ *
+ * <refsect2>
+ * <para>
+ *
+ * The pipeline for this module is:
+ *
+ * <informalexample>
+ * <programlisting>
+ *-----------------------------------------------------------------------------
+ *                      (src0) -> queue ->
+ * -> [post proc] -> tee <
+ *                      (src1) -> imageenc -> metadatamuxer -> filesink
+ *-----------------------------------------------------------------------------
+ * </programlisting>
+ * </informalexample>
+ *
+ * The property of elements are:
+ *
+ *   queue - "max-size-buffers", 1, "leaky", 2,
+ *
+ * The image bin opens file for image writing in READY to PAUSED state change.
+ * The image bin closes the file in PAUSED to READY state change.
+ *
+ * </para>
+ * </refsect2>
+ */
+
+/*
+ * includes
+ */
+
+#include <gst/gst.h>
+
+#include "camerabinimage.h"
+#include "camerabingeneral.h"
+
+#include "string.h"
+
+/* default internal element names */
+
+#define DEFAULT_SINK "filesink"
+#define DEFAULT_ENC "jpegenc"
+#define DEFAULT_META_MUX "metadatamux"
+
+enum
+{
+  PROP_0,
+  PROP_FILENAME
+};
+
+static gboolean gst_camerabin_image_create_elements (GstCameraBinImage * img);
+static void gst_camerabin_image_destroy_elements (GstCameraBinImage * img);
+
+static void gst_camerabin_image_dispose (GstCameraBinImage * sink);
+static GstStateChangeReturn
+gst_camerabin_image_change_state (GstElement * element,
+    GstStateChange transition);
+static gboolean gst_camerabin_image_send_event (GstElement * element,
+    GstEvent * event);
+static void gst_camerabin_image_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_camerabin_image_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+GST_BOILERPLATE (GstCameraBinImage, gst_camerabin_image, GstBin, GST_TYPE_BIN);
+
+static const GstElementDetails gst_camerabin_image_details =
+GST_ELEMENT_DETAILS ("Image capture bin for camerabin",
+    "Bin/Image",
+    "Process and store image data",
+    "Edgard Lima <edgard.lima@indt.org.br>\n"
+    "Nokia Corporation <multimedia@maemo.org>");
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+static void
+gst_camerabin_image_base_init (gpointer klass)
+{
+  GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (eklass,
+      gst_static_pad_template_get (&sink_template));
+  gst_element_class_add_pad_template (eklass,
+      gst_static_pad_template_get (&src_template));
+  gst_element_class_set_details (eklass, &gst_camerabin_image_details);
+}
+
+static void
+gst_camerabin_image_class_init (GstCameraBinImageClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->dispose =
+      (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_camerabin_image_dispose);
+  eklass->change_state = GST_DEBUG_FUNCPTR (gst_camerabin_image_change_state);
+  eklass->send_event = GST_DEBUG_FUNCPTR (gst_camerabin_image_send_event);
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_camerabin_image_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_camerabin_image_get_property);
+
+  /**
+   * GstCameraBinImage:filename
+   *
+   * This property can be used to specify the filename of the image.
+   *
+   **/
+  g_object_class_install_property (gobject_class, PROP_FILENAME,
+      g_param_spec_string ("filename", "Filename",
+          "Filename of the image to save", NULL, G_PARAM_READWRITE));
+}
+
+static void
+gst_camerabin_image_init (GstCameraBinImage * img,
+    GstCameraBinImageClass * g_class)
+{
+  img->filename = g_string_new ("");
+
+  img->pad_tee_enc = NULL;
+  img->pad_tee_view = NULL;
+
+  img->post = NULL;
+  img->tee = NULL;
+  img->enc = NULL;
+  img->user_enc = NULL;
+  img->meta_mux = NULL;
+  img->sink = NULL;
+  img->queue = NULL;
+
+  /* Create src and sink ghost pads */
+  img->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
+  gst_element_add_pad (GST_ELEMENT (img), img->sinkpad);
+
+  img->srcpad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
+  gst_element_add_pad (GST_ELEMENT (img), img->srcpad);
+
+  img->elements_created = FALSE;
+}
+
+static void
+gst_camerabin_image_dispose (GstCameraBinImage * img)
+{
+  g_string_free (img->filename, TRUE);
+  img->filename = NULL;
+
+  if (img->user_enc) {
+    gst_object_unref (img->user_enc);
+    img->user_enc = NULL;
+  }
+
+  if (img->post) {
+    gst_object_unref (img->post);
+    img->post = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose ((GObject *) img);
+}
+
+static GstStateChangeReturn
+gst_camerabin_image_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  GstCameraBinImage *img = GST_CAMERABIN_IMAGE (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      if (!gst_camerabin_image_create_elements (img)) {
+        return GST_STATE_CHANGE_FAILURE;
+      }
+      /* Allow setting filename when image bin in READY state */
+      gst_element_set_locked_state (img->sink, TRUE);
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      gst_element_set_locked_state (img->sink, FALSE);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* Set sink to NULL in order to write the file _now_ */
+      GST_INFO ("write img file: %s", img->filename->str);
+      gst_element_set_locked_state (img->sink, TRUE);
+      gst_element_set_state (img->sink, GST_STATE_NULL);
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      gst_camerabin_image_destroy_elements (img);
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+gboolean
+gst_camerabin_image_send_event (GstElement * element, GstEvent * event)
+{
+  GstCameraBinImage *bin = GST_CAMERABIN_IMAGE (element);
+  gboolean ret = FALSE;
+
+  GST_INFO ("got %s event", GST_EVENT_TYPE_NAME (event));
+
+  if (GST_EVENT_IS_DOWNSTREAM (event)) {
+    ret = gst_pad_send_event (bin->sinkpad, event);
+  } else {
+    if (bin->sink) {
+      ret = gst_element_send_event (bin->sink, event);
+    } else {
+      GST_WARNING ("upstream event handling failed");
+    }
+  }
+
+  return ret;
+}
+
+static void
+gst_camerabin_image_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstCameraBinImage *bin = GST_CAMERABIN_IMAGE (object);
+
+  switch (prop_id) {
+    case PROP_FILENAME:
+      g_string_assign (bin->filename, g_value_get_string (value));
+      if (bin->sink) {
+        g_object_set (G_OBJECT (bin->sink), "location", bin->filename->str,
+            NULL);
+      } else {
+        GST_INFO ("no sink, not setting name yet");
+      }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_camerabin_image_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstCameraBinImage *bin = GST_CAMERABIN_IMAGE (object);
+
+  switch (prop_id) {
+    case PROP_FILENAME:
+      g_value_set_string (value, bin->filename->str);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/*
+ * static helper functions implementation
+ */
+
+/**
+ * metadata_write_probe:
+ * @pad: sink pad of metadata muxer
+ * @buffer: received buffer
+ * @u_data: image bin object
+ *
+ * Buffer probe that sets Xmp.dc.type and Xmp.dc.format tags
+ * to metadata muxer based on preceding element src pad caps.
+ *
+ * Returns: TRUE always
+ */
+static gboolean
+metadata_write_probe (GstPad * pad, GstBuffer * buffer, gpointer u_data)
+{
+  /* Add XMP tags */
+  GstCameraBinImage *img = NULL;
+  GstTagSetter *setter = NULL;
+  GstPad *srcpad = NULL;
+  GstCaps *caps = NULL;
+  GstStructure *st = NULL;
+
+  img = GST_CAMERABIN_IMAGE (u_data);
+
+  g_return_val_if_fail (img != NULL, TRUE);
+
+  setter = GST_TAG_SETTER (img->meta_mux);
+
+  if (!setter) {
+    GST_WARNING_OBJECT (img, "setting tags failed");
+    goto done;
+  }
+
+  /* Xmp.dc.type tag */
+  gst_tag_setter_add_tags (setter, GST_TAG_MERGE_REPLACE,
+      GST_TAG_CODEC, "Image", NULL);
+  /* Xmp.dc.format tag */
+  if (img->enc) {
+    srcpad = gst_element_get_static_pad (img->enc, "src");
+  }
+  GST_LOG_OBJECT (img, "srcpad:%" GST_PTR_FORMAT, srcpad);
+  if (srcpad) {
+    caps = gst_pad_get_negotiated_caps (srcpad);
+    GST_LOG_OBJECT (img, "caps:%" GST_PTR_FORMAT, caps);
+    if (caps) {
+      /* If there are many structures, we can't know which one to use */
+      if (gst_caps_get_size (caps) != 1) {
+        GST_WARNING_OBJECT (img, "can't decide structure for format tag");
+        goto done;
+      }
+      st = gst_caps_get_structure (caps, 0);
+      if (st) {
+        GST_DEBUG_OBJECT (img, "Xmp.dc.format:%s", gst_structure_get_name (st));
+        gst_tag_setter_add_tags (setter, GST_TAG_MERGE_REPLACE,
+            GST_TAG_VIDEO_CODEC, gst_structure_get_name (st), NULL);
+      }
+    }
+  }
+done:
+  if (caps)
+    gst_caps_unref (caps);
+  if (srcpad)
+    gst_object_unref (srcpad);
+
+  return TRUE;
+}
+
+
+/**
+ * gst_camerabin_image_create_elements:
+ * @img: a pointer to #GstCameraBinImage object
+ *
+ * This function creates needed #GstElements and resources to capture images.
+ * Use gst_camerabin_image_destroy_elements to release these resources.
+ *
+ * Image bin:
+ *  img->sinkpad ! [ post process !] tee name=t0 ! encoder ! metadata ! filesink
+ *   t0. ! queue ! img->srcpad
+ *
+ * Returns: %TRUE if succeeded or FALSE if failed
+ */
+static gboolean
+gst_camerabin_image_create_elements (GstCameraBinImage * img)
+{
+  GstPad *sinkpad = NULL, *img_sinkpad = NULL, *img_srcpad = NULL;
+  gboolean ret = FALSE;
+  GstBin *imgbin = NULL;
+
+  g_return_val_if_fail (img != NULL, FALSE);
+
+  GST_DEBUG ("creating image capture elements");
+
+  imgbin = GST_BIN (img);
+
+  if (img->elements_created) {
+    GST_WARNING ("elements already created");
+    ret = TRUE;
+    goto done;
+  } else {
+    img->elements_created = TRUE;
+  }
+
+  /* Create image pre/post-processing element if any */
+  if (img->post) {
+    if (!gst_camerabin_add_element (imgbin, img->post)) {
+      goto done;
+    }
+    img_sinkpad = gst_element_get_static_pad (img->post, "sink");
+  }
+
+  /* Create tee */
+  if (!(img->tee = gst_camerabin_create_and_add_element (imgbin, "tee"))) {
+    goto done;
+  }
+
+  /* Set up sink ghost pad for img bin */
+  if (!img_sinkpad) {
+    img_sinkpad = gst_element_get_static_pad (img->tee, "sink");
+  }
+  gst_ghost_pad_set_target (GST_GHOST_PAD (img->sinkpad), img_sinkpad);
+
+  /* Add colorspace converter */
+  img->pad_tee_enc = gst_element_get_request_pad (img->tee, "src%d");
+  if (!gst_camerabin_create_and_add_element (imgbin, "ffmpegcolorspace")) {
+    goto done;
+  }
+
+  /* Create image encoder */
+  if (img->user_enc) {
+    img->enc = img->user_enc;
+    if (!gst_camerabin_add_element (imgbin, img->enc)) {
+      goto done;
+    }
+  } else if (!(img->enc =
+          gst_camerabin_create_and_add_element (imgbin, DEFAULT_ENC))) {
+    goto done;
+  }
+
+  /* Create metadata element */
+  if (!(img->meta_mux =
+          gst_camerabin_create_and_add_element (imgbin, DEFAULT_META_MUX))) {
+    goto done;
+  }
+  /* Add probe for XMP metadata writing */
+  sinkpad = gst_element_get_static_pad (img->meta_mux, "sink");
+  gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (metadata_write_probe), img);
+  gst_object_unref (sinkpad);
+  /* Set "Intel" exif byte-order if possible */
+  if (g_object_class_find_property (G_OBJECT_GET_CLASS (img->meta_mux),
+          "exif-byte-order")) {
+    g_object_set (G_OBJECT (img->meta_mux), "exif-byte-order", 1, NULL);
+  }
+
+  /* Create file sink element */
+  if (!(img->sink =
+          gst_camerabin_create_and_add_element (imgbin, DEFAULT_SINK))) {
+    goto done;
+  }
+
+  /* Create queue element leading to view finder, attaches it to the tee */
+  img->pad_tee_view = gst_element_get_request_pad (img->tee, "src%d");
+  if (!(img->queue = gst_camerabin_create_and_add_element (imgbin, "queue"))) {
+    goto done;
+  }
+
+  /* Set properties */
+  g_object_set (G_OBJECT (img->sink), "location", img->filename->str, NULL);
+  g_object_set (G_OBJECT (img->sink), "async", FALSE, NULL);
+
+  g_object_set (G_OBJECT (img->queue), "max-size-buffers", 1, "leaky", 2, NULL);
+
+  /* Set up src ghost pad for img bin */
+  img_srcpad = gst_element_get_static_pad (img->queue, "src");
+  gst_ghost_pad_set_target (GST_GHOST_PAD (img->srcpad), img_srcpad);
+
+  /* Never let image bin eos events reach view finder */
+  gst_pad_add_event_probe (img->srcpad,
+      G_CALLBACK (gst_camerabin_drop_eos_probe), img);
+
+  ret = TRUE;
+
+done:
+
+  if (img_srcpad) {
+    gst_object_unref (img_srcpad);
+  }
+  if (img_sinkpad) {
+    gst_object_unref (img_sinkpad);
+  }
+  if (!ret) {
+    gst_camerabin_image_destroy_elements (img);
+  }
+
+  return ret;
+}
+
+
+/**
+ * gst_camerabin_image_destroy_elements:
+ * @img: a pointer to #GstCameraBinImage object
+ *
+ * This function releases resources allocated in
+ * gst_camerabin_image_create_elements.
+ *
+ */
+static void
+gst_camerabin_image_destroy_elements (GstCameraBinImage * img)
+{
+  GST_LOG ("destroying img elements");
+  if (img->pad_tee_enc) {
+    gst_element_release_request_pad (img->tee, img->pad_tee_enc);
+    img->pad_tee_enc = NULL;
+  }
+
+  if (img->pad_tee_view) {
+    gst_element_release_request_pad (img->tee, img->pad_tee_view);
+    img->pad_tee_view = NULL;
+  }
+
+  gst_ghost_pad_set_target (GST_GHOST_PAD (img->sinkpad), NULL);
+  gst_ghost_pad_set_target (GST_GHOST_PAD (img->srcpad), NULL);
+
+  gst_camerabin_remove_elements_from_bin (GST_BIN (img));
+
+  img->post = NULL;
+  img->tee = NULL;
+  img->enc = NULL;
+  img->meta_mux = NULL;
+  img->sink = NULL;
+  img->queue = NULL;
+
+  img->elements_created = FALSE;
+}
+
+void
+gst_camerabin_image_set_encoder (GstCameraBinImage * img, GstElement * encoder)
+{
+  if (img->user_enc)
+    gst_object_unref (img->user_enc);
+  if (encoder)
+    gst_object_ref (encoder);
+
+  img->user_enc = encoder;
+}
+
+void
+gst_camerabin_image_set_postproc (GstCameraBinImage * img,
+    GstElement * postproc)
+{
+  if (img->post)
+    gst_object_unref (img->post);
+  if (postproc)
+    gst_object_ref (postproc);
+
+  img->post = postproc;
+}
+
+GstElement *
+gst_camerabin_image_get_encoder (GstCameraBinImage * img)
+{
+  GstElement *enc;
+
+  if (img->user_enc) {
+    enc = img->user_enc;
+  } else {
+    enc = img->enc;
+  }
+
+  return enc;
+}
+
+GstElement *
+gst_camerabin_image_get_postproc (GstCameraBinImage * img)
+{
+  return img->post;
+}
diff --git a/gst/camerabin/camerabinimage.h b/gst/camerabin/camerabinimage.h
new file mode 100644 (file)
index 0000000..c05f549
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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_IMAGE_H__
+#define __CAMERABIN_IMAGE_H__
+
+#include <gst/gstbin.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_CAMERABIN_IMAGE             (gst_camerabin_image_get_type())
+#define GST_CAMERABIN_IMAGE_CAST(obj)        ((GstCameraBinImage*)(obj))
+#define GST_CAMERABIN_IMAGE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAMERABIN_IMAGE,GstCameraBinImage))
+#define GST_CAMERABIN_IMAGE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAMERABIN_IMAGE,GstCameraBinImageClass))
+#define GST_IS_CAMERABIN_IMAGE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAMERABIN_IMAGE))
+#define GST_IS_CAMERABIN_IMAGE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAMERABIN_IMAGE))
+
+/**
+ * GstCameraBinImage:
+ *
+ * The opaque #GstCameraBinImage structure.
+ */
+
+typedef struct _GstCameraBinImage GstCameraBinImage;
+typedef struct _GstCameraBinImageClass GstCameraBinImageClass;
+
+struct _GstCameraBinImage
+{
+  GstBin parent;
+  GString *filename;
+
+  /* Ghost pads of image bin */
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  /* Tee src pad leading to image encoder */
+  GstPad *pad_tee_enc;
+  /* Tee src pad leading to view finder */
+  GstPad *pad_tee_view;
+
+  GstElement *post;
+
+  GstElement *tee;
+  GstElement *enc;
+  GstElement *user_enc;
+  GstElement *meta_mux;
+  GstElement *sink;
+  GstElement *queue;
+
+  gboolean elements_created;
+};
+
+struct _GstCameraBinImageClass
+{
+  GstBinClass parent_class;
+};
+
+GType gst_camerabin_image_get_type (void);
+
+void
+gst_camerabin_image_set_encoder (GstCameraBinImage * img, GstElement * encoder);
+
+void
+gst_camerabin_image_set_postproc (GstCameraBinImage * img,
+    GstElement * postproc);
+
+GstElement *gst_camerabin_image_get_encoder (GstCameraBinImage * img);
+
+GstElement *gst_camerabin_image_get_postproc (GstCameraBinImage * img);
+
+G_END_DECLS
+
+#endif /* #ifndef __CAMERABIN_IMAGE_H__ */
diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c
new file mode 100644 (file)
index 0000000..14a9506
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ * 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:camerabinvideo
+ * @short_description: video recording module of #GstCameraBin
+ *
+ * <refsect2>
+ * <para>
+ *
+ * The pipeline for this module is:
+ *
+ * <informalexample>
+ * <programlisting>
+ *-----------------------------------------------------------------------------
+ * audiosrc -> audio_queue -> audioconvert -> volume -> audioenc
+ *                                                       > videomux -> filesink
+ *                       video_queue -> [timeoverlay] -> videoenc
+ * -> [post proc] -> tee <
+ *                       queue ->
+ *-----------------------------------------------------------------------------
+ * </programlisting>
+ * </informalexample>
+ *
+ * The properties of elements are:
+ *
+ * queue - "leaky", 2 (Leaky on downstream (old buffers))
+ *
+ * </para>
+ * </refsect2>
+ */
+
+/*
+ * includes
+ */
+
+
+#include <gst/gst.h>
+#include "camerabingeneral.h"
+
+#include "camerabinvideo.h"
+
+/*
+ * defines and static global vars
+ */
+
+/* internal element names */
+
+#define DEFAULT_AUD_SRC "pulsesrc"
+#define DEFAULT_AUD_ENC "vorbisenc"
+#define DEFAULT_VID_ENC "theoraenc"
+#define DEFAULT_MUX "oggmux"
+#define DEFAULT_SINK "filesink"
+
+#define USE_AUDIO_CONVERSION 1
+
+enum
+{
+  PROP_0,
+  PROP_FILENAME
+};
+
+static void gst_camerabin_video_dispose (GstCameraBinVideo * sink);
+static void gst_camerabin_video_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_camerabin_video_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static GstClock *gst_camerabin_video_provide_clock (GstElement * elem);
+static GstStateChangeReturn
+gst_camerabin_video_change_state (GstElement * element,
+    GstStateChange transition);
+
+static
+    gboolean camerabin_video_pad_tee_src0_have_buffer (GstPad * pad,
+    GstBuffer * buffer, gpointer u_data);
+static gboolean camerabin_video_pad_aud_src_have_buffer (GstPad * pad,
+    GstBuffer * buffer, gpointer u_data);
+static gboolean camerabin_video_sink_have_event (GstPad * pad, GstEvent * event,
+    gpointer u_data);
+static gboolean gst_camerabin_video_create_elements (GstCameraBinVideo * vid);
+static void gst_camerabin_video_destroy_elements (GstCameraBinVideo * vid);
+
+GST_BOILERPLATE (GstCameraBinVideo, gst_camerabin_video, GstBin, GST_TYPE_BIN);
+
+static const GstElementDetails gst_camerabin_video_details =
+GST_ELEMENT_DETAILS ("Video capture bin for camerabin",
+    "Bin/Video",
+    "Process and store video data",
+    "Edgard Lima <edgard.lima@indt.org.br>\n"
+    "Nokia Corporation <multimedia@maemo.org>");
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+
+/* GObject methods implementation */
+
+static void
+gst_camerabin_video_base_init (gpointer klass)
+{
+  GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (eklass,
+      gst_static_pad_template_get (&sink_template));
+  gst_element_class_add_pad_template (eklass,
+      gst_static_pad_template_get (&src_template));
+  gst_element_class_set_details (eklass, &gst_camerabin_video_details);
+}
+
+static void
+gst_camerabin_video_class_init (GstCameraBinVideoClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->dispose =
+      (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_camerabin_video_dispose);
+  eklass->change_state = GST_DEBUG_FUNCPTR (gst_camerabin_video_change_state);
+
+  eklass->provide_clock = GST_DEBUG_FUNCPTR (gst_camerabin_video_provide_clock);
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_camerabin_video_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_camerabin_video_get_property);
+
+  /**
+   * GstCameraBinVideo:filename:
+   *
+   * This property can be used to specify the filename of the video.
+   *
+   **/
+  g_object_class_install_property (gobject_class, PROP_FILENAME,
+      g_param_spec_string ("filename", "Filename",
+          "Filename of the video to save", NULL, G_PARAM_READWRITE));
+}
+
+static void
+gst_camerabin_video_init (GstCameraBinVideo * vid,
+    GstCameraBinVideoClass * g_class)
+{
+  vid->filename = g_string_new ("");
+
+  vid->user_post = NULL;
+  vid->user_vid_enc = NULL;
+  vid->user_aud_enc = NULL;
+  vid->user_aud_src = NULL;
+  vid->user_mux = NULL;
+
+  vid->aud_src = NULL;
+  vid->sink = NULL;
+  vid->tee = NULL;
+  vid->volume = NULL;
+  vid->video_queue = NULL;
+
+  vid->tee_video_srcpad = NULL;
+  vid->tee_vf_srcpad = NULL;
+
+  vid->pending_eos = NULL;
+
+  /* Create src and sink ghost pads */
+  vid->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
+  gst_element_add_pad (GST_ELEMENT (vid), vid->sinkpad);
+
+  vid->srcpad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
+  gst_element_add_pad (GST_ELEMENT (vid), vid->srcpad);
+
+  /* Add probe for handling eos when stopping recording */
+  gst_pad_add_event_probe (vid->sinkpad,
+      G_CALLBACK (camerabin_video_sink_have_event), vid);
+}
+
+static void
+gst_camerabin_video_dispose (GstCameraBinVideo * vid)
+{
+  GST_DEBUG_OBJECT (vid, "disposing");
+
+  g_string_free (vid->filename, TRUE);
+  vid->filename = NULL;
+
+  if (vid->user_post) {
+    gst_object_unref (vid->user_post);
+    vid->user_post = NULL;
+  }
+
+  if (vid->user_vid_enc) {
+    gst_object_unref (vid->user_vid_enc);
+    vid->user_vid_enc = NULL;
+  }
+
+  if (vid->user_aud_enc) {
+    gst_object_unref (vid->user_aud_enc);
+    vid->user_aud_enc = NULL;
+  }
+
+  if (vid->user_aud_src) {
+    gst_object_unref (vid->user_aud_src);
+    vid->user_aud_src = NULL;
+  }
+
+  if (vid->user_mux) {
+    gst_object_unref (vid->user_mux);
+    vid->user_mux = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose ((GObject *) vid);
+}
+
+
+static void
+gst_camerabin_video_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstCameraBinVideo *bin = GST_CAMERABIN_VIDEO (object);
+
+  switch (prop_id) {
+    case PROP_FILENAME:
+      g_string_assign (bin->filename, g_value_get_string (value));
+      if (bin->sink) {
+        g_object_set (G_OBJECT (bin->sink), "location", bin->filename->str,
+            NULL);
+      } else {
+        GST_INFO ("no sink, not setting name yet");
+      }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_camerabin_video_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstCameraBinVideo *bin = GST_CAMERABIN_VIDEO (object);
+
+  switch (prop_id) {
+    case PROP_FILENAME:
+      g_value_set_string (value, bin->filename->str);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/* GstElement methods implementation */
+
+static GstClock *
+gst_camerabin_video_provide_clock (GstElement * elem)
+{
+  GstElement *aud_src = GST_CAMERABIN_VIDEO (elem)->aud_src;
+  if (aud_src) {
+    return gst_element_provide_clock (aud_src);
+  } else {
+    return NULL;
+  }
+}
+
+static GstStateChangeReturn
+gst_camerabin_video_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  GstCameraBinVideo *vid = GST_CAMERABIN_VIDEO (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      if (!gst_camerabin_video_create_elements (vid)) {
+        return GST_STATE_CHANGE_FAILURE;
+      }
+      /* Don't change sink to READY yet to allow changing the
+         filename in READY state. */
+      gst_element_set_locked_state (vid->sink, TRUE);
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      vid->calculate_adjust_ts_video = TRUE;
+      vid->calculate_adjust_ts_aud = TRUE;
+      g_object_set (G_OBJECT (vid->sink), "async", FALSE, NULL);
+      gst_element_set_locked_state (vid->sink, FALSE);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      vid->calculate_adjust_ts_video = TRUE;
+      vid->calculate_adjust_ts_aud = TRUE;
+      break;
+
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* Set sink to NULL in order to write the file _now_ */
+      GST_INFO ("write vid file: %s", vid->filename->str);
+      gst_element_set_locked_state (vid->sink, TRUE);
+      gst_element_set_state (vid->sink, GST_STATE_NULL);
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      if (vid->pending_eos) {
+        /* Video bin is still paused, so push eos directly to video queue */
+        GST_DEBUG_OBJECT (vid, "pushing pending eos");
+        gst_pad_push_event (vid->tee_video_srcpad, vid->pending_eos);
+        vid->pending_eos = NULL;
+      }
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* Reset counters related to timestamp rewriting */
+      vid->adjust_ts_video = 0;
+      vid->last_ts_video = 0;
+      vid->adjust_ts_aud = 0;
+      vid->last_ts_aud = 0;
+
+      if (vid->pending_eos) {
+        gst_event_unref (vid->pending_eos);
+        vid->pending_eos = NULL;
+      }
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      gst_camerabin_video_destroy_elements (vid);
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+/*
+ * static helper functions implementation
+ */
+
+/**
+ * camerabin_video_pad_tee_src0_have_buffer:
+ * @pad: tee src pad leading to video encoding
+ * @event: received buffer
+ * @u_data: video bin object
+ *
+ * Buffer probe for rewriting video buffer timestamps.
+ *
+ * Returns: TRUE always
+ */
+static gboolean
+camerabin_video_pad_tee_src0_have_buffer (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data)
+{
+  GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data;
+
+  GST_LOG ("buffer in with size %d ts %" GST_TIME_FORMAT,
+      GST_BUFFER_SIZE (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+
+  if (G_UNLIKELY (vid->calculate_adjust_ts_video)) {
+    GstEvent *event;
+    GstObject *tee;
+    GstPad *sinkpad;
+    vid->adjust_ts_video = GST_BUFFER_TIMESTAMP (buffer) - vid->last_ts_video;
+    vid->calculate_adjust_ts_video = FALSE;
+    event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+        0, GST_CLOCK_TIME_NONE, vid->last_ts_video);
+    /* Send the newsegment to both view finder and video bin */
+    tee = gst_pad_get_parent (pad);
+    sinkpad = gst_element_get_static_pad (GST_ELEMENT (tee), "sink");
+    gst_pad_send_event (sinkpad, event);
+    gst_object_unref (tee);
+    gst_object_unref (sinkpad);
+    GST_LOG_OBJECT (vid, "vid ts adjustment: %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (vid->adjust_ts_video));
+    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+  }
+  GST_BUFFER_TIMESTAMP (buffer) -= vid->adjust_ts_video;
+  vid->last_ts_video = GST_BUFFER_TIMESTAMP (buffer);
+  if (GST_BUFFER_DURATION_IS_VALID (buffer))
+    vid->last_ts_video += GST_BUFFER_DURATION (buffer);
+
+
+  GST_LOG ("buffer out with size %d ts %" GST_TIME_FORMAT,
+      GST_BUFFER_SIZE (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+  return TRUE;
+}
+
+/**
+ * camerabin_video_pad_aud_src_have_buffer:
+ * @pad: audio source src pad
+ * @event: received buffer
+ * @u_data: video bin object
+ *
+ * Buffer probe for rewriting audio buffer timestamps.
+ *
+ * Returns: TRUE always
+ */
+static gboolean
+camerabin_video_pad_aud_src_have_buffer (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data)
+{
+  GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data;
+
+  if (vid->calculate_adjust_ts_aud) {
+    GstEvent *event;
+    GstPad *peerpad = NULL;
+    vid->adjust_ts_aud = GST_BUFFER_TIMESTAMP (buffer) - vid->last_ts_aud;
+    vid->calculate_adjust_ts_aud = FALSE;
+    event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+        0, GST_CLOCK_TIME_NONE, vid->last_ts_aud);
+    peerpad = gst_pad_get_peer (pad);
+    if (peerpad) {
+      gst_pad_send_event (peerpad, event);
+      gst_object_unref (peerpad);
+    }
+    GST_LOG_OBJECT (vid, "aud ts adjustment: %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (vid->adjust_ts_aud));
+    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+  }
+  GST_BUFFER_TIMESTAMP (buffer) -= vid->adjust_ts_aud;
+  vid->last_ts_aud = GST_BUFFER_TIMESTAMP (buffer);
+  if (GST_BUFFER_DURATION_IS_VALID (buffer))
+    vid->last_ts_aud += GST_BUFFER_DURATION (buffer);
+  GST_LOG ("buffer out with size %d ts %" GST_TIME_FORMAT,
+      GST_BUFFER_SIZE (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+  return TRUE;
+}
+
+/**
+ * camerabin_video_sink_have_event:
+ * @pad: video bin sink pad
+ * @event: received event
+ * @u_data: video bin object
+ *
+ * Event probe for video bin eos handling.
+ * Copies the eos event to audio branch of video bin.
+ *
+ * Returns: FALSE to drop the event, TRUE otherwise
+ */
+static gboolean
+camerabin_video_sink_have_event (GstPad * pad, GstEvent * event,
+    gpointer u_data)
+{
+  GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data;
+  gboolean ret = TRUE;
+
+  GST_DEBUG_OBJECT (vid, "got videobin sink event: %s",
+      GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      if (vid->aud_src) {
+        GST_DEBUG_OBJECT (vid, "copying %s to audio branch",
+            GST_EVENT_TYPE_NAME (event));
+        gst_element_send_event (vid->aud_src, gst_event_copy (event));
+      }
+
+      /* If we're paused, we can't pass eos to video now to avoid blocking.
+         Instead send eos when changing to playing next time. */
+      if (GST_STATE (GST_ELEMENT (vid)) == GST_STATE_PAUSED) {
+        GST_DEBUG_OBJECT (vid, "paused, delay eos sending");
+        vid->pending_eos = gst_event_ref (event);
+        ret = FALSE;            /* Drop the event */
+      }
+      break;
+    default:
+      break;
+  }
+  return ret;
+}
+
+/**
+ * gst_camerabin_video_create_elements:
+ * @vid: a pointer to #GstCameraBinVideo
+ *
+ * This function creates the needed #GstElements and resources to record videos.
+ * Use gst_camerabin_video_destroy_elements() to free these resources.
+ *
+ * Returns: %TRUE if succeeded or FALSE if failed
+ */
+static gboolean
+gst_camerabin_video_create_elements (GstCameraBinVideo * vid)
+{
+  GstPad *pad = NULL, *vid_sinkpad = NULL, *vid_srcpad = NULL;
+  GstBin *vidbin = GST_BIN (vid);
+  GstElement *queue = NULL;
+
+  vid->adjust_ts_video = 0;
+  vid->last_ts_video = 0;
+  vid->calculate_adjust_ts_video = FALSE;
+
+  vid->adjust_ts_aud = 0;
+  vid->last_ts_aud = 0;
+  vid->calculate_adjust_ts_aud = FALSE;
+
+  /* Add video post processing element if any */
+  if (vid->user_post) {
+    if (!gst_camerabin_add_element (vidbin, vid->user_post)) {
+      goto error;
+    }
+    vid_sinkpad = gst_element_get_static_pad (vid->user_post, "sink");
+  }
+
+  /* Add tee element */
+  if (!(vid->tee = gst_camerabin_create_and_add_element (vidbin, "tee"))) {
+    goto error;
+  }
+
+  /* Set up sink ghost pad for video bin */
+  if (!vid_sinkpad) {
+    vid_sinkpad = gst_element_get_static_pad (vid->tee, "sink");
+  }
+  gst_ghost_pad_set_target (GST_GHOST_PAD (vid->sinkpad), vid_sinkpad);
+
+
+  /* Add queue element for video */
+  vid->tee_video_srcpad = gst_element_get_request_pad (vid->tee, "src%d");
+  if (!(vid->video_queue =
+          gst_camerabin_create_and_add_element (vidbin, "queue"))) {
+    goto error;
+  }
+
+  /* Add probe for rewriting video timestamps */
+  gst_pad_add_buffer_probe (vid->tee_video_srcpad,
+      G_CALLBACK (camerabin_video_pad_tee_src0_have_buffer), vid);
+
+
+#ifdef USE_TIMEOVERLAY
+  /* Add timeoverlay element to visualize elapsed time for debugging */
+  if (!(gst_camerabin_create_and_add_element (vidbin, "timeoverlay"))) {
+    goto error;
+  }
+#endif
+
+  /* Add user set or default video encoder element */
+  if (vid->user_vid_enc) {
+    vid->vid_enc = vid->user_vid_enc;
+    if (!gst_camerabin_add_element (vidbin, vid->vid_enc)) {
+      goto error;
+    }
+  } else if (!(vid->vid_enc =
+          gst_camerabin_create_and_add_element (vidbin, DEFAULT_VID_ENC))) {
+    goto error;
+  }
+
+  /* Add user set or default muxer element */
+  if (vid->user_mux) {
+    vid->muxer = vid->user_mux;
+    if (!gst_camerabin_add_element (vidbin, vid->muxer)) {
+      goto error;
+    }
+  } else if (!(vid->muxer =
+          gst_camerabin_create_and_add_element (vidbin, DEFAULT_MUX))) {
+    goto error;
+  }
+
+  /* Add sink element for storing the video */
+  if (!(vid->sink =
+          gst_camerabin_create_and_add_element (vidbin, DEFAULT_SINK))) {
+    goto error;
+  }
+  g_object_set (G_OBJECT (vid->sink), "location", vid->filename->str, NULL);
+
+
+  /* Add user set or default audio source element */
+  if (vid->user_aud_src) {
+    vid->aud_src = vid->user_aud_src;
+    if (!gst_camerabin_add_element (vidbin, vid->aud_src)) {
+      goto error;
+    }
+  } else if (!(vid->aud_src =
+          gst_camerabin_create_and_add_element (vidbin, DEFAULT_AUD_SRC))) {
+    goto error;
+  }
+
+  /* Add queue element for audio */
+  if (!(queue = gst_camerabin_create_and_add_element (vidbin, "queue"))) {
+    goto error;
+  }
+  queue = NULL;
+
+  /* Add optional audio conversion and volume elements and
+     raise no errors if adding them fails */
+#ifdef USE_AUDIO_CONVERSION
+  if (!gst_camerabin_try_add_element (vidbin,
+          gst_element_factory_make ("audioconvert", NULL))) {
+    GST_WARNING_OBJECT (vid, "unable to add audio conversion element");
+    /* gst_camerabin_try_add_element() destroyed the element */
+  }
+#endif
+  vid->volume = gst_element_factory_make ("volume", NULL);
+  if (!gst_camerabin_try_add_element (vidbin, vid->volume)) {
+    GST_WARNING_OBJECT (vid, "unable to add volume element");
+    /* gst_camerabin_try_add_element() destroyed the element */
+    vid->volume = NULL;
+  }
+
+  /* Add user set or default audio encoder element */
+  if (vid->user_aud_enc) {
+    vid->aud_enc = vid->user_aud_enc;
+    if (!gst_camerabin_add_element (vidbin, vid->aud_enc)) {
+      goto error;
+    }
+  } else if (!(vid->aud_enc =
+          gst_camerabin_create_and_add_element (vidbin, DEFAULT_AUD_ENC))) {
+    goto error;
+  }
+
+  /* Link audio part to the muxer */
+  if (!gst_element_link (vid->aud_enc, vid->muxer)) {
+    GST_ELEMENT_ERROR (vid, CORE, NEGOTIATION, (NULL),
+        ("linking audio encoder and muxer failed"));
+    goto error;
+  }
+
+  /* Add queue leading out of the video bin and to view finder */
+  vid->tee_vf_srcpad = gst_element_get_request_pad (vid->tee, "src%d");
+  if (!(queue = gst_camerabin_create_and_add_element (vidbin, "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, NULL);
+
+  /* Set up src ghost pad for video bin */
+  vid_srcpad = gst_element_get_static_pad (queue, "src");
+  gst_ghost_pad_set_target (GST_GHOST_PAD (vid->srcpad), vid_srcpad);
+  /* Never let video bin eos events reach view finder */
+  gst_pad_add_event_probe (vid_srcpad,
+      G_CALLBACK (gst_camerabin_drop_eos_probe), vid);
+
+  pad = gst_element_get_static_pad (vid->aud_src, "src");
+  gst_pad_add_buffer_probe (pad,
+      G_CALLBACK (camerabin_video_pad_aud_src_have_buffer), vid);
+  gst_object_unref (pad);
+
+  GST_DEBUG ("created video elements");
+
+  return TRUE;
+
+error:
+
+  gst_camerabin_video_destroy_elements (vid);
+
+  return FALSE;
+
+}
+
+/**
+ * gst_camerabin_video_destroy_elements:
+ * @vid: a pointer to #GstCameraBinVideo
+ *
+ * This function destroys all the elements created by
+ * gst_camerabin_video_create_elements().
+ *
+ */
+static void
+gst_camerabin_video_destroy_elements (GstCameraBinVideo * vid)
+{
+  GST_DEBUG ("destroying video elements");
+
+  /* Release tee request pads */
+  if (vid->tee_video_srcpad) {
+    gst_element_release_request_pad (vid->tee, vid->tee_video_srcpad);
+    vid->tee_video_srcpad = NULL;
+  }
+  if (vid->tee_vf_srcpad) {
+    gst_element_release_request_pad (vid->tee, vid->tee_vf_srcpad);
+    vid->tee_vf_srcpad = NULL;
+  }
+
+  gst_ghost_pad_set_target (GST_GHOST_PAD (vid->sinkpad), NULL);
+  gst_ghost_pad_set_target (GST_GHOST_PAD (vid->srcpad), NULL);
+
+  gst_camerabin_remove_elements_from_bin (GST_BIN (vid));
+
+  vid->aud_src = NULL;
+  vid->sink = NULL;
+  vid->tee = NULL;
+  vid->volume = NULL;
+  vid->video_queue = NULL;
+  vid->vid_enc = NULL;
+  vid->aud_enc = NULL;
+  vid->muxer = NULL;
+
+  if (vid->pending_eos) {
+    gst_event_unref (vid->pending_eos);
+    vid->pending_eos = NULL;
+  }
+
+  return;
+}
+
+/*
+ * Set & get mute and video capture elements
+ */
+
+void
+gst_camerabin_video_set_mute (GstCameraBinVideo * vid, gboolean mute)
+{
+  if (vid && vid->volume) {
+    GST_DEBUG_OBJECT (vid, "setting mute %s", mute ? "on" : "off");
+    g_object_set (vid->volume, "mute", mute, NULL);
+  }
+}
+
+void
+gst_camerabin_video_set_post (GstCameraBinVideo * vid, GstElement * post)
+{
+  GstElement **user_post;
+  GST_DEBUG_OBJECT (vid, "setting video post processing: %" GST_PTR_FORMAT,
+      post);
+  GST_OBJECT_LOCK (vid);
+  user_post = &vid->user_post;
+  gst_object_replace ((GstObject **) user_post, GST_OBJECT (post));
+  GST_OBJECT_UNLOCK (vid);
+}
+
+void
+gst_camerabin_video_set_video_enc (GstCameraBinVideo * vid,
+    GstElement * video_enc)
+{
+  GstElement **user_vid_enc;
+  GST_DEBUG_OBJECT (vid, "setting video encoder: %" GST_PTR_FORMAT, video_enc);
+  GST_OBJECT_LOCK (vid);
+  user_vid_enc = &vid->user_vid_enc;
+  gst_object_replace ((GstObject **) user_vid_enc, GST_OBJECT (video_enc));
+  GST_OBJECT_UNLOCK (vid);
+}
+
+void
+gst_camerabin_video_set_audio_enc (GstCameraBinVideo * vid,
+    GstElement * audio_enc)
+{
+  GstElement **user_aud_enc;
+  GST_DEBUG_OBJECT (vid, "setting audio encoder: %" GST_PTR_FORMAT, audio_enc);
+  GST_OBJECT_LOCK (vid);
+  user_aud_enc = &vid->user_aud_enc;
+  gst_object_replace ((GstObject **) user_aud_enc, GST_OBJECT (audio_enc));
+  GST_OBJECT_UNLOCK (vid);
+}
+
+void
+gst_camerabin_video_set_muxer (GstCameraBinVideo * vid, GstElement * muxer)
+{
+  GstElement **user_mux;
+  GST_DEBUG_OBJECT (vid, "setting muxer: %" GST_PTR_FORMAT, muxer);
+  GST_OBJECT_LOCK (vid);
+  user_mux = &vid->user_mux;
+  gst_object_replace ((GstObject **) user_mux, GST_OBJECT (muxer));
+  GST_OBJECT_UNLOCK (vid);
+}
+
+void
+gst_camerabin_video_set_audio_src (GstCameraBinVideo * vid,
+    GstElement * audio_src)
+{
+  GstElement **user_aud_src;
+  GST_DEBUG_OBJECT (vid, "setting audio source: %" GST_PTR_FORMAT, audio_src);
+  GST_OBJECT_LOCK (vid);
+  user_aud_src = &vid->user_aud_src;
+  gst_object_replace ((GstObject **) user_aud_src, GST_OBJECT (audio_src));
+  GST_OBJECT_UNLOCK (vid);
+}
+
+gboolean
+gst_camerabin_video_get_mute (GstCameraBinVideo * vid)
+{
+  gboolean mute = ARG_DEFAULT_MUTE;
+
+  if (vid && vid->volume) {
+    g_object_get (vid->volume, "mute", &mute, NULL);
+  }
+  return mute;
+}
+
+GstElement *
+gst_camerabin_video_get_post (GstCameraBinVideo * vid)
+{
+  return vid->user_post;
+}
+
+GstElement *
+gst_camerabin_video_get_video_enc (GstCameraBinVideo * vid)
+{
+  return vid->vid_enc ? vid->vid_enc : vid->user_vid_enc;
+}
+
+GstElement *
+gst_camerabin_video_get_audio_enc (GstCameraBinVideo * vid)
+{
+  return vid->aud_enc ? vid->aud_enc : vid->user_aud_enc;
+}
+
+GstElement *
+gst_camerabin_video_get_muxer (GstCameraBinVideo * vid)
+{
+  return vid->muxer ? vid->muxer : vid->user_mux;
+}
+
+GstElement *
+gst_camerabin_video_get_audio_src (GstCameraBinVideo * vid)
+{
+  return vid->aud_src ? vid->aud_src : vid->user_aud_src;
+}
diff --git a/gst/camerabin/camerabinvideo.h b/gst/camerabin/camerabinvideo.h
new file mode 100644 (file)
index 0000000..9f4f515
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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_VIDEO_H__
+#define __CAMERABIN_VIDEO_H__
+
+#include <gst/gstbin.h>
+
+G_BEGIN_DECLS
+
+//#define USE_TIMEOVERLAY 1
+
+#define ARG_DEFAULT_MUTE FALSE
+
+#define GST_TYPE_CAMERABIN_VIDEO             (gst_camerabin_video_get_type())
+#define GST_CAMERABIN_VIDEO_CAST(obj)        ((GstCameraBinVideo*)(obj))
+#define GST_CAMERABIN_VIDEO(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAMERABIN_VIDEO,GstCameraBinVideo))
+#define GST_CAMERABIN_VIDEO_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAMERABIN_VIDEO,GstCameraBinVideoClass))
+#define GST_IS_CAMERABIN_VIDEO(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAMERABIN_VIDEO))
+#define GST_IS_CAMERABIN_VIDEO_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAMERABIN_VIDEO))
+
+/**
+ * GstCameraBinVideo:
+ *
+ * The opaque #GstCameraBinVideo structure.
+ */
+
+typedef struct _GstCameraBinVideo GstCameraBinVideo;
+typedef struct _GstCameraBinVideoClass GstCameraBinVideoClass;
+
+struct _GstCameraBinVideo
+{
+  GstBin parent;
+
+  GString *filename;
+
+  /* A/V timestamp rewriting */
+  guint64 adjust_ts_video;
+  guint64 last_ts_video;
+  gboolean calculate_adjust_ts_video;
+
+  guint64 adjust_ts_aud;
+  guint64 last_ts_aud;
+  gboolean calculate_adjust_ts_aud;
+
+  /* Sink and src pads of video bin */
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  /* Tee src pads leading to video encoder and view finder */
+  GstPad *tee_video_srcpad;
+  GstPad *tee_vf_srcpad;
+
+  /* User set elements */
+  GstElement *user_post;        /* Video post processing */
+  GstElement *user_vid_enc;
+  GstElement *user_aud_enc;
+  GstElement *user_aud_src;
+  GstElement *user_mux;
+
+  /* Other elements */
+  GstElement *aud_src;          /* Audio source */
+  GstElement *sink;             /* Sink for recorded video */
+  GstElement *tee;              /* Split output to view finder and recording sink */
+  GstElement *volume;           /* Volume for muting */
+  GstElement *video_queue;      /* Buffer for raw video frames */
+  GstElement *vid_enc;          /* Video encoder */
+  GstElement *aud_enc;          /* Audio encoder */
+  GstElement *muxer;            /* Muxer */
+
+  GstEvent *pending_eos;
+};
+
+struct _GstCameraBinVideoClass
+{
+  GstBinClass parent_class;
+};
+
+GType gst_camerabin_video_get_type (void);
+
+/*
+ * external function prototypes
+ */
+
+void gst_camerabin_video_set_mute (GstCameraBinVideo * vid, gboolean mute);
+
+void gst_camerabin_video_set_post (GstCameraBinVideo * vid, GstElement * post);
+
+void
+gst_camerabin_video_set_video_enc (GstCameraBinVideo * vid,
+    GstElement * video_enc);
+
+void
+gst_camerabin_video_set_audio_enc (GstCameraBinVideo * vid,
+    GstElement * audio_enc);
+
+void
+gst_camerabin_video_set_muxer (GstCameraBinVideo * vid, GstElement * muxer);
+
+void
+gst_camerabin_video_set_audio_src (GstCameraBinVideo * vid,
+    GstElement * audio_src);
+
+
+gboolean gst_camerabin_video_get_mute (GstCameraBinVideo * vid);
+
+GstElement *gst_camerabin_video_get_post (GstCameraBinVideo * vid);
+
+GstElement *gst_camerabin_video_get_video_enc (GstCameraBinVideo * vid);
+
+GstElement *gst_camerabin_video_get_audio_enc (GstCameraBinVideo * vid);
+
+GstElement *gst_camerabin_video_get_muxer (GstCameraBinVideo * vid);
+
+GstElement *gst_camerabin_video_get_audio_src (GstCameraBinVideo * vid);
+
+G_END_DECLS
+
+#endif /* #ifndef __CAMERABIN_VIDEO_H__ */
diff --git a/gst/camerabin/gstcamerabin-marshal.list b/gst/camerabin/gstcamerabin-marshal.list
new file mode 100644 (file)
index 0000000..ef70f75
--- /dev/null
@@ -0,0 +1,6 @@
+# glib-genmarshal --header --prefix=gst_camerabin camerabin_marshal.marshal > camerabin_marshal.h
+# glib-genmarshal --body --prefix=gst_camerabin camerabin_marshal.marshal > camerabin_marshal.c
+
+VOID:INT,INT,INT,INT
+VOID:INT,INT
+
diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c
new file mode 100644 (file)
index 0000000..d3881f6
--- /dev/null
@@ -0,0 +1,2762 @@
+/*
+ * 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:gstcamerabin
+ * @short_description: camera capture bin
+ *
+ * <refsect2>
+ * <para>
+ * GstCameraBin is a high-level camera object that encapsulates the gstreamer
+ * internals and provides a task based API for the application. It consists of
+ * three main data paths: view-finder, image capture and video capture.
+ * </para>
+ * <informalfigure>
+ *   <mediaobject>
+ *     <imageobject><imagedata fileref="camerabin.png"/></imageobject>
+ *     <textobject><phrase>CameraBin structure</phrase></textobject>
+ *     <caption><para>Structural decomposition of CameraBin object.</para></caption>
+ *   </mediaobject>
+ * </informalfigure>
+ * </refsect2>
+ * <refsect2>
+ * <title>Example launch line</title>
+ * <para>
+ * <programlisting>
+ * gst-launch -v -m camerabin filename=test.jpeg
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Image capture</title>
+ * <para>
+ * Taking still images is initiated with the #GstCameraBin::user-start action
+ * signal. Once the image has captured, #GstCameraBin::img-done signal is fired.
+ * It allows to decide wheter to take another picture (burst capture, bracketing
+ * shot) or stop capturing. The last captured image is shown
+ * until one switches back to view finder using #GstCameraBin::user-stop action
+ * signal.
+ * </para>
+ * <para>
+ * Available resolutions can be taken from the #GstCameraBin:inputcaps property.
+ * Image capture resolution can be set with #GstCameraBin::user-image-res
+ * action signal.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Video capture</title>
+ * <para>
+ * Video capture is started with the #GstCameraBin::user-start action signal too.
+ * In addition to image capture one can use #GstCameraBin::user-pause to
+ * pause recording and #GstCameraBin::user-stop to end recording.
+ * </para>
+ * <para>
+ * Available resolutions and fps can be taken from the #GstCameraBin:inputcaps
+ * property. #GstCameraBin::user-res-fps action signal can be used to set frame
+ * rate and resolution for the video recording and view finder as well.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Photography interface</title>
+ * <para>
+ * GstCameraBin implements gst photography interface, which can be used to set
+ * and get different settings related to digital imaging. Since currently many
+ * of these settings require low-level support the photography interface support
+ * is dependent on video src element. In practice photography interface settings
+ * cannot be used successfully until in PAUSED state when the video src has
+ * opened the video device.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>States</title>
+ * <para>
+ * Elements within GstCameraBin are created and destroyed when switching
+ * between NULL and READY states. Therefore element properties should be set
+ * in NULL state. User set elements are not unreffed until GstCameraBin is
+ * unreffed or replaced by a new user set element. Initially only elements needed
+ * for view finder mode are created to speed up startup. Image bin and video bin
+ * elements are created when setting the mode or starting capture.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <note>
+ * <para>
+ * Since the muxers tested so far have problems with discontinous buffers, QoS
+ * has been disabled, and then in order to record video, you MUST ensure that
+ * there is enough CPU to encode the video. Thus choose smart resolution and
+ * frames per second values. It is also highly recommended to avoid color
+ * conversions; make sure all the elements involved work with the same colorspace
+ * (i.e. rgb or yuv i420 or whatelse).
+ * </para>
+ * </note>
+ * </refsect2>
+ */
+
+/*
+ * The pipeline in the camerabin is
+ *
+ *                                   "image bin"
+ * videosrc ! crop ! scale ! out-sel <------> in-sel ! scale ! ffmpegcsp ! vfsink
+ *                                   "video bin"
+ *
+ * it is possible to have 'ffmpegcolorspace' and 'capsfilter' just after
+ * v4l2camsrc
+ *
+ * The properties of elements are:
+ *
+ *   vfsink - "sync", FALSE, "qos", FALSE
+ *   output-selector - "resend-latest", FALSE
+ *   input-selector - "select-all", TRUE
+ */
+
+/*
+ * includes
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <gst/gst.h>
+/* FIXME: include #include <gst/gst-i18n-plugin.h> and use _(" ") */
+
+#include "gstcamerabin.h"
+#include "gstcamerabinxoverlay.h"
+#include "gstcamerabincolorbalance.h"
+#include "gstcamerabinphotography.h"
+
+#include "camerabingeneral.h"
+
+#include "gstcamerabin-marshal.h"
+
+/*
+ * enum and types
+ */
+
+enum
+{
+  /* action signals */
+  USER_START_SIGNAL,
+  USER_STOP_SIGNAL,
+  USER_PAUSE_SIGNAL,
+  USER_RES_FPS_SIGNAL,
+  USER_IMAGE_RES_SIGNAL,
+  /* emit signals */
+  IMG_DONE_SIGNAL,
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+  ARG_FILENAME,
+  ARG_MODE,
+  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
+};
+
+/*
+ * defines and static global vars
+ */
+
+static guint camerabin_signals[LAST_SIGNAL];
+
+#define GST_TYPE_CAMERABIN_MODE (gst_camerabin_mode_get_type ())
+
+/* default and range values for args */
+
+#define DEFAULT_MODE MODE_IMAGE
+#define DEFAULT_ZOOM 100
+#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 CAMERABIN_DEFAULT_VF_CAPS "video/x-raw-yuv,format=(fourcc)I420"
+/* Using "bilinear" as default zoom method */
+#define CAMERABIN_DEFAULT_ZOOM_METHOD 1
+
+#define MIN_ZOOM 100
+#define MAX_ZOOM 1000
+#define ZOOM_1X MIN_ZOOM
+
+#define DEFAULT_V4L2CAMSRC_DRIVER_NAME "omap3cam"
+
+/* internal element names */
+
+#define USE_COLOR_CONVERTER 1
+
+/* FIXME: use autovideosrc
+ * v4l2camsrc should have GST_RANK_PRIMARY in maemo and _NONE in plugins-bad
+ * for now */
+static const char SRC_VID_SRC[] = "v4l2camsrc";
+/*static const char SRC_VID_SRC[] = "v4l2src"; */
+
+static const char ZOOM_CROP[] = "videocrop";
+static const char ZOOM_SCALE[] = "videoscale";
+
+static const char CAPS_FILTER[] = "capsfilter";
+
+static const char COLOR_CONVERTER[] = "ffmpegcolorspace";
+
+static const char SRC_OUT_SEL[] = "output-selector";
+
+static const char VIEW_IN_SEL[] = "input-selector";
+static const char VIEW_SCALE[] = "videoscale";
+static const char VIEW_SINK[] = "autovideosink";
+
+/*
+ * static helper functions declaration
+ */
+
+static void camerabin_setup_src_elements (GstCameraBin * camera);
+
+static gboolean camerabin_create_src_elements (GstCameraBin * camera);
+
+static void camerabin_setup_view_elements (GstCameraBin * camera);
+
+static gboolean camerabin_create_view_elements (GstCameraBin * camera);
+
+static gboolean camerabin_create_elements (GstCameraBin * camera);
+
+static void camerabin_destroy_elements (GstCameraBin * camera);
+
+static void camerabin_dispose_elements (GstCameraBin * camera);
+
+static void gst_camerabin_change_mode (GstCameraBin * camera, gint mode);
+
+static void
+gst_camerabin_change_filename (GstCameraBin * camera, const gchar * name);
+
+static void gst_camerabin_setup_zoom (GstCameraBin * camera);
+
+static GstCaps *gst_camerabin_get_allowed_input_caps (GstCameraBin * camera);
+
+static void gst_camerabin_rewrite_tags (GstCameraBin * camera);
+
+static void
+gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps);
+
+static void gst_camerabin_start_image_capture (GstCameraBin * camera);
+
+static void gst_camerabin_start_video_recording (GstCameraBin * camera);
+
+static gboolean
+gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data);
+static gboolean
+gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data);
+
+static void gst_camerabin_reset_to_view_finder (GstCameraBin * camera);
+
+static void gst_camerabin_do_stop (GstCameraBin * camera);
+
+static void
+gst_camerabin_set_allowed_framerate (GstCameraBin * camera,
+    GstCaps * filter_caps);
+
+/*
+ * GObject callback functions declaration
+ */
+
+static void gst_camerabin_base_init (gpointer gclass);
+
+static void gst_camerabin_class_init (GstCameraBinClass * klass);
+
+static void
+gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass);
+
+static void gst_camerabin_dispose (GObject * object);
+
+static void gst_camerabin_finalize (GObject * object);
+
+static void gst_camerabin_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+
+static void gst_camerabin_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static const GValue *gst_camerabin_find_better_framerate (GstCameraBin * camera,
+    GstStructure * st, const GValue * orig_framerate);
+/*
+ * GstElement function declarations
+ */
+
+static GstStateChangeReturn
+gst_camerabin_change_state (GstElement * element, GstStateChange transition);
+
+
+/*
+ * GstBin function declarations
+ */
+static void
+gst_camerabin_handle_message_func (GstBin * bin, GstMessage * message);
+
+
+/*
+ * Action signal function declarations
+ */
+
+static void gst_camerabin_user_start (GstCameraBin * camera);
+
+static void gst_camerabin_user_stop (GstCameraBin * camera);
+
+static void gst_camerabin_user_pause (GstCameraBin * camera);
+
+static void
+gst_camerabin_user_res_fps (GstCameraBin * camera, gint width, gint height,
+    gint fps_n, gint fps_d);
+
+static void
+gst_camerabin_user_image_res (GstCameraBin * camera, gint width, gint height);
+
+
+/*
+ * GST BOILERPLATE and GObject types
+ */
+
+static GType
+gst_camerabin_mode_get_type (void)
+{
+  static GType gtype = 0;
+
+  if (gtype == 0) {
+    static const GEnumValue values[] = {
+      {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;
+}
+
+static gboolean
+gst_camerabin_iface_supported (GstImplementsInterface * iface, GType iface_type)
+{
+  GstCameraBin *camera = GST_CAMERABIN (iface);
+
+  if (iface_type == GST_TYPE_X_OVERLAY) {
+    if (camera->view_sink) {
+      return GST_IS_X_OVERLAY (camera->view_sink);
+    }
+  } else if (iface_type == GST_TYPE_COLOR_BALANCE) {
+    if (camera->src_vid_src) {
+      return GST_IS_COLOR_BALANCE (camera->src_vid_src);
+    }
+  } else if (iface_type == GST_TYPE_TAG_SETTER) {
+    /* Note: Tag setter elements aren't
+       present when image and video bin in NULL */
+    GstElement *setter;
+    setter = gst_bin_get_by_interface (GST_BIN (camera), iface_type);
+    if (setter) {
+      gst_object_unref (setter);
+      return TRUE;
+    } else {
+      return FALSE;
+    }
+  } else if (iface_type == GST_TYPE_PHOTOGRAPHY) {
+    if (camera->src_vid_src) {
+      return GST_IS_PHOTOGRAPHY (camera->src_vid_src);
+    }
+  }
+
+  return FALSE;
+}
+
+static void
+gst_camerabin_interface_init (GstImplementsInterfaceClass * klass)
+{
+  /*
+   * default virtual functions
+   */
+  klass->supported = gst_camerabin_iface_supported;
+}
+
+static void
+camerabin_init_interfaces (GType type)
+{
+
+  static const GInterfaceInfo camerabin_info = {
+    (GInterfaceInitFunc) gst_camerabin_interface_init,
+    NULL,
+    NULL,
+  };
+
+  static const GInterfaceInfo camerabin_xoverlay_info = {
+    (GInterfaceInitFunc) gst_camerabin_xoverlay_init,
+    NULL,
+    NULL,
+  };
+
+  static const GInterfaceInfo camerabin_color_balance_info = {
+    (GInterfaceInitFunc) gst_camerabin_color_balance_init,
+    NULL,
+    NULL,
+  };
+
+  static const GInterfaceInfo camerabin_tagsetter_info = {
+    NULL,
+    NULL,
+    NULL,
+  };
+  static const GInterfaceInfo camerabin_photography_info = {
+    (GInterfaceInitFunc) gst_camerabin_photography_init,
+    NULL,
+    NULL,
+  };
+
+  g_type_add_interface_static (type,
+      GST_TYPE_IMPLEMENTS_INTERFACE, &camerabin_info);
+
+  g_type_add_interface_static (type, GST_TYPE_X_OVERLAY,
+      &camerabin_xoverlay_info);
+
+  g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE,
+      &camerabin_color_balance_info);
+
+  g_type_add_interface_static (type, GST_TYPE_TAG_SETTER,
+      &camerabin_tagsetter_info);
+
+  g_type_add_interface_static (type, GST_TYPE_PHOTOGRAPHY,
+      &camerabin_photography_info);
+}
+
+GST_BOILERPLATE_FULL (GstCameraBin, gst_camerabin, GstPipeline,
+    GST_TYPE_PIPELINE, camerabin_init_interfaces);
+
+/*
+ * static helper functions implementation
+ */
+
+/*
+ * camerabin_setup_src_elements:
+ * @camera: camerabin object
+ *
+ * This function updates camerabin capsfilters according
+ * to fps, resolution and zoom that have been configured
+ * to camerabin.
+ */
+static void
+camerabin_setup_src_elements (GstCameraBin * camera)
+{
+  GstStructure *st;
+  GstCaps *new_caps;
+  gboolean detect_framerate = FALSE;
+
+  if (!camera->view_finder_caps) {
+    st = gst_structure_from_string (CAMERABIN_DEFAULT_VF_CAPS, NULL);
+  } else {
+    st = gst_structure_copy (gst_caps_get_structure (camera->view_finder_caps,
+            0));
+  }
+
+  if (camera->width > 0 && camera->height > 0) {
+    gst_structure_set (st,
+        "width", G_TYPE_INT, camera->width,
+        "height", G_TYPE_INT, camera->height, NULL);
+  }
+
+  if (camera->fps_n > 0 && camera->fps_d > 0) {
+    if (camera->night_mode) {
+      GST_WARNING_OBJECT (camera,
+          "night mode, lowest allowed fps will be forced");
+      camera->pre_night_fps_n = camera->fps_n;
+      camera->pre_night_fps_d = camera->fps_d;
+      detect_framerate = TRUE;
+    } else {
+      gst_structure_set (st,
+          "framerate", GST_TYPE_FRACTION, camera->fps_n, camera->fps_d, NULL);
+      new_caps = gst_caps_new_full (st, NULL);
+    }
+  } else {
+    GST_DEBUG_OBJECT (camera, "no framerate specified");
+    detect_framerate = TRUE;
+  }
+
+  if (detect_framerate) {
+    GST_DEBUG_OBJECT (camera, "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 */
+    gst_camerabin_set_allowed_framerate (camera, new_caps);
+  }
+
+  /* Set default zoom method */
+  g_object_set (camera->src_zoom_scale, "method",
+      CAMERABIN_DEFAULT_ZOOM_METHOD, NULL);
+
+  gst_caps_replace (&camera->view_finder_caps, new_caps);
+
+  /* Set caps for view finder mode */
+  gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+}
+
+/*
+ * camerabin_create_src_elements:
+ * @camera: camerabin object
+ *
+ * This function creates and links upstream side elements for camerabin.
+ * videosrc ! cspconv ! capsfilter ! crop ! scale ! capsfilter ! out-sel !
+ *
+ * Returns: TRUE, if elements were successfully created, FALSE otherwise
+ */
+static gboolean
+camerabin_create_src_elements (GstCameraBin * camera)
+{
+  gboolean ret = FALSE;
+  GstBin *cbin = GST_BIN (camera);
+  gchar *driver_name = NULL;
+
+  if (camera->user_vid_src) {
+    camera->src_vid_src = camera->user_vid_src;
+
+    if (!gst_camerabin_add_element (cbin, camera->src_vid_src)) {
+      camera->src_vid_src = NULL;
+      goto done;
+    }
+  } else if (!(camera->src_vid_src =
+          gst_camerabin_create_and_add_element (cbin, SRC_VID_SRC)))
+    goto done;
+#ifdef USE_COLOR_CONVERTER
+  if (!gst_camerabin_create_and_add_element (cbin, COLOR_CONVERTER))
+    goto done;
+#endif
+  if (!(camera->src_filter =
+          gst_camerabin_create_and_add_element (cbin, CAPS_FILTER)))
+    goto done;
+  if (!(camera->src_zoom_crop =
+          gst_camerabin_create_and_add_element (cbin, ZOOM_CROP)))
+    goto done;
+  if (!(camera->src_zoom_scale =
+          gst_camerabin_create_and_add_element (cbin, ZOOM_SCALE)))
+    goto done;
+  if (!(camera->src_zoom_filter =
+          gst_camerabin_create_and_add_element (cbin, CAPS_FILTER)))
+    goto done;
+  if (!(camera->src_out_sel =
+          gst_camerabin_create_and_add_element (cbin, SRC_OUT_SEL)))
+    goto done;
+
+  camera->srcpad_zoom_filter =
+      gst_element_get_static_pad (camera->src_zoom_filter, "src");
+
+  /* Set default "driver-name" for v4l2camsrc if not set */
+  if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src),
+          "driver-name")) {
+    g_object_get (G_OBJECT (camera->src_vid_src), "driver-name",
+        &driver_name, NULL);
+    if (!driver_name) {
+      g_object_set (G_OBJECT (camera->src_vid_src), "driver-name",
+          DEFAULT_V4L2CAMSRC_DRIVER_NAME, NULL);
+    }
+  }
+
+  ret = TRUE;
+done:
+  return ret;
+}
+
+/*
+ * camerabin_setup_view_elements:
+ * @camera: camerabin object
+ *
+ * This function configures properties for view finder sink element.
+ */
+static void
+camerabin_setup_view_elements (GstCameraBin * camera)
+{
+  GST_DEBUG_OBJECT (camera, "setting view finder properties");
+  g_object_set (G_OBJECT (camera->view_in_sel), "select-all", TRUE, NULL);
+  /* Set properties for view finder sink */
+  /* Find the actual sink if using bin like autovideosink */
+  if (GST_IS_BIN (camera->view_sink)) {
+    GList *child = NULL, *children = GST_BIN_CHILDREN (camera->view_sink);
+    for (child = children; child != NULL; child = g_list_next (children)) {
+      GObject *ch = G_OBJECT (child->data);
+      if (g_object_class_find_property (G_OBJECT_GET_CLASS (ch), "sync")) {
+        g_object_set (G_OBJECT (ch), "sync", FALSE, "qos", FALSE, "async",
+            FALSE, NULL);
+      }
+    }
+  } else {
+    g_object_set (G_OBJECT (camera->view_sink), "sync", FALSE, "qos", FALSE,
+        "async", FALSE, NULL);
+  }
+}
+
+/*
+ * camerabin_create_view_elements:
+ * @camera: camerabin object
+ *
+ * This function creates and links downstream side elements for camerabin.
+ * ! scale ! cspconv ! view finder sink
+ *
+ * Returns: TRUE, if elements were successfully created, FALSE otherwise
+ */
+static gboolean
+camerabin_create_view_elements (GstCameraBin * camera)
+{
+  const GList *pads;
+
+  if (!(camera->view_in_sel =
+          gst_camerabin_create_and_add_element (GST_BIN (camera),
+              VIEW_IN_SEL))) {
+    goto error;
+  }
+
+  /* Look for recently added input selector sink pad, we need to release it later */
+  pads = GST_ELEMENT_PADS (camera->view_in_sel);
+  while (pads != NULL
+      && (GST_PAD_DIRECTION (GST_PAD (pads->data)) != GST_PAD_SINK)) {
+    pads = g_list_next (pads);
+  }
+  camera->pad_view_img = GST_PAD (pads->data);
+
+  if (!(camera->view_scale =
+          gst_camerabin_create_and_add_element (GST_BIN (camera),
+              VIEW_SCALE))) {
+    goto error;
+  }
+#ifdef USE_COLOR_CONVERTER
+  if (!gst_camerabin_create_and_add_element (GST_BIN (camera), COLOR_CONVERTER)) {
+    goto error;
+  }
+#endif
+  if (camera->user_vf_sink) {
+    camera->view_sink = camera->user_vf_sink;
+    if (!gst_camerabin_add_element (GST_BIN (camera), camera->view_sink)) {
+      goto error;
+    }
+  } else if (!(camera->view_sink =
+          gst_camerabin_create_and_add_element (GST_BIN (camera), VIEW_SINK))) {
+    goto error;
+  }
+
+  return TRUE;
+error:
+  return FALSE;
+}
+
+/*
+ * camerabin_create_elements:
+ * @camera: camerabin object
+ *
+ * This function creates and links all elements for camerabin,
+ *
+ * Returns: TRUE, if elements were successfully created, FALSE otherwise
+ */
+static gboolean
+camerabin_create_elements (GstCameraBin * camera)
+{
+  gboolean ret = FALSE;
+  GstPadLinkReturn link_ret = GST_PAD_LINK_REFUSED;
+  GstPad *unconnected_pad;
+
+  GST_LOG_OBJECT (camera, "creating elems");
+
+  /* Create "src" elements */
+  if (!camerabin_create_src_elements (camera)) {
+    goto done;
+  }
+
+  /* Add image bin */
+  camera->pad_src_img =
+      gst_element_get_request_pad (camera->src_out_sel, "src%d");
+  if (!gst_camerabin_add_element (GST_BIN (camera), camera->imgbin)) {
+    goto done;
+  }
+  gst_pad_add_buffer_probe (camera->pad_src_img,
+      G_CALLBACK (gst_camerabin_have_img_buffer), camera);
+
+  /* Create view finder elements, this also links it to image bin */
+  if (!camerabin_create_view_elements (camera)) {
+    GST_WARNING_OBJECT (camera, "creating view failed");
+    goto done;
+  }
+
+  /* Link output selector ! view_finder */
+  camera->pad_src_view =
+      gst_element_get_request_pad (camera->src_out_sel, "src%d");
+  camera->pad_view_src =
+      gst_element_get_request_pad (camera->view_in_sel, "sink%d");
+  link_ret = gst_pad_link (camera->pad_src_view, camera->pad_view_src);
+  if (GST_PAD_LINK_FAILED (link_ret)) {
+    GST_ELEMENT_ERROR (camera, CORE, NEGOTIATION,
+        ("linking view finder failed"), (NULL));
+    goto done;
+  }
+
+  /* Set view finder active as default */
+  g_object_set (G_OBJECT (camera->src_out_sel), "active-pad",
+      camera->pad_src_view, NULL);
+
+  /* Add video bin */
+  camera->pad_src_vid =
+      gst_element_get_request_pad (camera->src_out_sel, "src%d");
+  if (!gst_camerabin_add_element (GST_BIN (camera), camera->vidbin)) {
+    goto done;
+  }
+  gst_pad_add_buffer_probe (camera->pad_src_vid,
+      G_CALLBACK (gst_camerabin_have_vid_buffer), camera);
+
+  /* Link video bin ! view finder */
+  unconnected_pad = gst_bin_find_unlinked_pad (GST_BIN (camera), GST_PAD_SRC);
+  camera->pad_view_vid =
+      gst_element_get_request_pad (camera->view_in_sel, "sink%d");
+  link_ret = gst_pad_link (unconnected_pad, camera->pad_view_vid);
+  gst_object_unref (unconnected_pad);
+  if (GST_PAD_LINK_FAILED (link_ret)) {
+    GST_ELEMENT_ERROR (camera, CORE, NEGOTIATION, (NULL),
+        ("linking video bin and view finder failed"));
+    goto done;
+  }
+
+  ret = TRUE;
+
+done:
+
+  if (FALSE == ret)
+    camerabin_destroy_elements (camera);
+
+  return ret;
+}
+
+/*
+ * camerabin_destroy_elements:
+ * @camera: camerabin object
+ *
+ * This function removes all elements from camerabin.
+ */
+static void
+camerabin_destroy_elements (GstCameraBin * camera)
+{
+  GST_DEBUG_OBJECT (camera, "destroying elements");
+
+  /* Release request pads */
+  if (camera->pad_view_vid) {
+    gst_element_release_request_pad (camera->view_in_sel, camera->pad_view_vid);
+    camera->pad_view_vid = NULL;
+  }
+  if (camera->pad_src_vid) {
+    gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_vid);
+    camera->pad_src_vid = NULL;
+  }
+  if (camera->pad_view_img) {
+    gst_element_release_request_pad (camera->view_in_sel, camera->pad_view_img);
+    camera->pad_view_img = NULL;
+  }
+  if (camera->pad_src_img) {
+    gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_img);
+    camera->pad_src_img = NULL;
+  }
+  if (camera->pad_view_src) {
+    gst_element_release_request_pad (camera->view_in_sel, camera->pad_view_src);
+    camera->pad_view_src = NULL;
+  }
+  if (camera->pad_src_view) {
+    gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_view);
+    camera->pad_src_view = NULL;
+  }
+
+  camera->view_sink = NULL;
+  camera->view_scale = NULL;
+  camera->view_in_sel = NULL;
+
+  camera->src_out_sel = NULL;
+  camera->src_filter = NULL;
+  camera->src_zoom_crop = NULL;
+  camera->src_zoom_scale = NULL;
+  camera->src_zoom_filter = NULL;
+  camera->src_vid_src = NULL;
+
+  camera->active_bin = NULL;
+
+  /* Remove elements */
+  gst_camerabin_remove_elements_from_bin (GST_BIN (camera));
+}
+
+/*
+ * camerabin_dispose_elements:
+ * @camera: camerabin object
+ *
+ * This function releases all allocated camerabin resources.
+ */
+static void
+camerabin_dispose_elements (GstCameraBin * camera)
+{
+  if (camera->capture_mutex) {
+    g_mutex_free (camera->capture_mutex);
+    camera->capture_mutex = NULL;
+  }
+  if (camera->cond) {
+    g_cond_free (camera->cond);
+    camera->cond = NULL;
+  }
+  if (camera->filename) {
+    g_string_free (camera->filename, TRUE);
+    camera->filename = NULL;
+  }
+  /* Unref user set elements */
+  if (camera->user_vf_sink) {
+    gst_object_unref (camera->user_vf_sink);
+    camera->user_vf_sink = NULL;
+  }
+  if (camera->user_vid_src) {
+    gst_object_unref (camera->user_vid_src);
+    camera->user_vid_src = NULL;
+  }
+
+  if (camera->image_capture_caps) {
+    gst_caps_unref (camera->image_capture_caps);
+    camera->image_capture_caps = NULL;
+  }
+
+  if (camera->view_finder_caps) {
+    gst_caps_unref (camera->view_finder_caps);
+    camera->view_finder_caps = NULL;
+  }
+
+  if (camera->allowed_caps) {
+    gst_caps_unref (camera->allowed_caps);
+    camera->allowed_caps = NULL;
+  }
+}
+
+/*
+ * gst_camerabin_image_capture_continue:
+ * @camera: camerabin object
+ * @filename: new filename set by user
+ * @cont: TRUE to continue image capture, FALSE otherwise
+ *
+ * Check if user wants to continue image capturing by using g_signal.
+ */
+static void
+gst_camerabin_image_capture_continue (GstCameraBin * camera, GString * filename,
+    gboolean * cont)
+{
+  GST_DEBUG_OBJECT (camera, "emitting img_done signal, filename: %s",
+      filename->str);
+  g_signal_emit (G_OBJECT (camera), camerabin_signals[IMG_DONE_SIGNAL], 0,
+      filename, cont);
+
+  GST_DEBUG_OBJECT (camera, "emitted img_done, new filename:%s, continue:%d",
+      filename->str, *cont);
+}
+
+/*
+ * gst_camerabin_change_mode:
+ * @camera: camerabin object
+ * @mode: image or video mode
+ *
+ * Change camerabin mode between image and video capture.
+ * Changing mode will stop ongoing capture.
+ */
+static void
+gst_camerabin_change_mode (GstCameraBin * camera, gint mode)
+{
+  if (camera->mode != mode || !camera->active_bin) {
+    GST_DEBUG_OBJECT (camera, "setting mode: %d", mode);
+    /* Interrupt ongoing capture */
+    gst_camerabin_do_stop (camera);
+    camera->mode = mode;
+    if (camera->active_bin) {
+      gst_element_set_state (camera->active_bin, GST_STATE_NULL);
+    }
+    if (camera->mode == MODE_IMAGE) {
+      camera->active_bin = camera->imgbin;
+    } else if (camera->mode == MODE_VIDEO) {
+      camera->active_bin = camera->vidbin;
+    }
+    gst_camerabin_reset_to_view_finder (camera);
+  }
+}
+
+/*
+ * gst_camerabin_change_filename:
+ * @camera: camerabin object
+ * @name: new filename for capture
+ *
+ * Change filename for image or video capture.
+ * Changing filename will stop ongoing capture.
+ */
+static void
+gst_camerabin_change_filename (GstCameraBin * camera, const gchar * name)
+{
+  if (0 != strcmp (camera->filename->str, name)) {
+    GST_DEBUG_OBJECT (camera, "changing filename from %s to %s",
+        camera->filename->str, name);
+    /* Interrupt ongoing capture */
+    gst_camerabin_do_stop (camera);
+    gst_camerabin_reset_to_view_finder (camera);
+
+    if (camera->active_bin) {
+      g_object_set (G_OBJECT (camera->active_bin), "filename", name, NULL);
+    }
+
+    g_string_assign (camera->filename, name);
+  }
+}
+
+/*
+ * gst_camerabin_setup_zoom:
+ * @camera: camerabin object
+ *
+ * Apply zoom configured to camerabin to capture.
+ */
+static void
+gst_camerabin_setup_zoom (GstCameraBin * camera)
+{
+  gint zoom;
+  gboolean done = FALSE;
+
+  g_return_if_fail (camera != NULL);
+  g_return_if_fail (camera->src_zoom_crop != NULL);
+
+  zoom = g_atomic_int_get (&camera->zoom);
+
+  g_return_if_fail (zoom);
+
+  if (GST_IS_ELEMENT (camera->src_vid_src) &&
+      gst_element_implements_interface (camera->src_vid_src,
+          GST_TYPE_PHOTOGRAPHY)) {
+    /* Try setting (hardware) zoom using photography interface */
+    GstPhotography *photo;
+    GstPhotoCaps pcaps;
+
+    photo = GST_PHOTOGRAPHY (camera->src_vid_src);
+    pcaps = gst_photography_get_capabilities (photo);
+
+    if (pcaps & GST_PHOTOGRAPHY_CAPS_ZOOM) {
+      done = gst_photography_set_zoom (photo, (gfloat) zoom / 100.0);
+    }
+  }
+
+  if (!done) {
+    /* Update capsfilters to apply the (software) zoom */
+    gint w2_crop = 0;
+    gint h2_crop = 0;
+    GstPad *pad_zoom_sink = NULL;
+
+    GST_INFO_OBJECT (camera, "zoom: %d, orig size: %dx%d", zoom,
+        camera->width, camera->height);
+
+    if (zoom != ZOOM_1X) {
+      w2_crop = (camera->width - (camera->width * ZOOM_1X / zoom)) / 2;
+      h2_crop = (camera->height - (camera->height * ZOOM_1X / zoom)) / 2;
+    }
+
+    pad_zoom_sink = gst_element_get_static_pad (camera->src_zoom_crop, "sink");
+
+    GST_INFO_OBJECT (camera,
+        "sw cropping: left:%d, right:%d, top:%d, bottom:%d", w2_crop, w2_crop,
+        h2_crop, h2_crop);
+
+    GST_PAD_STREAM_LOCK (pad_zoom_sink);
+    g_object_set (camera->src_zoom_crop, "left", w2_crop, "right", w2_crop,
+        "top", h2_crop, "bottom", h2_crop, NULL);
+
+    GST_PAD_STREAM_UNLOCK (pad_zoom_sink);
+    gst_object_unref (pad_zoom_sink);
+  }
+  GST_LOG_OBJECT (camera, "zoom set");
+}
+
+/*
+ * gst_camerabin_get_allowed_input_caps:
+ * @camera: camerabin object
+ *
+ * Retrieve caps from videosrc describing formats it supports
+ *
+ * Returns: caps object from videosrc
+ */
+static GstCaps *
+gst_camerabin_get_allowed_input_caps (GstCameraBin * camera)
+{
+  GstCaps *caps = NULL;
+  GstPad *pad = NULL, *peer_pad = NULL;
+  GstState state;
+  gboolean temp_videosrc_pause = FALSE;
+  GstElement *videosrc;
+
+  g_return_val_if_fail (camera != NULL, NULL);
+
+  videosrc = camera->src_vid_src ? camera->src_vid_src : camera->user_vid_src;
+
+  if (!videosrc) {
+    GST_WARNING_OBJECT (camera, "no videosrc, can't get allowed caps");
+    goto failed;
+  }
+
+  if (camera->allowed_caps) {
+    GST_DEBUG_OBJECT (camera, "returning cached caps");
+    goto done;
+  }
+
+  pad = gst_element_get_static_pad (videosrc, "src");
+
+  if (!pad) {
+    GST_WARNING_OBJECT (camera, "no srcpad in videosrc");
+    goto failed;
+  }
+
+  state = GST_STATE (videosrc);
+
+  /* Make this function work also in READY and NULL state */
+  if (state == GST_STATE_READY || state == GST_STATE_NULL) {
+    GST_DEBUG_OBJECT (camera, "setting videosrc to paused temporarily");
+    temp_videosrc_pause = TRUE;
+    peer_pad = gst_pad_get_peer (pad);
+    if (peer_pad) {
+      gst_pad_unlink (pad, peer_pad);
+    }
+    /* Set videosrc to PAUSED to open video device */
+    gst_element_set_locked_state (videosrc, TRUE);
+    gst_element_set_state (videosrc, GST_STATE_PAUSED);
+  }
+
+  camera->allowed_caps = gst_pad_get_caps (pad);
+
+  /* Restore state and re-link if necessary */
+  if (temp_videosrc_pause) {
+    GST_DEBUG_OBJECT (camera, "restoring videosrc state %d", state);
+    /* Reset videosrc to NULL state, some drivers seem to need this */
+    gst_element_set_state (videosrc, GST_STATE_NULL);
+    gst_element_set_state (videosrc, state);
+    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 (camera->allowed_caps) {
+    caps = gst_caps_copy (camera->allowed_caps);
+  }
+failed:
+  GST_INFO_OBJECT (camera, "allowed caps:%" GST_PTR_FORMAT, caps);
+  return caps;
+}
+
+/*
+ * gst_camerabin_rewrite_tags_to_bin:
+ * @bin: bin holding tag setter elements
+ * @list: tag list to be written
+ *
+ * This function looks for certain tag setters from given bin
+ * and REPLACES ALL setter tags with given tag list
+ *
+ */
+static void
+gst_camerabin_rewrite_tags_to_bin (GstBin * bin, const GstTagList * list)
+{
+  GstElement *setter;
+  GstElementFactory *setter_factory;
+  const gchar *klass;
+  GstIterator *iter;
+  GstIteratorResult res = GST_ITERATOR_OK;
+  gpointer data;
+
+  iter = gst_bin_iterate_all_by_interface (bin, GST_TYPE_TAG_SETTER);
+
+  while (res == GST_ITERATOR_OK || res == GST_ITERATOR_RESYNC) {
+    res = gst_iterator_next (iter, &data);
+    switch (res) {
+      case GST_ITERATOR_DONE:
+        break;
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (iter);
+        break;
+      case GST_ITERATOR_ERROR:
+        GST_WARNING ("error iterating tag setters");
+        break;
+      case GST_ITERATOR_OK:
+        setter = GST_ELEMENT (data);
+        GST_LOG ("iterating tag setters: %" GST_PTR_FORMAT, setter);
+        setter_factory = gst_element_get_factory (setter);
+        klass = gst_element_factory_get_klass (setter_factory);
+        /* FIXME: check if tags should be written to all tag setters,
+           set tags only to Muxer elements for now */
+        if (g_strrstr (klass, "Muxer")) {
+          GST_DEBUG ("replacement tags %" GST_PTR_FORMAT, list);
+          gst_tag_setter_merge_tags (GST_TAG_SETTER (setter), list,
+              GST_TAG_MERGE_REPLACE_ALL);
+        }
+        gst_object_unref (setter);
+        break;
+      default:
+        break;
+    }
+  }
+
+  gst_iterator_free (iter);
+}
+
+/*
+ * gst_camerabin_get_internal_tags:
+ * @camera: the camera bin element
+ *
+ * Returns tag list containing metadata from camerabin
+ * and it's elements
+ */
+static GstTagList *
+gst_camerabin_get_internal_tags (GstCameraBin * camera)
+{
+  GstTagList *list = gst_tag_list_new ();
+  GstColorBalance *balance = NULL;
+  const GList *controls = NULL, *item;
+  GstColorBalanceChannel *channel;
+  gint min_value, max_value, mid_value, cur_value;
+
+
+  if (camera->active_bin == camera->vidbin) {
+    /* FIXME: check if internal video tag setting is needed */
+    goto done;
+  }
+
+  gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+      "image-width", camera->width, "image-height", camera->height, NULL);
+
+  gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+      "capture-digital-zoom", camera->zoom, 100, NULL);
+
+  if (gst_element_implements_interface (GST_ELEMENT (camera),
+          GST_TYPE_COLOR_BALANCE)) {
+    balance = GST_COLOR_BALANCE (camera);
+  }
+
+  if (balance) {
+    controls = gst_color_balance_list_channels (balance);
+  }
+  for (item = controls; item; item = g_list_next (item)) {
+    channel = item->data;
+    min_value = channel->min_value;
+    max_value = channel->max_value;
+    /* the default value would probably better */
+    mid_value = min_value + ((max_value - min_value) / 2);
+    cur_value = gst_color_balance_get_value (balance, channel);
+
+    if (!strcasecmp (channel->label, "brightness")) {
+      /* The value of brightness. The unit is the APEX value (Additive System of Photographic Exposure).
+       * Ordinarily it is given in the range of -99.99 to 99.99. Note that
+       * if the numerator of the recorded value is 0xFFFFFFFF, Unknown shall be indicated.
+       *
+       * BrightnessValue (Bv) = log2 ( B/NK )
+       * Note that: B:cd/cm² (candela per square centimeter), N,K: constant
+       *
+       * http://johnlind.tripod.com/science/scienceexposure.html
+       *
+       */
+      gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+          "capture-brightness", cur_value, 1, NULL);
+    } else if (!strcasecmp (channel->label, "contrast")) {
+      /* 0 = Normal, 1 = Soft, 2 = Hard */
+
+      gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+          "capture-contrast",
+          (cur_value == mid_value) ? 0 : ((cur_value < mid_value) ? 1 : 2),
+          NULL);
+    } else if (!strcasecmp (channel->label, "gain")) {
+      /* 0 = Normal, 1 = Low Up, 2 = High Up, 3 = Low Down, 4 = Hight Down */
+      gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+          "capture-gain",
+          (guint) (cur_value == mid_value) ? 0 : ((cur_value <
+                  mid_value) ? 1 : 3), NULL);
+    } else if (!strcasecmp (channel->label, "saturation")) {
+      /* 0 = Normal, 1 = Low, 2 = High */
+      gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+          "capture-saturation",
+          (cur_value == mid_value) ? 0 : ((cur_value < mid_value) ? 1 : 2),
+          NULL);
+    }
+  }
+
+done:
+
+  return list;
+}
+
+/*
+ * gst_camerabin_rewrite_tags:
+ * @camera: the camera bin element
+ *
+ * Merges application set tags to camerabin internal tags,
+ * and writes them using image or video bin tag setters.
+ */
+static void
+gst_camerabin_rewrite_tags (GstCameraBin * camera)
+{
+  const GstTagList *app_tag_list = NULL;
+  GstTagList *list = NULL;
+
+  /* Get application set tags */
+  app_tag_list = gst_tag_setter_get_tag_list (GST_TAG_SETTER (camera));
+
+  /* Get tags from camerabin and it's elements */
+  list = gst_camerabin_get_internal_tags (camera);
+
+  if (app_tag_list) {
+    gst_tag_list_insert (list, app_tag_list, GST_TAG_MERGE_REPLACE);
+  }
+
+  /* Write tags */
+  gst_camerabin_rewrite_tags_to_bin (GST_BIN (camera->active_bin), list);
+
+  gst_tag_list_free (list);
+}
+
+/*
+ * gst_camerabin_set_capsfilter_caps:
+ * @camera: camerabin object
+ * @new_caps: pointer to caps object to set
+ *
+ * Set given caps to camerabin capsfilters.
+ */
+static void
+gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps)
+{
+  GstStructure *st;
+
+  GST_INFO_OBJECT (camera, "new_caps:%" GST_PTR_FORMAT, new_caps);
+
+  st = gst_caps_get_structure (new_caps, 0);
+
+  gst_structure_get_int (st, "width", &camera->width);
+  gst_structure_get_int (st, "height", &camera->height);
+
+  if (gst_structure_has_field (st, "framerate")) {
+    gst_structure_get_fraction (st, "framerate", &camera->fps_n,
+        &camera->fps_d);
+  }
+
+  /* Update zoom */
+  gst_camerabin_setup_zoom (camera);
+
+  /* Update capsfilters */
+  g_object_set (G_OBJECT (camera->src_filter), "caps", new_caps, NULL);
+  g_object_set (G_OBJECT (camera->src_zoom_filter), "caps", new_caps, NULL);
+}
+
+/*
+ * img_capture_prepared:
+ * @data: camerabin object
+ *
+ * Callback which is called after image capture has been prepared.
+ */
+static void
+img_capture_prepared (gpointer data)
+{
+  GstCameraBin *camera = GST_CAMERABIN (data);
+
+  GST_INFO_OBJECT (camera, "image capture prepared");
+
+  if (camera->image_capture_caps) {
+    /* Set capsfilters to match arriving image data */
+    gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps);
+  }
+
+  g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE,
+      "active-pad", camera->pad_src_img, NULL);
+  gst_camerabin_rewrite_tags (camera);
+  gst_element_set_state (GST_ELEMENT (camera->imgbin), GST_STATE_PLAYING);
+}
+
+/*
+ * gst_camerabin_start_image_capture:
+ * @camera: camerabin object
+ *
+ * Initiates image capture.
+ */
+static void
+gst_camerabin_start_image_capture (GstCameraBin * camera)
+{
+  GstStateChangeReturn state_ret;
+  gboolean wait_for_prepare = FALSE;
+  gint width = 0, height = 0, fps_n = 0, fps_d = 0;
+  GstStructure *st;
+
+  GST_INFO_OBJECT (camera, "starting image capture");
+
+  if (GST_IS_ELEMENT (camera->src_vid_src) &&
+      gst_element_implements_interface (camera->src_vid_src,
+          GST_TYPE_PHOTOGRAPHY)) {
+    /* Start image capture preparations using photography iface */
+    wait_for_prepare = TRUE;
+    g_mutex_lock (camera->capture_mutex);
+    if (camera->image_capture_caps) {
+      st = gst_caps_get_structure (camera->image_capture_caps, 0);
+    } else {
+      st = gst_caps_get_structure (camera->view_finder_caps, 0);
+    }
+    gst_structure_get_int (st, "width", &width);
+    gst_structure_get_int (st, "height", &height);
+    gst_structure_get_fraction (st, "framerate", &fps_n, &fps_d);
+    /* Set image capture resolution and frame rate */
+    g_signal_emit_by_name (camera->src_vid_src, "user-res-fps",
+        width, height, fps_n, fps_d, 0);
+
+    /* Enable still image capture mode in v4l2camsrc */
+    if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src),
+            "capture-mode")) {
+      g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 1, NULL);
+    }
+
+    /* Start preparations for image capture */
+    gst_photography_prepare_for_capture (GST_PHOTOGRAPHY (camera->src_vid_src),
+        (GstPhotoCapturePrepared) img_capture_prepared, camera);
+    camera->capturing = TRUE;
+    g_mutex_unlock (camera->capture_mutex);
+  }
+
+  if (!wait_for_prepare) {
+    gst_camerabin_rewrite_tags (camera);
+    state_ret = gst_element_set_state (camera->imgbin, GST_STATE_PLAYING);
+    if (state_ret != GST_STATE_CHANGE_FAILURE) {
+      g_mutex_lock (camera->capture_mutex);
+      g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", TRUE,
+          "active-pad", camera->pad_src_img, NULL);
+      camera->capturing = TRUE;
+      g_mutex_unlock (camera->capture_mutex);
+    } else {
+      GST_WARNING_OBJECT (camera, "imagebin state change failed");
+      gst_element_set_state (camera->imgbin, GST_STATE_NULL);
+    }
+  }
+}
+
+/*
+ * gst_camerabin_start_video_recording:
+ * @camera: camerabin object
+ *
+ * Initiates video recording.
+ */
+static void
+gst_camerabin_start_video_recording (GstCameraBin * camera)
+{
+  GstStateChangeReturn state_ret;
+  /* FIXME: how to ensure resolution and fps is supported by CPU?
+   * use a queue overrun signal?
+   */
+  GST_INFO_OBJECT (camera, "starting video capture");
+
+  gst_camerabin_rewrite_tags (camera);
+
+  /* Pause the pipeline in order to distribute new clock in paused_to_playing */
+  /* audio src timestamps will be 0 without state change to READY. ??? */
+  gst_element_set_state (GST_ELEMENT (camera), GST_STATE_READY);
+  gst_element_set_locked_state (camera->vidbin, FALSE);
+  state_ret = gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED);
+
+  if (state_ret != GST_STATE_CHANGE_FAILURE) {
+    g_mutex_lock (camera->capture_mutex);
+    g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE,
+        "active-pad", camera->pad_src_vid, NULL);
+
+    /* Enable video mode in v4l2camsrc */
+    if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src),
+            "capture-mode")) {
+      g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 2, NULL);
+    }
+
+    gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PLAYING);
+    gst_element_set_locked_state (camera->vidbin, TRUE);
+    camera->capturing = TRUE;
+    g_mutex_unlock (camera->capture_mutex);
+  } else {
+    GST_WARNING_OBJECT (camera, "videobin state change failed");
+    gst_element_set_state (camera->vidbin, GST_STATE_NULL);
+    gst_camerabin_reset_to_view_finder (camera);
+  }
+}
+
+/*
+ * gst_camerabin_send_video_eos:
+ * @camera: camerabin object
+ *
+ * Generate and send eos event to video bin in order to
+ * finish recording properly.
+ */
+static void
+gst_camerabin_send_video_eos (GstCameraBin * camera)
+{
+  GstPad *videopad;
+
+  g_return_if_fail (camera != NULL);
+
+  /* Send eos event to video bin */
+  GST_INFO_OBJECT (camera, "sending eos to videobin");
+  videopad = gst_element_get_static_pad (camera->vidbin, "sink");
+  gst_pad_send_event (videopad, gst_event_new_eos ());
+  gst_object_unref (videopad);
+}
+
+/*
+ * image_pad_blocked:
+ * @pad: pad to block/unblock
+ * @blocked: TRUE to block, FALSE to unblock
+ * @u_data: camera bin object
+ *
+ * Sends eos event to image bin if blocking pad leading to image bin.
+ * The pad will be unblocked when image bin posts eos message.
+ */
+static void
+image_pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
+{
+  GstCameraBin *camera;
+
+  camera = (GstCameraBin *) user_data;
+
+  GST_DEBUG_OBJECT (camera, "%s %s:%s",
+      blocked ? "blocking" : "unblocking", GST_DEBUG_PAD_NAME (pad));
+
+  if (blocked && (pad == camera->pad_src_img)) {
+    /* Send eos and block until image bin reaches eos */
+    GST_DEBUG_OBJECT (camera, "sending eos to image bin");
+    gst_element_send_event (camera->imgbin, gst_event_new_eos ());
+  }
+}
+
+/*
+ * gst_camerabin_have_img_buffer:
+ * @pad: output-selector src pad leading to image bin
+ * @buffer: still image frame
+ * @u_data: camera bin object
+ *
+ * Buffer probe called before sending each buffer to image bin.
+ *
+ * First buffer is always passed directly to image bin. Then pad
+ * is blocked in order to interleave buffers with eos events.
+ * Interleaving eos events and buffers is needed when we have
+ * decoupled elements in the image bin capture pipeline.
+ * After image bin posts eos message, then pad is unblocked.
+ * Next, image bin is changed to READY state in order to save the
+ * file and the application is allowed to decide whether to
+ * continue image capture. If yes, only then the next buffer is
+ * passed to image bin.
+ */
+static gboolean
+gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data)
+{
+  GstCameraBin *camera = (GstCameraBin *) u_data;
+  gboolean ret = TRUE;
+
+  GST_LOG ("got buffer #%d %p with size %d", camera->num_img_buffers,
+      buffer, GST_BUFFER_SIZE (buffer));
+
+  /* Image filename should be set by now */
+  if (g_str_equal (camera->filename->str, "")) {
+    GST_DEBUG_OBJECT (camera, "filename not set, dropping buffer");
+    ret = FALSE;
+    goto done;
+  }
+
+  /* Check for first buffer after capture start, we want to
+     pass it forward directly. */
+  if (!camera->num_img_buffers) {
+    /* Restore filter caps for view finder mode if necessary.
+       The v4l2camsrc switches automatically to view finder
+       resolution after hi-res still image capture. */
+    if (camera->image_capture_caps) {
+      gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+    }
+    goto done;
+  }
+
+  /* Close the file of saved image */
+  gst_element_set_state (camera->imgbin, GST_STATE_READY);
+
+  /* Check if the application wants to continue */
+  gst_camerabin_image_capture_continue (camera, camera->filename, &ret);
+
+  if (ret && !camera->stop_requested) {
+    GST_DEBUG_OBJECT (camera, "capturing image \"%s\"", camera->filename->str);
+    g_object_set (G_OBJECT (camera->imgbin), "filename",
+        camera->filename->str, NULL);
+    gst_element_set_state (camera->imgbin, GST_STATE_PLAYING);
+  } else {
+    GST_DEBUG_OBJECT (camera, "not continuing (cont:%d, stop_req:%d)",
+        ret, camera->stop_requested);
+    /* Reset filename to force application set new filename */
+    g_string_assign (camera->filename, "");
+
+    /* Block dataflow to the output-selector to show preview image in
+       view finder. Continue and unblock when capture is stopped */
+    gst_pad_set_blocked_async (camera->srcpad_zoom_filter, TRUE,
+        (GstPadBlockCallback) image_pad_blocked, camera);
+    ret = FALSE;                /* Drop the buffer */
+
+    g_mutex_lock (camera->capture_mutex);
+    camera->capturing = FALSE;
+    g_cond_signal (camera->cond);
+    g_mutex_unlock (camera->capture_mutex);
+  }
+
+done:
+
+  if (ret) {
+    camera->num_img_buffers++;
+    /* Block when next buffer arrives, we want to push eos event
+       between frames and make sure that eos reaches the filesink
+       before processing the next buffer. */
+    gst_pad_set_blocked_async (pad, TRUE,
+        (GstPadBlockCallback) image_pad_blocked, camera);
+  }
+
+  return ret;
+}
+
+/*
+ * gst_camerabin_have_vid_buffer:
+ * @pad: output-selector src pad leading to video bin
+ * @buffer: buffer pushed to the pad
+ * @u_data: camerabin object
+ *
+ * Buffer probe for src pad leading to video bin.
+ * Sends eos event to video bin if stop requested and drops
+ * all buffers after this.
+ */
+static gboolean
+gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data)
+{
+  GstCameraBin *camera = (GstCameraBin *) u_data;
+  gboolean ret = TRUE;
+  GST_LOG ("got video buffer %p with size %d",
+      buffer, GST_BUFFER_SIZE (buffer));
+  if (camera->stop_requested) {
+    gst_camerabin_send_video_eos (camera);
+    ret = FALSE;                /* Drop buffer */
+  }
+
+  return ret;
+}
+
+/*
+ * gst_camerabin_reset_to_view_finder:
+ * @camera: camerabin object
+ *
+ * Stop capturing and set camerabin to view finder mode.
+ * Reset capture counters and flags.
+ */
+static void
+gst_camerabin_reset_to_view_finder (GstCameraBin * camera)
+{
+  GstStateChangeReturn state_ret;
+  GST_DEBUG_OBJECT (camera, "resetting");
+
+  /* Set active bin to READY state */
+  if (camera->active_bin) {
+    state_ret = gst_element_set_state (camera->active_bin, GST_STATE_READY);
+    if (state_ret == GST_STATE_CHANGE_FAILURE) {
+      GST_WARNING_OBJECT (camera, "state change failed");
+      gst_element_set_state (camera->active_bin, GST_STATE_NULL);
+      camera->active_bin = NULL;
+    }
+  }
+
+  /* Reset counters and flags */
+  camera->num_img_buffers = 0;
+  camera->stop_requested = FALSE;
+  camera->paused = FALSE;
+
+  if (camera->src_out_sel) {
+    /* Set selector to forward data to view finder */
+    g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE,
+        "active-pad", camera->pad_src_view, NULL);
+  }
+
+  /* Unblock, if dataflow to output-selector is blocked due to image preview */
+  if (camera->srcpad_zoom_filter &&
+      gst_pad_is_blocked (camera->srcpad_zoom_filter)) {
+    gst_pad_set_blocked_async (camera->srcpad_zoom_filter, FALSE,
+        (GstPadBlockCallback) image_pad_blocked, camera);
+  }
+  /* Unblock, if dataflow to image bin is blocked due to waiting for eos */
+  if (camera->pad_src_img && gst_pad_is_blocked (camera->pad_src_img)) {
+    gst_pad_set_blocked_async (camera->pad_src_img, FALSE,
+        (GstPadBlockCallback) image_pad_blocked, camera);
+  }
+
+  /* Enable view finder mode in v4l2camsrc */
+  if (camera->src_vid_src &&
+      g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src),
+          "capture-mode")) {
+    g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 0, NULL);
+  }
+
+  GST_DEBUG_OBJECT (camera, "reset done");
+}
+
+/*
+ * gst_camerabin_do_stop:
+ * @camera: camerabin object
+ *
+ * Raise flag to indicate to image and video bin capture stop.
+ * Stopping paused video recording handled as a special case.
+ * Wait for ongoing capturing to finish.
+ */
+static void
+gst_camerabin_do_stop (GstCameraBin * camera)
+{
+  g_mutex_lock (camera->capture_mutex);
+  if (camera->capturing) {
+    GST_DEBUG_OBJECT (camera, "mark stop");
+    camera->stop_requested = TRUE;
+
+    /* Take special care when stopping paused video capture */
+    if ((camera->active_bin == camera->vidbin) && camera->paused) {
+      /* Send eos event to video bin before setting it to playing */
+      gst_camerabin_send_video_eos (camera);
+      /* We must change to playing now in order to get video bin eos events
+         and buffered data through and finish recording properly */
+      gst_element_set_state (GST_ELEMENT (camera->vidbin), GST_STATE_PLAYING);
+      camera->paused = FALSE;
+    }
+
+    GST_DEBUG_OBJECT (camera, "waiting for capturing to finish");
+    g_cond_wait (camera->cond, camera->capture_mutex);
+    GST_DEBUG_OBJECT (camera, "capturing finished");
+  }
+  g_mutex_unlock (camera->capture_mutex);
+}
+
+/*
+ * gst_camerabin_default_signal_img_done:
+ * @camera: camerabin object
+ * @fname: new filename
+ *
+ * Default handler for #GstCameraBin::img-done signal,
+ * stops always capture.
+ *
+ * Returns: FALSE always
+ */
+static gboolean
+gst_camerabin_default_signal_img_done (GstCameraBin * camera, GString * fname)
+{
+  return FALSE;
+}
+
+/*
+ * gst_camerabin_set_allowed_framerate:
+ * @camera: camerabin 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
+gst_camerabin_set_allowed_framerate (GstCameraBin * camera,
+    GstCaps * filter_caps)
+{
+  GstStructure *structure;
+  GstCaps *allowed_caps = NULL, *intersect = NULL;
+  const GValue *framerate = NULL;
+  guint caps_size, i;
+
+  /* Get supported caps from video src that matches with new filter caps */
+  GST_INFO_OBJECT (camera, "filter caps:%" GST_PTR_FORMAT, filter_caps);
+  allowed_caps = gst_camerabin_get_allowed_input_caps (camera);
+  intersect = gst_caps_intersect (allowed_caps, filter_caps);
+  GST_INFO_OBJECT (camera, "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_camerabin_find_better_framerate (camera, structure, framerate);
+  }
+
+  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);
+  }
+
+  if (allowed_caps) {
+    gst_caps_unref (allowed_caps);
+  }
+  if (intersect) {
+    gst_caps_unref (intersect);
+  }
+}
+
+
+/**
+ * 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;
+}
+
+/*
+ * gst_camerabin_find_better_framerate:
+ * @camera: camerabin 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
+ */
+static const GValue *
+gst_camerabin_find_better_framerate (GstCameraBin * camera, GstStructure * st,
+    const GValue * orig_framerate)
+{
+  const GValue *framerate = NULL;
+  guint i, i_best, list_size;
+  gint res, comparison;
+
+  if (camera->night_mode) {
+    GST_LOG_OBJECT (camera, "finding min framerate");
+    comparison = GST_VALUE_LESS_THAN;
+  } else {
+    GST_LOG_OBJECT (camera, "finding max framerate");
+    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 (camera, "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 (camera, "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 (camera->night_mode) {
+        GST_LOG_OBJECT (camera, "getting min framerate from range");
+        framerate = gst_value_get_fraction_range_min (framerate);
+      } else {
+        GST_LOG_OBJECT (camera, "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 (camera, "original framerate was the best");
+      framerate = orig_framerate;
+    }
+  }
+
+  return framerate;
+}
+
+/*
+ * GObject callback functions implementation
+ */
+
+static void
+gst_camerabin_base_init (gpointer gclass)
+{
+  static GstElementDetails element_details = {
+    "Camera Bin",
+    "Generic/Bin/Camera",
+    "Handle lot of features present in DSC",
+    "Nokia Corporation <multimedia@maemo.org>\n"
+        "Edgard Lima <edgard.lima@indt.org.br>"
+  };
+  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+  gst_element_class_set_details (element_class, &element_details);
+}
+
+static void
+gst_camerabin_class_init (GstCameraBinClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBinClass *gstbin_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gstelement_class = GST_ELEMENT_CLASS (klass);
+  gstbin_class = GST_BIN_CLASS (klass);
+
+  /* gobject */
+
+  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_camerabin_dispose);
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_camerabin_finalize);
+
+  gobject_class->set_property = gst_camerabin_set_property;
+  gobject_class->get_property = gst_camerabin_get_property;
+
+  /**
+   * GstCameraBin:filename:
+   *
+   * Set filename for the still image capturing or video capturing.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_FILENAME,
+      g_param_spec_string ("filename", "Filename",
+          "Filename of the image or video to save", "", G_PARAM_READWRITE));
+
+  /**
+   * GstCameraBin:mode:
+   *
+   * Set the mode of operation: still image capturing or video recording.
+   * Setting the mode will create and destroy image bin or video bin elements
+   * according to the mode. You can set this property at any time, changing
+   * the mode will stop ongoing capture.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_MODE,
+      g_param_spec_enum ("mode", "Mode",
+          "The capture mode (still image capture or video recording)",
+          GST_TYPE_CAMERABIN_MODE, DEFAULT_MODE, G_PARAM_READWRITE));
+
+  /**
+   * GstCameraBin:mute:
+   *
+   * Mute audio in video recording mode.
+   * Set this property only when #GstCameraBin is in READY, PAUSED or PLAYING.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_MUTE,
+      g_param_spec_boolean ("mute", "Mute",
+          "True to mute the recording. False to record with audio",
+          ARG_DEFAULT_MUTE, G_PARAM_READWRITE));
+
+  /**
+   * GstCameraBin:zoom:
+   *
+   * Set up the zoom applied to the frames.
+   * Set this property only when #GstCameraBin is in READY, PAUSED or PLAYING.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_ZOOM,
+      g_param_spec_int ("zoom", "Zoom",
+          "The zoom. 100 for 1x, 200 for 2x and so on",
+          MIN_ZOOM, MAX_ZOOM, DEFAULT_ZOOM, G_PARAM_READWRITE));
+
+  /**
+   * GstCameraBin:imagepp:
+   *
+   * Set up an element to do image post processing.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+  g_object_class_install_property (gobject_class, ARG_IMAGE_POST,
+      g_param_spec_object ("imagepp", "Image post processing element",
+          "Image Post-Processing GStreamer element (default is NULL)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   *  GstCameraBin:imageenc:
+   *
+   * Set up an image encoder (for example, jpegenc or pngenc) element.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_IMAGE_ENC,
+      g_param_spec_object ("imageenc", "Image encoder",
+          "Image encoder GStreamer element (default is jpegenc)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   *  GstCameraBin:videopp:
+   *
+   * Set up an element to do video post processing.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_VIDEO_POST,
+      g_param_spec_object ("videopp", "Video post processing element",
+          "Video post processing GStreamer element (default is NULL)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   *  GstCameraBin:videoenc:
+   *
+   * Set up a video encoder element.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_VIDEO_ENC,
+      g_param_spec_object ("videoenc", "Video encoder",
+          "Video encoder GStreamer element (default is theoraenc)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   *  GstCameraBin:audioenc:
+   *
+   * Set up an audio encoder element.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_AUDIO_ENC,
+      g_param_spec_object ("audioenc", "Audio encoder",
+          "Audio encoder GStreamer element (default is vorbisenc)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   *  GstCameraBin:videomux:
+   *
+   * Set up a video muxer element.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_VIDEO_MUX,
+      g_param_spec_object ("videomux", "Video muxer",
+          "Video muxer GStreamer element (default is oggmux)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   *  GstCameraBin:vfsink:
+   *
+   * Set up a sink element to render frames in view finder.
+   * By default "autovideosink" will be the sink element.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_VF_SINK,
+      g_param_spec_object ("vfsink", "View finder sink",
+          "View finder sink GStreamer element (default is autovideosink)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   *  GstCameraBin:videosrc:
+   *
+   * Set up a video source element.
+   * By default "v4l2camsrc" will be the src element.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_VIDEO_SRC,
+      g_param_spec_object ("videosrc", "Video source element",
+          "Video source GStreamer element (default is v4l2camsrc)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+  /**
+   *  GstCameraBin:audiosrc:
+   *
+   * Set up an audio source element.
+   * By default "pulsesrc" will be the source element.
+   * This property can only be set while #GstCameraBin is in NULL state.
+   * The ownership of the element will be taken by #GstCameraBin.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_AUDIO_SRC,
+      g_param_spec_object ("audiosrc", "Audio source element",
+          "Audio source GStreamer element (default is pulsesrc)",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
+  /**
+   * GstCameraBin:inputcaps:
+   *
+   * The allowed modes of operation of the video source. Have in mind that it
+   * doesn't mean #GstCameraBin can operate in all those modes,
+   * it depends also on the other elements in the pipeline. Remember to
+   * gst_caps_unref after using it.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_INPUT_CAPS,
+      g_param_spec_boxed ("inputcaps", "Input caps",
+          "The allowed modes of the video source operation",
+          GST_TYPE_CAPS, G_PARAM_READABLE));
+
+  /**
+   * GstCameraBin:filter-caps:
+   *
+   * Filter video source element caps using this property.
+   * This is an alternative to #GstCamerabin::user-res-fps action
+   * signal that allows more fine grained control of video source.
+   */
+
+  g_object_class_install_property (gobject_class, ARG_FILTER_CAPS,
+      g_param_spec_boxed ("filter-caps", "Filter caps",
+          "Capsfilter caps used to control video source operation",
+          GST_TYPE_CAPS, G_PARAM_READWRITE));
+
+  /**
+   * GstCameraBin::user-start:
+   * @camera: the camera bin element
+   *
+   * Starts image capture or video recording depending on the Mode.
+   * If there is a capture already going on, does nothing.
+   * Resumes video recording if it has been paused.
+   */
+
+  camerabin_signals[USER_START_SIGNAL] =
+      g_signal_new ("user-start",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (GstCameraBinClass, user_start),
+      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+  /**
+   * GstCameraBin::user-stop:
+   * @camera: the camera bin element
+   *
+   * Stops still image preview, continuous image capture and video
+   * recording and returns to the view finder mode.
+   */
+
+  camerabin_signals[USER_STOP_SIGNAL] =
+      g_signal_new ("user-stop",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (GstCameraBinClass, user_stop),
+      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+  /**
+   * GstCameraBin::user-pause:
+   * @camera: the camera bin element
+   *
+   * Pauses video recording or resumes paused video recording.
+   * If in image mode or not recording, does nothing.
+   */
+
+  camerabin_signals[USER_PAUSE_SIGNAL] =
+      g_signal_new ("user-pause",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (GstCameraBinClass, user_pause),
+      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+  /**
+   * GstCameraBin::user-res-fps:
+   * @camera: the camera bin element
+   * @width: number of horizontal pixels
+   * @height: number of vertical pixels
+   * @fps_n: frames per second numerator
+   * @fps_d: frames per second denominator
+   *
+   * Changes the frame resolution and frames per second of the video source.
+   * The application must be aware of the resolutions supported by the camera.
+   * Supported resolutions and frame rates can be get using input-caps property.
+   *
+   * Setting @fps_n or @fps_d to 0 configures maximum framerate for the
+   * given resolution, unless in night mode when minimum is configured.
+   */
+
+  camerabin_signals[USER_RES_FPS_SIGNAL] =
+      g_signal_new ("user-res-fps",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (GstCameraBinClass, user_res_fps),
+      NULL, NULL, gst_camerabin_marshal_VOID__INT_INT_INT_INT, G_TYPE_NONE, 4,
+      G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+
+  /**
+   * GstCameraBin::user-image-res:
+   * @camera: the camera bin element
+   * @width: number of horizontal pixels
+   * @height: number of vertical pixels
+   *
+   * Changes the resolution used for still image capture.
+   * Does not affect view finder mode and video recording.
+   * Use this action signal in PAUSED or PLAYING state.
+   */
+
+  camerabin_signals[USER_IMAGE_RES_SIGNAL] =
+      g_signal_new ("user-image-res",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (GstCameraBinClass, user_image_res),
+      NULL, NULL, gst_camerabin_marshal_VOID__INT_INT, G_TYPE_NONE, 2,
+      G_TYPE_INT, G_TYPE_INT);
+
+  /**
+   * GstCameraBin::img-done:
+   * @camera: the camera bin element
+   * @filename: the name of the file just saved
+   *
+   * Signal emited when the file has just been saved. To continue taking
+   * pictures just update @filename and return TRUE, otherwise return FALSE.
+   *
+   * Don't call any #GstCameraBin method from this signal, if you do so there
+   * will be a deadlock.
+   */
+
+  camerabin_signals[IMG_DONE_SIGNAL] =
+      g_signal_new ("img-done", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstCameraBinClass, img_done),
+      g_signal_accumulator_true_handled, NULL, gst_marshal_BOOLEAN__POINTER,
+      G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+
+  klass->user_start = gst_camerabin_user_start;
+  klass->user_stop = gst_camerabin_user_stop;
+  klass->user_pause = gst_camerabin_user_pause;
+  klass->user_res_fps = gst_camerabin_user_res_fps;
+  klass->user_image_res = gst_camerabin_user_image_res;
+
+  klass->img_done = gst_camerabin_default_signal_img_done;
+
+  /* gstelement */
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_camerabin_change_state);
+
+  /* gstbin */
+  /* override handle_message to peek when video or image bin reaches eos */
+  gstbin_class->handle_message =
+      GST_DEBUG_FUNCPTR (gst_camerabin_handle_message_func);
+
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set functions
+ * initialize structure
+ */
+static void
+gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
+{
+  /* GstElementClass *klass = GST_ELEMENT_GET_CLASS (camera); */
+
+  camera->filename = g_string_new ("");
+  camera->mode = DEFAULT_MODE;
+  camera->num_img_buffers = 0;
+  camera->stop_requested = FALSE;
+  camera->paused = FALSE;
+  camera->capturing = FALSE;
+  camera->night_mode = FALSE;
+
+  camera->width = DEFAULT_WIDTH;
+  camera->height = DEFAULT_HEIGHT;
+  camera->fps_n = DEFAULT_FPS_N;
+  camera->fps_d = DEFAULT_FPS_D;
+
+  camera->image_capture_caps = NULL;
+  camera->view_finder_caps = NULL;
+  camera->allowed_caps = NULL;
+
+  camera->zoom = DEFAULT_ZOOM;
+
+  /* concurrency control */
+  camera->capture_mutex = g_mutex_new ();
+  camera->cond = g_cond_new ();
+
+  /* pad names for output and input selectors */
+  camera->pad_src_view = NULL;
+  camera->pad_view_src = NULL;
+  camera->pad_src_img = NULL;
+  camera->pad_view_img = NULL;
+  camera->pad_src_vid = NULL;
+  camera->pad_view_vid = NULL;
+  camera->srcpad_zoom_filter = NULL;
+
+  /* source elements */
+  camera->src_vid_src = NULL;
+  camera->src_filter = NULL;
+  camera->src_zoom_crop = NULL;
+  camera->src_zoom_scale = NULL;
+  camera->src_zoom_filter = NULL;
+  camera->src_out_sel = NULL;
+
+  camera->user_vf_sink = NULL;
+
+  /* image capture bin */
+  camera->imgbin = g_object_new (GST_TYPE_CAMERABIN_IMAGE, NULL);
+  gst_object_ref (camera->imgbin);
+
+  /* video capture bin */
+  camera->vidbin = g_object_new (GST_TYPE_CAMERABIN_VIDEO, NULL);
+  gst_object_ref (camera->vidbin);
+
+  camera->active_bin = NULL;
+
+  /* view finder elements */
+  camera->view_in_sel = NULL;
+  camera->view_scale = NULL;
+  camera->view_sink = NULL;
+}
+
+static void
+gst_camerabin_dispose (GObject * object)
+{
+  GstCameraBin *camera;
+
+  camera = GST_CAMERABIN (object);
+
+  GST_DEBUG_OBJECT (camera, "disposing");
+
+  gst_element_set_state (camera->imgbin, GST_STATE_NULL);
+  gst_object_unref (camera->imgbin);
+
+  gst_element_set_state (camera->vidbin, GST_STATE_NULL);
+  gst_object_unref (camera->vidbin);
+
+  camerabin_destroy_elements (camera);
+
+  camerabin_dispose_elements (camera);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_camerabin_finalize (GObject * object)
+{
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_camerabin_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstCameraBin *camera = GST_CAMERABIN (object);
+
+  switch (prop_id) {
+    case ARG_MUTE:
+      gst_camerabin_video_set_mute (GST_CAMERABIN_VIDEO (camera->vidbin),
+          g_value_get_boolean (value));
+      break;
+    case ARG_ZOOM:
+      g_atomic_int_set (&camera->zoom, g_value_get_int (value));
+      gst_camerabin_setup_zoom (camera);
+      break;
+    case ARG_MODE:
+      gst_camerabin_change_mode (camera, g_value_get_enum (value));
+      break;
+    case ARG_FILENAME:
+      gst_camerabin_change_filename (camera, g_value_get_string (value));
+      break;
+    case ARG_VIDEO_POST:
+      if (GST_STATE (camera->vidbin) != GST_STATE_NULL) {
+        GST_WARNING_OBJECT (camera,
+            "can't use set element until next video bin NULL to READY state change");
+      }
+      gst_camerabin_video_set_post (GST_CAMERABIN_VIDEO (camera->vidbin),
+          g_value_get_object (value));
+      break;
+    case ARG_VIDEO_ENC:
+      if (GST_STATE (camera->vidbin) != GST_STATE_NULL) {
+        GST_WARNING_OBJECT (camera,
+            "can't use set element until next video bin NULL to READY state change");
+      }
+      gst_camerabin_video_set_video_enc (GST_CAMERABIN_VIDEO (camera->vidbin),
+          g_value_get_object (value));
+      break;
+    case ARG_AUDIO_ENC:
+      if (GST_STATE (camera->vidbin) != GST_STATE_NULL) {
+        GST_WARNING_OBJECT (camera,
+            "can't use set element until next video bin NULL to READY state change");
+      }
+      gst_camerabin_video_set_audio_enc (GST_CAMERABIN_VIDEO (camera->vidbin),
+          g_value_get_object (value));
+      break;
+    case ARG_VIDEO_MUX:
+      if (GST_STATE (camera->vidbin) != GST_STATE_NULL) {
+        GST_WARNING_OBJECT (camera->vidbin,
+            "can't use set element until next video bin NULL to READY state change");
+      }
+      gst_camerabin_video_set_muxer (GST_CAMERABIN_VIDEO (camera->vidbin),
+          g_value_get_object (value));
+      break;
+    case ARG_IMAGE_POST:
+      if (GST_STATE (camera->imgbin) != GST_STATE_NULL) {
+        GST_WARNING_OBJECT (camera,
+            "can't use set element until next image bin NULL to READY state change");
+      }
+      gst_camerabin_image_set_postproc (GST_CAMERABIN_IMAGE (camera->imgbin),
+          g_value_get_object (value));
+      break;
+    case ARG_IMAGE_ENC:
+      if (GST_STATE (camera->imgbin) != GST_STATE_NULL) {
+        GST_WARNING_OBJECT (camera,
+            "can't use set element until next image bin NULL to READY state change");
+      }
+      gst_camerabin_image_set_encoder (GST_CAMERABIN_IMAGE (camera->imgbin),
+          g_value_get_object (value));
+      break;
+    case ARG_VF_SINK:
+      if (GST_STATE (camera) != GST_STATE_NULL) {
+        GST_ELEMENT_ERROR (camera, CORE, FAILED,
+            ("camerabin must be in NULL state when setting the view finder element"),
+            (NULL));
+      } else {
+        if (camera->user_vf_sink)
+          gst_object_unref (camera->user_vf_sink);
+        camera->user_vf_sink = g_value_get_object (value);
+        gst_object_ref (camera->user_vf_sink);
+      }
+      break;
+    case ARG_VIDEO_SRC:
+      if (GST_STATE (camera) != GST_STATE_NULL) {
+        GST_ELEMENT_ERROR (camera, CORE, FAILED,
+            ("camerabin must be in NULL state when setting the video source element"),
+            (NULL));
+      } else {
+        if (camera->user_vid_src)
+          gst_object_unref (camera->user_vid_src);
+        camera->user_vid_src = g_value_get_object (value);
+        gst_object_ref (camera->user_vid_src);
+      }
+      break;
+    case ARG_AUDIO_SRC:
+      if (GST_STATE (camera->vidbin) != GST_STATE_NULL) {
+        GST_WARNING_OBJECT (camera,
+            "can't use set element until next video bin NULL to READY state change");
+      }
+      gst_camerabin_video_set_audio_src (GST_CAMERABIN_VIDEO (camera->vidbin),
+          g_value_get_object (value));
+      break;
+    case ARG_FILTER_CAPS:
+      GST_OBJECT_LOCK (camera);
+      if (camera->view_finder_caps) {
+        gst_caps_unref (camera->view_finder_caps);
+      }
+      camera->view_finder_caps = gst_caps_copy (gst_value_get_caps (value));
+      GST_OBJECT_UNLOCK (camera);
+      gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_camerabin_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstCameraBin *camera = GST_CAMERABIN (object);
+
+  switch (prop_id) {
+    case ARG_FILENAME:
+      g_value_set_string (value, camera->filename->str);
+      break;
+    case ARG_MODE:
+      g_value_set_enum (value, camera->mode);
+      break;
+    case ARG_MUTE:
+      g_value_set_boolean (value,
+          gst_camerabin_video_get_mute (GST_CAMERABIN_VIDEO (camera->vidbin)));
+      break;
+    case ARG_ZOOM:
+      g_value_set_int (value, g_atomic_int_get (&camera->zoom));
+      break;
+    case ARG_IMAGE_POST:
+      g_value_set_object (value,
+          gst_camerabin_image_get_postproc (GST_CAMERABIN_IMAGE
+              (camera->imgbin)));
+      break;
+    case ARG_IMAGE_ENC:
+      g_value_set_object (value,
+          gst_camerabin_image_get_encoder (GST_CAMERABIN_IMAGE
+              (camera->imgbin)));
+      break;
+    case ARG_VIDEO_POST:
+      g_value_set_object (value,
+          gst_camerabin_video_get_post (GST_CAMERABIN_VIDEO (camera->vidbin)));
+      break;
+    case ARG_VIDEO_ENC:
+      g_value_set_object (value,
+          gst_camerabin_video_get_video_enc (GST_CAMERABIN_VIDEO
+              (camera->vidbin)));
+      break;
+    case ARG_AUDIO_ENC:
+      g_value_set_object (value,
+          gst_camerabin_video_get_audio_enc (GST_CAMERABIN_VIDEO
+              (camera->vidbin)));
+      break;
+    case ARG_VIDEO_MUX:
+      g_value_set_object (value,
+          gst_camerabin_video_get_muxer (GST_CAMERABIN_VIDEO (camera->vidbin)));
+      break;
+    case ARG_VF_SINK:
+      g_value_set_object (value, camera->user_vf_sink);
+      break;
+    case ARG_VIDEO_SRC:
+      g_value_set_object (value, camera->src_vid_src);
+      break;
+    case ARG_AUDIO_SRC:
+      g_value_set_object (value,
+          gst_camerabin_video_get_audio_src (GST_CAMERABIN_VIDEO
+              (camera->vidbin)));
+      break;
+    case ARG_INPUT_CAPS:
+      gst_value_set_caps (value, gst_camerabin_get_allowed_input_caps (camera));
+      break;
+    case ARG_FILTER_CAPS:
+      gst_value_set_caps (value, camera->view_finder_caps);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/*
+ * GstElement functions implementation
+ */
+
+static GstStateChangeReturn
+gst_camerabin_change_state (GstElement * element, GstStateChange transition)
+{
+  GstCameraBin *camera = GST_CAMERABIN (element);
+  GstStateChangeReturn ret;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      if (!camerabin_create_elements (camera)) {
+        ret = GST_STATE_CHANGE_FAILURE;
+        goto done;
+      }
+      /* Lock to control image and video bin state separately
+         from view finder */
+      gst_element_set_locked_state (camera->imgbin, TRUE);
+      gst_element_set_locked_state (camera->vidbin, TRUE);
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      camerabin_setup_src_elements (camera);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      /* If using autovideosink, set view finder sink properties
+         now that actual sink has been created. */
+      camerabin_setup_view_elements (camera);
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      gst_element_set_locked_state (camera->imgbin, FALSE);
+      gst_element_set_locked_state (camera->vidbin, FALSE);
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      GST_LOG_OBJECT (camera, "PAUSED to READY");
+      g_mutex_lock (camera->capture_mutex);
+      if (camera->capturing) {
+        GST_WARNING_OBJECT (camera, "was capturing when changing to READY");
+        camera->capturing = FALSE;
+        /* Reset capture and don't wait for capturing to finish properly.
+           Proper capturing should have been finished before going to READY. */
+        gst_camerabin_reset_to_view_finder (camera);
+        g_cond_signal (camera->cond);
+      }
+      g_mutex_unlock (camera->capture_mutex);
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      camerabin_destroy_elements (camera);
+      break;
+    default:
+      break;
+  }
+
+done:
+
+  return ret;
+}
+
+/*
+ * GstBin functions implementation
+ */
+
+/* Peek eos messages but don't interfere with bin msg handling */
+static void
+gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg)
+{
+  GstCameraBin *camera = GST_CAMERABIN (bin);
+
+  switch (GST_MESSAGE_TYPE (msg)) {
+    case GST_MESSAGE_EOS:
+      if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->vidbin)) {
+        /* Video eos */
+        GST_DEBUG_OBJECT (camera,
+            "got video eos message, stopping video capture");
+        g_mutex_lock (camera->capture_mutex);
+        camera->capturing = FALSE;
+        g_cond_signal (camera->cond);
+        g_mutex_unlock (camera->capture_mutex);
+      } else if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->imgbin)) {
+        /* Image eos */
+        GST_DEBUG_OBJECT (camera, "got image eos message");
+        /* Unblock pad to process next buffer */
+        gst_pad_set_blocked_async (camera->pad_src_img, FALSE,
+            (GstPadBlockCallback) image_pad_blocked, camera);
+      }
+      break;
+    default:
+      break;
+  }
+  GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
+}
+
+/*
+ * Action signal function implementation
+ */
+
+static void
+gst_camerabin_user_start (GstCameraBin * camera)
+{
+
+  GST_INFO_OBJECT (camera, "starting capture");
+  if (camera->paused) {
+    gst_camerabin_user_pause (camera);
+    return;
+  }
+
+  if (!camera->active_bin) {
+    GST_INFO_OBJECT (camera, "mode not explicitly set by application");
+    gst_camerabin_change_mode (camera, camera->mode);
+  }
+
+  if (g_str_equal (camera->filename->str, "")) {
+    GST_ELEMENT_ERROR (camera, CORE, FAILED,
+        ("set filename before starting capture"), (NULL));
+    return;
+  }
+
+  g_mutex_lock (camera->capture_mutex);
+  if (camera->capturing) {
+    GST_WARNING_OBJECT (camera, "capturing \"%s\" ongoing, set new filename",
+        camera->filename->str);
+    g_mutex_unlock (camera->capture_mutex);
+    return;
+  }
+  g_mutex_unlock (camera->capture_mutex);
+
+  g_object_set (G_OBJECT (camera->active_bin), "filename",
+      camera->filename->str, NULL);
+
+  if (camera->active_bin == camera->imgbin) {
+    gst_camerabin_start_image_capture (camera);
+  } else if (camera->active_bin == camera->vidbin) {
+    gst_camerabin_start_video_recording (camera);
+  }
+}
+
+static void
+gst_camerabin_user_stop (GstCameraBin * camera)
+{
+  GST_INFO_OBJECT (camera, "stopping %s capture",
+      camera->mode ? "video" : "image");
+  gst_camerabin_do_stop (camera);
+  gst_camerabin_reset_to_view_finder (camera);
+}
+
+static void
+gst_camerabin_user_pause (GstCameraBin * camera)
+{
+  if (camera->active_bin == camera->vidbin) {
+    if (!camera->paused) {
+      GST_INFO_OBJECT (camera, "pausing capture");
+
+      /* Bring all camerabin elements to PAUSED */
+      gst_element_set_locked_state (camera->vidbin, FALSE);
+      gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED);
+
+      /* Switch to view finder mode */
+      g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE,
+          "active-pad", camera->pad_src_view, NULL);
+
+      /* Enable view finder mode in v4l2camsrc */
+      if (g_object_class_find_property (G_OBJECT_GET_CLASS
+              (camera->src_vid_src), "capture-mode")) {
+        g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 0, NULL);
+      }
+
+      /* Set view finder to PLAYING and leave videobin PAUSED */
+      gst_element_set_locked_state (camera->vidbin, TRUE);
+      gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PLAYING);
+
+      camera->paused = TRUE;
+    } else {
+      GST_INFO_OBJECT (camera, "unpausing capture");
+
+      /* Bring all camerabin elements to PAUSED */
+      gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED);
+
+      /* Switch to video recording mode */
+      g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", TRUE,
+          "active-pad", camera->pad_src_vid, NULL);
+
+      /* Enable video recording mode in v4l2camsrc */
+      if (g_object_class_find_property (G_OBJECT_GET_CLASS
+              (camera->src_vid_src), "capture-mode")) {
+        g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 2, NULL);
+      }
+
+      /* Bring all camerabin elements to PLAYING */
+      gst_element_set_locked_state (camera->vidbin, FALSE);
+      gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PLAYING);
+      gst_element_set_locked_state (camera->vidbin, TRUE);
+
+      camera->paused = FALSE;
+    }
+    GST_DEBUG_OBJECT (camera, "pause done");
+  } else {
+    GST_WARNING ("pausing in image capture mode disabled");
+  }
+}
+
+static void
+gst_camerabin_user_res_fps (GstCameraBin * camera, gint width, gint height,
+    gint fps_n, gint fps_d)
+{
+  GstState state;
+
+  GST_INFO_OBJECT (camera, "switching resolution to %dx%d and fps to %d/%d",
+      width, height, fps_n, fps_d);
+
+  state = GST_STATE (camera);
+  gst_element_set_state (GST_ELEMENT (camera), GST_STATE_READY);
+  camera->width = width;
+  camera->height = height;
+  camera->fps_n = fps_n;
+  camera->fps_d = fps_d;
+  gst_element_set_state (GST_ELEMENT (camera), state);
+}
+
+static void
+gst_camerabin_user_image_res (GstCameraBin * camera, gint width, gint height)
+{
+  GstStructure *structure;
+  GstCaps *new_caps = NULL;
+  guint32 format = 0;
+
+  g_return_if_fail (camera != NULL);
+
+  if (width && height && camera->view_finder_caps) {
+    /* Use view finder mode caps as a basis */
+    structure = gst_caps_get_structure (camera->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 format according to current videosrc format */
+    format = get_srcpad_current_format (camera->src_vid_src);
+    if (format) {
+      gst_caps_set_simple (new_caps, "format", GST_TYPE_FOURCC, format, NULL);
+    }
+
+    /* Set allowed framerate for the resolution. */
+    gst_camerabin_set_allowed_framerate (camera, new_caps);
+
+    /* Reset the format to match with view finder mode caps */
+    if (gst_structure_get_fourcc (structure, "format", &format)) {
+      gst_caps_set_simple (new_caps, "format", GST_TYPE_FOURCC, format, NULL);
+    }
+  }
+
+  GST_INFO_OBJECT (camera,
+      "init filter caps for image capture %" GST_PTR_FORMAT, new_caps);
+  gst_caps_replace (&camera->image_capture_caps, new_caps);
+}
+
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and pad templates
+ * register the features
+ */
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_camerabin_debug, "camerabin", 0, "CameraBin");
+
+  return gst_element_register (plugin, "camerabin",
+      GST_RANK_NONE, GST_TYPE_CAMERABIN);
+}
+
+/* this is the structure that gstreamer looks for to register plugins
+ */
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "camerabin",
+    "High level api for DC (Digital Camera) application",
+    plugin_init, VERSION, "LGPL", "Nokia", "http://www.nokia.com/")
diff --git a/gst/camerabin/gstcamerabin.h b/gst/camerabin/gstcamerabin.h
new file mode 100644 (file)
index 0000000..809ce72
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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 __GST_CAMERABIN_H__
+#define __GST_CAMERABIN_H__
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gstbin.h>
+#include <gst/interfaces/photography.h>
+
+#include "camerabinimage.h"
+#include "camerabinvideo.h"
+
+G_BEGIN_DECLS
+
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_CAMERABIN \
+  (gst_camerabin_get_type())
+#define GST_CAMERABIN(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAMERABIN,GstCameraBin))
+#define GST_CAMERABIN_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAMERABIN,GstCameraBinClass))
+#define GST_IS_CAMERABIN(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAMERABIN))
+#define GST_IS_CAMERABIN_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAMERABIN))
+
+typedef struct _GstCameraBin GstCameraBin;
+typedef struct _GstCameraBinClass GstCameraBinClass;
+
+/**
+ * GstCameraBin:
+ *
+ * The opaque #GstCameraBin structure.
+ */
+
+struct _GstCameraBin
+{
+  GstPipeline parent;
+
+  /* private */
+  GString *filename;
+  gint mode;                    /* MODE_IMAGE or MODE_VIDEO */
+  guint num_img_buffers;        /* no of image buffers captured */
+  gboolean stop_requested;      /* TRUE if capturing stop needed */
+  gboolean paused;              /* TRUE if capturing paused */
+
+  /* resolution and frames per second of image captured by v4l2 device */
+  gint width;
+  gint height;
+  gint fps_n;
+  gint fps_d;
+
+  /* Caps applied to capsfilters when taking still image */
+  GstCaps *image_capture_caps;
+
+  /* Caps applied to capsfilters when in view finder mode */
+  GstCaps *view_finder_caps;
+
+  /* Caps that videosrc supports */
+  GstCaps *allowed_caps;
+
+  /* The digital zoom (from 100% to 1000%) */
+  gint zoom;
+
+  /* concurrency control */
+  GMutex *capture_mutex;
+  GCond *cond;
+  gboolean capturing;
+
+  /* pad names for output and input selectors */
+  GstPad *pad_src_view;
+  GstPad *pad_view_src;
+  GstPad *pad_src_img;
+  GstPad *pad_view_img;
+  GstPad *pad_src_vid;
+  GstPad *pad_view_vid;
+
+  GstPad *srcpad_zoom_filter;
+
+  GstElement *imgbin;           /* bin that holds image capturing elements */
+  GstElement *vidbin;           /*  bin that holds video capturing elements */
+  GstElement *active_bin;       /* image or video bin that is currently in use */
+
+  /* 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;
+
+  /* view finder elements */
+  GstElement *view_in_sel;
+  GstElement *view_scale;
+  GstElement *view_sink;
+
+  /* User configurable elements */
+  GstElement *user_vid_src;
+  GstElement *user_vf_sink;
+
+  /* Night mode handling */
+  gboolean night_mode;
+  gint pre_night_fps_n;
+  gint pre_night_fps_d;
+};
+
+/**
+ * GstCameraBinClass:
+ *
+ * The #GstCameraBin class structure.
+ */
+struct _GstCameraBinClass
+{
+  GstPipelineClass parent_class;
+
+  /* action signals */
+
+  void (*user_start) (GstCameraBin * camera);
+  void (*user_stop) (GstCameraBin * camera);
+  void (*user_pause) (GstCameraBin * camera);
+  void (*user_res_fps) (GstCameraBin * camera, gint width, gint height,
+      gint fps_n, gint fps_d);
+  void (*user_image_res) (GstCameraBin * camera, gint width, gint height);
+
+  /* signals (callback) */
+
+    gboolean (*img_done) (GstCameraBin * camera, GString * filename);
+};
+
+/**
+ * GstCameraBinMode:
+ * @MODE_IMAGE: image capture
+ * @MODE_VIDEO: video capture
+ *
+ * Capture mode to use.
+ */
+typedef enum
+{
+  MODE_IMAGE = 0,
+  MODE_VIDEO
+} GstCameraBinMode;
+
+GType gst_camerabin_get_type (void);
+
+G_END_DECLS
+
+#endif /* #ifndef __GST_CAMERABIN_H__ */
diff --git a/gst/camerabin/gstcamerabincolorbalance.c b/gst/camerabin/gstcamerabincolorbalance.c
new file mode 100644 (file)
index 0000000..73a325d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+/*
+ * Includes
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstcamerabincolorbalance.h"
+#include "gstcamerabin.h"
+
+/*
+ * static functions implementation
+ */
+
+static const GList *
+gst_camerabin_color_balance_list_channels (GstColorBalance * cb)
+{
+  if (cb && GST_CAMERABIN (cb)->src_vid_src) {
+    GstColorBalance *cbl = GST_COLOR_BALANCE (GST_CAMERABIN (cb)->src_vid_src);
+    return gst_color_balance_list_channels (cbl);
+  } else {
+    return NULL;
+  }
+}
+
+static void
+gst_camerabin_color_balance_set_value (GstColorBalance * cb,
+    GstColorBalanceChannel * channel, gint value)
+{
+  if (cb && GST_CAMERABIN (cb)->src_vid_src) {
+    GstColorBalance *cbl = GST_COLOR_BALANCE (GST_CAMERABIN (cb)->src_vid_src);
+    gst_color_balance_set_value (cbl, channel, value);
+  }
+}
+
+static gint
+gst_camerabin_color_balance_get_value (GstColorBalance * cb,
+    GstColorBalanceChannel * channel)
+{
+  if (cb && GST_CAMERABIN (cb)->src_vid_src) {
+    GstColorBalance *cbl = GST_COLOR_BALANCE (GST_CAMERABIN (cb)->src_vid_src);
+    return gst_color_balance_get_value (cbl, channel);
+  } else {
+    return 0;
+  }
+}
+
+/*
+ * extern functions implementation
+ */
+
+void
+gst_camerabin_color_balance_init (GstColorBalanceClass * iface)
+{
+  /* FIXME: to get the same type as v4l2src */
+  GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
+  iface->list_channels = gst_camerabin_color_balance_list_channels;
+  iface->set_value = gst_camerabin_color_balance_set_value;
+  iface->get_value = gst_camerabin_color_balance_get_value;
+}
diff --git a/gst/camerabin/gstcamerabincolorbalance.h b/gst/camerabin/gstcamerabincolorbalance.h
new file mode 100644 (file)
index 0000000..442a23b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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 __GST_CAMERA_COLOR_BALANCE_H__
+#define __GST_CAMERA_COLOR_BALANCE_H__
+
+#include <gst/interfaces/colorbalance.h>
+
+extern void gst_camerabin_color_balance_init (GstColorBalanceClass * iface);
+
+#endif /* #ifndef __GST_CAMERA_COLOR_BALANCE_H__ */
diff --git a/gst/camerabin/gstcamerabinphotography.c b/gst/camerabin/gstcamerabinphotography.c
new file mode 100644 (file)
index 0000000..8a95bcd
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
+ *
+ * Photography interface implementation for camerabin.
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstcamerabinphotography.h"
+#include "gstcamerabin.h"
+
+GST_DEBUG_CATEGORY_STATIC (camerabinphoto_debug);
+#define GST_CAT_DEFAULT camerabinphoto_debug
+
+#define PHOTOGRAPHY_IS_OK(photo_elem) (GST_IS_ELEMENT (photo_elem) && \
+                                       gst_element_implements_interface (photo_elem, GST_TYPE_PHOTOGRAPHY))
+
+#define GST_PHOTOGRAPHY_IMPL_TEMPLATE(function_name, param_type) \
+static gboolean \
+gst_camerabin_set_ ## function_name (GstPhotography *photo, param_type param) \
+{ \
+  GstCameraBin *camera; \
+  gboolean ret = FALSE; \
+  g_return_val_if_fail (photo != NULL, FALSE); \
+  camera = GST_CAMERABIN (photo); \
+  if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) { \
+    ret = gst_photography_set_ ## function_name (GST_PHOTOGRAPHY (camera->src_vid_src), param); \
+  } \
+  return ret; \
+} \
+static gboolean \
+gst_camerabin_get_ ## function_name (GstPhotography *photo, param_type * param) \
+{ \
+  GstCameraBin *camera; \
+  gboolean ret = FALSE; \
+  g_return_val_if_fail (photo != NULL, FALSE); \
+  camera = GST_CAMERABIN (photo); \
+  if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) { \
+    ret = gst_photography_get_ ## function_name (GST_PHOTOGRAPHY (camera->src_vid_src), param); \
+  } \
+  return ret; \
+}
+
+GST_PHOTOGRAPHY_IMPL_TEMPLATE (ev_compensation, gfloat);
+GST_PHOTOGRAPHY_IMPL_TEMPLATE (iso_speed, guint);
+GST_PHOTOGRAPHY_IMPL_TEMPLATE (white_balance_mode, GstWhiteBalanceMode);
+GST_PHOTOGRAPHY_IMPL_TEMPLATE (colour_tone_mode, GstColourToneMode);
+GST_PHOTOGRAPHY_IMPL_TEMPLATE (flash_mode, GstFlashMode);
+
+static gboolean
+gst_camerabin_set_zoom (GstPhotography * photo, gfloat zoom)
+{
+  GstCameraBin *camera;
+
+  g_return_val_if_fail (photo != NULL, FALSE);
+
+  camera = GST_CAMERABIN (photo);
+
+  /* camerabin can zoom by itself */
+  g_object_set (camera, "zoom", (gint) (CLAMP (zoom, 1.0, 10.0) * 100), NULL);
+
+  return TRUE;
+}
+
+static gboolean
+gst_camerabin_get_zoom (GstPhotography * photo, gfloat * zoom)
+{
+  GstCameraBin *camera;
+  gint cb_zoom = 0;
+
+  g_return_val_if_fail (photo != NULL, FALSE);
+
+  camera = GST_CAMERABIN (photo);
+
+  g_object_get (camera, "zoom", &cb_zoom, NULL);
+  *zoom = (gfloat) (cb_zoom / 100.0);
+
+  return TRUE;
+}
+
+static gboolean
+gst_camerabin_set_scene_mode (GstPhotography * photo, GstSceneMode scene_mode)
+{
+  GstCameraBin *camera;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (photo != NULL, FALSE);
+
+  camera = GST_CAMERABIN (photo);
+
+  if (scene_mode == GST_PHOTOGRAPHY_SCENE_MODE_NIGHT) {
+    GST_DEBUG ("enabling night mode, lowering fps");
+    /* Make camerabin select the lowest allowed frame rate */
+    camera->night_mode = TRUE;
+    /* Remember frame rate before setting night mode */
+    camera->pre_night_fps_n = camera->fps_n;
+    camera->pre_night_fps_d = camera->fps_d;
+    g_signal_emit_by_name (camera, "user-res-fps", camera->width,
+        camera->height, 0, 0, 0);
+  } else {
+    if (camera->night_mode) {
+      GST_DEBUG ("disabling night mode, restoring fps to %d/%d",
+          camera->pre_night_fps_n, camera->pre_night_fps_d);
+      camera->night_mode = FALSE;
+      g_signal_emit_by_name (camera, "user-res-fps", camera->width,
+          camera->height, camera->pre_night_fps_n, camera->pre_night_fps_d, 0);
+    }
+  }
+
+  if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+    ret = gst_photography_set_scene_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
+        scene_mode);
+  }
+  return ret;
+}
+
+static gboolean
+gst_camerabin_get_scene_mode (GstPhotography * photo, GstSceneMode * scene_mode)
+{
+  GstCameraBin *camera;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (photo != NULL, FALSE);
+
+  camera = GST_CAMERABIN (photo);
+
+  if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+    ret = gst_photography_get_scene_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
+        scene_mode);
+  }
+  return ret;
+}
+
+static GstPhotoCaps
+gst_camerabin_get_capabilities (GstPhotography * photo)
+{
+  GstCameraBin *camera;
+  /* camerabin can zoom by itself */
+  GstPhotoCaps pcaps = GST_PHOTOGRAPHY_CAPS_ZOOM;
+
+  g_return_val_if_fail (photo != NULL, FALSE);
+
+  camera = GST_CAMERABIN (photo);
+
+  if (GST_IS_ELEMENT (camera->src_vid_src) &&
+      gst_element_implements_interface (camera->src_vid_src,
+          GST_TYPE_PHOTOGRAPHY)) {
+    GstPhotography *p2 = GST_PHOTOGRAPHY (camera->src_vid_src);
+    pcaps |= gst_photography_get_capabilities (p2);
+  }
+
+  return pcaps;
+}
+
+static void
+gst_camerabin_set_autofocus (GstPhotography * photo, gboolean on)
+{
+  GstCameraBin *camera;
+
+  g_return_if_fail (photo != NULL);
+
+  camera = GST_CAMERABIN (photo);
+
+  GST_DEBUG_OBJECT (camera, "setting autofocus %s", on ? "ON" : "OFF");
+
+  if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+    gst_photography_set_autofocus (GST_PHOTOGRAPHY (camera->src_vid_src), on);
+  }
+}
+
+
+void
+gst_camerabin_photography_init (GstPhotographyInterface * iface)
+{
+  GST_DEBUG_CATEGORY_INIT (camerabinphoto_debug, "camerabinphoto", 0,
+      "Camerabin photography interface debugging");
+
+  GST_INFO ("initing");
+
+  iface->set_ev_compensation = gst_camerabin_set_ev_compensation;
+  iface->get_ev_compensation = gst_camerabin_get_ev_compensation;
+
+  iface->set_iso_speed = gst_camerabin_set_iso_speed;
+  iface->get_iso_speed = gst_camerabin_get_iso_speed;
+
+  iface->set_white_balance_mode = gst_camerabin_set_white_balance_mode;
+  iface->get_white_balance_mode = gst_camerabin_get_white_balance_mode;
+
+  iface->set_colour_tone_mode = gst_camerabin_set_colour_tone_mode;
+  iface->get_colour_tone_mode = gst_camerabin_get_colour_tone_mode;
+
+  iface->set_scene_mode = gst_camerabin_set_scene_mode;
+  iface->get_scene_mode = gst_camerabin_get_scene_mode;
+
+  iface->set_flash_mode = gst_camerabin_set_flash_mode;
+  iface->get_flash_mode = gst_camerabin_get_flash_mode;
+
+  iface->set_zoom = gst_camerabin_set_zoom;
+  iface->get_zoom = gst_camerabin_get_zoom;
+
+  iface->get_capabilities = gst_camerabin_get_capabilities;
+
+  iface->set_autofocus = gst_camerabin_set_autofocus;
+}
diff --git a/gst/camerabin/gstcamerabinphotography.h b/gst/camerabin/gstcamerabinphotography.h
new file mode 100644 (file)
index 0000000..721efab
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
+ *
+ * Photography interface implementation for camerabin
+ *
+ * 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_PHOTOGRAPHY_H__
+#define __GST_CAMERABIN_PHOTOGRAPHY_H__
+
+#include <gst/interfaces/photography.h>
+
+void gst_camerabin_photography_init (GstPhotographyInterface * iface);
+
+#endif /* #ifndef __GST_CAMERABIN_PHOTOGRAPHY_H__ */
diff --git a/gst/camerabin/gstcamerabinxoverlay.c b/gst/camerabin/gstcamerabinxoverlay.c
new file mode 100644 (file)
index 0000000..7a84765
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+/*
+ * Includes
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstcamerabinxoverlay.h"
+#include "gstcamerabin.h"
+
+/*
+ * static functions implementation
+ */
+
+static void
+gst_camerabin_expose (GstXOverlay * overlay)
+{
+  if (overlay && GST_CAMERABIN (overlay)->view_sink) {
+    GstXOverlay *xoverlay = GST_X_OVERLAY (GST_CAMERABIN (overlay)->view_sink);
+    gst_x_overlay_expose (xoverlay);
+  }
+}
+
+static void
+gst_camerabin_set_xwindow_id (GstXOverlay * overlay, gulong xwindow_id)
+{
+  if (overlay && GST_CAMERABIN (overlay)->view_sink) {
+    GstXOverlay *xoverlay = GST_X_OVERLAY (GST_CAMERABIN (overlay)->view_sink);
+    gst_x_overlay_set_xwindow_id (xoverlay, xwindow_id);
+  }
+}
+
+static void
+gst_camerabin_set_event_handling (GstXOverlay * overlay, gboolean handle_events)
+{
+  if (overlay && GST_CAMERABIN (overlay)->view_sink) {
+    GstXOverlay *xoverlay = GST_X_OVERLAY (GST_CAMERABIN (overlay)->view_sink);
+    gst_x_overlay_handle_events (xoverlay, handle_events);
+  }
+}
+
+/*
+ * extern functions implementation
+ */
+
+void
+gst_camerabin_xoverlay_init (GstXOverlayClass * iface)
+{
+  iface->set_xwindow_id = gst_camerabin_set_xwindow_id;
+  iface->expose = gst_camerabin_expose;
+  iface->handle_events = gst_camerabin_set_event_handling;
+}
diff --git a/gst/camerabin/gstcamerabinxoverlay.h b/gst/camerabin/gstcamerabinxoverlay.h
new file mode 100644 (file)
index 0000000..b9e9d9b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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 __GST_CAMERAXOVERLAY_H__
+#define __GST_CAMERAXOVERLAY_H__
+
+#include <gst/interfaces/xoverlay.h>
+
+extern void gst_camerabin_xoverlay_init (GstXOverlayClass * iface);
+
+#endif /* #ifndef __GST_CAMERAXOVERLAY_H__ */