summaryrefslogtreecommitdiffstats
path: root/gst/camerabin
diff options
context:
space:
mode:
authorDave Robillard <dave@drobilla.net>2009-06-19 21:03:10 -0400
committerDave Robillard <dave@drobilla.net>2009-06-19 21:03:10 -0400
commit23953f27c870c42ce369d717bc15b7f8001691a1 (patch)
tree19b3999c7b0c36fcefcbbcaaaa9102f612670ca7 /gst/camerabin
parentd365eafd8f2cdb1ded93fe4bd95e568026abf0da (diff)
parent925e83ee60c5406b2e5f0f39b0da0f90370efc27 (diff)
downloadgst-plugins-bad-23953f27c870c42ce369d717bc15b7f8001691a1.tar.gz
gst-plugins-bad-23953f27c870c42ce369d717bc15b7f8001691a1.tar.bz2
gst-plugins-bad-23953f27c870c42ce369d717bc15b7f8001691a1.zip
Merge branch 'fdo' into lv2
Diffstat (limited to 'gst/camerabin')
-rw-r--r--gst/camerabin/Makefile.am8
-rw-r--r--gst/camerabin/camerabingeneral.c5
-rw-r--r--gst/camerabin/camerabinimage.c107
-rw-r--r--gst/camerabin/camerabinimage.h15
-rw-r--r--gst/camerabin/camerabinpreview.c257
-rw-r--r--gst/camerabin/camerabinpreview.h37
-rw-r--r--gst/camerabin/camerabinvideo.c10
-rw-r--r--gst/camerabin/gstcamerabin.c681
-rw-r--r--gst/camerabin/gstcamerabin.h25
-rw-r--r--gst/camerabin/gstcamerabinphotography.c319
-rw-r--r--gst/camerabin/gstcamerabinxoverlay.c73
-rw-r--r--gst/camerabin/gstcamerabinxoverlay.h28
12 files changed, 1144 insertions, 421 deletions
diff --git a/gst/camerabin/Makefile.am b/gst/camerabin/Makefile.am
index 3691af85..d5085da9 100644
--- a/gst/camerabin/Makefile.am
+++ b/gst/camerabin/Makefile.am
@@ -14,28 +14,28 @@ EXTRA_DIST = gstcamerabin-marshal.list
plugin_LTLIBRARIES = libgstcamerabin.la
libgstcamerabin_la_SOURCES = gstcamerabin.c \
- gstcamerabinxoverlay.c \
gstcamerabincolorbalance.c \
camerabinimage.c \
camerabinvideo.c \
camerabingeneral.c \
+ camerabinpreview.c \
gstcamerabinphotography.c
nodist_libgstcamerabin_la_SOURCES = $(built_sources)
libgstcamerabin_la_CFLAGS = \
$(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -DGST_USE_UNSTABLE_API
libgstcamerabin_la_LIBADD = \
+ $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-$(GST_MAJORMINOR).la \
$(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \
- -lgstinterfaces-$(GST_MAJORMINOR) \
- $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-$(GST_MAJORMINOR).la
+ -lgstinterfaces-$(GST_MAJORMINOR)
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 \
+ camerabinpreview.h \
gstcamerabinphotography.h
diff --git a/gst/camerabin/camerabingeneral.c b/gst/camerabin/camerabingeneral.c
index 627e0328..d9d9a202 100644
--- a/gst/camerabin/camerabingeneral.c
+++ b/gst/camerabin/camerabingeneral.c
@@ -150,9 +150,9 @@ gst_camerabin_try_add_element (GstBin * bin, GstElement * new_elem)
}
/* 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));
+ GST_DEBUG ("adding %" GST_PTR_FORMAT " to %s:%s", new_elem,
+ 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 */
@@ -185,7 +185,6 @@ 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),
diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c
index e4ff6b86..da979f0f 100644
--- a/gst/camerabin/camerabinimage.c
+++ b/gst/camerabin/camerabinimage.c
@@ -30,17 +30,13 @@
* <informalexample>
* <programlisting>
*-----------------------------------------------------------------------------
- * (src0) -> queue ->
- * -> [post proc] -> tee <
- * (src1) -> imageenc -> metadatamuxer -> filesink
+ *
+ * -> [post proc] -> csp -> 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.
*
@@ -150,24 +146,16 @@ gst_camerabin_image_init (GstCameraBinImage * img,
{
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;
}
@@ -196,6 +184,7 @@ gst_camerabin_image_change_state (GstElement * element,
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstCameraBinImage *img = GST_CAMERABIN_IMAGE (element);
+ GstObject *camerabin = NULL;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
@@ -204,15 +193,25 @@ gst_camerabin_image_change_state (GstElement * element,
}
/* Allow setting filename when image bin in READY state */
gst_element_set_locked_state (img->sink, TRUE);
+ GST_INFO_OBJECT (img, "locking imagebin->sink state to %s",
+ gst_element_state_get_name (GST_STATE (img->sink)));
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- gst_element_set_locked_state (img->sink, FALSE);
+ if (!g_str_equal (img->filename->str, "")) {
+ GST_INFO_OBJECT (img, "preparing image with filename: %s",
+ img->filename->str);
+ gst_element_set_locked_state (img->sink, FALSE);
+ } else {
+ GST_INFO_OBJECT (img, "keep sink locked, we have no filename yet");
+ }
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_INFO_OBJECT (img, "write image with filename: %s",
+ img->filename->str);
gst_element_set_locked_state (img->sink, TRUE);
gst_element_set_state (img->sink, GST_STATE_NULL);
+ g_string_assign (img->filename, "");
break;
default:
break;
@@ -221,6 +220,14 @@ gst_camerabin_image_change_state (GstElement * element,
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ camerabin = gst_element_get_parent (img);
+ /* Write debug graph to file */
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camerabin),
+ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE |
+ GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS, "imagebin.playing");
+ gst_object_unref (camerabin);
+ break;
case GST_STATE_CHANGE_READY_TO_NULL:
gst_camerabin_image_destroy_elements (img);
break;
@@ -261,11 +268,18 @@ gst_camerabin_image_set_property (GObject * object, guint prop_id,
switch (prop_id) {
case PROP_FILENAME:
g_string_assign (bin->filename, g_value_get_string (value));
+ GST_INFO_OBJECT (bin, "received filename: '%s'", bin->filename->str);
if (bin->sink) {
- g_object_set (G_OBJECT (bin->sink), "location", bin->filename->str,
- NULL);
+ if (!g_str_equal (bin->filename->str, "")) {
+ g_object_set (G_OBJECT (bin->sink), "location", bin->filename->str,
+ NULL);
+ gst_element_set_locked_state (bin->sink, FALSE);
+ gst_element_sync_state_with_parent (bin->sink);
+ } else {
+ GST_INFO_OBJECT (bin, "empty filename");
+ }
} else {
- GST_INFO ("no sink, not setting name yet");
+ GST_INFO_OBJECT (bin, "no sink, not setting name yet");
}
break;
default:
@@ -369,17 +383,17 @@ done:
* 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
+ * img->sinkpad ! [ post process !] csp ! encoder ! metadata ! filesink
*
* 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;
+ GstPad *sinkpad = NULL, *img_sinkpad = NULL;
gboolean ret = FALSE;
GstBin *imgbin = NULL;
+ GstElement *csp = NULL;
g_return_val_if_fail (img != NULL, FALSE);
@@ -403,23 +417,18 @@ gst_camerabin_image_create_elements (GstCameraBinImage * img)
img_sinkpad = gst_element_get_static_pad (img->post, "sink");
}
- /* Create tee */
- if (!(img->tee = gst_camerabin_create_and_add_element (imgbin, "tee"))) {
+ /* Add colorspace converter */
+ if (!(csp =
+ gst_camerabin_create_and_add_element (imgbin, "ffmpegcolorspace"))) {
goto done;
}
/* Set up sink ghost pad for img bin */
if (!img_sinkpad) {
- img_sinkpad = gst_element_get_static_pad (img->tee, "sink");
+ img_sinkpad = gst_element_get_static_pad (csp, "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;
@@ -452,33 +461,14 @@ gst_camerabin_image_create_elements (GstCameraBinImage * img)
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);
}
@@ -502,27 +492,14 @@ 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;
}
@@ -530,6 +507,7 @@ gst_camerabin_image_destroy_elements (GstCameraBinImage * img)
void
gst_camerabin_image_set_encoder (GstCameraBinImage * img, GstElement * encoder)
{
+ GST_DEBUG ("setting image encoder %" GST_PTR_FORMAT, encoder);
if (img->user_enc)
gst_object_unref (img->user_enc);
if (encoder)
@@ -542,6 +520,7 @@ void
gst_camerabin_image_set_postproc (GstCameraBinImage * img,
GstElement * postproc)
{
+ GST_DEBUG ("setting image postprocessing element %" GST_PTR_FORMAT, postproc);
if (img->post)
gst_object_unref (img->post);
if (postproc)
diff --git a/gst/camerabin/camerabinimage.h b/gst/camerabin/camerabinimage.h
index c05f5498..8214e9cb 100644
--- a/gst/camerabin/camerabinimage.h
+++ b/gst/camerabin/camerabinimage.h
@@ -24,20 +24,17 @@
#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;
@@ -48,21 +45,12 @@ struct _GstCameraBinImage
/* 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;
};
@@ -86,5 +74,4 @@ GstElement *gst_camerabin_image_get_encoder (GstCameraBinImage * img);
GstElement *gst_camerabin_image_get_postproc (GstCameraBinImage * img);
G_END_DECLS
-
-#endif /* #ifndef __CAMERABIN_IMAGE_H__ */
+#endif /* #ifndef __CAMERABIN_IMAGE_H__ */
diff --git a/gst/camerabin/camerabinpreview.c b/gst/camerabin/camerabinpreview.c
new file mode 100644
index 00000000..b64b2143
--- /dev/null
+++ b/gst/camerabin/camerabinpreview.c
@@ -0,0 +1,257 @@
+/*
+* GStreamer
+* Copyright (C) 2009 Nokia Corporation <multimedia@maemo.org>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Library General Public
+* License as published by the Free Software Foundation; either
+* version 2 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Library General Public License for more details.
+*
+* You should have received a copy of the GNU Library General Public
+* License along with this library; if not, write to the
+* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+* Boston, MA 02111-1307, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <string.h>
+
+#include "camerabingeneral.h"
+#include "camerabinpreview.h"
+
+static void
+save_result (GstElement * sink, GstBuffer * buf, GstPad * pad, gpointer data)
+{
+ GstBuffer **p_buf = (GstBuffer **) data;
+
+ *p_buf = gst_buffer_ref (buf);
+
+ GST_DEBUG ("received converted buffer %p with caps %" GST_PTR_FORMAT,
+ *p_buf, GST_BUFFER_CAPS (*p_buf));
+}
+
+static gboolean
+create_element (const gchar * factory_name, const gchar * elem_name,
+ GstElement ** element, GError ** err)
+{
+ *element = gst_element_factory_make (factory_name, elem_name);
+ if (*element)
+ return TRUE;
+
+ if (err && *err == NULL) {
+ *err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN,
+ "cannot create element '%s' - please check your GStreamer installation",
+ factory_name);
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * gst_camerabin_preview_create_pipeline:
+ * @camera: camerabin object
+ *
+ * Create a preview converter pipeline.
+ *
+ * Returns: TRUE if pipeline was constructed, otherwise FALSE.
+ */
+gboolean
+gst_camerabin_preview_create_pipeline (GstCameraBin * camera)
+{
+ GstElement *src, *csp, *filter, *vscale, *sink;
+ GError *error = NULL;
+
+ if (!camera->preview_caps) {
+ return FALSE;
+ }
+
+ /* Destroy old pipeline, if any */
+ gst_camerabin_preview_destroy_pipeline (camera);
+
+ GST_DEBUG ("creating elements");
+
+ if (!create_element ("appsrc", "prev_src", &src, &error) ||
+ !create_element ("videoscale", NULL, &vscale, &error) ||
+ !create_element ("ffmpegcolorspace", NULL, &csp, &error) ||
+ !create_element ("capsfilter", NULL, &filter, &error) ||
+ !create_element ("fakesink", "prev_sink", &sink, &error))
+ goto no_elements;
+
+ camera->preview_pipeline = gst_pipeline_new ("preview-pipeline");
+ if (camera->preview_pipeline == NULL)
+ goto no_pipeline;
+
+ GST_DEBUG ("adding elements");
+ gst_bin_add_many (GST_BIN (camera->preview_pipeline),
+ src, csp, filter, vscale, sink, NULL);
+
+ g_object_set (filter, "caps", camera->preview_caps, NULL);
+ g_object_set (sink, "preroll-queue-len", 1, "signal-handoffs", TRUE, NULL);
+ g_object_set (vscale, "method", 0, NULL);
+
+ /* FIXME: linking is still way too expensive, profile this properly */
+ GST_DEBUG ("linking src->vscale");
+ if (!gst_element_link_pads (src, "src", vscale, "sink"))
+ return FALSE;
+
+ GST_DEBUG ("linking vscale->csp");
+ if (!gst_element_link_pads (vscale, "src", csp, "sink"))
+ return FALSE;
+
+ GST_DEBUG ("linking csp->capsfilter");
+ if (!gst_element_link_pads (csp, "src", filter, "sink"))
+ return FALSE;
+
+ GST_DEBUG ("linking capsfilter->sink");
+ if (!gst_element_link_pads (filter, "src", sink, "sink"))
+ return FALSE;
+
+ return TRUE;
+
+ /* ERRORS */
+no_elements:
+ {
+ g_warning ("Could not make preview pipeline: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+no_pipeline:
+ {
+ g_warning ("Could not make preview pipeline: %s",
+ "no pipeline (unknown error)");
+ return FALSE;
+ }
+}
+
+
+/**
+ * gst_camerabin_preview_destroy_pipeline:
+ * @camera: camerabin object
+ *
+ * Destroy preview converter pipeline.
+ */
+void
+gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera)
+{
+ if (camera->preview_pipeline) {
+ gst_element_set_state (camera->preview_pipeline, GST_STATE_NULL);
+ gst_object_unref (camera->preview_pipeline);
+ camera->preview_pipeline = NULL;
+ }
+}
+
+
+/**
+ * gst_camerabin_preview_convert:
+ * @camera: camerabin object
+ * @buf: #GstBuffer that contains the frame to be converted
+ *
+ * Create a preview image of the given frame.
+ *
+ * Returns: converted preview image, or NULL if operation failed.
+ */
+GstBuffer *
+gst_camerabin_preview_convert (GstCameraBin * camera, GstBuffer * buf)
+{
+ GstMessage *msg;
+ GstBuffer *result = NULL;
+ GError *error = NULL;
+ GstBus *bus;
+ GstElement *src, *sink;
+ GstBufferFlag bflags;
+ GstFlowReturn fret;
+
+ g_return_val_if_fail (GST_BUFFER_CAPS (buf) != NULL, NULL);
+
+ if (camera->preview_pipeline == NULL) {
+ GST_WARNING ("pipeline is NULL");
+ goto no_pipeline;
+ }
+
+ src = gst_bin_get_by_name (GST_BIN (camera->preview_pipeline), "prev_src");
+ sink = gst_bin_get_by_name (GST_BIN (camera->preview_pipeline), "prev_sink");
+
+ if (!src || !sink) {
+ GST_WARNING ("pipeline doesn't have src / sink elements");
+ goto no_pipeline;
+ }
+
+ g_object_set (src, "size", (gint64) GST_BUFFER_SIZE (buf),
+ "blocksize", (guint32) GST_BUFFER_SIZE (buf),
+ "caps", GST_BUFFER_CAPS (buf), "num-buffers", 1, NULL);
+
+ g_signal_connect (sink, "handoff", G_CALLBACK (save_result), &result);
+
+ bflags = GST_BUFFER_FLAGS (buf);
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
+
+ GST_DEBUG ("running conversion pipeline");
+ gst_element_set_state (camera->preview_pipeline, GST_STATE_PLAYING);
+
+ g_signal_emit_by_name (src, "push-buffer", buf, &fret);
+
+ /* TODO: do we need to use a bus poll, can we just register a callback to the bus? */
+ bus = gst_element_get_bus (camera->preview_pipeline);
+ msg =
+ gst_bus_poll (bus, GST_MESSAGE_ERROR | GST_MESSAGE_EOS, 25 * GST_SECOND);
+
+ if (msg) {
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_EOS:{
+ if (result) {
+ GST_DEBUG ("preview image successful: result = %p", result);
+ } else {
+ GST_WARNING ("EOS but no result frame?!");
+ }
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ gchar *dbg = NULL;
+
+ gst_message_parse_error (msg, &error, &dbg);
+ if (error) {
+ g_warning ("Could not make preview image: %s", error->message);
+ GST_DEBUG ("%s [debug: %s]", error->message, GST_STR_NULL (dbg));
+ g_error_free (error);
+ } else {
+ g_warning ("Could not make preview image (and NULL error!)");
+ }
+ g_free (dbg);
+ result = NULL;
+ break;
+ }
+ default:{
+ g_return_val_if_reached (NULL);
+ }
+ }
+ } else {
+ g_warning ("Could not make preview image: %s", "timeout during conversion");
+ result = NULL;
+ }
+
+ g_signal_handlers_disconnect_by_func (sink, G_CALLBACK (save_result),
+ &result);
+ gst_element_set_state (camera->preview_pipeline, GST_STATE_READY);
+
+ GST_BUFFER_FLAGS (buf) = bflags;
+
+ return result;
+
+ /* ERRORS */
+no_pipeline:
+ {
+ g_warning ("Could not make preview image: %s",
+ "no pipeline (unknown error)");
+ return NULL;
+ }
+}
diff --git a/gst/camerabin/camerabinpreview.h b/gst/camerabin/camerabinpreview.h
new file mode 100644
index 00000000..cadefed6
--- /dev/null
+++ b/gst/camerabin/camerabinpreview.h
@@ -0,0 +1,37 @@
+/*
+* GStreamer
+* Copyright (C) 2009 Nokia Corporation <multimedia@maemo.org>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Library General Public
+* License as published by the Free Software Foundation; either
+* version 2 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Library General Public License for more details.
+*
+* You should have received a copy of the GNU Library General Public
+* License along with this library; if not, write to the
+* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+* Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __CAMERABINPREVIEW_H__
+#define __CAMERABINPREVIEW_H__
+
+#include <gst/gst.h>
+
+#include "gstcamerabin.h"
+
+G_BEGIN_DECLS
+ gboolean gst_camerabin_preview_create_pipeline (GstCameraBin * camera);
+
+void gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera);
+
+GstBuffer *gst_camerabin_preview_convert (GstCameraBin * camera,
+ GstBuffer * buf);
+
+G_END_DECLS
+#endif /* __CAMERABINPREVIEW_H__ */
diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c
index 14a95064..fef9ac26 100644
--- a/gst/camerabin/camerabinvideo.c
+++ b/gst/camerabin/camerabinvideo.c
@@ -290,6 +290,7 @@ gst_camerabin_video_change_state (GstElement * element,
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstCameraBinVideo *vid = GST_CAMERABIN_VIDEO (element);
+ GstObject *camerabin = NULL;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
@@ -325,6 +326,13 @@ gst_camerabin_video_change_state (GstElement * element,
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ camerabin = gst_element_get_parent (vid);
+ /* Write debug graph to file */
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camerabin),
+ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE |
+ GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS, "videobin.playing");
+ gst_object_unref (camerabin);
+
if (vid->pending_eos) {
/* Video bin is still paused, so push eos directly to video queue */
GST_DEBUG_OBJECT (vid, "pushing pending eos");
@@ -641,7 +649,7 @@ gst_camerabin_video_create_elements (GstCameraBinVideo * vid)
}
/* 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);
+ g_object_set (G_OBJECT (queue), "leaky", 2, "max-size-buffers", 1, NULL);
/* Set up src ghost pad for video bin */
vid_srcpad = gst_element_get_static_pad (queue, "src");
diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c
index 2ddb34e0..f1cd897a 100644
--- a/gst/camerabin/gstcamerabin.c
+++ b/gst/camerabin/gstcamerabin.c
@@ -36,18 +36,19 @@
* <refsect2>
* <title>Example launch line</title>
* |[
- * gst-launch -v -m camerabin filename=test.jpeg
+ * gst-launch -v -m camerabin
* ]|
* </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.
+ * signal. Once the image has been captured, "image-captured" gst message is
+ * posted to the bus and capturing another image is possible. If application
+ * has set #GstCameraBin:preview-caps property, then a "preview-image" gst
+ * message is posted to bus containing preview image formatted according to
+ * specified caps. Eventually when image has been saved #GstCameraBin::img-done
+ * signal is emitted.
*
* Available resolutions can be taken from the #GstCameraBin:inputcaps property.
* Image capture resolution can be set with #GstCameraBin::user-image-res
@@ -74,7 +75,8 @@
* 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.
+ * opened the video device. However it is possible to configure photography
+ * settings in NULL state and camerabin will try applying them later.
* </para>
* </refsect2>
* <refsect2>
@@ -105,16 +107,23 @@
/*
* The pipeline in the camerabin is
*
- * "image bin"
- * videosrc ! crop ! scale ! out-sel <------> in-sel ! scale ! ffmpegcsp ! vfsink
- * "video bin"
+ * videosrc [ ! ffmpegcsp ] ! capsfilter ! crop ! scale ! capsfilter ! \
+ * out-sel name=osel ! queue name=img_q
*
- * it is possible to have 'ffmpegcolorspace' and 'capsfilter' just after
- * v4l2camsrc
+ * View finder:
+ * osel. ! in-sel name=isel ! scale ! capsfilter [ ! ffmpegcsp ] ! vfsink
+ *
+ * Image bin:
+ * img_q. [ ! ipp ] ! ffmpegcsp ! imageenc ! metadatamux ! filesink
+ *
+ * Video bin:
+ * osel. ! tee name=t ! queue ! videoenc ! videomux name=mux ! filesink
+ * t. ! queue ! isel.
+ * audiosrc ! queue ! audioconvert ! volume ! audioenc ! mux.
*
* The properties of elements are:
*
- * vfsink - "sync", FALSE, "qos", FALSE
+ * vfsink - "sync", FALSE, "qos", FALSE, "async", FALSE
* output-selector - "resend-latest", FALSE
* input-selector - "select-all", TRUE
*/
@@ -134,11 +143,11 @@
/* 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 "camerabinpreview.h"
#include "gstcamerabin-marshal.h"
@@ -176,7 +185,8 @@ enum
ARG_VIDEO_SRC,
ARG_AUDIO_SRC,
ARG_INPUT_CAPS,
- ARG_FILTER_CAPS
+ ARG_FILTER_CAPS,
+ ARG_PREVIEW_CAPS
};
/*
@@ -216,6 +226,11 @@ static guint camerabin_signals[LAST_SIGNAL];
#define DEFAULT_VIEW_SINK "autovideosink"
+#define CAMERABIN_MAX_VF_WIDTH 848
+#define CAMERABIN_MAX_VF_HEIGHT 848
+#define PREVIEW_MESSAGE_NAME "preview-image"
+#define IMG_CAPTURED_MESSAGE_NAME "image-captured"
+
/*
* static helper functions declaration
*/
@@ -258,6 +273,12 @@ gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
static gboolean
gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer,
gpointer u_data);
+static gboolean
+gst_camerabin_have_queue_data (GstPad * pad, GstMiniObject * mini_obj,
+ gpointer u_data);
+static gboolean
+gst_camerabin_have_src_buffer (GstPad * pad, GstBuffer * buffer,
+ gpointer u_data);
static void gst_camerabin_reset_to_view_finder (GstCameraBin * camera);
@@ -275,6 +296,8 @@ static const GValue *gst_camerabin_find_better_framerate (GstCameraBin * camera,
static void
gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps);
+static void gst_camerabin_finish_image_capture (GstCameraBin * camera);
+
/*
* GObject callback functions declaration
*/
@@ -355,11 +378,7 @@ 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 (iface_type == GST_TYPE_COLOR_BALANCE) {
if (camera->src_vid_src) {
return GST_IS_COLOR_BALANCE (camera->src_vid_src);
}
@@ -375,9 +394,8 @@ gst_camerabin_iface_supported (GstImplementsInterface * iface, GType iface_type)
return FALSE;
}
} else if (iface_type == GST_TYPE_PHOTOGRAPHY) {
- if (camera->src_vid_src) {
- return GST_IS_PHOTOGRAPHY (camera->src_vid_src);
- }
+ /* Always support photography interface */
+ return TRUE;
}
return FALSE;
@@ -402,12 +420,6 @@ camerabin_init_interfaces (GType type)
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,
@@ -428,9 +440,6 @@ camerabin_init_interfaces (GType type)
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);
@@ -470,6 +479,14 @@ camerabin_setup_src_elements (GstCameraBin * camera)
0));
}
+ /* Update photography interface settings */
+ if (GST_IS_ELEMENT (camera->src_vid_src) &&
+ gst_element_implements_interface (camera->src_vid_src,
+ GST_TYPE_PHOTOGRAPHY)) {
+ gst_photography_set_config (GST_PHOTOGRAPHY (camera->src_vid_src),
+ &camera->photo_settings);
+ }
+
if (camera->width > 0 && camera->height > 0) {
gst_structure_set (st,
"width", G_TYPE_INT, camera->width,
@@ -561,12 +578,6 @@ camerabin_create_src_elements (GstCameraBin * camera)
gst_camerabin_create_and_add_element (cbin, "output-selector")))
goto done;
- camera->srcpad_videosrc =
- gst_element_get_static_pad (camera->src_vid_src, "src");
-
- 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")) {
@@ -637,14 +648,16 @@ camerabin_create_view_elements (GstCameraBin * camera)
&& (GST_PAD_DIRECTION (GST_PAD (pads->data)) != GST_PAD_SINK)) {
pads = g_list_next (pads);
}
- camera->pad_view_img = GST_PAD (pads->data);
+ camera->pad_view_src = GST_PAD (pads->data);
+ /* Add videoscale in case we need to downscale frame for view finder */
if (!(camera->view_scale =
gst_camerabin_create_and_add_element (GST_BIN (camera),
"videoscale"))) {
goto error;
}
+ /* Add capsfilter to maintain aspect ratio while scaling */
if (!(camera->aspect_filter =
gst_camerabin_create_and_add_element (GST_BIN (camera),
"capsfilter"))) {
@@ -694,30 +707,44 @@ camerabin_create_elements (GstCameraBin * 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");
+ /* Add image queue */
+ if (!(camera->img_queue =
+ gst_camerabin_create_and_add_element (GST_BIN (camera), "queue"))) {
+ goto done;
+ }
+
+ /* To avoid deadlock, we won't restrict the image queue size */
+ /* FIXME: actually we would like to have some kind of restriction here (size),
+ but deadlocks must be handled somehow... */
+ g_object_set (G_OBJECT (camera->img_queue), "max-size-time",
+ G_GUINT64_CONSTANT (0), NULL);
+ g_object_set (G_OBJECT (camera->img_queue), "max-size-bytes",
+ G_GUINT64_CONSTANT (0), NULL);
+ g_object_set (G_OBJECT (camera->img_queue), "max-size-buffers",
+ G_GUINT64_CONSTANT (0), NULL);
+
+ camera->pad_src_queue = gst_element_get_static_pad (camera->img_queue, "src");
+
+ gst_pad_add_data_probe (camera->pad_src_queue,
+ G_CALLBACK (gst_camerabin_have_queue_data), camera);
+
+ /* Add image bin */
+ if (!gst_camerabin_add_element (GST_BIN (camera), camera->imgbin)) {
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));
+
+ /* Create view finder elements */
+ if (!camerabin_create_view_elements (camera)) {
+ GST_WARNING_OBJECT (camera, "creating view finder elements failed");
goto done;
}
@@ -776,10 +803,6 @@ camerabin_destroy_elements (GstCameraBin * camera)
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;
@@ -793,14 +816,9 @@ camerabin_destroy_elements (GstCameraBin * camera)
camera->pad_src_view = NULL;
}
- if (camera->srcpad_zoom_filter) {
- gst_object_unref (camera->srcpad_zoom_filter);
- camera->srcpad_zoom_filter = NULL;
- }
-
- if (camera->srcpad_videosrc) {
- gst_object_unref (camera->srcpad_videosrc);
- camera->srcpad_videosrc = NULL;
+ if (camera->pad_src_queue) {
+ gst_object_unref (camera->pad_src_queue);
+ camera->pad_src_queue = NULL;
}
camera->view_sink = NULL;
@@ -865,13 +883,23 @@ camerabin_dispose_elements (GstCameraBin * camera)
gst_caps_unref (camera->allowed_caps);
camera->allowed_caps = NULL;
}
+
+ if (camera->preview_caps) {
+ gst_caps_unref (camera->preview_caps);
+ camera->preview_caps = NULL;
+ }
+
+ if (camera->event_tags) {
+ gst_tag_list_free (camera->event_tags);
+ camera->event_tags = NULL;
+ }
}
/*
* gst_camerabin_image_capture_continue:
* @camera: camerabin object
*
- * Check if application wants to continue image capturing by using g_signal.
+ * Notify application that image has been saved with a signal.
*
* Returns TRUE if another image should be captured, FALSE otherwise.
*/
@@ -923,7 +951,16 @@ gst_camerabin_change_mode (GstCameraBin * camera, gint mode)
gst_element_set_state (camera->active_bin, GST_STATE_NULL);
}
if (camera->mode == MODE_IMAGE) {
+ GstStateChangeReturn state_ret;
+
camera->active_bin = camera->imgbin;
+ 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;
+ }
} else if (camera->mode == MODE_VIDEO) {
camera->active_bin = camera->vidbin;
}
@@ -1114,6 +1151,46 @@ failed:
}
/*
+ * gst_camerabin_send_img_queue_event:
+ * @camera: camerabin object
+ * @event: event to be sent
+ *
+ * Send the given event to image queue.
+ */
+static void
+gst_camerabin_send_img_queue_event (GstCameraBin * camera, GstEvent * event)
+{
+ GstPad *queue_sink;
+
+ g_return_if_fail (camera != NULL);
+ g_return_if_fail (event != NULL);
+
+ queue_sink = gst_element_get_static_pad (camera->img_queue, "sink");
+ gst_pad_send_event (queue_sink, event);
+ gst_object_unref (queue_sink);
+}
+
+/*
+ * gst_camerabin_send_img_queue_custom_event:
+ * @camera: camerabin object
+ * @ev_struct: event structure to be sent
+ *
+ * Generate and send a custom event to image queue.
+ */
+static void
+gst_camerabin_send_img_queue_custom_event (GstCameraBin * camera,
+ GstStructure * ev_struct)
+{
+ GstEvent *event;
+
+ g_return_if_fail (camera != NULL);
+ g_return_if_fail (ev_struct != NULL);
+
+ event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, ev_struct);
+ gst_camerabin_send_img_queue_event (camera, event);
+}
+
+/*
* gst_camerabin_rewrite_tags_to_bin:
* @bin: bin holding tag setter elements
* @list: tag list to be written
@@ -1275,7 +1352,13 @@ gst_camerabin_rewrite_tags (GstCameraBin * camera)
}
/* Write tags */
- gst_camerabin_rewrite_tags_to_bin (GST_BIN (camera->active_bin), list);
+ if (camera->active_bin == camera->vidbin) {
+ gst_camerabin_rewrite_tags_to_bin (GST_BIN (camera->active_bin), list);
+ } else {
+ /* Image tags need to be sent as a serialized event into image queue */
+ GstEvent *tagevent = gst_event_new_tag (gst_tag_list_copy (list));
+ gst_camerabin_send_img_queue_event (camera, tagevent);
+ }
gst_tag_list_free (list);
}
@@ -1313,18 +1396,36 @@ gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps)
gst_camerabin_update_aspect_filter (camera, new_caps);
}
+/*
+ * gst_camerabin_adapt_video_resolution:
+ * @camera: camerabin object
+ * @caps: caps describing the next incoming buffer format
+ *
+ * This function adjusts capsfilter and crop elements in order to modify
+ * the incoming buffer to the resolution that application requested.
+ *
+ */
static void
gst_camerabin_adapt_video_resolution (GstCameraBin * camera, GstCaps * caps)
{
GstStructure *st;
gint width = 0, height = 0;
GstCaps *filter_caps = NULL;
+ gint top, bottom, left, right, crop;
+ gdouble ratio_w, ratio_h;
+
+ g_return_if_fail (camera->width != 0 && camera->height != 0);
/* Get width and height from caps */
st = gst_caps_get_structure (caps, 0);
gst_structure_get_int (st, "width", &width);
gst_structure_get_int (st, "height", &height);
+ if (width == camera->width && height == camera->height) {
+ GST_DEBUG_OBJECT (camera, "no adaptation with resolution needed");
+ return;
+ }
+
GST_DEBUG_OBJECT (camera,
"changing %dx%d -> %dx%d filter to %" GST_PTR_FORMAT,
camera->width, camera->height, width, height, camera->src_filter);
@@ -1336,7 +1437,31 @@ gst_camerabin_adapt_video_resolution (GstCameraBin * camera, GstCaps * caps)
"height", G_TYPE_INT, height, NULL);
g_object_set (G_OBJECT (camera->src_filter), "caps", filter_caps, NULL);
gst_caps_unref (filter_caps);
- /* FIXME: implement cropping according to requested aspect ratio */
+
+ /* Crop if requested aspect ratio differs from incoming frame aspect ratio */
+
+ /* Don't override original crop values in case we have zoom applied */
+ g_object_get (G_OBJECT (camera->src_zoom_crop), "top", &top, "bottom",
+ &bottom, "left", &left, "right", &right, NULL);
+
+ ratio_w = (gdouble) width / camera->width;
+ ratio_h = (gdouble) height / camera->height;
+
+ if (ratio_w < ratio_h) {
+ crop = height - (camera->height * ratio_w);
+ top += crop / 2;
+ bottom += crop / 2;
+ } else {
+ crop = width - (camera->width * ratio_h);
+ left += crop / 2;
+ right += crop / 2;
+ }
+
+ GST_INFO_OBJECT (camera,
+ "updating crop: left:%d, right:%d, top:%d, bottom:%d", left, right, top,
+ bottom);
+ g_object_set (G_OBJECT (camera->src_zoom_crop), "top", top, "bottom", bottom,
+ "left", left, "right", right, NULL);
}
/*
@@ -1353,11 +1478,13 @@ img_capture_prepared (gpointer data, GstCaps * caps)
GstStructure *st, *new_st;
gint i;
const gchar *field_name;
+ gboolean adapt = FALSE;
GST_INFO_OBJECT (camera, "image capture prepared");
/* It is possible we are about to get something else that we requested */
if (!gst_caps_is_equal (camera->image_capture_caps, caps)) {
+ adapt = TRUE;
/* If capture preparation has added new fields to requested caps,
we need to copy them */
st = gst_caps_get_structure (camera->image_capture_caps, 0);
@@ -1378,14 +1505,17 @@ img_capture_prepared (gpointer data, GstCaps * caps)
/* Update capsfilters */
gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps);
- /* If incoming buffer resolution is different from what application
- requested, then we need to fix this in camerabin */
- gst_camerabin_adapt_video_resolution (camera, caps);
-
+ if (adapt) {
+ /* If incoming buffer resolution is different from what application
+ requested, then we can fix this in camerabin */
+ gst_camerabin_adapt_video_resolution (camera, 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);
+
+ if (!GST_CAMERABIN_IMAGE (camera->imgbin)->elements_created) {
+ gst_element_set_state (camera->imgbin, GST_STATE_READY);
+ }
}
/*
@@ -1431,8 +1561,11 @@ gst_camerabin_start_image_capture (GstCameraBin * camera)
}
if (!wait_for_prepare) {
- gst_camerabin_rewrite_tags (camera);
- state_ret = gst_element_set_state (camera->imgbin, GST_STATE_PLAYING);
+ /* Image queue's srcpad data probe will set imagebin to PLAYING */
+ state_ret = gst_element_set_state (camera->imgbin, GST_STATE_PAUSED);
+ GST_DEBUG_OBJECT (camera, "setting imagebin to paused: %s",
+ gst_element_state_change_return_get_name (state_ret));
+
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,
@@ -1535,12 +1668,48 @@ image_pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
GST_DEBUG_OBJECT (camera, "%s %s:%s",
blocked ? "blocking" : "unblocking", GST_DEBUG_PAD_NAME (pad));
+}
+
+/*
+ * gst_camerabin_send_preview:
+ * @camera: camerabin object
+ * @buffer: received buffer
+ *
+ * Convert given buffer to desired preview format and send is as a #GstMessage
+ * to application.
+ *
+ * Returns: TRUE always
+ */
+static gboolean
+gst_camerabin_send_preview (GstCameraBin * camera, GstBuffer * buffer)
+{
+ GstBuffer *prev = NULL;
+ GstStructure *s;
+ GstMessage *msg;
+ gboolean ret = FALSE;
+
+ GST_DEBUG_OBJECT (camera, "creating preview");
+
+ prev = gst_camerabin_preview_convert (camera, buffer);
- if (blocked && (pad == camera->srcpad_videosrc)) {
- /* 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_DEBUG_OBJECT (camera, "preview created: %p", prev);
+
+ if (prev) {
+ s = gst_structure_new (PREVIEW_MESSAGE_NAME,
+ "buffer", GST_TYPE_BUFFER, prev, NULL);
+
+ msg = gst_message_new_element (GST_OBJECT (camera), s);
+
+ GST_DEBUG_OBJECT (camera, "sending message with preview image");
+
+ if (gst_element_post_message (GST_ELEMENT (camera), msg) == FALSE) {
+ GST_WARNING_OBJECT (camera,
+ "This element has no bus, therefore no message sent!");
+ }
+ ret = TRUE;
}
+
+ return ret;
}
/*
@@ -1549,27 +1718,19 @@ image_pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
* @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.
+ * Buffer probe called before sending each buffer to image queue.
+ * Generates and sends preview image as gst message if requested.
*/
static gboolean
gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
gpointer u_data)
{
GstCameraBin *camera = (GstCameraBin *) u_data;
+ GstStructure *fn_ev_struct = NULL;
gboolean ret = TRUE;
+ GstPad *os_sink = NULL;
- GST_LOG ("got buffer #%d %p with size %d", camera->num_img_buffers,
- buffer, GST_BUFFER_SIZE (buffer));
+ GST_LOG ("got buffer %p with size %d", buffer, GST_BUFFER_SIZE (buffer));
/* Image filename should be set by now */
if (g_str_equal (camera->filename->str, "")) {
@@ -1578,54 +1739,38 @@ gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
goto done;
}
- /* Check for first buffer after capture start, we want to
- pass it forward directly. */
- if (!camera->num_img_buffers) {
- goto done;
+ if (camera->preview_caps) {
+ gst_camerabin_send_preview (camera, buffer);
}
- /* Close the file of saved image */
- gst_element_set_state (camera->imgbin, GST_STATE_READY);
-
- /* Reset filename to force application set new filename */
- g_string_assign (camera->filename, "");
-
- /* Check if the application wants to continue */
- ret = gst_camerabin_image_capture_continue (camera);
+ gst_camerabin_rewrite_tags (camera);
- 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);
+ /* Send a custom event which tells the filename to image queue */
+ /* NOTE: This needs to be THE FIRST event to be sent to queue for
+ every image. It triggers imgbin state change to PLAYING. */
+ fn_ev_struct = gst_structure_new ("img-filename",
+ "filename", G_TYPE_STRING, camera->filename->str, NULL);
+ GST_DEBUG_OBJECT (camera, "sending filename event to image queue");
+ gst_camerabin_send_img_queue_custom_event (camera, fn_ev_struct);
+
+ /* Add buffer probe to outputselector's sink pad. It sends
+ EOS event to image queue. */
+ os_sink = gst_element_get_static_pad (camera->src_out_sel, "sink");
+ camera->image_captured_id = gst_pad_add_buffer_probe (os_sink,
+ G_CALLBACK (gst_camerabin_have_src_buffer), camera);
+ gst_object_unref (os_sink);
- /* 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 */
+done:
- g_mutex_lock (camera->capture_mutex);
- camera->capturing = FALSE;
- g_cond_signal (camera->cond);
- g_mutex_unlock (camera->capture_mutex);
- }
+ /* HACK: v4l2camsrc changes to view finder resolution automatically
+ after one captured still image */
+ gst_camerabin_finish_image_capture (camera);
-done:
+ gst_camerabin_reset_to_view_finder (camera);
- 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 (camera->srcpad_videosrc, TRUE,
- (GstPadBlockCallback) image_pad_blocked, camera);
- }
+ GST_DEBUG_OBJECT (camera, "switched back to viewfinder");
- return ret;
+ return TRUE;
}
/*
@@ -1655,6 +1800,120 @@ gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer,
}
/*
+ * gst_camerabin_have_src_buffer:
+ * @pad: output-selector sink pad which receives frames from video source
+ * @buffer: buffer pushed to the pad
+ * @u_data: camerabin object
+ *
+ * Buffer probe for sink pad. It sends custom eos event to image queue and
+ * notifies application by sending a "image-captured" message to GstBus.
+ * This probe is installed after image has been captured and it disconnects
+ * itself after EOS has been sent.
+ */
+static gboolean
+gst_camerabin_have_src_buffer (GstPad * pad, GstBuffer * buffer,
+ gpointer u_data)
+{
+ GstCameraBin *camera = (GstCameraBin *) u_data;
+ GstMessage *msg;
+
+ GST_LOG_OBJECT (camera, "got image buffer %p with size %d",
+ buffer, GST_BUFFER_SIZE (buffer));
+
+ /* We can't send real EOS event, since it would switch the image queue
+ into "draining mode". Therefore we send our own custom eos and
+ catch & drop it later in queue's srcpad data probe */
+ GST_DEBUG_OBJECT (camera, "sending eos to image queue");
+ gst_camerabin_send_img_queue_custom_event (camera,
+ gst_structure_new ("img-eos", NULL));
+
+ /* our work is done, disconnect */
+ gst_pad_remove_buffer_probe (pad, camera->image_captured_id);
+
+ g_mutex_lock (camera->capture_mutex);
+ camera->capturing = FALSE;
+ g_cond_signal (camera->cond);
+ g_mutex_unlock (camera->capture_mutex);
+
+ msg = gst_message_new_element (GST_OBJECT (camera),
+ gst_structure_new (IMG_CAPTURED_MESSAGE_NAME, NULL));
+
+ GST_DEBUG_OBJECT (camera, "sending 'image captured' message");
+
+ if (gst_element_post_message (GST_ELEMENT (camera), msg) == FALSE) {
+ GST_WARNING_OBJECT (camera,
+ "This element has no bus, therefore no message sent!");
+ }
+
+ return TRUE;
+}
+
+/*
+ * gst_camerabin_have_queue_data:
+ * @pad: image queue src pad leading to image bin
+ * @mini_obj: buffer or event pushed to the pad
+ * @u_data: camerabin object
+ *
+ * Buffer probe for image queue src pad leading to image bin. It sets imgbin
+ * into PLAYING mode when image buffer is passed to it. This probe also
+ * monitors our internal custom events and handles them accordingly.
+ */
+static gboolean
+gst_camerabin_have_queue_data (GstPad * pad, GstMiniObject * mini_obj,
+ gpointer u_data)
+{
+ GstCameraBin *camera = (GstCameraBin *) u_data;
+ gboolean ret = TRUE;
+
+ if (GST_IS_BUFFER (mini_obj)) {
+ GstEvent *tagevent;
+
+ GST_LOG_OBJECT (camera, "queue sending image buffer to imgbin");
+
+ tagevent = gst_event_new_tag (gst_tag_list_copy (camera->event_tags));
+ gst_element_send_event (camera->imgbin, tagevent);
+ gst_tag_list_free (camera->event_tags);
+ camera->event_tags = gst_tag_list_new ();
+ } else if (GST_IS_EVENT (mini_obj)) {
+ const GstStructure *evs;
+ GstEvent *event;
+
+ event = GST_EVENT_CAST (mini_obj);
+ evs = gst_event_get_structure (event);
+
+ GST_LOG_OBJECT (camera, "got event %s", GST_EVENT_TYPE_NAME (event));
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
+ GstTagList *tlist;
+
+ gst_event_parse_tag (event, &tlist);
+ gst_tag_list_insert (camera->event_tags, tlist, GST_TAG_MERGE_REPLACE);
+ ret = FALSE;
+ } else if (evs && gst_structure_has_name (evs, "img-filename")) {
+ const gchar *fname;
+
+ GST_LOG_OBJECT (camera, "queue setting image filename to imagebin");
+ fname = gst_structure_get_string (evs, "filename");
+ g_object_set (G_OBJECT (camera->imgbin), "filename", fname, NULL);
+
+ /* imgbin fails to start unless the filename is set */
+ gst_element_set_state (camera->imgbin, GST_STATE_PLAYING);
+ GST_LOG_OBJECT (camera, "Set imgbin to PLAYING");
+
+ ret = FALSE;
+ } else if (evs && gst_structure_has_name (evs, "img-eos")) {
+ GST_LOG_OBJECT (camera, "queue sending EOS to image pipeline");
+ gst_pad_set_blocked_async (camera->pad_src_queue, TRUE,
+ (GstPadBlockCallback) image_pad_blocked, camera);
+ gst_element_send_event (camera->imgbin, gst_event_new_eos ());
+ ret = FALSE;
+ }
+ }
+
+ return ret;
+}
+
+/*
* gst_camerabin_reset_to_view_finder:
* @camera: camerabin object
*
@@ -1667,8 +1926,8 @@ 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) {
+ /* Set video bin to READY state */
+ if (camera->active_bin == camera->vidbin) {
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");
@@ -1678,7 +1937,6 @@ gst_camerabin_reset_to_view_finder (GstCameraBin * camera)
}
/* Reset counters and flags */
- camera->num_img_buffers = 0;
camera->stop_requested = FALSE;
camera->paused = FALSE;
@@ -1688,18 +1946,6 @@ gst_camerabin_reset_to_view_finder (GstCameraBin * camera)
"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 in videosrc is blocked due to waiting for eos */
- if (camera->srcpad_videosrc && gst_pad_is_blocked (camera->srcpad_videosrc)) {
- gst_pad_set_blocked_async (camera->srcpad_videosrc, 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),
@@ -2023,6 +2269,26 @@ gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps)
}
/*
+ * gst_camerabin_finish_image_capture:
+ * @camera: camerabin object
+ *
+ * Perform finishing operations after image capture is done and
+ * returning back to view finder mode.
+ */
+static void
+gst_camerabin_finish_image_capture (GstCameraBin * camera)
+{
+ if (camera->image_capture_caps) {
+ /* If we used specific caps for image capture we need to
+ restore the caps and zoom/crop for view finder mode */
+ GST_DEBUG_OBJECT (camera, "resetting crop in camerabin");
+ g_object_set (camera->src_zoom_crop, "left", 0, "right", 0,
+ "top", 0, "bottom", 0, NULL);
+ gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+ }
+}
+
+/*
* GObject callback functions implementation
*/
@@ -2243,14 +2509,29 @@ gst_camerabin_class_init (GstCameraBinClass * klass)
/**
* 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.
+ * Caps applied to capsfilter element after videosrc [ ! ffmpegcsp ].
+ * You can use this e.g. to make sure video color format matches with
+ * encoders and other elements configured to camerabin and/or change
+ * resolution and frame rate.
*/
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",
+ "Filter video data coming from videosrc element",
+ GST_TYPE_CAPS, G_PARAM_READWRITE));
+
+ /**
+ * GstCameraBin:preview-caps:
+ *
+ * If application wants to receive a preview image, it needs to
+ * set this property to depict the desired image format caps. When
+ * this property is not set (NULL), message containing the preview
+ * image is not sent.
+ */
+
+ g_object_class_install_property (gobject_class, ARG_PREVIEW_CAPS,
+ g_param_spec_boxed ("preview-caps", "Preview caps",
+ "Caps defining the preview image format",
GST_TYPE_CAPS, G_PARAM_READWRITE));
/**
@@ -2347,9 +2628,7 @@ gst_camerabin_class_init (GstCameraBinClass * klass)
* @camera: the camera bin element
* @filename: the name of the file just saved
*
- * Signal emitted when the file has just been saved. To continue taking
- * pictures set new filename using #GstCameraBin:filename property and return
- * TRUE, otherwise return FALSE.
+ * Signal emitted when the file has just been saved.
*
* Don't call any #GstCameraBin method from this signal, if you do so there
* will be a deadlock.
@@ -2394,7 +2673,6 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->filename = g_string_new ("");
camera->mode = DEFAULT_MODE;
- camera->num_img_buffers = 0;
camera->stop_requested = FALSE;
camera->paused = FALSE;
camera->capturing = FALSE;
@@ -2405,6 +2683,8 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->fps_n = DEFAULT_FPS_N;
camera->fps_d = DEFAULT_FPS_D;
+ camera->event_tags = gst_tag_list_new ();
+
camera->image_capture_caps = NULL;
camera->view_finder_caps = NULL;
camera->allowed_caps = NULL;
@@ -2419,13 +2699,9 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
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;
- camera->srcpad_videosrc = NULL;
-
/* source elements */
camera->src_vid_src = NULL;
camera->src_filter = NULL;
@@ -2450,6 +2726,8 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->view_in_sel = NULL;
camera->view_scale = NULL;
camera->view_sink = NULL;
+
+ memset (&camera->photo_settings, 0, sizeof (GstPhotoSettings));
}
static void
@@ -2467,6 +2745,8 @@ gst_camerabin_dispose (GObject * object)
gst_element_set_state (camera->vidbin, GST_STATE_NULL);
gst_object_unref (camera->vidbin);
+ gst_camerabin_preview_destroy_pipeline (camera);
+
camerabin_destroy_elements (camera);
camerabin_dispose_elements (camera);
@@ -2527,7 +2807,7 @@ gst_camerabin_set_property (GObject * object, guint prop_id,
break;
case ARG_VIDEO_MUX:
if (GST_STATE (camera->vidbin) != GST_STATE_NULL) {
- GST_WARNING_OBJECT (camera->vidbin,
+ GST_WARNING_OBJECT (camera,
"can't use set element until next video bin NULL to READY state change");
}
gst_camerabin_video_set_muxer (GST_CAMERABIN_VIDEO (camera->vidbin),
@@ -2588,7 +2868,18 @@ gst_camerabin_set_property (GObject * object, guint prop_id,
}
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);
+ if (GST_STATE (camera) != GST_STATE_NULL) {
+ gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+ }
+ break;
+ case ARG_PREVIEW_CAPS:
+ GST_OBJECT_LOCK (camera);
+ if (camera->preview_caps) {
+ gst_caps_unref (camera->preview_caps);
+ }
+ camera->preview_caps = gst_caps_copy (gst_value_get_caps (value));
+ GST_OBJECT_UNLOCK (camera);
+ gst_camerabin_preview_create_pipeline (camera);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2661,6 +2952,9 @@ gst_camerabin_get_property (GObject * object, guint prop_id,
case ARG_FILTER_CAPS:
gst_value_set_caps (value, camera->view_finder_caps);
break;
+ case ARG_PREVIEW_CAPS:
+ gst_value_set_caps (value, camera->preview_caps);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2732,6 +3026,29 @@ done:
return ret;
}
+static gboolean
+gst_camerabin_imgbin_finished (gpointer u_data)
+{
+ GstCameraBin *camera = GST_CAMERABIN (u_data);
+
+ GST_DEBUG_OBJECT (camera, "Image encoding finished");
+
+ /* Close the file of saved image */
+ gst_element_set_state (camera->imgbin, GST_STATE_READY);
+ GST_DEBUG_OBJECT (camera, "Image pipeline set to READY");
+
+ /* Send img-done signal */
+ gst_camerabin_image_capture_continue (camera);
+
+ /* Unblock image queue pad to process next buffer */
+ gst_pad_set_blocked_async (camera->pad_src_queue, FALSE,
+ (GstPadBlockCallback) image_pad_blocked, camera);
+ GST_DEBUG_OBJECT (camera, "Queue srcpad unblocked");
+
+ /* disconnect automatically */
+ return FALSE;
+}
+
/*
* GstBin functions implementation
*/
@@ -2755,17 +3072,20 @@ gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg)
} else if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->imgbin)) {
/* Image eos */
GST_DEBUG_OBJECT (camera, "got image eos message");
-
- /* Still image capture buffer handled, restore filter caps */
- if (camera->image_capture_caps) {
- gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
- }
-
- /* Unblock pad to process next buffer */
- gst_pad_set_blocked_async (camera->srcpad_videosrc, FALSE,
- (GstPadBlockCallback) image_pad_blocked, camera);
+ g_idle_add (gst_camerabin_imgbin_finished, camera);
}
break;
+ case GST_MESSAGE_ERROR:
+ GST_DEBUG_OBJECT (camera, "error from child %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (msg));
+ g_mutex_lock (camera->capture_mutex);
+ if (camera->capturing) {
+ gst_camerabin_finish_image_capture (camera);
+ camera->capturing = FALSE;
+ g_cond_signal (camera->cond);
+ }
+ g_mutex_unlock (camera->capture_mutex);
+ break;
default:
break;
}
@@ -2807,12 +3127,11 @@ gst_camerabin_user_start (GstCameraBin * camera)
g_mutex_unlock (camera->capture_mutex);
if (camera->active_bin) {
- 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) {
+ g_object_set (G_OBJECT (camera->active_bin), "filename",
+ camera->filename->str, NULL);
gst_camerabin_start_video_recording (camera);
}
}
@@ -2821,10 +3140,13 @@ gst_camerabin_user_start (GstCameraBin * 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);
+ if (camera->active_bin == camera->vidbin) {
+ GST_INFO_OBJECT (camera, "stopping video capture");
+ gst_camerabin_do_stop (camera);
+ gst_camerabin_reset_to_view_finder (camera);
+ } else {
+ GST_INFO_OBJECT (camera, "stopping image capture isn't needed");
+ }
}
static void
@@ -2891,6 +3213,9 @@ gst_camerabin_user_res_fps (GstCameraBin * camera, gint width, gint height,
GST_INFO_OBJECT (camera, "switching resolution to %dx%d and fps to %d/%d",
width, height, fps_n, fps_d);
+ /* Interrupt ongoing capture */
+ gst_camerabin_do_stop (camera);
+
gst_element_get_state (GST_ELEMENT (camera), &state, &pending, 0);
if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING) {
GST_INFO_OBJECT (camera,
diff --git a/gst/camerabin/gstcamerabin.h b/gst/camerabin/gstcamerabin.h
index 1bb3f489..cd88c7ca 100644
--- a/gst/camerabin/gstcamerabin.h
+++ b/gst/camerabin/gstcamerabin.h
@@ -59,7 +59,6 @@ struct _GstCameraBin
/* 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 */
@@ -69,6 +68,9 @@ struct _GstCameraBin
gint fps_n;
gint fps_d;
+ /* Image tags are collected here first before sending to imgbin */
+ GstTagList *event_tags;
+
/* Caps applied to capsfilters when taking still image */
GstCaps *image_capture_caps;
@@ -78,6 +80,9 @@ struct _GstCameraBin
/* Caps that videosrc supports */
GstCaps *allowed_caps;
+ /* Caps used to create preview image */
+ GstCaps *preview_caps;
+
/* The digital zoom (from 100% to 1000%) */
gint zoom;
@@ -90,16 +95,16 @@ struct _GstCameraBin
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 *pad_src_queue;
- GstPad *srcpad_zoom_filter;
- GstPad *srcpad_videosrc;
-
+ GstElement *img_queue; /* queue for decoupling capture from
+ image-postprocessing and saving */
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 */
+ GstElement *preview_pipeline; /* pipeline for creating preview images */
/* source elements */
GstElement *src_vid_src;
@@ -123,6 +128,12 @@ struct _GstCameraBin
gboolean night_mode;
gint pre_night_fps_n;
gint pre_night_fps_d;
+
+ /* Cache the photography interface settings */
+ GstPhotoSettings photo_settings;
+
+ /* Buffer probe id for captured image handling */
+ gulong image_captured_id;
};
/**
@@ -145,7 +156,7 @@ struct _GstCameraBinClass
/* signals (callback) */
- gboolean (*img_done) (GstCameraBin * camera, const gchar * filename);
+ gboolean (*img_done) (GstCameraBin * camera, const gchar * filename);
};
/**
@@ -164,4 +175,4 @@ typedef enum
GType gst_camerabin_get_type (void);
G_END_DECLS
-#endif /* #ifndef __GST_CAMERABIN_H__ */
+#endif /* #ifndef __GST_CAMERABIN_H__ */
diff --git a/gst/camerabin/gstcamerabinphotography.c b/gst/camerabin/gstcamerabinphotography.c
index 8a95bcd7..cb17abca 100644
--- a/gst/camerabin/gstcamerabinphotography.c
+++ b/gst/camerabin/gstcamerabinphotography.c
@@ -24,6 +24,7 @@
#include <config.h>
#endif
+#include <string.h>
#include "gstcamerabinphotography.h"
#include "gstcamerabin.h"
@@ -32,38 +33,206 @@ GST_DEBUG_CATEGORY_STATIC (camerabinphoto_debug);
#define PHOTOGRAPHY_IS_OK(photo_elem) (GST_IS_ELEMENT (photo_elem) && \
gst_element_implements_interface (photo_elem, GST_TYPE_PHOTOGRAPHY))
+static void
+gst_camerabin_handle_scene_mode (GstCameraBin * camera,
+ GstSceneMode scene_mode);
+
+static gboolean
+gst_camerabin_set_ev_compensation (GstPhotography * photo,
+ gfloat ev_compensation)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.ev_compensation = ev_compensation;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_ev_compensation (GST_PHOTOGRAPHY
+ (camera->src_vid_src), ev_compensation);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_ev_compensation (GstPhotography * photo,
+ gfloat * ev_compensation)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
-#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);
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_get_ev_compensation (GST_PHOTOGRAPHY
+ (camera->src_vid_src), ev_compensation);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_iso_speed (GstPhotography * photo, guint iso_speed)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.iso_speed = iso_speed;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret = gst_photography_set_iso_speed (GST_PHOTOGRAPHY (camera->src_vid_src),
+ iso_speed);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_iso_speed (GstPhotography * photo, guint * iso_speed)
+{
+ 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_iso_speed (GST_PHOTOGRAPHY (camera->src_vid_src),
+ iso_speed);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_white_balance_mode (GstPhotography * photo,
+ GstWhiteBalanceMode white_balance_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.wb_mode = white_balance_mode;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_white_balance_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), white_balance_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_white_balance_mode (GstPhotography * photo,
+ GstWhiteBalanceMode * white_balance_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_white_balance_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), white_balance_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_colour_tone_mode (GstPhotography * photo,
+ GstColourToneMode colour_tone_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.tone_mode = colour_tone_mode;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_colour_tone_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), colour_tone_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_colour_tone_mode (GstPhotography * photo,
+ GstColourToneMode * colour_tone_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_colour_tone_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), colour_tone_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_flash_mode (GstPhotography * photo, GstFlashMode flash_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.flash_mode = flash_mode;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret = gst_photography_set_flash_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
+ flash_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_flash_mode (GstPhotography * photo, GstFlashMode * flash_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_flash_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
+ flash_mode);
+ }
+ return ret;
+}
static gboolean
gst_camerabin_set_zoom (GstPhotography * photo, gfloat zoom)
@@ -106,24 +275,10 @@ gst_camerabin_set_scene_mode (GstPhotography * photo, GstSceneMode scene_mode)
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);
- }
- }
+ /* Cache the setting */
+ camera->photo_settings.scene_mode = scene_mode;
+
+ gst_camerabin_handle_scene_mode (camera, scene_mode);
if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
ret = gst_photography_set_scene_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
@@ -186,6 +341,69 @@ gst_camerabin_set_autofocus (GstPhotography * photo, gboolean on)
}
}
+static gboolean
+gst_camerabin_set_config (GstPhotography * photo, GstPhotoSettings * config)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+ g_return_val_if_fail (photo != NULL, FALSE);
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the settings */
+ memcpy (&camera->photo_settings, config, sizeof (GstPhotoSettings));
+
+ /* Handle night mode */
+ gst_camerabin_handle_scene_mode (camera, config->scene_mode);
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_config (GST_PHOTOGRAPHY (camera->src_vid_src),
+ config);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_config (GstPhotography * photo, GstPhotoSettings * config)
+{
+ 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_config (GST_PHOTOGRAPHY (camera->src_vid_src),
+ config);
+ }
+ return ret;
+}
+
+static void
+gst_camerabin_handle_scene_mode (GstCameraBin * camera, GstSceneMode scene_mode)
+{
+ if (scene_mode == GST_PHOTOGRAPHY_SCENE_MODE_NIGHT) {
+ if (!camera->night_mode) {
+ 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 {
+ GST_DEBUG ("night mode already enabled");
+ }
+ } 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);
+ }
+ }
+}
void
gst_camerabin_photography_init (GstPhotographyInterface * iface)
@@ -219,4 +437,7 @@ gst_camerabin_photography_init (GstPhotographyInterface * iface)
iface->get_capabilities = gst_camerabin_get_capabilities;
iface->set_autofocus = gst_camerabin_set_autofocus;
+
+ iface->set_config = gst_camerabin_set_config;
+ iface->get_config = gst_camerabin_get_config;
}
diff --git a/gst/camerabin/gstcamerabinxoverlay.c b/gst/camerabin/gstcamerabinxoverlay.c
deleted file mode 100644
index 7a84765a..00000000
--- a/gst/camerabin/gstcamerabinxoverlay.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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
deleted file mode 100644
index b9e9d9b6..00000000
--- a/gst/camerabin/gstcamerabinxoverlay.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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__ */