diff -r 04c79585b4c7 -r 29ecd5cb86b3 gst_plugins_good/gst/camerabin/gstcamerabin.c --- a/gst_plugins_good/gst/camerabin/gstcamerabin.c Fri Jun 11 13:57:07 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2769 +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. - */ - -/** - * SECTION:gstcamerabin - * @short_description: camera capture bin - * - * - * - * GstCameraBin is a high-level camera object that encapsulates the gstreamer - * internals and provides a task based API for the application. It consists of - * three main data paths: view-finder, image capture and video capture. - * - * - * - * - * CameraBin structure - * Structural decomposition of CameraBin object. - * - * - * - * - * Example launch line - * - * - * gst-launch -v -m camerabin filename=test.jpeg - * - * - * - * - * 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. - * - * - * Available resolutions can be taken from the #GstCameraBin:inputcaps property. - * Image capture resolution can be set with #GstCameraBin::user-image-res - * action signal. - * - * - * - * Video capture - * - * Video capture is started with the #GstCameraBin::user-start action signal too. - * In addition to image capture one can use #GstCameraBin::user-pause to - * pause recording and #GstCameraBin::user-stop to end recording. - * - * - * Available resolutions and fps can be taken from the #GstCameraBin:inputcaps - * property. #GstCameraBin::user-res-fps action signal can be used to set frame - * rate and resolution for the video recording and view finder as well. - * - * - * - * Photography interface - * - * GstCameraBin implements gst photography interface, which can be used to set - * and get different settings related to digital imaging. Since currently many - * of these settings require low-level support the photography interface support - * is dependent on video src element. In practice photography interface settings - * cannot be used successfully until in PAUSED state when the video src has - * opened the video device. - * - * - * - * States - * - * Elements within GstCameraBin are created and destroyed when switching - * between NULL and READY states. Therefore element properties should be set - * in NULL state. User set elements are not unreffed until GstCameraBin is - * unreffed or replaced by a new user set element. Initially only elements needed - * for view finder mode are created to speed up startup. Image bin and video bin - * elements are created when setting the mode or starting capture. - * - * - * - * - * - * Since the muxers tested so far have problems with discontinous buffers, QoS - * has been disabled, and then in order to record video, you MUST ensure that - * there is enough CPU to encode the video. Thus choose smart resolution and - * frames per second values. It is also highly recommended to avoid color - * conversions; make sure all the elements involved work with the same colorspace - * (i.e. rgb or yuv i420 or whatelse). - * - * - * - */ - -/* - * The pipeline in the camerabin is - * - * "image bin" - * videosrc ! crop ! scale ! out-sel <------> in-sel ! scale ! ffmpegcsp ! vfsink - * "video bin" - * - * it is possible to have 'ffmpegcolorspace' and 'capsfilter' just after - * v4l2camsrc - * - * The properties of elements are: - * - * vfsink - "sync", FALSE, "qos", FALSE - * output-selector - "resend-latest", FALSE - * input-selector - "select-all", TRUE - */ - -/* - * includes - */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - - -#include -#include - -#include -/* FIXME: include #include and use _(" ") */ - -#ifndef __SYMBIAN32__ -#include -#else -#include -#endif - -#ifdef __SYMBIAN32__ -#include -#endif - - - - -#include "gstcamerabin.h" -#include "gstcamerabinxoverlay.h" -#include "gstcamerabincolorbalance.h" -#include "gstcamerabinphotography.h" - -#include "camerabingeneral.h" - -#include "gstcamerabin-marshal.h" - -#ifdef __SYMBIAN32__ -#include -#endif -/* - * enum and types - */ - -enum -{ - /* action signals */ - USER_START_SIGNAL, - USER_STOP_SIGNAL, - USER_PAUSE_SIGNAL, - USER_RES_FPS_SIGNAL, - USER_IMAGE_RES_SIGNAL, - /* emit signals */ - IMG_DONE_SIGNAL, - LAST_SIGNAL -}; - -enum -{ - ARG_0, - ARG_FILENAME, - ARG_MODE, - ARG_MUTE, - ARG_ZOOM, - ARG_IMAGE_POST, - ARG_IMAGE_ENC, - ARG_VIDEO_POST, - ARG_VIDEO_ENC, - ARG_AUDIO_ENC, - ARG_VIDEO_MUX, - ARG_VF_SINK, - ARG_VIDEO_SRC, - ARG_AUDIO_SRC, - ARG_INPUT_CAPS, - ARG_FILTER_CAPS -}; - -/* - * defines and static global vars - */ - -static guint camerabin_signals[LAST_SIGNAL]; - -#define GST_TYPE_CAMERABIN_MODE (gst_camerabin_mode_get_type ()) - -/* default and range values for args */ - -#define DEFAULT_MODE MODE_IMAGE -#define DEFAULT_ZOOM 100 -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 -#define DEFAULT_CAPTURE_WIDTH 800 -#define DEFAULT_CAPTURE_HEIGHT 600 -#define DEFAULT_FPS_N 0 /* makes it use the default */ -#define DEFAULT_FPS_D 1 -#define CAMERABIN_DEFAULT_VF_CAPS "video/x-raw-yuv,format=(fourcc)I420" -/* Using "bilinear" as default zoom method */ -#define CAMERABIN_DEFAULT_ZOOM_METHOD 1 - -#define MIN_ZOOM 100 -#define MAX_ZOOM 1000 -#define ZOOM_1X MIN_ZOOM - -#define DEFAULT_V4L2CAMSRC_DRIVER_NAME "omap3cam" - -/* internal element names */ - -#define USE_COLOR_CONVERTER 1 - -/* FIXME: Make sure this can work with autovideosrc and use that. */ -#define DEFAULT_SRC_VID_SRC "v4l2src" - -#define DEFAULT_VIEW_SINK "autovideosink" - -/* - * static helper functions declaration - */ - -static void camerabin_setup_src_elements (GstCameraBin * camera); - -static gboolean camerabin_create_src_elements (GstCameraBin * camera); - -static void camerabin_setup_view_elements (GstCameraBin * camera); - -static gboolean camerabin_create_view_elements (GstCameraBin * camera); - -static gboolean camerabin_create_elements (GstCameraBin * camera); - -static void camerabin_destroy_elements (GstCameraBin * camera); - -static void camerabin_dispose_elements (GstCameraBin * camera); - -static void gst_camerabin_change_mode (GstCameraBin * camera, gint mode); - -static void -gst_camerabin_change_filename (GstCameraBin * camera, const gchar * name); - -static void gst_camerabin_setup_zoom (GstCameraBin * camera); - -static GstCaps *gst_camerabin_get_allowed_input_caps (GstCameraBin * camera); - -static void gst_camerabin_rewrite_tags (GstCameraBin * camera); - -static void -gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps); - -static void gst_camerabin_start_image_capture (GstCameraBin * camera); - -static void gst_camerabin_start_video_recording (GstCameraBin * camera); - -static gboolean -gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer, - gpointer u_data); -static gboolean -gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer, - gpointer u_data); - -static void gst_camerabin_reset_to_view_finder (GstCameraBin * camera); - -static void gst_camerabin_do_stop (GstCameraBin * camera); - -static void -gst_camerabin_set_allowed_framerate (GstCameraBin * camera, - GstCaps * filter_caps); - -/* - * GObject callback functions declaration - */ - -static void gst_camerabin_base_init (gpointer gclass); - -static void gst_camerabin_class_init (GstCameraBinClass * klass); - -static void -gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass); - -static void gst_camerabin_dispose (GObject * object); - -static void gst_camerabin_finalize (GObject * object); - -static void gst_camerabin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); - -static void gst_camerabin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static const GValue *gst_camerabin_find_better_framerate (GstCameraBin * camera, - GstStructure * st, const GValue * orig_framerate); -/* - * GstElement function declarations - */ - -static GstStateChangeReturn -gst_camerabin_change_state (GstElement * element, GstStateChange transition); - - -/* - * GstBin function declarations - */ -static void -gst_camerabin_handle_message_func (GstBin * bin, GstMessage * message); - - -/* - * Action signal function declarations - */ - -static void gst_camerabin_user_start (GstCameraBin * camera); - -static void gst_camerabin_user_stop (GstCameraBin * camera); - -static void gst_camerabin_user_pause (GstCameraBin * camera); - -static void -gst_camerabin_user_res_fps (GstCameraBin * camera, gint width, gint height, - gint fps_n, gint fps_d); - -static void -gst_camerabin_user_image_res (GstCameraBin * camera, gint width, gint height); - - -/* - * GST BOILERPLATE and GObject types - */ - -static GType -gst_camerabin_mode_get_type (void) -{ - static GType gtype = 0; - - if (gtype == 0) { - static const GEnumValue values[] = { - {MODE_IMAGE, "Still image capture (default)", "mode-image"}, - {MODE_VIDEO, "Video recording", "mode-video"}, - {0, NULL, NULL} - }; - - gtype = g_enum_register_static ("GstCameraBinMode", values); - } - return gtype; -} - -static gboolean -gst_camerabin_iface_supported (GstImplementsInterface * iface, GType iface_type) -{ - GstCameraBin *camera = GST_CAMERABIN (iface); - - if (iface_type == GST_TYPE_X_OVERLAY) { - if (camera->view_sink) { - return GST_IS_X_OVERLAY (camera->view_sink); - } - } else if (iface_type == GST_TYPE_COLOR_BALANCE) { - if (camera->src_vid_src) { - return GST_IS_COLOR_BALANCE (camera->src_vid_src); - } - } else if (iface_type == GST_TYPE_TAG_SETTER) { - /* Note: Tag setter elements aren't - present when image and video bin in NULL */ - GstElement *setter; - setter = gst_bin_get_by_interface (GST_BIN (camera), iface_type); - if (setter) { - gst_object_unref (setter); - return TRUE; - } else { - return FALSE; - } - } else if (iface_type == GST_TYPE_PHOTOGRAPHY) { - if (camera->src_vid_src) { - return GST_IS_PHOTOGRAPHY (camera->src_vid_src); - } - } - - return FALSE; -} - -static void -gst_camerabin_interface_init (GstImplementsInterfaceClass * klass) -{ - /* - * default virtual functions - */ - klass->supported = gst_camerabin_iface_supported; -} - -static void -camerabin_init_interfaces (GType type) -{ - - static const GInterfaceInfo camerabin_info = { - (GInterfaceInitFunc) gst_camerabin_interface_init, - NULL, - NULL, - }; - - static const GInterfaceInfo camerabin_xoverlay_info = { - (GInterfaceInitFunc) gst_camerabin_xoverlay_init, - NULL, - NULL, - }; - - static const GInterfaceInfo camerabin_color_balance_info = { - (GInterfaceInitFunc) gst_camerabin_color_balance_init, - NULL, - NULL, - }; - - static const GInterfaceInfo camerabin_tagsetter_info = { - NULL, - NULL, - NULL, - }; - static const GInterfaceInfo camerabin_photography_info = { - (GInterfaceInitFunc) gst_camerabin_photography_init, - NULL, - NULL, - }; - - g_type_add_interface_static (type, - GST_TYPE_IMPLEMENTS_INTERFACE, &camerabin_info); - - g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, - &camerabin_xoverlay_info); - - g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, - &camerabin_color_balance_info); - - g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, - &camerabin_tagsetter_info); - - g_type_add_interface_static (type, GST_TYPE_PHOTOGRAPHY, - &camerabin_photography_info); -} - -GST_BOILERPLATE_FULL (GstCameraBin, gst_camerabin, GstPipeline, - GST_TYPE_PIPELINE, camerabin_init_interfaces); - -/* - * static helper functions implementation - */ - -/* - * camerabin_setup_src_elements: - * @camera: camerabin object - * - * This function updates camerabin capsfilters according - * to fps, resolution and zoom that have been configured - * to camerabin. - */ -static void -camerabin_setup_src_elements (GstCameraBin * camera) -{ - GstStructure *st; - GstCaps *new_caps; - gboolean detect_framerate = FALSE; - - if (!camera->view_finder_caps) { - st = gst_structure_from_string (CAMERABIN_DEFAULT_VF_CAPS, NULL); - } else { - st = gst_structure_copy (gst_caps_get_structure (camera->view_finder_caps, - 0)); - } - - if (camera->width > 0 && camera->height > 0) { - gst_structure_set (st, - "width", G_TYPE_INT, camera->width, - "height", G_TYPE_INT, camera->height, NULL); - } - - if (camera->fps_n > 0 && camera->fps_d > 0) { - if (camera->night_mode) { - GST_WARNING_OBJECT (camera, - "night mode, lowest allowed fps will be forced"); - camera->pre_night_fps_n = camera->fps_n; - camera->pre_night_fps_d = camera->fps_d; - detect_framerate = TRUE; - } else { - gst_structure_set (st, - "framerate", GST_TYPE_FRACTION, camera->fps_n, camera->fps_d, NULL); - new_caps = gst_caps_new_full (st, NULL); - } - } else { - GST_DEBUG_OBJECT (camera, "no framerate specified"); - detect_framerate = TRUE; - } - - if (detect_framerate) { - GST_DEBUG_OBJECT (camera, "detecting allowed framerate"); - /* Remove old framerate if any */ - if (gst_structure_has_field (st, "framerate")) { - gst_structure_remove_field (st, "framerate"); - } - new_caps = gst_caps_new_full (st, NULL); - - /* Set allowed framerate for the resolution */ - gst_camerabin_set_allowed_framerate (camera, new_caps); - } - - /* Set default zoom method */ - g_object_set (camera->src_zoom_scale, "method", - CAMERABIN_DEFAULT_ZOOM_METHOD, NULL); - - gst_caps_replace (&camera->view_finder_caps, new_caps); - - /* Set caps for view finder mode */ - gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps); -} - -/* - * camerabin_create_src_elements: - * @camera: camerabin object - * - * This function creates and links upstream side elements for camerabin. - * videosrc ! cspconv ! capsfilter ! crop ! scale ! capsfilter ! out-sel ! - * - * Returns: TRUE, if elements were successfully created, FALSE otherwise - */ -static gboolean -camerabin_create_src_elements (GstCameraBin * camera) -{ - gboolean ret = FALSE; - GstBin *cbin = GST_BIN (camera); - gchar *driver_name = NULL; - - if (camera->user_vid_src) { - camera->src_vid_src = camera->user_vid_src; - - if (!gst_camerabin_add_element (cbin, camera->src_vid_src)) { - camera->src_vid_src = NULL; - goto done; - } - } else if (!(camera->src_vid_src = - gst_camerabin_create_and_add_element (cbin, DEFAULT_SRC_VID_SRC))) - goto done; -#ifdef USE_COLOR_CONVERTER - if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace")) - goto done; -#endif - if (!(camera->src_filter = - gst_camerabin_create_and_add_element (cbin, "capsfilter"))) - goto done; - if (!(camera->src_zoom_crop = - gst_camerabin_create_and_add_element (cbin, "videocrop"))) - goto done; - if (!(camera->src_zoom_scale = - gst_camerabin_create_and_add_element (cbin, "videoscale"))) - goto done; - if (!(camera->src_zoom_filter = - gst_camerabin_create_and_add_element (cbin, "capsfilter"))) - goto done; - if (!(camera->src_out_sel = - gst_camerabin_create_and_add_element (cbin, "output-selector"))) - goto done; - - camera->srcpad_zoom_filter = - gst_element_get_static_pad (camera->src_zoom_filter, "src"); - - /* Set default "driver-name" for v4l2camsrc if not set */ - if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src), - "driver-name")) { - g_object_get (G_OBJECT (camera->src_vid_src), "driver-name", - &driver_name, NULL); - if (!driver_name) { - g_object_set (G_OBJECT (camera->src_vid_src), "driver-name", - DEFAULT_V4L2CAMSRC_DRIVER_NAME, NULL); - } - } - - ret = TRUE; -done: - return ret; -} - -/* - * camerabin_setup_view_elements: - * @camera: camerabin object - * - * This function configures properties for view finder sink element. - */ -static void -camerabin_setup_view_elements (GstCameraBin * camera) -{ - GST_DEBUG_OBJECT (camera, "setting view finder properties"); - g_object_set (G_OBJECT (camera->view_in_sel), "select-all", TRUE, NULL); - /* Set properties for view finder sink */ - /* Find the actual sink if using bin like autovideosink */ - if (GST_IS_BIN (camera->view_sink)) { - GList *child = NULL, *children = GST_BIN_CHILDREN (camera->view_sink); - for (child = children; child != NULL; child = g_list_next (children)) { - GObject *ch = G_OBJECT (child->data); - if (g_object_class_find_property (G_OBJECT_GET_CLASS (ch), "sync")) { - g_object_set (G_OBJECT (ch), "sync", FALSE, "qos", FALSE, "async", - FALSE, NULL); - } - } - } else { - g_object_set (G_OBJECT (camera->view_sink), "sync", FALSE, "qos", FALSE, - "async", FALSE, NULL); - } -} - -/* - * camerabin_create_view_elements: - * @camera: camerabin object - * - * This function creates and links downstream side elements for camerabin. - * ! scale ! cspconv ! view finder sink - * - * Returns: TRUE, if elements were successfully created, FALSE otherwise - */ -static gboolean -camerabin_create_view_elements (GstCameraBin * camera) -{ - const GList *pads; - - if (!(camera->view_in_sel = - gst_camerabin_create_and_add_element (GST_BIN (camera), - "input-selector"))) { - goto error; - } - - /* Look for recently added input selector sink pad, we need to release it later */ - pads = GST_ELEMENT_PADS (camera->view_in_sel); - while (pads != NULL - && (GST_PAD_DIRECTION (GST_PAD (pads->data)) != GST_PAD_SINK)) { - pads = g_list_next (pads); - } - camera->pad_view_img = GST_PAD (pads->data); - - if (!(camera->view_scale = - gst_camerabin_create_and_add_element (GST_BIN (camera), - "videoscale"))) { - goto error; - } -#ifdef USE_COLOR_CONVERTER - if (!gst_camerabin_create_and_add_element (GST_BIN (camera), - "ffmpegcolorspace")) { - goto error; - } -#endif - if (camera->user_vf_sink) { - camera->view_sink = camera->user_vf_sink; - if (!gst_camerabin_add_element (GST_BIN (camera), camera->view_sink)) { - goto error; - } - } else if (!(camera->view_sink = - gst_camerabin_create_and_add_element (GST_BIN (camera), - DEFAULT_VIEW_SINK))) { - goto error; - } - - return TRUE; -error: - return FALSE; -} - -/* - * camerabin_create_elements: - * @camera: camerabin object - * - * This function creates and links all elements for camerabin, - * - * Returns: TRUE, if elements were successfully created, FALSE otherwise - */ -static gboolean -camerabin_create_elements (GstCameraBin * camera) -{ - gboolean ret = FALSE; - GstPadLinkReturn link_ret = GST_PAD_LINK_REFUSED; - GstPad *unconnected_pad; - - GST_LOG_OBJECT (camera, "creating elems"); - - /* Create "src" elements */ - if (!camerabin_create_src_elements (camera)) { - goto done; - } - - /* Add image bin */ - camera->pad_src_img = - gst_element_get_request_pad (camera->src_out_sel, "src%d"); - if (!gst_camerabin_add_element (GST_BIN (camera), camera->imgbin)) { - goto done; - } - gst_pad_add_buffer_probe (camera->pad_src_img, - G_CALLBACK (gst_camerabin_have_img_buffer), camera); - - /* Create view finder elements, this also links it to image bin */ - if (!camerabin_create_view_elements (camera)) { - GST_WARNING_OBJECT (camera, "creating view failed"); - goto done; - } - - /* Link output selector ! view_finder */ - camera->pad_src_view = - gst_element_get_request_pad (camera->src_out_sel, "src%d"); - camera->pad_view_src = - gst_element_get_request_pad (camera->view_in_sel, "sink%d"); - link_ret = gst_pad_link (camera->pad_src_view, camera->pad_view_src); - if (GST_PAD_LINK_FAILED (link_ret)) { - GST_ELEMENT_ERROR (camera, CORE, NEGOTIATION, - ("linking view finder failed"), (NULL)); - goto done; - } - - /* Set view finder active as default */ - g_object_set (G_OBJECT (camera->src_out_sel), "active-pad", - camera->pad_src_view, NULL); - - /* Add video bin */ - camera->pad_src_vid = - gst_element_get_request_pad (camera->src_out_sel, "src%d"); - if (!gst_camerabin_add_element (GST_BIN (camera), camera->vidbin)) { - goto done; - } - gst_pad_add_buffer_probe (camera->pad_src_vid, - G_CALLBACK (gst_camerabin_have_vid_buffer), camera); - - /* Link video bin ! view finder */ - // unconnected_pad = gst_bin_find_unlinked_pad (GST_BIN (camera), GST_PAD_SRC); - unconnected_pad = gst_bin_find_unconnected_pad (GST_BIN (camera), GST_PAD_SRC); - camera->pad_view_vid = - gst_element_get_request_pad (camera->view_in_sel, "sink%d"); - link_ret = gst_pad_link (unconnected_pad, camera->pad_view_vid); - gst_object_unref (unconnected_pad); - if (GST_PAD_LINK_FAILED (link_ret)) { - GST_ELEMENT_ERROR (camera, CORE, NEGOTIATION, (NULL), - ("linking video bin and view finder failed")); - goto done; - } - - ret = TRUE; - -done: - - if (FALSE == ret) - camerabin_destroy_elements (camera); - - return ret; -} - -/* - * camerabin_destroy_elements: - * @camera: camerabin object - * - * This function removes all elements from camerabin. - */ -static void -camerabin_destroy_elements (GstCameraBin * camera) -{ - GST_DEBUG_OBJECT (camera, "destroying elements"); - - /* Release request pads */ - if (camera->pad_view_vid) { - gst_element_release_request_pad (camera->view_in_sel, camera->pad_view_vid); - camera->pad_view_vid = NULL; - } - if (camera->pad_src_vid) { - gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_vid); - camera->pad_src_vid = NULL; - } - if (camera->pad_view_img) { - gst_element_release_request_pad (camera->view_in_sel, camera->pad_view_img); - camera->pad_view_img = NULL; - } - if (camera->pad_src_img) { - gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_img); - camera->pad_src_img = NULL; - } - if (camera->pad_view_src) { - gst_element_release_request_pad (camera->view_in_sel, camera->pad_view_src); - camera->pad_view_src = NULL; - } - if (camera->pad_src_view) { - gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_view); - camera->pad_src_view = NULL; - } - - camera->view_sink = NULL; - camera->view_scale = NULL; - camera->view_in_sel = NULL; - - camera->src_out_sel = NULL; - camera->src_filter = NULL; - camera->src_zoom_crop = NULL; - camera->src_zoom_scale = NULL; - camera->src_zoom_filter = NULL; - camera->src_vid_src = NULL; - - camera->active_bin = NULL; - - /* Remove elements */ - gst_camerabin_remove_elements_from_bin (GST_BIN (camera)); -} - -/* - * camerabin_dispose_elements: - * @camera: camerabin object - * - * This function releases all allocated camerabin resources. - */ -static void -camerabin_dispose_elements (GstCameraBin * camera) -{ - if (camera->capture_mutex) { - g_mutex_free (camera->capture_mutex); - camera->capture_mutex = NULL; - } - if (camera->cond) { - g_cond_free (camera->cond); - camera->cond = NULL; - } - if (camera->filename) { - g_string_free (camera->filename, TRUE); - camera->filename = NULL; - } - /* Unref user set elements */ - if (camera->user_vf_sink) { - gst_object_unref (camera->user_vf_sink); - camera->user_vf_sink = NULL; - } - if (camera->user_vid_src) { - gst_object_unref (camera->user_vid_src); - camera->user_vid_src = NULL; - } - - if (camera->image_capture_caps) { - gst_caps_unref (camera->image_capture_caps); - camera->image_capture_caps = NULL; - } - - if (camera->view_finder_caps) { - gst_caps_unref (camera->view_finder_caps); - camera->view_finder_caps = NULL; - } - - if (camera->allowed_caps) { - gst_caps_unref (camera->allowed_caps); - camera->allowed_caps = NULL; - } -} - -/* - * gst_camerabin_image_capture_continue: - * @camera: camerabin object - * @filename: new filename set by user - * @cont: TRUE to continue image capture, FALSE otherwise - * - * Check if user wants to continue image capturing by using g_signal. - */ -static void -gst_camerabin_image_capture_continue (GstCameraBin * camera, GString * filename, - gboolean * cont) -{ - GST_DEBUG_OBJECT (camera, "emitting img_done signal, filename: %s", - filename->str); - g_signal_emit (G_OBJECT (camera), camerabin_signals[IMG_DONE_SIGNAL], 0, - filename, cont); - - GST_DEBUG_OBJECT (camera, "emitted img_done, new filename:%s, continue:%d", - filename->str, *cont); -} - -/* - * gst_camerabin_change_mode: - * @camera: camerabin object - * @mode: image or video mode - * - * Change camerabin mode between image and video capture. - * Changing mode will stop ongoing capture. - */ -static void -gst_camerabin_change_mode (GstCameraBin * camera, gint mode) -{ - if (camera->mode != mode || !camera->active_bin) { - GST_DEBUG_OBJECT (camera, "setting mode: %d", mode); - /* Interrupt ongoing capture */ - gst_camerabin_do_stop (camera); - camera->mode = mode; - if (camera->active_bin) { - gst_element_set_state (camera->active_bin, GST_STATE_NULL); - } - if (camera->mode == MODE_IMAGE) { - camera->active_bin = camera->imgbin; - } else if (camera->mode == MODE_VIDEO) { - camera->active_bin = camera->vidbin; - } - gst_camerabin_reset_to_view_finder (camera); - } -} - -/* - * gst_camerabin_change_filename: - * @camera: camerabin object - * @name: new filename for capture - * - * Change filename for image or video capture. - * Changing filename will stop ongoing capture. - */ -static void -gst_camerabin_change_filename (GstCameraBin * camera, const gchar * name) -{ - if (0 != strcmp (camera->filename->str, name)) { - GST_DEBUG_OBJECT (camera, "changing filename from %s to %s", - camera->filename->str, name); - /* Interrupt ongoing capture */ - gst_camerabin_do_stop (camera); - gst_camerabin_reset_to_view_finder (camera); - - if (camera->active_bin) { - g_object_set (G_OBJECT (camera->active_bin), "filename", name, NULL); - } - - g_string_assign (camera->filename, name); - } -} - -/* - * gst_camerabin_setup_zoom: - * @camera: camerabin object - * - * Apply zoom configured to camerabin to capture. - */ -static void -gst_camerabin_setup_zoom (GstCameraBin * camera) -{ - gint zoom; - gboolean done = FALSE; - - g_return_if_fail (camera != NULL); - g_return_if_fail (camera->src_zoom_crop != NULL); - - zoom = g_atomic_int_get (&camera->zoom); - - g_return_if_fail (zoom); - - if (GST_IS_ELEMENT (camera->src_vid_src) && - gst_element_implements_interface (camera->src_vid_src, - GST_TYPE_PHOTOGRAPHY)) { - /* Try setting (hardware) zoom using photography interface */ - GstPhotography *photo; - GstPhotoCaps pcaps; - - photo = GST_PHOTOGRAPHY (camera->src_vid_src); - pcaps = gst_photography_get_capabilities (photo); - - if (pcaps & GST_PHOTOGRAPHY_CAPS_ZOOM) { - done = gst_photography_set_zoom (photo, (gfloat) zoom / 100.0); - } - } - - if (!done) { - /* Update capsfilters to apply the (software) zoom */ - gint w2_crop = 0; - gint h2_crop = 0; - GstPad *pad_zoom_sink = NULL; - - GST_INFO_OBJECT (camera, "zoom: %d, orig size: %dx%d", zoom, - camera->width, camera->height); - - if (zoom != ZOOM_1X) { - w2_crop = (camera->width - (camera->width * ZOOM_1X / zoom)) / 2; - h2_crop = (camera->height - (camera->height * ZOOM_1X / zoom)) / 2; - } - - pad_zoom_sink = gst_element_get_static_pad (camera->src_zoom_crop, "sink"); - - GST_INFO_OBJECT (camera, - "sw cropping: left:%d, right:%d, top:%d, bottom:%d", w2_crop, w2_crop, - h2_crop, h2_crop); - - GST_PAD_STREAM_LOCK (pad_zoom_sink); - g_object_set (camera->src_zoom_crop, "left", w2_crop, "right", w2_crop, - "top", h2_crop, "bottom", h2_crop, NULL); - - GST_PAD_STREAM_UNLOCK (pad_zoom_sink); - gst_object_unref (pad_zoom_sink); - } - GST_LOG_OBJECT (camera, "zoom set"); -} - -/* - * gst_camerabin_get_allowed_input_caps: - * @camera: camerabin object - * - * Retrieve caps from videosrc describing formats it supports - * - * Returns: caps object from videosrc - */ -static GstCaps * -gst_camerabin_get_allowed_input_caps (GstCameraBin * camera) -{ - GstCaps *caps = NULL; - GstPad *pad = NULL, *peer_pad = NULL; - GstState state; - gboolean temp_videosrc_pause = FALSE; - GstElement *videosrc; - - g_return_val_if_fail (camera != NULL, NULL); - - videosrc = camera->src_vid_src ? camera->src_vid_src : camera->user_vid_src; - - if (!videosrc) { - GST_WARNING_OBJECT (camera, "no videosrc, can't get allowed caps"); - goto failed; - } - - if (camera->allowed_caps) { - GST_DEBUG_OBJECT (camera, "returning cached caps"); - goto done; - } - - pad = gst_element_get_static_pad (videosrc, "src"); - - if (!pad) { - GST_WARNING_OBJECT (camera, "no srcpad in videosrc"); - goto failed; - } - - state = GST_STATE (videosrc); - - /* Make this function work also in READY and NULL state */ - if (state == GST_STATE_READY || state == GST_STATE_NULL) { - GST_DEBUG_OBJECT (camera, "setting videosrc to paused temporarily"); - temp_videosrc_pause = TRUE; - peer_pad = gst_pad_get_peer (pad); - if (peer_pad) { - gst_pad_unlink (pad, peer_pad); - } - /* Set videosrc to PAUSED to open video device */ - gst_element_set_locked_state (videosrc, TRUE); - gst_element_set_state (videosrc, GST_STATE_PAUSED); - } - - camera->allowed_caps = gst_pad_get_caps (pad); - - /* Restore state and re-link if necessary */ - if (temp_videosrc_pause) { - GST_DEBUG_OBJECT (camera, "restoring videosrc state %d", state); - /* Reset videosrc to NULL state, some drivers seem to need this */ - gst_element_set_state (videosrc, GST_STATE_NULL); - gst_element_set_state (videosrc, state); - if (peer_pad) { - gst_pad_link (pad, peer_pad); - gst_object_unref (peer_pad); - } - gst_element_set_locked_state (videosrc, FALSE); - } - - gst_object_unref (pad); - -done: - if (camera->allowed_caps) { - caps = gst_caps_copy (camera->allowed_caps); - } -failed: - GST_INFO_OBJECT (camera, "allowed caps:%" GST_PTR_FORMAT, caps); - return caps; -} - -/* - * gst_camerabin_rewrite_tags_to_bin: - * @bin: bin holding tag setter elements - * @list: tag list to be written - * - * This function looks for certain tag setters from given bin - * and REPLACES ALL setter tags with given tag list - * - */ -static void -gst_camerabin_rewrite_tags_to_bin (GstBin * bin, const GstTagList * list) -{ - GstElement *setter; - GstElementFactory *setter_factory; - const gchar *klass; - GstIterator *iter; - GstIteratorResult res = GST_ITERATOR_OK; - gpointer data; - - iter = gst_bin_iterate_all_by_interface (bin, GST_TYPE_TAG_SETTER); - - while (res == GST_ITERATOR_OK || res == GST_ITERATOR_RESYNC) { - res = gst_iterator_next (iter, &data); - switch (res) { - case GST_ITERATOR_DONE: - break; - case GST_ITERATOR_RESYNC: - gst_iterator_resync (iter); - break; - case GST_ITERATOR_ERROR: - GST_WARNING ("error iterating tag setters"); - break; - case GST_ITERATOR_OK: - setter = GST_ELEMENT (data); - GST_LOG ("iterating tag setters: %" GST_PTR_FORMAT, setter); - setter_factory = gst_element_get_factory (setter); - klass = gst_element_factory_get_klass (setter_factory); - /* FIXME: check if tags should be written to all tag setters, - set tags only to Muxer elements for now */ - if (g_strrstr (klass, "Muxer")) { - GST_DEBUG ("replacement tags %" GST_PTR_FORMAT, list); - gst_tag_setter_merge_tags (GST_TAG_SETTER (setter), list, - GST_TAG_MERGE_REPLACE_ALL); - } - gst_object_unref (setter); - break; - default: - break; - } - } - - gst_iterator_free (iter); -} - -/* - * gst_camerabin_get_internal_tags: - * @camera: the camera bin element - * - * Returns tag list containing metadata from camerabin - * and it's elements - */ -static GstTagList * -gst_camerabin_get_internal_tags (GstCameraBin * camera) -{ - GstTagList *list = gst_tag_list_new (); - GstColorBalance *balance = NULL; - const GList *controls = NULL, *item; - GstColorBalanceChannel *channel; - gint min_value, max_value, mid_value, cur_value; - - - if (camera->active_bin == camera->vidbin) { - /* FIXME: check if internal video tag setting is needed */ - goto done; - } - - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - "image-width", camera->width, "image-height", camera->height, NULL); - - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - "capture-digital-zoom", camera->zoom, 100, NULL); - - if (gst_element_implements_interface (GST_ELEMENT (camera), - GST_TYPE_COLOR_BALANCE)) { - balance = GST_COLOR_BALANCE (camera); - } - - if (balance) { - controls = gst_color_balance_list_channels (balance); - } - for (item = controls; item; item = g_list_next (item)) { - channel = item->data; - min_value = channel->min_value; - max_value = channel->max_value; - /* the default value would probably better */ - mid_value = min_value + ((max_value - min_value) / 2); - cur_value = gst_color_balance_get_value (balance, channel); - - if (!strcasecmp (channel->label, "brightness")) { - /* The value of brightness. The unit is the APEX value (Additive System of Photographic Exposure). - * Ordinarily it is given in the range of -99.99 to 99.99. Note that - * if the numerator of the recorded value is 0xFFFFFFFF, Unknown shall be indicated. - * - * BrightnessValue (Bv) = log2 ( B/NK ) - * Note that: B:cd/cm² (candela per square centimeter), N,K: constant - * - * http://johnlind.tripod.com/science/scienceexposure.html - * - */ - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - "capture-brightness", cur_value, 1, NULL); - } else if (!strcasecmp (channel->label, "contrast")) { - /* 0 = Normal, 1 = Soft, 2 = Hard */ - - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - "capture-contrast", - (cur_value == mid_value) ? 0 : ((cur_value < mid_value) ? 1 : 2), - NULL); - } else if (!strcasecmp (channel->label, "gain")) { - /* 0 = Normal, 1 = Low Up, 2 = High Up, 3 = Low Down, 4 = Hight Down */ - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - "capture-gain", - (guint) (cur_value == mid_value) ? 0 : ((cur_value < - mid_value) ? 1 : 3), NULL); - } else if (!strcasecmp (channel->label, "saturation")) { - /* 0 = Normal, 1 = Low, 2 = High */ - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - "capture-saturation", - (cur_value == mid_value) ? 0 : ((cur_value < mid_value) ? 1 : 2), - NULL); - } - } - -done: - - return list; -} - -/* - * gst_camerabin_rewrite_tags: - * @camera: the camera bin element - * - * Merges application set tags to camerabin internal tags, - * and writes them using image or video bin tag setters. - */ -static void -gst_camerabin_rewrite_tags (GstCameraBin * camera) -{ - const GstTagList *app_tag_list = NULL; - GstTagList *list = NULL; - - /* Get application set tags */ - app_tag_list = gst_tag_setter_get_tag_list (GST_TAG_SETTER (camera)); - - /* Get tags from camerabin and it's elements */ - list = gst_camerabin_get_internal_tags (camera); - - if (app_tag_list) { - gst_tag_list_insert (list, app_tag_list, GST_TAG_MERGE_REPLACE); - } - - /* Write tags */ - gst_camerabin_rewrite_tags_to_bin (GST_BIN (camera->active_bin), list); - - gst_tag_list_free (list); -} - -/* - * gst_camerabin_set_capsfilter_caps: - * @camera: camerabin object - * @new_caps: pointer to caps object to set - * - * Set given caps to camerabin capsfilters. - */ -static void -gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps) -{ - GstStructure *st; - - GST_INFO_OBJECT (camera, "new_caps:%" GST_PTR_FORMAT, new_caps); - - st = gst_caps_get_structure (new_caps, 0); - - gst_structure_get_int (st, "width", &camera->width); - gst_structure_get_int (st, "height", &camera->height); - - if (gst_structure_has_field (st, "framerate")) { - gst_structure_get_fraction (st, "framerate", &camera->fps_n, - &camera->fps_d); - } - - /* Update zoom */ - gst_camerabin_setup_zoom (camera); - - /* Update capsfilters */ - g_object_set (G_OBJECT (camera->src_filter), "caps", new_caps, NULL); - g_object_set (G_OBJECT (camera->src_zoom_filter), "caps", new_caps, NULL); -} - -/* - * img_capture_prepared: - * @data: camerabin object - * - * Callback which is called after image capture has been prepared. - */ -static void -img_capture_prepared (gpointer data) -{ - GstCameraBin *camera = GST_CAMERABIN (data); - - GST_INFO_OBJECT (camera, "image capture prepared"); - - if (camera->image_capture_caps) { - /* Set capsfilters to match arriving image data */ - gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps); - } - - g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE, - "active-pad", camera->pad_src_img, NULL); - gst_camerabin_rewrite_tags (camera); - gst_element_set_state (GST_ELEMENT (camera->imgbin), GST_STATE_PLAYING); -} - -/* - * gst_camerabin_start_image_capture: - * @camera: camerabin object - * - * Initiates image capture. - */ -static void -gst_camerabin_start_image_capture (GstCameraBin * camera) -{ - GstStateChangeReturn state_ret; - gboolean wait_for_prepare = FALSE; - gint width = 0, height = 0, fps_n = 0, fps_d = 0; - GstStructure *st; - - GST_INFO_OBJECT (camera, "starting image capture"); - - if (GST_IS_ELEMENT (camera->src_vid_src) && - gst_element_implements_interface (camera->src_vid_src, - GST_TYPE_PHOTOGRAPHY)) { - /* Start image capture preparations using photography iface */ - wait_for_prepare = TRUE; - g_mutex_lock (camera->capture_mutex); - if (camera->image_capture_caps) { - st = gst_caps_get_structure (camera->image_capture_caps, 0); - } else { - st = gst_caps_get_structure (camera->view_finder_caps, 0); - } - gst_structure_get_int (st, "width", &width); - gst_structure_get_int (st, "height", &height); - gst_structure_get_fraction (st, "framerate", &fps_n, &fps_d); - /* Set image capture resolution and frame rate */ - g_signal_emit_by_name (camera->src_vid_src, "user-res-fps", - width, height, fps_n, fps_d, 0); - - /* Enable still image capture mode in v4l2camsrc */ - if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src), - "capture-mode")) { - g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 1, NULL); - } - - /* Start preparations for image capture */ - gst_photography_prepare_for_capture (GST_PHOTOGRAPHY (camera->src_vid_src), - (GstPhotoCapturePrepared) img_capture_prepared, camera); - camera->capturing = TRUE; - g_mutex_unlock (camera->capture_mutex); - } - - if (!wait_for_prepare) { - gst_camerabin_rewrite_tags (camera); - state_ret = gst_element_set_state (camera->imgbin, GST_STATE_PLAYING); - if (state_ret != GST_STATE_CHANGE_FAILURE) { - g_mutex_lock (camera->capture_mutex); - g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", TRUE, - "active-pad", camera->pad_src_img, NULL); - camera->capturing = TRUE; - g_mutex_unlock (camera->capture_mutex); - } else { - GST_WARNING_OBJECT (camera, "imagebin state change failed"); - gst_element_set_state (camera->imgbin, GST_STATE_NULL); - } - } -} - -/* - * gst_camerabin_start_video_recording: - * @camera: camerabin object - * - * Initiates video recording. - */ -static void -gst_camerabin_start_video_recording (GstCameraBin * camera) -{ - GstStateChangeReturn state_ret; - /* FIXME: how to ensure resolution and fps is supported by CPU? - * use a queue overrun signal? - */ - GST_INFO_OBJECT (camera, "starting video capture"); - - gst_camerabin_rewrite_tags (camera); - - /* Pause the pipeline in order to distribute new clock in paused_to_playing */ - /* audio src timestamps will be 0 without state change to READY. ??? */ - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_READY); - gst_element_set_locked_state (camera->vidbin, FALSE); - state_ret = gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED); - - if (state_ret != GST_STATE_CHANGE_FAILURE) { - g_mutex_lock (camera->capture_mutex); - g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE, - "active-pad", camera->pad_src_vid, NULL); - - /* Enable video mode in v4l2camsrc */ - if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src), - "capture-mode")) { - g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 2, NULL); - } - - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PLAYING); - gst_element_set_locked_state (camera->vidbin, TRUE); - camera->capturing = TRUE; - g_mutex_unlock (camera->capture_mutex); - } else { - GST_WARNING_OBJECT (camera, "videobin state change failed"); - gst_element_set_state (camera->vidbin, GST_STATE_NULL); - gst_camerabin_reset_to_view_finder (camera); - } -} - -/* - * gst_camerabin_send_video_eos: - * @camera: camerabin object - * - * Generate and send eos event to video bin in order to - * finish recording properly. - */ -static void -gst_camerabin_send_video_eos (GstCameraBin * camera) -{ - GstPad *videopad; - - g_return_if_fail (camera != NULL); - - /* Send eos event to video bin */ - GST_INFO_OBJECT (camera, "sending eos to videobin"); - videopad = gst_element_get_static_pad (camera->vidbin, "sink"); - gst_pad_send_event (videopad, gst_event_new_eos ()); - gst_object_unref (videopad); -} - -/* - * image_pad_blocked: - * @pad: pad to block/unblock - * @blocked: TRUE to block, FALSE to unblock - * @u_data: camera bin object - * - * Sends eos event to image bin if blocking pad leading to image bin. - * The pad will be unblocked when image bin posts eos message. - */ -static void -image_pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data) -{ - GstCameraBin *camera; - - camera = (GstCameraBin *) user_data; - - GST_DEBUG_OBJECT (camera, "%s %s:%s", - blocked ? "blocking" : "unblocking", GST_DEBUG_PAD_NAME (pad)); - - if (blocked && (pad == camera->pad_src_img)) { - /* Send eos and block until image bin reaches eos */ - GST_DEBUG_OBJECT (camera, "sending eos to image bin"); - gst_element_send_event (camera->imgbin, gst_event_new_eos ()); - } -} - -/* - * gst_camerabin_have_img_buffer: - * @pad: output-selector src pad leading to image bin - * @buffer: still image frame - * @u_data: camera bin object - * - * Buffer probe called before sending each buffer to image bin. - * - * First buffer is always passed directly to image bin. Then pad - * is blocked in order to interleave buffers with eos events. - * Interleaving eos events and buffers is needed when we have - * decoupled elements in the image bin capture pipeline. - * After image bin posts eos message, then pad is unblocked. - * Next, image bin is changed to READY state in order to save the - * file and the application is allowed to decide whether to - * continue image capture. If yes, only then the next buffer is - * passed to image bin. - */ -static gboolean -gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer, - gpointer u_data) -{ - GstCameraBin *camera = (GstCameraBin *) u_data; - gboolean ret = TRUE; - - GST_LOG ("got buffer #%d %p with size %d", camera->num_img_buffers, - buffer, GST_BUFFER_SIZE (buffer)); - - /* Image filename should be set by now */ - if (g_str_equal (camera->filename->str, "")) { - GST_DEBUG_OBJECT (camera, "filename not set, dropping buffer"); - ret = FALSE; - goto done; - } - - /* Check for first buffer after capture start, we want to - pass it forward directly. */ - if (!camera->num_img_buffers) { - /* Restore filter caps for view finder mode if necessary. - The v4l2camsrc switches automatically to view finder - resolution after hi-res still image capture. */ - if (camera->image_capture_caps) { - gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps); - } - goto done; - } - - /* Close the file of saved image */ - gst_element_set_state (camera->imgbin, GST_STATE_READY); - - /* Check if the application wants to continue */ - gst_camerabin_image_capture_continue (camera, camera->filename, &ret); - - if (ret && !camera->stop_requested) { - GST_DEBUG_OBJECT (camera, "capturing image \"%s\"", camera->filename->str); - g_object_set (G_OBJECT (camera->imgbin), "filename", - camera->filename->str, NULL); - gst_element_set_state (camera->imgbin, GST_STATE_PLAYING); - } else { - GST_DEBUG_OBJECT (camera, "not continuing (cont:%d, stop_req:%d)", - ret, camera->stop_requested); - /* Reset filename to force application set new filename */ - g_string_assign (camera->filename, ""); - - /* Block dataflow to the output-selector to show preview image in - view finder. Continue and unblock when capture is stopped */ - gst_pad_set_blocked_async (camera->srcpad_zoom_filter, TRUE, - (GstPadBlockCallback) image_pad_blocked, camera); - ret = FALSE; /* Drop the buffer */ - - g_mutex_lock (camera->capture_mutex); - camera->capturing = FALSE; - g_cond_signal (camera->cond); - g_mutex_unlock (camera->capture_mutex); - } - -done: - - if (ret) { - camera->num_img_buffers++; - /* Block when next buffer arrives, we want to push eos event - between frames and make sure that eos reaches the filesink - before processing the next buffer. */ - gst_pad_set_blocked_async (pad, TRUE, - (GstPadBlockCallback) image_pad_blocked, camera); - } - - return ret; -} - -/* - * gst_camerabin_have_vid_buffer: - * @pad: output-selector src pad leading to video bin - * @buffer: buffer pushed to the pad - * @u_data: camerabin object - * - * Buffer probe for src pad leading to video bin. - * Sends eos event to video bin if stop requested and drops - * all buffers after this. - */ -static gboolean -gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer, - gpointer u_data) -{ - GstCameraBin *camera = (GstCameraBin *) u_data; - gboolean ret = TRUE; - GST_LOG ("got video buffer %p with size %d", - buffer, GST_BUFFER_SIZE (buffer)); - if (camera->stop_requested) { - gst_camerabin_send_video_eos (camera); - ret = FALSE; /* Drop buffer */ - } - - return ret; -} - -/* - * gst_camerabin_reset_to_view_finder: - * @camera: camerabin object - * - * Stop capturing and set camerabin to view finder mode. - * Reset capture counters and flags. - */ -static void -gst_camerabin_reset_to_view_finder (GstCameraBin * camera) -{ - GstStateChangeReturn state_ret; - GST_DEBUG_OBJECT (camera, "resetting"); - - /* Set active bin to READY state */ - if (camera->active_bin) { - state_ret = gst_element_set_state (camera->active_bin, GST_STATE_READY); - if (state_ret == GST_STATE_CHANGE_FAILURE) { - GST_WARNING_OBJECT (camera, "state change failed"); - gst_element_set_state (camera->active_bin, GST_STATE_NULL); - camera->active_bin = NULL; - } - } - - /* Reset counters and flags */ - camera->num_img_buffers = 0; - camera->stop_requested = FALSE; - camera->paused = FALSE; - - if (camera->src_out_sel) { - /* Set selector to forward data to view finder */ - g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE, - "active-pad", camera->pad_src_view, NULL); - } - - /* Unblock, if dataflow to output-selector is blocked due to image preview */ - if (camera->srcpad_zoom_filter && - gst_pad_is_blocked (camera->srcpad_zoom_filter)) { - gst_pad_set_blocked_async (camera->srcpad_zoom_filter, FALSE, - (GstPadBlockCallback) image_pad_blocked, camera); - } - /* Unblock, if dataflow to image bin is blocked due to waiting for eos */ - if (camera->pad_src_img && gst_pad_is_blocked (camera->pad_src_img)) { - gst_pad_set_blocked_async (camera->pad_src_img, FALSE, - (GstPadBlockCallback) image_pad_blocked, camera); - } - - /* Enable view finder mode in v4l2camsrc */ - if (camera->src_vid_src && - g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src), - "capture-mode")) { - g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 0, NULL); - } - - GST_DEBUG_OBJECT (camera, "reset done"); -} - -/* - * gst_camerabin_do_stop: - * @camera: camerabin object - * - * Raise flag to indicate to image and video bin capture stop. - * Stopping paused video recording handled as a special case. - * Wait for ongoing capturing to finish. - */ -static void -gst_camerabin_do_stop (GstCameraBin * camera) -{ - g_mutex_lock (camera->capture_mutex); - if (camera->capturing) { - GST_DEBUG_OBJECT (camera, "mark stop"); - camera->stop_requested = TRUE; - - /* Take special care when stopping paused video capture */ - if ((camera->active_bin == camera->vidbin) && camera->paused) { - /* Send eos event to video bin before setting it to playing */ - gst_camerabin_send_video_eos (camera); - /* We must change to playing now in order to get video bin eos events - and buffered data through and finish recording properly */ - gst_element_set_state (GST_ELEMENT (camera->vidbin), GST_STATE_PLAYING); - camera->paused = FALSE; - } - - GST_DEBUG_OBJECT (camera, "waiting for capturing to finish"); - g_cond_wait (camera->cond, camera->capture_mutex); - GST_DEBUG_OBJECT (camera, "capturing finished"); - } - g_mutex_unlock (camera->capture_mutex); -} - -/* - * gst_camerabin_default_signal_img_done: - * @camera: camerabin object - * @fname: new filename - * - * Default handler for #GstCameraBin::img-done signal, - * stops always capture. - * - * Returns: FALSE always - */ -static gboolean -gst_camerabin_default_signal_img_done (GstCameraBin * camera, GString * fname) -{ - return FALSE; -} - -/* - * gst_camerabin_set_allowed_framerate: - * @camera: camerabin object - * @filter_caps: update allowed framerate to these caps - * - * Find allowed frame rate from video source that matches with - * resolution in @filter_caps. Set found frame rate to @filter_caps. - */ -static void -gst_camerabin_set_allowed_framerate (GstCameraBin * camera, - GstCaps * filter_caps) -{ - GstStructure *structure; - GstCaps *allowed_caps = NULL, *intersect = NULL; - const GValue *framerate = NULL; - guint caps_size, i; - - /* Get supported caps from video src that matches with new filter caps */ - GST_INFO_OBJECT (camera, "filter caps:%" GST_PTR_FORMAT, filter_caps); - allowed_caps = gst_camerabin_get_allowed_input_caps (camera); - intersect = gst_caps_intersect (allowed_caps, filter_caps); - GST_INFO_OBJECT (camera, "intersect caps:%" GST_PTR_FORMAT, intersect); - - /* Find the best framerate from the caps */ - caps_size = gst_caps_get_size (intersect); - for (i = 0; i < caps_size; i++) { - structure = gst_caps_get_structure (intersect, i); - framerate = - gst_camerabin_find_better_framerate (camera, structure, framerate); - } - - if (GST_VALUE_HOLDS_FRACTION (framerate)) { - gst_caps_set_simple (filter_caps, - "framerate", GST_TYPE_FRACTION, - gst_value_get_fraction_numerator (framerate), - gst_value_get_fraction_denominator (framerate), NULL); - } - - if (allowed_caps) { - gst_caps_unref (allowed_caps); - } - if (intersect) { - gst_caps_unref (intersect); - } -} - - -/** - * get_srcpad_current_format: - * @element: element to get the format from - * - * Helper function to get the negotiated fourcc - * format from @element src pad. - * - * Returns: negotiated format (fourcc), 0 if not found - */ -static guint32 -get_srcpad_current_format (GstElement * element) -{ - GstPad *srcpad = NULL; - GstCaps *srccaps = NULL; - GstStructure *structure; - guint32 format = 0; - - g_return_val_if_fail (element != NULL, 0); - - if ((srcpad = gst_element_get_static_pad (element, "src")) == NULL) { - goto no_pad; - } - - if ((srccaps = gst_pad_get_negotiated_caps (srcpad)) == NULL) { - goto no_caps; - } - - GST_LOG ("negotiated caps %" GST_PTR_FORMAT, srccaps); - - structure = gst_caps_get_structure (srccaps, 0); - if (gst_structure_has_field (structure, "format")) { - gst_structure_get_fourcc (structure, "format", &format); - } - - gst_caps_unref (srccaps); -no_caps: - gst_object_unref (srcpad); -no_pad: - GST_DEBUG ("current format for %" GST_PTR_FORMAT ": %" GST_FOURCC_FORMAT, - element, GST_FOURCC_ARGS (format)); - return format; -} - -/* - * gst_camerabin_find_better_framerate: - * @camera: camerabin object - * @st: structure that contains framerate candidates - * @orig_framerate: best framerate so far - * - * Looks for framerate better than @orig_framerate from @st structure. - * In night mode lowest framerate is considered best, otherwise highest is - * best. - * - * Returns: @orig_framerate or better if found - */ -static const GValue * -gst_camerabin_find_better_framerate (GstCameraBin * camera, GstStructure * st, - const GValue * orig_framerate) -{ - const GValue *framerate = NULL; - guint i, i_best, list_size; - gint res, comparison; - - if (camera->night_mode) { - GST_LOG_OBJECT (camera, "finding min framerate"); - comparison = GST_VALUE_LESS_THAN; - } else { - GST_LOG_OBJECT (camera, "finding max framerate"); - comparison = GST_VALUE_GREATER_THAN; - } - - if (gst_structure_has_field (st, "framerate")) { - framerate = gst_structure_get_value (st, "framerate"); - /* Handle framerate lists */ - if (GST_VALUE_HOLDS_LIST (framerate)) { - list_size = gst_value_list_get_size (framerate); - GST_LOG_OBJECT (camera, "finding framerate from list"); - for (i = 0, i_best = 0; i < list_size; i++) { - res = gst_value_compare (gst_value_list_get_value (framerate, i), - gst_value_list_get_value (framerate, i_best)); - if (comparison == res) { - i_best = i; - } - } - GST_LOG_OBJECT (camera, "found best framerate from index %d", i_best); - framerate = gst_value_list_get_value (framerate, i_best); - } - /* Handle framerate ranges */ - if (GST_VALUE_HOLDS_FRACTION_RANGE (framerate)) { - if (camera->night_mode) { - GST_LOG_OBJECT (camera, "getting min framerate from range"); - framerate = gst_value_get_fraction_range_min (framerate); - } else { - GST_LOG_OBJECT (camera, "getting max framerate from range"); - framerate = gst_value_get_fraction_range_max (framerate); - } - } - } - - /* Check if we found better framerate */ - if (orig_framerate && framerate) { - res = gst_value_compare (orig_framerate, framerate); - if (comparison == res) { - GST_LOG_OBJECT (camera, "original framerate was the best"); - framerate = orig_framerate; - } - } - - return framerate; -} - -/* - * GObject callback functions implementation - */ - -static void -gst_camerabin_base_init (gpointer gclass) -{ - static GstElementDetails element_details = { - "Camera Bin", - "Generic/Bin/Camera", - "Handle lot of features present in DSC", - "Nokia Corporation \n" - "Edgard Lima " - }; - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - gst_element_class_set_details (element_class, &element_details); -} - -static void -gst_camerabin_class_init (GstCameraBinClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - GstBinClass *gstbin_class; - - gobject_class = G_OBJECT_CLASS (klass); - gstelement_class = GST_ELEMENT_CLASS (klass); - gstbin_class = GST_BIN_CLASS (klass); - - /* gobject */ - - gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_camerabin_dispose); - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_camerabin_finalize); - - gobject_class->set_property = gst_camerabin_set_property; - gobject_class->get_property = gst_camerabin_get_property; - - /** - * GstCameraBin:filename: - * - * Set filename for the still image capturing or video capturing. - */ - - g_object_class_install_property (gobject_class, ARG_FILENAME, - g_param_spec_string ("filename", "Filename", - "Filename of the image or video to save", "", G_PARAM_READWRITE)); - - /** - * GstCameraBin:mode: - * - * Set the mode of operation: still image capturing or video recording. - * Setting the mode will create and destroy image bin or video bin elements - * according to the mode. You can set this property at any time, changing - * the mode will stop ongoing capture. - */ - - g_object_class_install_property (gobject_class, ARG_MODE, - g_param_spec_enum ("mode", "Mode", - "The capture mode (still image capture or video recording)", - GST_TYPE_CAMERABIN_MODE, DEFAULT_MODE, G_PARAM_READWRITE)); - - /** - * GstCameraBin:mute: - * - * Mute audio in video recording mode. - * Set this property only when #GstCameraBin is in READY, PAUSED or PLAYING. - */ - - g_object_class_install_property (gobject_class, ARG_MUTE, - g_param_spec_boolean ("mute", "Mute", - "True to mute the recording. False to record with audio", - ARG_DEFAULT_MUTE, G_PARAM_READWRITE)); - - /** - * GstCameraBin:zoom: - * - * Set up the zoom applied to the frames. - * Set this property only when #GstCameraBin is in READY, PAUSED or PLAYING. - */ - - g_object_class_install_property (gobject_class, ARG_ZOOM, - g_param_spec_int ("zoom", "Zoom", - "The zoom. 100 for 1x, 200 for 2x and so on", - MIN_ZOOM, MAX_ZOOM, DEFAULT_ZOOM, G_PARAM_READWRITE)); - - /** - * GstCameraBin:imagepp: - * - * Set up an element to do image post processing. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - g_object_class_install_property (gobject_class, ARG_IMAGE_POST, - g_param_spec_object ("imagepp", "Image post processing element", - "Image Post-Processing GStreamer element (default is NULL)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:imageenc: - * - * Set up an image encoder (for example, jpegenc or pngenc) element. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_IMAGE_ENC, - g_param_spec_object ("imageenc", "Image encoder", - "Image encoder GStreamer element (default is jpegenc)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:videopp: - * - * Set up an element to do video post processing. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_VIDEO_POST, - g_param_spec_object ("videopp", "Video post processing element", - "Video post processing GStreamer element (default is NULL)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:videoenc: - * - * Set up a video encoder element. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_VIDEO_ENC, - g_param_spec_object ("videoenc", "Video encoder", - "Video encoder GStreamer element (default is theoraenc)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:audioenc: - * - * Set up an audio encoder element. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_AUDIO_ENC, - g_param_spec_object ("audioenc", "Audio encoder", - "Audio encoder GStreamer element (default is vorbisenc)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:videomux: - * - * Set up a video muxer element. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_VIDEO_MUX, - g_param_spec_object ("videomux", "Video muxer", - "Video muxer GStreamer element (default is oggmux)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:vfsink: - * - * Set up a sink element to render frames in view finder. - * By default "autovideosink" will be the sink element. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_VF_SINK, - g_param_spec_object ("vfsink", "View finder sink", - "View finder sink GStreamer element (default is " DEFAULT_VIEW_SINK - ")", GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:videosrc: - * - * Set up a video source element. - * By default "v4l2src" will be the src element. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_VIDEO_SRC, - g_param_spec_object ("videosrc", "Video source element", - "Video source GStreamer element (default is " DEFAULT_SRC_VID_SRC ")", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - /** - * GstCameraBin:audiosrc: - * - * Set up an audio source element. - * By default "pulsesrc" will be the source element. - * This property can only be set while #GstCameraBin is in NULL state. - * The ownership of the element will be taken by #GstCameraBin. - */ - - g_object_class_install_property (gobject_class, ARG_AUDIO_SRC, - g_param_spec_object ("audiosrc", "Audio source element", - "Audio source GStreamer element (default is pulsesrc)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - - /** - * GstCameraBin:inputcaps: - * - * The allowed modes of operation of the video source. Have in mind that it - * doesn't mean #GstCameraBin can operate in all those modes, - * it depends also on the other elements in the pipeline. Remember to - * gst_caps_unref after using it. - */ - - g_object_class_install_property (gobject_class, ARG_INPUT_CAPS, - g_param_spec_boxed ("inputcaps", "Input caps", - "The allowed modes of the video source operation", - GST_TYPE_CAPS, G_PARAM_READABLE)); - - /** - * GstCameraBin:filter-caps: - * - * Filter video source element caps using this property. - * This is an alternative to #GstCamerabin::user-res-fps action - * signal that allows more fine grained control of video source. - */ - - g_object_class_install_property (gobject_class, ARG_FILTER_CAPS, - g_param_spec_boxed ("filter-caps", "Filter caps", - "Capsfilter caps used to control video source operation", - GST_TYPE_CAPS, G_PARAM_READWRITE)); - - /** - * GstCameraBin::user-start: - * @camera: the camera bin element - * - * Starts image capture or video recording depending on the Mode. - * If there is a capture already going on, does nothing. - * Resumes video recording if it has been paused. - */ - - camerabin_signals[USER_START_SIGNAL] = - g_signal_new ("user-start", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GstCameraBinClass, user_start), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - /** - * GstCameraBin::user-stop: - * @camera: the camera bin element - * - * Stops still image preview, continuous image capture and video - * recording and returns to the view finder mode. - */ - - camerabin_signals[USER_STOP_SIGNAL] = - g_signal_new ("user-stop", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GstCameraBinClass, user_stop), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - /** - * GstCameraBin::user-pause: - * @camera: the camera bin element - * - * Pauses video recording or resumes paused video recording. - * If in image mode or not recording, does nothing. - */ - - camerabin_signals[USER_PAUSE_SIGNAL] = - g_signal_new ("user-pause", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GstCameraBinClass, user_pause), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - /** - * GstCameraBin::user-res-fps: - * @camera: the camera bin element - * @width: number of horizontal pixels - * @height: number of vertical pixels - * @fps_n: frames per second numerator - * @fps_d: frames per second denominator - * - * Changes the frame resolution and frames per second of the video source. - * The application must be aware of the resolutions supported by the camera. - * Supported resolutions and frame rates can be get using input-caps property. - * - * Setting @fps_n or @fps_d to 0 configures maximum framerate for the - * given resolution, unless in night mode when minimum is configured. - */ - - camerabin_signals[USER_RES_FPS_SIGNAL] = - g_signal_new ("user-res-fps", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GstCameraBinClass, user_res_fps), - NULL, NULL, gst_camerabin_marshal_VOID__INT_INT_INT_INT, G_TYPE_NONE, 4, - G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); - - /** - * GstCameraBin::user-image-res: - * @camera: the camera bin element - * @width: number of horizontal pixels - * @height: number of vertical pixels - * - * Changes the resolution used for still image capture. - * Does not affect view finder mode and video recording. - * Use this action signal in PAUSED or PLAYING state. - */ - - camerabin_signals[USER_IMAGE_RES_SIGNAL] = - g_signal_new ("user-image-res", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GstCameraBinClass, user_image_res), - NULL, NULL, gst_camerabin_marshal_VOID__INT_INT, G_TYPE_NONE, 2, - G_TYPE_INT, G_TYPE_INT); - - /** - * GstCameraBin::img-done: - * @camera: the camera bin element - * @filename: the name of the file just saved - * - * Signal emited when the file has just been saved. To continue taking - * pictures just update @filename and return TRUE, otherwise return FALSE. - * - * Don't call any #GstCameraBin method from this signal, if you do so there - * will be a deadlock. - */ - - camerabin_signals[IMG_DONE_SIGNAL] = - g_signal_new ("img-done", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstCameraBinClass, img_done), - g_signal_accumulator_true_handled, NULL, gst_marshal_BOOLEAN__POINTER, - G_TYPE_BOOLEAN, 1, G_TYPE_POINTER); - - klass->user_start = gst_camerabin_user_start; - klass->user_stop = gst_camerabin_user_stop; - klass->user_pause = gst_camerabin_user_pause; - klass->user_res_fps = gst_camerabin_user_res_fps; - klass->user_image_res = gst_camerabin_user_image_res; - - klass->img_done = gst_camerabin_default_signal_img_done; - - /* gstelement */ - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_camerabin_change_state); - - /* gstbin */ - /* override handle_message to peek when video or image bin reaches eos */ - gstbin_class->handle_message = - GST_DEBUG_FUNCPTR (gst_camerabin_handle_message_func); - -} - -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ -static void -gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass) -{ - /* GstElementClass *klass = GST_ELEMENT_GET_CLASS (camera); */ - - camera->filename = g_string_new (""); - camera->mode = DEFAULT_MODE; - camera->num_img_buffers = 0; - camera->stop_requested = FALSE; - camera->paused = FALSE; - camera->capturing = FALSE; - camera->night_mode = FALSE; - - camera->width = DEFAULT_WIDTH; - camera->height = DEFAULT_HEIGHT; - camera->fps_n = DEFAULT_FPS_N; - camera->fps_d = DEFAULT_FPS_D; - - camera->image_capture_caps = NULL; - camera->view_finder_caps = NULL; - camera->allowed_caps = NULL; - - camera->zoom = DEFAULT_ZOOM; - - /* concurrency control */ - camera->capture_mutex = g_mutex_new (); - camera->cond = g_cond_new (); - - /* pad names for output and input selectors */ - camera->pad_src_view = NULL; - camera->pad_view_src = NULL; - camera->pad_src_img = NULL; - camera->pad_view_img = NULL; - camera->pad_src_vid = NULL; - camera->pad_view_vid = NULL; - camera->srcpad_zoom_filter = NULL; - - /* source elements */ - camera->src_vid_src = NULL; - camera->src_filter = NULL; - camera->src_zoom_crop = NULL; - camera->src_zoom_scale = NULL; - camera->src_zoom_filter = NULL; - camera->src_out_sel = NULL; - - camera->user_vf_sink = NULL; - - /* image capture bin */ - camera->imgbin = g_object_new (GST_TYPE_CAMERABIN_IMAGE, NULL); - gst_object_ref (camera->imgbin); - - /* video capture bin */ - camera->vidbin = g_object_new (GST_TYPE_CAMERABIN_VIDEO, NULL); - gst_object_ref (camera->vidbin); - - camera->active_bin = NULL; - - /* view finder elements */ - camera->view_in_sel = NULL; - camera->view_scale = NULL; - camera->view_sink = NULL; -} - -static void -gst_camerabin_dispose (GObject * object) -{ - GstCameraBin *camera; - - camera = GST_CAMERABIN (object); - - GST_DEBUG_OBJECT (camera, "disposing"); - - gst_element_set_state (camera->imgbin, GST_STATE_NULL); - gst_object_unref (camera->imgbin); - - gst_element_set_state (camera->vidbin, GST_STATE_NULL); - gst_object_unref (camera->vidbin); - - camerabin_destroy_elements (camera); - - camerabin_dispose_elements (camera); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_camerabin_finalize (GObject * object) -{ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_camerabin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstCameraBin *camera = GST_CAMERABIN (object); - - switch (prop_id) { - case ARG_MUTE: - gst_camerabin_video_set_mute (GST_CAMERABIN_VIDEO (camera->vidbin), - g_value_get_boolean (value)); - break; - case ARG_ZOOM: - g_atomic_int_set (&camera->zoom, g_value_get_int (value)); - gst_camerabin_setup_zoom (camera); - break; - case ARG_MODE: - gst_camerabin_change_mode (camera, g_value_get_enum (value)); - break; - case ARG_FILENAME: - gst_camerabin_change_filename (camera, g_value_get_string (value)); - break; - case ARG_VIDEO_POST: - if (GST_STATE (camera->vidbin) != GST_STATE_NULL) { - GST_WARNING_OBJECT (camera, - "can't use set element until next video bin NULL to READY state change"); - } - gst_camerabin_video_set_post (GST_CAMERABIN_VIDEO (camera->vidbin), - g_value_get_object (value)); - break; - case ARG_VIDEO_ENC: - if (GST_STATE (camera->vidbin) != GST_STATE_NULL) { - GST_WARNING_OBJECT (camera, - "can't use set element until next video bin NULL to READY state change"); - } - gst_camerabin_video_set_video_enc (GST_CAMERABIN_VIDEO (camera->vidbin), - g_value_get_object (value)); - break; - case ARG_AUDIO_ENC: - if (GST_STATE (camera->vidbin) != GST_STATE_NULL) { - GST_WARNING_OBJECT (camera, - "can't use set element until next video bin NULL to READY state change"); - } - gst_camerabin_video_set_audio_enc (GST_CAMERABIN_VIDEO (camera->vidbin), - g_value_get_object (value)); - break; - case ARG_VIDEO_MUX: - if (GST_STATE (camera->vidbin) != GST_STATE_NULL) { - GST_WARNING_OBJECT (camera->vidbin, - "can't use set element until next video bin NULL to READY state change"); - } - gst_camerabin_video_set_muxer (GST_CAMERABIN_VIDEO (camera->vidbin), - g_value_get_object (value)); - break; - case ARG_IMAGE_POST: - if (GST_STATE (camera->imgbin) != GST_STATE_NULL) { - GST_WARNING_OBJECT (camera, - "can't use set element until next image bin NULL to READY state change"); - } - gst_camerabin_image_set_postproc (GST_CAMERABIN_IMAGE (camera->imgbin), - g_value_get_object (value)); - break; - case ARG_IMAGE_ENC: - if (GST_STATE (camera->imgbin) != GST_STATE_NULL) { - GST_WARNING_OBJECT (camera, - "can't use set element until next image bin NULL to READY state change"); - } - gst_camerabin_image_set_encoder (GST_CAMERABIN_IMAGE (camera->imgbin), - g_value_get_object (value)); - break; - case ARG_VF_SINK: - if (GST_STATE (camera) != GST_STATE_NULL) { - GST_ELEMENT_ERROR (camera, CORE, FAILED, - ("camerabin must be in NULL state when setting the view finder element"), - (NULL)); - } else { - if (camera->user_vf_sink) - gst_object_unref (camera->user_vf_sink); - camera->user_vf_sink = g_value_get_object (value); - gst_object_ref (camera->user_vf_sink); - } - break; - case ARG_VIDEO_SRC: - if (GST_STATE (camera) != GST_STATE_NULL) { - GST_ELEMENT_ERROR (camera, CORE, FAILED, - ("camerabin must be in NULL state when setting the video source element"), - (NULL)); - } else { - if (camera->user_vid_src) - gst_object_unref (camera->user_vid_src); - camera->user_vid_src = g_value_get_object (value); - gst_object_ref (camera->user_vid_src); - } - break; - case ARG_AUDIO_SRC: - if (GST_STATE (camera->vidbin) != GST_STATE_NULL) { - GST_WARNING_OBJECT (camera, - "can't use set element until next video bin NULL to READY state change"); - } - gst_camerabin_video_set_audio_src (GST_CAMERABIN_VIDEO (camera->vidbin), - g_value_get_object (value)); - break; - case ARG_FILTER_CAPS: - GST_OBJECT_LOCK (camera); - if (camera->view_finder_caps) { - gst_caps_unref (camera->view_finder_caps); - } - camera->view_finder_caps = gst_caps_copy (gst_value_get_caps (value)); - GST_OBJECT_UNLOCK (camera); - gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_camerabin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstCameraBin *camera = GST_CAMERABIN (object); - - switch (prop_id) { - case ARG_FILENAME: - g_value_set_string (value, camera->filename->str); - break; - case ARG_MODE: - g_value_set_enum (value, camera->mode); - break; - case ARG_MUTE: - g_value_set_boolean (value, - gst_camerabin_video_get_mute (GST_CAMERABIN_VIDEO (camera->vidbin))); - break; - case ARG_ZOOM: - g_value_set_int (value, g_atomic_int_get (&camera->zoom)); - break; - case ARG_IMAGE_POST: - g_value_set_object (value, - gst_camerabin_image_get_postproc (GST_CAMERABIN_IMAGE - (camera->imgbin))); - break; - case ARG_IMAGE_ENC: - g_value_set_object (value, - gst_camerabin_image_get_encoder (GST_CAMERABIN_IMAGE - (camera->imgbin))); - break; - case ARG_VIDEO_POST: - g_value_set_object (value, - gst_camerabin_video_get_post (GST_CAMERABIN_VIDEO (camera->vidbin))); - break; - case ARG_VIDEO_ENC: - g_value_set_object (value, - gst_camerabin_video_get_video_enc (GST_CAMERABIN_VIDEO - (camera->vidbin))); - break; - case ARG_AUDIO_ENC: - g_value_set_object (value, - gst_camerabin_video_get_audio_enc (GST_CAMERABIN_VIDEO - (camera->vidbin))); - break; - case ARG_VIDEO_MUX: - g_value_set_object (value, - gst_camerabin_video_get_muxer (GST_CAMERABIN_VIDEO (camera->vidbin))); - break; - case ARG_VF_SINK: - g_value_set_object (value, camera->user_vf_sink); - break; - case ARG_VIDEO_SRC: - g_value_set_object (value, camera->src_vid_src); - break; - case ARG_AUDIO_SRC: - g_value_set_object (value, - gst_camerabin_video_get_audio_src (GST_CAMERABIN_VIDEO - (camera->vidbin))); - break; - case ARG_INPUT_CAPS: - gst_value_set_caps (value, gst_camerabin_get_allowed_input_caps (camera)); - break; - case ARG_FILTER_CAPS: - gst_value_set_caps (value, camera->view_finder_caps); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* - * GstElement functions implementation - */ - -static GstStateChangeReturn -gst_camerabin_change_state (GstElement * element, GstStateChange transition) -{ - GstCameraBin *camera = GST_CAMERABIN (element); - GstStateChangeReturn ret; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!camerabin_create_elements (camera)) { - ret = GST_STATE_CHANGE_FAILURE; - goto done; - } - /* Lock to control image and video bin state separately - from view finder */ - gst_element_set_locked_state (camera->imgbin, TRUE); - gst_element_set_locked_state (camera->vidbin, TRUE); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - camerabin_setup_src_elements (camera); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - /* If using autovideosink, set view finder sink properties - now that actual sink has been created. */ - camerabin_setup_view_elements (camera); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - gst_element_set_locked_state (camera->imgbin, FALSE); - gst_element_set_locked_state (camera->vidbin, FALSE); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_LOG_OBJECT (camera, "PAUSED to READY"); - g_mutex_lock (camera->capture_mutex); - if (camera->capturing) { - GST_WARNING_OBJECT (camera, "was capturing when changing to READY"); - camera->capturing = FALSE; - /* Reset capture and don't wait for capturing to finish properly. - Proper capturing should have been finished before going to READY. */ - gst_camerabin_reset_to_view_finder (camera); - g_cond_signal (camera->cond); - } - g_mutex_unlock (camera->capture_mutex); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - camerabin_destroy_elements (camera); - break; - default: - break; - } - -done: - - return ret; -} - -/* - * GstBin functions implementation - */ - -/* Peek eos messages but don't interfere with bin msg handling */ -static void -gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg) -{ - GstCameraBin *camera = GST_CAMERABIN (bin); - - switch (GST_MESSAGE_TYPE (msg)) { - case GST_MESSAGE_EOS: - if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->vidbin)) { - /* Video eos */ - GST_DEBUG_OBJECT (camera, - "got video eos message, stopping video capture"); - g_mutex_lock (camera->capture_mutex); - camera->capturing = FALSE; - g_cond_signal (camera->cond); - g_mutex_unlock (camera->capture_mutex); - } else if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->imgbin)) { - /* Image eos */ - GST_DEBUG_OBJECT (camera, "got image eos message"); - /* Unblock pad to process next buffer */ - gst_pad_set_blocked_async (camera->pad_src_img, FALSE, - (GstPadBlockCallback) image_pad_blocked, camera); - } - break; - default: - break; - } - GST_BIN_CLASS (parent_class)->handle_message (bin, msg); -} - -/* - * Action signal function implementation - */ - -static void -gst_camerabin_user_start (GstCameraBin * camera) -{ - - GST_INFO_OBJECT (camera, "starting capture"); - if (camera->paused) { - gst_camerabin_user_pause (camera); - return; - } - - if (!camera->active_bin) { - GST_INFO_OBJECT (camera, "mode not explicitly set by application"); - gst_camerabin_change_mode (camera, camera->mode); - } - - if (g_str_equal (camera->filename->str, "")) { - GST_ELEMENT_ERROR (camera, CORE, FAILED, - ("set filename before starting capture"), (NULL)); - return; - } - - g_mutex_lock (camera->capture_mutex); - if (camera->capturing) { - GST_WARNING_OBJECT (camera, "capturing \"%s\" ongoing, set new filename", - camera->filename->str); - g_mutex_unlock (camera->capture_mutex); - return; - } - g_mutex_unlock (camera->capture_mutex); - - g_object_set (G_OBJECT (camera->active_bin), "filename", - camera->filename->str, NULL); - - if (camera->active_bin == camera->imgbin) { - gst_camerabin_start_image_capture (camera); - } else if (camera->active_bin == camera->vidbin) { - gst_camerabin_start_video_recording (camera); - } -} - -static void -gst_camerabin_user_stop (GstCameraBin * camera) -{ - GST_INFO_OBJECT (camera, "stopping %s capture", - camera->mode ? "video" : "image"); - gst_camerabin_do_stop (camera); - gst_camerabin_reset_to_view_finder (camera); -} - -static void -gst_camerabin_user_pause (GstCameraBin * camera) -{ - if (camera->active_bin == camera->vidbin) { - if (!camera->paused) { - GST_INFO_OBJECT (camera, "pausing capture"); - - /* Bring all camerabin elements to PAUSED */ - gst_element_set_locked_state (camera->vidbin, FALSE); - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED); - - /* Switch to view finder mode */ - g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE, - "active-pad", camera->pad_src_view, NULL); - - /* Enable view finder mode in v4l2camsrc */ - if (g_object_class_find_property (G_OBJECT_GET_CLASS - (camera->src_vid_src), "capture-mode")) { - g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 0, NULL); - } - - /* Set view finder to PLAYING and leave videobin PAUSED */ - gst_element_set_locked_state (camera->vidbin, TRUE); - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PLAYING); - - camera->paused = TRUE; - } else { - GST_INFO_OBJECT (camera, "unpausing capture"); - - /* Bring all camerabin elements to PAUSED */ - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED); - - /* Switch to video recording mode */ - g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", TRUE, - "active-pad", camera->pad_src_vid, NULL); - - /* Enable video recording mode in v4l2camsrc */ - if (g_object_class_find_property (G_OBJECT_GET_CLASS - (camera->src_vid_src), "capture-mode")) { - g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 2, NULL); - } - - /* Bring all camerabin elements to PLAYING */ - gst_element_set_locked_state (camera->vidbin, FALSE); - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PLAYING); - gst_element_set_locked_state (camera->vidbin, TRUE); - - camera->paused = FALSE; - } - GST_DEBUG_OBJECT (camera, "pause done"); - } else { - GST_WARNING ("pausing in image capture mode disabled"); - } -} - -static void -gst_camerabin_user_res_fps (GstCameraBin * camera, gint width, gint height, - gint fps_n, gint fps_d) -{ - GstState state; - - GST_INFO_OBJECT (camera, "switching resolution to %dx%d and fps to %d/%d", - width, height, fps_n, fps_d); - - state = GST_STATE (camera); - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_READY); - camera->width = width; - camera->height = height; - camera->fps_n = fps_n; - camera->fps_d = fps_d; - gst_element_set_state (GST_ELEMENT (camera), state); -} - -static void -gst_camerabin_user_image_res (GstCameraBin * camera, gint width, gint height) -{ - GstStructure *structure; - GstCaps *new_caps = NULL; - guint32 format = 0; - - g_return_if_fail (camera != NULL); - - if (width && height && camera->view_finder_caps) { - /* Use view finder mode caps as a basis */ - structure = gst_caps_get_structure (camera->view_finder_caps, 0); - - /* Set new resolution for image capture */ - new_caps = gst_caps_new_simple (gst_structure_get_name (structure), - "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); - - /* Set format according to current videosrc format */ - format = get_srcpad_current_format (camera->src_vid_src); - if (format) { - gst_caps_set_simple (new_caps, "format", GST_TYPE_FOURCC, format, NULL); - } - - /* Set allowed framerate for the resolution. */ - gst_camerabin_set_allowed_framerate (camera, new_caps); - - /* Reset the format to match with view finder mode caps */ - if (gst_structure_get_fourcc (structure, "format", &format)) { - gst_caps_set_simple (new_caps, "format", GST_TYPE_FOURCC, format, NULL); - } - } - - GST_INFO_OBJECT (camera, - "init filter caps for image capture %" GST_PTR_FORMAT, new_caps); - gst_caps_replace (&camera->image_capture_caps, new_caps); -} - -/* entry point to initialize the plug-in - * initialize the plug-in itself - * register the element factories and pad templates - * register the features - */ -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_camerabin_debug, "camerabin", 0, "CameraBin"); - - return gst_element_register (plugin, "camerabin", - GST_RANK_NONE, GST_TYPE_CAMERABIN); -} - -/* this is the structure that gstreamer looks for to register plugins - */ -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "camerabin", - "High level api for DC (Digital Camera) application", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)