From 3e449256ec94772eef6ea4b65ec3cde66890f8b7 Mon Sep 17 00:00:00 2001 From: Nikita Yushchenko Date: Sat, 26 Sep 2015 11:12:23 +0300 Subject: [PATCH] v4l2src: implement autofocus control This patch adds simple GstPhotography integration, and implements autofocus related part of GstPhotography. GstPhotography implementation is fare incomplete - only autofocus related part is implemented. Side effect: had to rename capture-mode property to imx-capture mode, to avoid conflict with "capture-mode" setting from WrapperCameraBinSrc. Signed-off-by: Nikita Yushchenko --- src/v4l2src/v4l2src.c | 558 +++++++++++++++++++++++++++++++++++++++++++++++++- src/v4l2src/v4l2src.h | 4 + src/v4l2src/wscript | 2 +- wscript | 1 + 4 files changed, 556 insertions(+), 9 deletions(-) diff --git a/src/v4l2src/v4l2src.c b/src/v4l2src/v4l2src.c index cbf4190..6bb59d1 100644 --- a/src/v4l2src/v4l2src.c +++ b/src/v4l2src/v4l2src.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,29 @@ enum IMX_V4L2SRC_INPUT, IMX_V4L2SRC_DEVICE, IMX_V4L2SRC_QUEUE_SIZE, + + /* Properties required to be recongnized by GstPhotography implementor */ + PROP_WB_MODE, + PROP_COLOR_TONE, + PROP_SCENE_MODE, + PROP_FLASH_MODE, + PROP_FLICKER_MODE, + PROP_FOCUS_MODE, + PROP_CAPABILITIES, + PROP_EV_COMP, + PROP_ISO_SPEED, + PROP_APERTURE, + PROP_EXPOSURE_TIME, + PROP_IMAGE_CAPTURE_SUPPORTED_CAPS, + PROP_IMAGE_PREVIEW_SUPPORTED_CAPS, + PROP_ZOOM, + PROP_COLOR_TEMPERATURE, + PROP_WHITE_POINT, + PROP_ANALOG_GAIN, + PROP_LENS_FOCUS, + PROP_MIN_EXPOSURE_TIME, + PROP_MAX_EXPOSURE_TIME, + PROP_NOISE_REDUCTION, }; static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( @@ -67,11 +91,21 @@ GST_DEBUG_CATEGORY_STATIC(gst_imx_v4l2src_debug_category); static void gst_imx_v4l2src_uri_handler_init(gpointer g_iface, gpointer iface_data); +static void gst_imx_v4l2src_photography_init(gpointer g_iface, + gpointer iface_data); G_DEFINE_TYPE_WITH_CODE(GstImxV4l2VideoSrc, gst_imx_v4l2src, GST_TYPE_PUSH_SRC, G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, gst_imx_v4l2src_uri_handler_init); + G_IMPLEMENT_INTERFACE(GST_TYPE_PHOTOGRAPHY, gst_imx_v4l2src_photography_init); DEBUG_INIT) +static void gst_imx_v4l2src_apply_focus_settings(GstImxV4l2VideoSrc *v4l2src, + gboolean activate); +static gboolean gst_imx_v4l2src_set_focus_mode(GstPhotography *photo, + GstPhotographyFocusMode focus_mode); +static gboolean gst_imx_v4l2src_get_focus_mode(GstPhotography *photo, + GstPhotographyFocusMode *focus_mode); + static gint gst_imx_v4l2src_capture_setup(GstImxV4l2VideoSrc *v4l2src) { struct v4l2_format fmt = {0}; @@ -186,6 +220,10 @@ static gboolean gst_imx_v4l2src_start(GstBaseSrc *src) v4l2src->fps_d, v4l2src->fps_n); v4l2src->count = 0; + g_mutex_lock(&v4l2src->af_mutex); + gst_imx_v4l2src_apply_focus_settings(v4l2src, TRUE); + g_mutex_unlock(&v4l2src->af_mutex); + return TRUE; } @@ -195,6 +233,10 @@ static gboolean gst_imx_v4l2src_stop(GstBaseSrc *src) GST_LOG_OBJECT(v4l2src, "stop"); + g_mutex_lock(&v4l2src->af_mutex); + gst_imx_v4l2src_apply_focus_settings(v4l2src, FALSE); + g_mutex_unlock(&v4l2src->af_mutex); + gst_imx_fd_object_unref(v4l2src->fd_obj_v4l); return TRUE; @@ -280,10 +322,8 @@ static GstFlowReturn gst_imx_v4l2src_fill(GstPushSrc *src, GstBuffer *buf) return GST_FLOW_OK; } -static gboolean gst_imx_v4l2src_negotiate(GstBaseSrc *src) +static GstCaps *gst_imx_v4l2src_caps_for_current_setup(GstImxV4l2VideoSrc *v4l2src) { - GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(src); - GstCaps *caps; GstVideoFormat gst_fmt; const gchar *pixel_format = NULL; const gchar *interlace_mode = "progressive"; @@ -292,7 +332,7 @@ static gboolean gst_imx_v4l2src_negotiate(GstBaseSrc *src) fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(GST_IMX_FD_OBJECT_GET_FD(v4l2src->fd_obj_v4l), VIDIOC_G_FMT, &fmt) < 0) { GST_ERROR_OBJECT(v4l2src, "VIDIOC_G_FMT failed"); - return FALSE; + return NULL; } switch (fmt.fmt.pix.pixelformat) { @@ -310,9 +350,7 @@ static gboolean gst_imx_v4l2src_negotiate(GstBaseSrc *src) if (fmt.fmt.pix.field == V4L2_FIELD_INTERLACED) interlace_mode = "interleaved"; - /* not much to negotiate; - * we already performed setup, so that is what will be streamed */ - caps = gst_caps_new_simple("video/x-raw", + return gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, pixel_format, "width", G_TYPE_INT, v4l2src->capture_width, "height", G_TYPE_INT, v4l2src->capture_height, @@ -320,6 +358,18 @@ static gboolean gst_imx_v4l2src_negotiate(GstBaseSrc *src) "framerate", GST_TYPE_FRACTION, v4l2src->fps_n, v4l2src->fps_d, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL); +} + +static gboolean gst_imx_v4l2src_negotiate(GstBaseSrc *src) +{ + GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(src); + GstCaps *caps; + + /* not much to negotiate; + * we already performed setup, so that is what will be streamed */ + caps = gst_imx_v4l2src_caps_for_current_setup(v4l2src); + if (!caps) + return FALSE; GST_INFO_OBJECT(src, "negotiated caps %" GST_PTR_FORMAT, (gpointer)caps); @@ -387,6 +437,33 @@ static void gst_imx_v4l2src_set_property(GObject *object, guint prop_id, v4l2src->queue_size = g_value_get_int(value); break; + case PROP_FOCUS_MODE: + gst_imx_v4l2src_set_focus_mode(GST_PHOTOGRAPHY(v4l2src), g_value_get_enum(value)); + break; + + case PROP_WB_MODE: + case PROP_COLOR_TONE: + case PROP_SCENE_MODE: + case PROP_FLASH_MODE: + case PROP_FLICKER_MODE: + case PROP_CAPABILITIES: + case PROP_EV_COMP: + case PROP_ISO_SPEED: + case PROP_APERTURE: + case PROP_EXPOSURE_TIME: + case PROP_IMAGE_CAPTURE_SUPPORTED_CAPS: + case PROP_IMAGE_PREVIEW_SUPPORTED_CAPS: + case PROP_ZOOM: + case PROP_COLOR_TEMPERATURE: + case PROP_WHITE_POINT: + case PROP_ANALOG_GAIN: + case PROP_LENS_FOCUS: + case PROP_MIN_EXPOSURE_TIME: + case PROP_MAX_EXPOSURE_TIME: + case PROP_NOISE_REDUCTION: + GST_WARNING_OBJECT(v4l2src, "setting GstPhotography properties is not supported"); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -420,6 +497,93 @@ static void gst_imx_v4l2src_get_property(GObject *object, guint prop_id, g_value_set_int(value, v4l2src->queue_size); break; + case PROP_FOCUS_MODE: + { + GstPhotographyFocusMode focus_mode; + gst_imx_v4l2src_get_focus_mode(GST_PHOTOGRAPHY(v4l2src), &focus_mode); + g_value_set_enum(value, focus_mode); + } + break; + + case PROP_WB_MODE: + g_value_set_enum(value, GST_PHOTOGRAPHY_WB_MODE_AUTO); + break; + + case PROP_COLOR_TONE: + g_value_set_enum(value, GST_PHOTOGRAPHY_COLOR_TONE_MODE_NORMAL); + break; + + case PROP_SCENE_MODE: + g_value_set_enum(value, GST_TYPE_PHOTOGRAPHY_SCENE_MODE); + break; + + case PROP_FLASH_MODE: + g_value_set_enum(value, GST_PHOTOGRAPHY_FLASH_MODE_AUTO); + break; + + case PROP_FLICKER_MODE: + g_value_set_enum(value, GST_PHOTOGRAPHY_FLICKER_REDUCTION_OFF); + break; + + case PROP_CAPABILITIES: + g_value_set_ulong(value, GST_PHOTOGRAPHY_CAPS_NONE); + break; + + case PROP_EV_COMP: + g_value_set_float(value, 0.0f); + break; + + case PROP_ISO_SPEED: + g_value_set_uint(value, 0); + break; + + case PROP_APERTURE: + g_value_set_uint(value, 0); + break; + + case PROP_EXPOSURE_TIME: + g_value_set_uint(value, 0); + break; + + case PROP_IMAGE_CAPTURE_SUPPORTED_CAPS: + case PROP_IMAGE_PREVIEW_SUPPORTED_CAPS: + if (v4l2src->fd_obj_v4l) + gst_value_set_caps(value, gst_imx_v4l2src_caps_for_current_setup(v4l2src)); + else + GST_DEBUG_OBJECT(v4l2src, "not connected to hardware, don't know supported caps"); + break; + + case PROP_ZOOM: + g_value_set_float(value, 1.0f); + break; + + case PROP_COLOR_TEMPERATURE: + g_value_set_uint(value, 0); + break; + + case PROP_WHITE_POINT: + g_value_set_boxed(value, NULL); + break; + + case PROP_ANALOG_GAIN: + g_value_set_float(value, 1.0f); + break; + + case PROP_LENS_FOCUS: + g_value_set_float(value, 0.0f); + break; + + case PROP_MIN_EXPOSURE_TIME: + g_value_set_uint(value, 0); + break; + + case PROP_MAX_EXPOSURE_TIME: + g_value_set_uint(value, 0); + break; + + case PROP_NOISE_REDUCTION: + g_value_set_flags(value, 0); + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -436,10 +600,24 @@ static void gst_imx_v4l2src_init(GstImxV4l2VideoSrc *v4l2src) v4l2src->queue_size = DEFAULT_QUEUE_SIZE; v4l2src->fd_obj_v4l = NULL; + g_mutex_init(&v4l2src->af_mutex); + v4l2src->focus_mode = GST_PHOTOGRAPHY_FOCUS_MODE_AUTO; + v4l2src->af_clock_id = NULL; + gst_base_src_set_format(GST_BASE_SRC(v4l2src), GST_FORMAT_TIME); gst_base_src_set_live(GST_BASE_SRC(v4l2src), TRUE); } +static void gst_imx_v4l2src_finalize(GObject *object) +{ + GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(object); + + g_free(v4l2src->devicename); + g_mutex_clear(&v4l2src->af_mutex); + + G_OBJECT_CLASS(gst_imx_v4l2src_parent_class)->finalize(object); +} + static void gst_imx_v4l2src_class_init(GstImxV4l2VideoSrcClass *klass) { GObjectClass *gobject_class; @@ -454,9 +632,10 @@ static void gst_imx_v4l2src_class_init(GstImxV4l2VideoSrcClass *klass) gobject_class->set_property = gst_imx_v4l2src_set_property; gobject_class->get_property = gst_imx_v4l2src_get_property; + gobject_class->finalize = gst_imx_v4l2src_finalize; g_object_class_install_property(gobject_class, IMX_V4L2SRC_CAPTURE_MODE, - g_param_spec_int("capture-mode", "Capture mode", + g_param_spec_int("imx-capture-mode", "Capture mode", "Capture mode of camera, varies with each v4l2 driver,\n" "\t\t\t\tfor example ov5460:\n " "\t\t\t\tov5640_mode_VGA_640_480 = 0,\n" @@ -491,6 +670,51 @@ static void gst_imx_v4l2src_class_init(GstImxV4l2VideoSrcClass *klass) 0, G_MAXINT, DEFAULT_QUEUE_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* Being GstPhotography implementation implies overriding all properties + * defined by GstPhotography */ + g_object_class_override_property(gobject_class, + PROP_WB_MODE, GST_PHOTOGRAPHY_PROP_WB_MODE); + g_object_class_override_property(gobject_class, + PROP_COLOR_TONE, GST_PHOTOGRAPHY_PROP_COLOR_TONE); + g_object_class_override_property(gobject_class, + PROP_SCENE_MODE, GST_PHOTOGRAPHY_PROP_SCENE_MODE); + g_object_class_override_property(gobject_class, + PROP_FLASH_MODE, GST_PHOTOGRAPHY_PROP_FLASH_MODE); + g_object_class_override_property(gobject_class, + PROP_FLICKER_MODE, GST_PHOTOGRAPHY_PROP_FLICKER_MODE); + g_object_class_override_property(gobject_class, + PROP_FOCUS_MODE, GST_PHOTOGRAPHY_PROP_FOCUS_MODE); + g_object_class_override_property(gobject_class, + PROP_CAPABILITIES, GST_PHOTOGRAPHY_PROP_CAPABILITIES); + g_object_class_override_property(gobject_class, + PROP_EV_COMP, GST_PHOTOGRAPHY_PROP_EV_COMP); + g_object_class_override_property(gobject_class, + PROP_ISO_SPEED, GST_PHOTOGRAPHY_PROP_ISO_SPEED); + g_object_class_override_property(gobject_class, + PROP_APERTURE, GST_PHOTOGRAPHY_PROP_APERTURE); + g_object_class_override_property(gobject_class, + PROP_EXPOSURE_TIME, GST_PHOTOGRAPHY_PROP_EXPOSURE_TIME); + g_object_class_override_property(gobject_class, + PROP_IMAGE_CAPTURE_SUPPORTED_CAPS, GST_PHOTOGRAPHY_PROP_IMAGE_CAPTURE_SUPPORTED_CAPS); + g_object_class_override_property(gobject_class, + PROP_IMAGE_PREVIEW_SUPPORTED_CAPS, GST_PHOTOGRAPHY_PROP_IMAGE_PREVIEW_SUPPORTED_CAPS); + g_object_class_override_property(gobject_class, + PROP_ZOOM, GST_PHOTOGRAPHY_PROP_ZOOM); + g_object_class_override_property(gobject_class, + PROP_COLOR_TEMPERATURE, GST_PHOTOGRAPHY_PROP_COLOR_TEMPERATURE); + g_object_class_override_property(gobject_class, + PROP_WHITE_POINT, GST_PHOTOGRAPHY_PROP_WHITE_POINT); + g_object_class_override_property(gobject_class, + PROP_ANALOG_GAIN, GST_PHOTOGRAPHY_PROP_ANALOG_GAIN); + g_object_class_override_property(gobject_class, + PROP_LENS_FOCUS, GST_PHOTOGRAPHY_PROP_LENS_FOCUS); + g_object_class_override_property(gobject_class, + PROP_MIN_EXPOSURE_TIME, GST_PHOTOGRAPHY_PROP_MIN_EXPOSURE_TIME); + g_object_class_override_property(gobject_class, + PROP_MAX_EXPOSURE_TIME, GST_PHOTOGRAPHY_PROP_MAX_EXPOSURE_TIME); + g_object_class_override_property(gobject_class, + PROP_NOISE_REDUCTION, GST_PHOTOGRAPHY_PROP_NOISE_REDUCTION); + basesrc_class->negotiate = gst_imx_v4l2src_negotiate; basesrc_class->get_caps = gst_imx_v4l2src_get_caps; basesrc_class->set_caps = gst_imx_v4l2src_set_caps; @@ -559,6 +783,324 @@ static void gst_imx_v4l2src_uri_handler_init(gpointer g_iface, gpointer iface_da iface->set_uri = gst_imx_v4l2src_uri_set_uri; } +/* GstPhotographyFocusMode actually incapsulates two independent parameters: + * - where to focus (infinity/normal/macro) + * - when to focus (single/continuous) + * + * What is implemented: + * - if GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_* is set: + * - V4L2 focus range is set to NORMAL, and continuous autofocus is enabled + * and kept on while element is in PLAYING state, + * - set_autofocus(TRUE) locks focus via V4L2_CID_3A_LOCK + * - set_autofocus(FALSE) unlocks focus via V4L2_CID_3A_LOCK + * - if other supported mode is set: + * - set_autofocus(TRUE) triggers autofocus via V4L2_CID_AUTO_FOCUS_START + * - set_autofocus(FALSE) stops autofocus via V4L2_CID_AUTO_FOCUS_STOP + * - GST_PHOTOGRAPHY_FOCUS_DONE message is generated when focused + * - mode is mapped to V4L2 focus range as follows: + * GST_PHOTOGRAPHY_FOCUS_MODE_AUTO => V4L2_AUTO_FOCUS_RANGE_AUTO + * GST_PHOTOGRAPHY_FOCUS_MODE_MACRO => V4L2_AUTO_FOCUS_RANGE_MACRO + * GST_PHOTOGRAPHY_FOCUS_MODE_PORTRAIT => V4L2_AUTO_FOCUS_RANGE_NORMAL + * GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY => V4L2_AUTO_FOCUS_RANGE_INFINITY + * - not supported: + * - GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL + * - GST_PHOTOGRAPHY_FOCUS_MODE_EXTENDED + * - GST_PHOTOGRAPHY_FOCUS_MODE_MANUAL + */ + +static inline const char *ctrl_name(int id) +{ + switch (id) { + case V4L2_CID_FOCUS_AUTO: + return "V4L2_CID_FOCUS_AUTO"; + case V4L2_CID_AUTO_FOCUS_RANGE: + return "V4L2_CID_FOCUS_RANGE"; + case V4L2_CID_AUTO_FOCUS_START: + return "V4L2_CID_AUTO_FOCUS_START"; + case V4L2_CID_AUTO_FOCUS_STOP: + return "V4L2_CID_AUTO_FOCUS_STOP"; + case V4L2_CID_AUTO_FOCUS_STATUS: + return "V4L2_CID_AUTO_FOCUS_STATUS"; + case V4L2_CID_3A_LOCK: + return "V4L2_CID_3A_LOCK"; + default: + return ""; + } +} + +static int v4l2_g_ctrl(GstImxV4l2VideoSrc *v4l2src, int id, int *value) +{ + struct v4l2_control control; + int ret; + + control.id = id; + ret = ioctl(GST_IMX_FD_OBJECT_GET_FD(v4l2src->fd_obj_v4l), VIDIOC_G_CTRL, &control); + + if (ret < 0) + GST_LOG_OBJECT(v4l2src, "VIDIOC_G_CTRL(%s) failed", ctrl_name(id)); + else { + GST_LOG_OBJECT(v4l2src, "VIDIOC_G_CTRL(%s) returned %d", ctrl_name(id), control.value); + *value = control.value; + } + + return ret; +} + +static inline int v4l2_s_ctrl(GstImxV4l2VideoSrc *v4l2src, int id, int value) +{ + struct v4l2_control control; + int ret; + + GST_LOG_OBJECT(v4l2src, "VIDIOC_S_CTRL(%s, %d)", ctrl_name(id), value); + + control.id = id; + control.value = value; + ret = ioctl(GST_IMX_FD_OBJECT_GET_FD(v4l2src->fd_obj_v4l), VIDIOC_S_CTRL, &control); + + if (ret < 0) + GST_LOG_OBJECT(v4l2src, "VIDIOC_S_CTRL(%s, %d) failed", ctrl_name(id), value); + else + GST_LOG_OBJECT(v4l2src, "VIDIOC_S_CTRL(%s, %d) succeed", ctrl_name(id), value); + + return ret; +} + +static void gst_imx_v4l2src_apply_focus_settings(GstImxV4l2VideoSrc *v4l2src, + gboolean activate) +{ + int locks, range; + + /* even when activating, first ensure that it is not running */ + + /* ensure that continuous autofocus is not running */ + v4l2_s_ctrl(v4l2src, V4L2_CID_FOCUS_AUTO, 0); + /* ensure that single shot AF is not running */ + v4l2_s_ctrl(v4l2src, V4L2_CID_AUTO_FOCUS_STOP, 0); + if (v4l2src->af_clock_id) { + gst_clock_id_unschedule(v4l2src->af_clock_id); + gst_clock_id_unref(v4l2src->af_clock_id); + v4l2src->af_clock_id = NULL; + } + /* ensure that focus is not locked */ + if (v4l2_g_ctrl(v4l2src, V4L2_CID_3A_LOCK, &locks) == 0 && (locks & V4L2_LOCK_FOCUS)) + v4l2_s_ctrl(v4l2src, V4L2_CID_3A_LOCK, locks & ~V4L2_LOCK_FOCUS); + + if (activate) { + + /* set focus range */ + + switch (v4l2src->focus_mode) { + case GST_PHOTOGRAPHY_FOCUS_MODE_AUTO: + range = V4L2_AUTO_FOCUS_RANGE_AUTO; + break; + case GST_PHOTOGRAPHY_FOCUS_MODE_MACRO: + range = V4L2_AUTO_FOCUS_RANGE_MACRO; + break; + case GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY: + range = V4L2_AUTO_FOCUS_RANGE_INFINITY; + break; + default: + range = V4L2_AUTO_FOCUS_RANGE_NORMAL; + break; + } + v4l2_s_ctrl(v4l2src, V4L2_CID_AUTO_FOCUS_RANGE, range); + + /* enable continuous autofocus if requested */ + + if (v4l2src->focus_mode == GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL) + v4l2_s_ctrl(v4l2src, V4L2_CID_FOCUS_AUTO, 1); + } +} + +static gboolean gst_imx_v4l2src_set_focus_mode(GstPhotography *photo, + GstPhotographyFocusMode focus_mode) +{ + GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(photo); + + GST_LOG_OBJECT(v4l2src, "setting focus mode to %d", focus_mode); + + switch (focus_mode) { + case GST_PHOTOGRAPHY_FOCUS_MODE_AUTO: + case GST_PHOTOGRAPHY_FOCUS_MODE_MACRO: + case GST_PHOTOGRAPHY_FOCUS_MODE_PORTRAIT: + case GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY: + break; + case GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL: + case GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_EXTENDED: + focus_mode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL; + break; + default: + GST_WARNING_OBJECT(v4l2src, "focus mode %d is not supported", focus_mode); + return FALSE; + } + + g_mutex_lock(&v4l2src->af_mutex); + + if (v4l2src->focus_mode != focus_mode) { + v4l2src->focus_mode = focus_mode; + + if (GST_STATE(v4l2src) == GST_STATE_PAUSED || GST_STATE(v4l2src) == GST_STATE_PLAYING) + gst_imx_v4l2src_apply_focus_settings(v4l2src, TRUE); + } + + g_mutex_unlock(&v4l2src->af_mutex); + + return TRUE; +} + +static gboolean gst_imx_v4l2src_get_focus_mode(GstPhotography *photo, + GstPhotographyFocusMode *focus_mode) +{ + GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(photo); + + g_mutex_lock(&v4l2src->af_mutex); + *focus_mode = v4l2src->focus_mode; + g_mutex_unlock(&v4l2src->af_mutex); + + return TRUE; +} + + +static gboolean gst_imx_v4l2src_af_status_cb(GstClock *clock, GstClockTime time, + GstClockID id, gpointer user_data); + +static void gst_imx_v4l2src_af_check_status(GstImxV4l2VideoSrc *v4l2src) +{ + int status; + gboolean send_message; + GstPhotographyFocusStatus message_status; + gboolean schedule_recheck; + + if (v4l2_g_ctrl(v4l2src, V4L2_CID_AUTO_FOCUS_STATUS, &status) < 0) + goto none; + + switch (status) { + case V4L2_AUTO_FOCUS_STATUS_IDLE: + default: + none: + send_message = TRUE; + message_status = GST_PHOTOGRAPHY_FOCUS_STATUS_NONE; + schedule_recheck = FALSE; + break; + case V4L2_AUTO_FOCUS_STATUS_BUSY: + send_message = FALSE; + schedule_recheck = TRUE; + break; + case V4L2_AUTO_FOCUS_STATUS_REACHED: + send_message = TRUE; + message_status = GST_PHOTOGRAPHY_FOCUS_STATUS_SUCCESS; + schedule_recheck = FALSE; + break; + case V4L2_AUTO_FOCUS_STATUS_FAILED: + send_message = TRUE; + message_status = GST_PHOTOGRAPHY_FOCUS_STATUS_FAIL; + schedule_recheck = FALSE; + break; + } + + if (send_message) { + GstStructure *s; + GstMessage *m; + + s = gst_structure_new(GST_PHOTOGRAPHY_AUTOFOCUS_DONE, + "status", G_TYPE_INT, message_status, + NULL); + m = gst_message_new_custom(GST_MESSAGE_ELEMENT, + GST_OBJECT(v4l2src), s); + + if (!gst_element_post_message(GST_ELEMENT(v4l2src), m)) + GST_ERROR_OBJECT(v4l2src, "failed to post message"); + } + + if (schedule_recheck) { + GstClock *c; + GstClockTime t; + + c = gst_system_clock_obtain(); + t = gst_clock_get_time(c) + 50 * GST_MSECOND; + v4l2src->af_clock_id = gst_clock_new_single_shot_id(c, t); + gst_object_unref(c); + + if (gst_clock_id_wait_async(v4l2src->af_clock_id, + gst_imx_v4l2src_af_status_cb, + v4l2src, NULL) != GST_CLOCK_OK) + GST_ERROR_OBJECT(v4l2src, "failed to schedule recheck"); + } +} + +static gboolean gst_imx_v4l2src_af_status_cb(GstClock *clock, GstClockTime time, + GstClockID id, gpointer user_data) +{ + GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(user_data); + + g_mutex_lock(&v4l2src->af_mutex); + + if (v4l2src->af_clock_id == id) { + gst_clock_id_unref(v4l2src->af_clock_id); + v4l2src->af_clock_id = NULL; + + gst_imx_v4l2src_af_check_status(v4l2src); + } + + g_mutex_unlock(&v4l2src->af_mutex); + return TRUE; +} + +void gst_imx_v4l2src_set_autofocus(GstPhotography *photo, gboolean on) +{ + GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(photo); + int locks; + + g_mutex_lock(&v4l2src->af_mutex); + + if (v4l2src->af_clock_id) { + gst_clock_id_unschedule(v4l2src->af_clock_id); + gst_clock_id_unref(v4l2src->af_clock_id); + v4l2src->af_clock_id = NULL; + } + + if (v4l2src->focus_mode == GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL) { + + if (v4l2_g_ctrl(v4l2src, V4L2_CID_3A_LOCK, &locks) == 0) { + if (on && !(locks & V4L2_LOCK_FOCUS)) + v4l2_s_ctrl(v4l2src, V4L2_CID_3A_LOCK, locks | V4L2_LOCK_FOCUS); + else if (!on && (locks & V4L2_LOCK_FOCUS)) + v4l2_s_ctrl(v4l2src, V4L2_CID_3A_LOCK, locks & ~V4L2_LOCK_FOCUS); + } + + } else { + + if (on) { + if (v4l2_s_ctrl(v4l2src, V4L2_CID_AUTO_FOCUS_START, 0) == 0) + gst_imx_v4l2src_af_check_status(v4l2src); + } else + v4l2_s_ctrl(v4l2src, V4L2_CID_AUTO_FOCUS_STOP, 0); + } + + g_mutex_unlock(&v4l2src->af_mutex); +} + +static gboolean gst_imx_v4lsrc_prepare_for_capture(GstPhotography *photo, + GstPhotographyCapturePrepared func, GstCaps *capture_caps, gpointer user_data) +{ + GstImxV4l2VideoSrc *v4l2src = GST_IMX_V4L2SRC(photo); + + GST_LOG_OBJECT(v4l2src, "capture_caps: %" GST_PTR_FORMAT, capture_caps); + + func(user_data, capture_caps); + return TRUE; +} + +static void gst_imx_v4l2src_photography_init(gpointer g_iface, gpointer iface_data) +{ + GstPhotographyInterface *iface = (GstPhotographyInterface *) g_iface; + + iface->set_focus_mode = gst_imx_v4l2src_set_focus_mode; + iface->get_focus_mode = gst_imx_v4l2src_get_focus_mode; + iface->set_autofocus = gst_imx_v4l2src_set_autofocus; + iface->prepare_for_capture = gst_imx_v4lsrc_prepare_for_capture; +} + static gboolean plugin_init(GstPlugin *plugin) { return gst_element_register(plugin, "imxv4l2videosrc", GST_RANK_PRIMARY, diff --git a/src/v4l2src/v4l2src.h b/src/v4l2src/v4l2src.h index 3c4ab20..e9fbee6 100644 --- a/src/v4l2src/v4l2src.h +++ b/src/v4l2src/v4l2src.h @@ -51,6 +51,10 @@ struct _GstImxV4l2VideoSrc guint32 count; GstClockTime time_per_frame; + GMutex af_mutex; + GstPhotographyFocusMode focus_mode; + GstClockID af_clock_id; + /* properties */ gint capture_mode; gint fps_n; diff --git a/src/v4l2src/wscript b/src/v4l2src/wscript index 9e32ced..81c3406 100644 --- a/src/v4l2src/wscript +++ b/src/v4l2src/wscript @@ -11,7 +11,7 @@ def build(bld): features = ['c', bld.env['CLIBTYPE']], includes = ['.', '../..'], uselib = bld.env['COMMON_USELIB'], - use = 'gstimxcommon', + use = 'gstimxcommon GSTPHOTOGRAPHY', target = 'gstimxv4l2videosrc', source = bld.path.ant_glob('*.c'), install_path = bld.env['PLUGIN_INSTALL_PATH'] diff --git a/wscript b/wscript index 8d1583f..6b9909c 100644 --- a/wscript +++ b/wscript @@ -157,6 +157,7 @@ def configure(conf): conf.check_cfg(package = 'gstreamer-base-1.0 >= 1.2.0', uselib_store = 'GSTREAMER_BASE', args = '--cflags --libs', mandatory = 1) conf.check_cfg(package = 'gstreamer-audio-1.0 >= 1.2.0', uselib_store = 'GSTREAMER_VIDEO', args = '--cflags --libs', mandatory = 0) conf.check_cfg(package = 'gstreamer-video-1.0 >= 1.2.0', uselib_store = 'GSTREAMER_VIDEO', args = '--cflags --libs', mandatory = 1) + conf.check_cc(lib = 'gstphotography-1.0', uselib_store = 'GSTPHOTOGRAPHY', mandatory = 1) # check the kernel header path -- 2.1.4