From 53e6e4b0d5b7e17ac8b1a2d96d39ad38dc41bad7 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Mon, 9 Mar 2009 18:00:45 +0200 Subject: camerabin: crop still image capture frame if necessary If incoming still image capture frame aspect ratio differs from aspect ratio that application requested, then apply crop to the frame. --- gst/camerabin/gstcamerabin.c | 62 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 6 deletions(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 2ddb34e0..17ca9def 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -1313,18 +1313,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 +1354,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 +1395,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,10 +1422,11 @@ 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); @@ -2756,8 +2801,13 @@ gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg) /* Image eos */ GST_DEBUG_OBJECT (camera, "got image eos message"); - /* Still image capture buffer handled, restore filter caps */ + /* HACK: v4l2camsrc changes to view finder resolution automatically + after one captured still image */ if (camera->image_capture_caps) { + GST_DEBUG_OBJECT (camera, "resetting crop in camerabin"); + g_object_set (camera->src_zoom_crop, "left", 0, "right", 0, + "top", 0, "bottom", 0, NULL); + /* Still image capture buffer handled, restore filter caps */ gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps); } -- cgit v1.2.1 From 710faaef6874b95ab629e6ed89a505469abe93e5 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Mon, 20 Apr 2009 17:06:14 +0300 Subject: camerabin: allow configuring photography settings when in NULL state Cache the photography settings if set in NULL state and apply them later --- gst/camerabin/gstcamerabin.c | 18 +- gst/camerabin/gstcamerabin.h | 3 + gst/camerabin/gstcamerabinphotography.c | 315 +++++++++++++++++++++++++++----- 3 files changed, 283 insertions(+), 53 deletions(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 17ca9def..707e0789 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -74,7 +74,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. * * * @@ -375,9 +376,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; @@ -470,6 +470,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, @@ -2495,6 +2503,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 diff --git a/gst/camerabin/gstcamerabin.h b/gst/camerabin/gstcamerabin.h index 1bb3f489..16b242c0 100644 --- a/gst/camerabin/gstcamerabin.h +++ b/gst/camerabin/gstcamerabin.h @@ -123,6 +123,9 @@ struct _GstCameraBin gboolean night_mode; gint pre_night_fps_n; gint pre_night_fps_d; + + /* Cache the photography interface settings */ + GstPhotoSettings photo_settings; }; /** diff --git a/gst/camerabin/gstcamerabinphotography.c b/gst/camerabin/gstcamerabinphotography.c index 8a95bcd7..de95a196 100644 --- a/gst/camerabin/gstcamerabinphotography.c +++ b/gst/camerabin/gstcamerabinphotography.c @@ -24,6 +24,7 @@ #include #endif +#include #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); + + 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); -#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_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,65 @@ 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) { + 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); + } + } +} void gst_camerabin_photography_init (GstPhotographyInterface * iface) @@ -219,4 +433,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; } -- cgit v1.2.1 From 3ff71282937b7ab749d2445496ccba4d3b680a5e Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Wed, 22 Apr 2009 12:29:14 +0300 Subject: camerabin: don't proxy gst xoverlay interface Default view finder sink in camerabin is autovideosink which doesn't support xoverlay iface. If application decides to use another sink that supports xoverlay, then it can use the interface directly. --- gst/camerabin/Makefile.am | 2 - gst/camerabin/gstcamerabin.c | 16 +------- gst/camerabin/gstcamerabinxoverlay.c | 73 ------------------------------------ gst/camerabin/gstcamerabinxoverlay.h | 28 -------------- 4 files changed, 1 insertion(+), 118 deletions(-) delete mode 100644 gst/camerabin/gstcamerabinxoverlay.c delete mode 100644 gst/camerabin/gstcamerabinxoverlay.h (limited to 'gst/camerabin') diff --git a/gst/camerabin/Makefile.am b/gst/camerabin/Makefile.am index 3691af85..060de0c4 100644 --- a/gst/camerabin/Makefile.am +++ b/gst/camerabin/Makefile.am @@ -14,7 +14,6 @@ EXTRA_DIST = gstcamerabin-marshal.list plugin_LTLIBRARIES = libgstcamerabin.la libgstcamerabin_la_SOURCES = gstcamerabin.c \ - gstcamerabinxoverlay.c \ gstcamerabincolorbalance.c \ camerabinimage.c \ camerabinvideo.c \ @@ -33,7 +32,6 @@ libgstcamerabin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstcamerabin_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gstcamerabin.h \ - gstcamerabinxoverlay.h \ gstcamerabincolorbalance.h \ camerabinimage.h \ camerabinvideo.h \ diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 707e0789..55475589 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -135,7 +135,6 @@ /* FIXME: include #include and use _(" ") */ #include "gstcamerabin.h" -#include "gstcamerabinxoverlay.h" #include "gstcamerabincolorbalance.h" #include "gstcamerabinphotography.h" @@ -356,11 +355,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); } @@ -402,12 +397,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 +417,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); 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 - * - * 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 -#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 - * - * 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 - -extern void gst_camerabin_xoverlay_init (GstXOverlayClass * iface); - -#endif /* #ifndef __GST_CAMERAXOVERLAY_H__ */ -- cgit v1.2.1 From cad76384b3bb676dfc0fc3b8e339798264b0b0f2 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Thu, 23 Apr 2009 18:15:43 +0300 Subject: camerabin: limit the size of video bin queue leading to view finder We don't want to show old buffers in view finder. --- gst/camerabin/camerabinvideo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c index 14a95064..4729d1e4 100644 --- a/gst/camerabin/camerabinvideo.c +++ b/gst/camerabin/camerabinvideo.c @@ -641,7 +641,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"); -- cgit v1.2.1 From cbcd4e33303f5d7fc2bd6728e79f7e0c3608e06b Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Mon, 4 May 2009 10:32:10 +0300 Subject: camerabin: stop ongoing capture if changing resolution --- gst/camerabin/gstcamerabin.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'gst/camerabin') diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 55475589..d7c6d032 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -2937,6 +2937,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, -- cgit v1.2.1 From ca4bfb2e473ee6fd2ce257129a9a73e2bca1152f Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Mon, 4 May 2009 10:33:18 +0300 Subject: camerabin: ignore night mode handling if already in night mode --- gst/camerabin/gstcamerabinphotography.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/gstcamerabinphotography.c b/gst/camerabin/gstcamerabinphotography.c index de95a196..cb17abca 100644 --- a/gst/camerabin/gstcamerabinphotography.c +++ b/gst/camerabin/gstcamerabinphotography.c @@ -382,14 +382,18 @@ static void gst_camerabin_handle_scene_mode (GstCameraBin * camera, GstSceneMode scene_mode) { 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); + 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", -- cgit v1.2.1 From 430f34352212dc0954d6c6801244f38e17d24177 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Tue, 5 May 2009 14:19:46 +0300 Subject: camerabin: unblock if error occurs during capture --- gst/camerabin/gstcamerabin.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index d7c6d032..dc0dde5e 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -275,6 +275,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 */ @@ -2061,6 +2063,26 @@ gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps) gst_caps_unref (ar_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 */ @@ -2797,21 +2819,24 @@ gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg) /* Image eos */ GST_DEBUG_OBJECT (camera, "got image eos message"); - /* HACK: v4l2camsrc changes to view finder resolution automatically - after one captured still image */ - if (camera->image_capture_caps) { - GST_DEBUG_OBJECT (camera, "resetting crop in camerabin"); - g_object_set (camera->src_zoom_crop, "left", 0, "right", 0, - "top", 0, "bottom", 0, NULL); - /* Still image capture buffer handled, restore filter caps */ - gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps); - } + gst_camerabin_finish_image_capture (camera); /* Unblock pad to process next buffer */ gst_pad_set_blocked_async (camera->srcpad_videosrc, FALSE, (GstPadBlockCallback) image_pad_blocked, 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; } -- cgit v1.2.1 From 683b72a5fe0c26178855840cc1d0097df5389cc7 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Tue, 12 May 2009 12:49:33 +0300 Subject: camerabin: add debug including dot graph writing --- gst/camerabin/camerabingeneral.c | 5 ++--- gst/camerabin/camerabinimage.c | 11 +++++++++++ gst/camerabin/camerabinvideo.c | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'gst/camerabin') 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..7c29922c 100644 --- a/gst/camerabin/camerabinimage.c +++ b/gst/camerabin/camerabinimage.c @@ -196,6 +196,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: @@ -221,6 +222,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; @@ -530,6 +539,7 @@ gst_camerabin_image_destroy_elements (GstCameraBinImage * img) void gst_camerabin_image_set_encoder (GstCameraBinImage * img, GstElement * encoder) { + GST_DEBUG ("setting encoder %" GST_PTR_FORMAT, encoder); if (img->user_enc) gst_object_unref (img->user_enc); if (encoder) @@ -542,6 +552,7 @@ void gst_camerabin_image_set_postproc (GstCameraBinImage * img, GstElement * postproc) { + GST_DEBUG ("setting post processing element %" GST_PTR_FORMAT, postproc); if (img->post) gst_object_unref (img->post); if (postproc) diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c index 4729d1e4..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"); -- cgit v1.2.1 From bbf48697fbdf1c7b037c07d7876c3907336fd049 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Tue, 19 May 2009 16:58:36 +0300 Subject: camerabin: don't lose post processing element in first reset --- gst/camerabin/camerabinimage.c | 1 - 1 file changed, 1 deletion(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c index 7c29922c..99bf20b6 100644 --- a/gst/camerabin/camerabinimage.c +++ b/gst/camerabin/camerabinimage.c @@ -526,7 +526,6 @@ gst_camerabin_image_destroy_elements (GstCameraBinImage * img) gst_camerabin_remove_elements_from_bin (GST_BIN (img)); - img->post = NULL; img->tee = NULL; img->enc = NULL; img->meta_mux = NULL; -- cgit v1.2.1 From 6a47f6f594740e28dcf9176fb88260114a94f8b3 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Wed, 27 May 2009 11:33:01 +0300 Subject: camerabin: preview image sending optimization * decouple image capturing from image post-processing and encoding * post image-captured message after image is captured * post preview-image message with snapshot of captured image --- gst/camerabin/Makefile.am | 2 + gst/camerabin/camerabinimage.c | 72 +---- gst/camerabin/camerabinimage.h | 15 +- gst/camerabin/camerabinpreview.c | 257 ++++++++++++++++++ gst/camerabin/camerabinpreview.h | 37 +++ gst/camerabin/gstcamerabin.c | 550 ++++++++++++++++++++++++++++----------- gst/camerabin/gstcamerabin.h | 22 +- 7 files changed, 723 insertions(+), 232 deletions(-) create mode 100644 gst/camerabin/camerabinpreview.c create mode 100644 gst/camerabin/camerabinpreview.h (limited to 'gst/camerabin') diff --git a/gst/camerabin/Makefile.am b/gst/camerabin/Makefile.am index 060de0c4..4f382243 100644 --- a/gst/camerabin/Makefile.am +++ b/gst/camerabin/Makefile.am @@ -18,6 +18,7 @@ libgstcamerabin_la_SOURCES = gstcamerabin.c \ camerabinimage.c \ camerabinvideo.c \ camerabingeneral.c \ + camerabinpreview.c \ gstcamerabinphotography.c nodist_libgstcamerabin_la_SOURCES = $(built_sources) @@ -36,4 +37,5 @@ noinst_HEADERS = gstcamerabin.h \ camerabinimage.h \ camerabinvideo.h \ camerabingeneral.h \ + camerabinpreview.h \ gstcamerabinphotography.h diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c index 99bf20b6..338d2e3d 100644 --- a/gst/camerabin/camerabinimage.c +++ b/gst/camerabin/camerabinimage.c @@ -30,17 +30,13 @@ * * *----------------------------------------------------------------------------- - * (src0) -> queue -> - * -> [post proc] -> tee < - * (src1) -> imageenc -> metadatamuxer -> filesink + * + * -> [post proc] -> csp -> imageenc -> metadatamuxer -> filesink + * *----------------------------------------------------------------------------- * * * - * 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; } @@ -378,17 +366,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); @@ -412,23 +400,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; @@ -461,33 +444,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); } @@ -511,26 +475,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->tee = NULL; img->enc = NULL; img->meta_mux = NULL; img->sink = NULL; - img->queue = NULL; img->elements_created = FALSE; } @@ -538,7 +490,7 @@ gst_camerabin_image_destroy_elements (GstCameraBinImage * img) void gst_camerabin_image_set_encoder (GstCameraBinImage * img, GstElement * encoder) { - GST_DEBUG ("setting encoder %" GST_PTR_FORMAT, encoder); + GST_DEBUG ("setting image encoder %" GST_PTR_FORMAT, encoder); if (img->user_enc) gst_object_unref (img->user_enc); if (encoder) @@ -551,7 +503,7 @@ void gst_camerabin_image_set_postproc (GstCameraBinImage * img, GstElement * postproc) { - GST_DEBUG ("setting post processing element %" GST_PTR_FORMAT, 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 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 +* +* 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 +#include + +#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 +* +* 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 + +#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/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index dc0dde5e..22bf1465 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -36,18 +36,19 @@ * * Example launch line * |[ - * gst-launch -v -m camerabin filename=test.jpeg + * gst-launch -v -m camerabin * ]| * * * Image capture * * 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 @@ -106,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 */ @@ -139,6 +147,7 @@ #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); @@ -557,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")) { @@ -633,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"))) { @@ -690,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; } @@ -772,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; @@ -789,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; @@ -861,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. */ @@ -919,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; } @@ -1109,6 +1150,46 @@ failed: return caps; } +/* + * 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 @@ -1271,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); } @@ -1425,8 +1512,10 @@ img_capture_prepared (gpointer data, GstCaps * 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); + } } /* @@ -1472,8 +1561,8 @@ 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_READY); 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, @@ -1576,12 +1665,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; - 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, "creating preview"); + + prev = gst_camerabin_preview_convert (camera, buffer); + + 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; } /* @@ -1590,27 +1715,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, "")) { @@ -1619,54 +1736,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; } /* @@ -1695,6 +1796,120 @@ gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer, return ret; } +/* + * 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 @@ -1708,8 +1923,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"); @@ -1719,7 +1934,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; @@ -1729,18 +1943,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), @@ -2304,14 +2506,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)); /** @@ -2408,9 +2625,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. @@ -2455,7 +2670,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; @@ -2466,6 +2680,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; @@ -2480,13 +2696,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; @@ -2530,6 +2742,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); @@ -2590,7 +2804,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), @@ -2651,7 +2865,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); @@ -2724,6 +2949,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; @@ -2795,6 +3023,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 */ @@ -2818,12 +3069,7 @@ 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"); - - gst_camerabin_finish_image_capture (camera); - - /* 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: @@ -2878,12 +3124,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); } } @@ -2892,10 +3137,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 diff --git a/gst/camerabin/gstcamerabin.h b/gst/camerabin/gstcamerabin.h index 16b242c0..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; @@ -126,6 +131,9 @@ struct _GstCameraBin /* Cache the photography interface settings */ GstPhotoSettings photo_settings; + + /* Buffer probe id for captured image handling */ + gulong image_captured_id; }; /** @@ -148,7 +156,7 @@ struct _GstCameraBinClass /* signals (callback) */ - gboolean (*img_done) (GstCameraBin * camera, const gchar * filename); + gboolean (*img_done) (GstCameraBin * camera, const gchar * filename); }; /** @@ -167,4 +175,4 @@ typedef enum GType gst_camerabin_get_type (void); G_END_DECLS -#endif /* #ifndef __GST_CAMERABIN_H__ */ +#endif /* #ifndef __GST_CAMERABIN_H__ */ -- cgit v1.2.1 From 4d9a0e832cfc69c612e32902017390560024cd7e Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Fri, 5 Jun 2009 21:17:55 +0300 Subject: camerabin: fix link order Move local version up in the link order. --- gst/camerabin/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/Makefile.am b/gst/camerabin/Makefile.am index 4f382243..d5085da9 100644 --- a/gst/camerabin/Makefile.am +++ b/gst/camerabin/Makefile.am @@ -25,9 +25,9 @@ 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 -- cgit v1.2.1 From 80408b1de27ebb2c5f6f68518a5b07719e1b3e50 Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Fri, 12 Jun 2009 10:26:30 +0300 Subject: camerabin: set imagebin to PAUSED on capture and delayed filename setting We need to set imagebin to PAUSED to not fail the bufferalloc. We also need to keep the filesinks state locked until we have the filename for the run. --- gst/camerabin/camerabinimage.c | 26 +++++++++++++++++++++----- gst/camerabin/gstcamerabin.c | 5 ++++- 2 files changed, 25 insertions(+), 6 deletions(-) (limited to 'gst/camerabin') diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c index 338d2e3d..10b0adf0 100644 --- a/gst/camerabin/camerabinimage.c +++ b/gst/camerabin/camerabinimage.c @@ -193,13 +193,22 @@ 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); break; @@ -258,11 +267,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: diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 22bf1465..f1cd897a 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -1562,7 +1562,10 @@ gst_camerabin_start_image_capture (GstCameraBin * camera) if (!wait_for_prepare) { /* Image queue's srcpad data probe will set imagebin to PLAYING */ - state_ret = gst_element_set_state (camera->imgbin, GST_STATE_READY); + 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, -- cgit v1.2.1 From 2bb31a8d4a3df3604799bcc07cf6673fed12ebb7 Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Tue, 16 Jun 2009 17:09:26 +0300 Subject: camerabin: reset filename after capture to avoid racy when capturing again If we don't reset the filename we cannot check if we still expect the filename for a subsequent capture pass. The imagebin would good to paused and we set the filename in that state. This trigers an error-message and would capture to the old filename. --- gst/camerabin/camerabinimage.c | 1 + 1 file changed, 1 insertion(+) (limited to 'gst/camerabin') diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c index 10b0adf0..da979f0f 100644 --- a/gst/camerabin/camerabinimage.c +++ b/gst/camerabin/camerabinimage.c @@ -211,6 +211,7 @@ gst_camerabin_image_change_state (GstElement * element, 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; -- cgit v1.2.1