X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=subprojects%2Fgst-plugins-good%2Fsys%2Fv4l2%2Fgstv4l2src.c;h=3d3a4b6ed95195c67d35d62327315e5639edd3d2;hb=9fecc618c16a4ed99834d0202046b3e741613878;hp=d00f7a80dc671cc1b0438018b44d3945cc95d2d2;hpb=6bb75890d914beb5bfc27f4d26d30d6046a19b6f;p=platform%2Fupstream%2Fgstreamer.git diff --git a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2src.c b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2src.c index d00f7a8..3d3a4b6 100644 --- a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2src.c +++ b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2src.c @@ -61,7 +61,7 @@ #include "gstv4l2tuner.h" #include "gstv4l2vidorient.h" -#include "gst/gst-i18n-plugin.h" +#include GST_DEBUG_CATEGORY (v4l2src_debug); #define GST_CAT_DEFAULT v4l2src_debug @@ -72,6 +72,17 @@ enum { PROP_0, V4L2_STD_OBJECT_PROPS, + PROP_CROP_TOP, + PROP_CROP_LEFT, + PROP_CROP_BOTTOM, + PROP_CROP_RIGHT, + PROP_CROP_BOUNDS, +#ifdef TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID + PROP_CAMERA_ID, +#endif /* TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID */ +#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE + PROP_AUTO_SCAN_DEVICE, +#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */ PROP_LAST }; @@ -158,6 +169,112 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass) DEFAULT_PROP_DEVICE); /** + * GstV4l2Src:crop-top: + * + * Number of pixels to crop from the top edge of captured video + * stream + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_CROP_TOP, + g_param_spec_uint ("crop-top", "Crop top", + "Pixels to crop at top of video capture input", + 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstV4l2Src:crop-left: + * + * Number of pixels to crop from the left edge of captured video + * stream + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_CROP_LEFT, + g_param_spec_uint ("crop-left", "Crop left", + "Pixels to crop at left of video capture input", + 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstV4l2Src:crop-bottom: + * + * Number of pixels to crop from the bottom edge of captured video + * stream + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_CROP_BOTTOM, + g_param_spec_uint ("crop-bottom", "Crop bottom", + "Pixels to crop at bottom of video capture input", + 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstV4l2Src:crop-right: + * + * Number of pixels to crop from the right edge of captured video + * stream + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_CROP_RIGHT, + g_param_spec_uint ("crop-right", "Crop right", + "Pixels to crop at right of video capture input", + 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstV4l2Src:crop-bounds: + * + * Crop bounding region. All crop regions must lie within this region. + * The bounds are represented as a four element array, that descibes the + * [x, y, width, height] of the area. + * + * The size and position of the crop + * bounds will only be known, once the v4l2 device is opened and the + * input source selected. Applications can connect to the + * "notify::crop-bounds" signal to be notified when the bounding region is + * updated, and set an appropriate crop region. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_CROP_BOUNDS, + gst_param_spec_array ("crop-bounds", "Crop bounds", + "The bounding region for crop rectangles ('').", + g_param_spec_int ("rect-value", "Rectangle Value", + "One of x, y, width or height value.", G_MININT, G_MAXINT, -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS), + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + +#ifdef TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID + /** + * GstV4l2Src:camera-id: + * + * The value which is set by application will be used as a number of device node. + * ex) 1 -> /dev/video1 + */ + g_object_class_install_property (gobject_class, PROP_CAMERA_ID, + g_param_spec_uint ("camera-id", "Camera ID", + "Camera ID for device node", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif /* TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID */ +#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE + /** + * GstV4l2Src:auto-scan-device: + */ + g_object_class_install_property (gobject_class, PROP_AUTO_SCAN_DEVICE, + g_param_spec_boolean ("auto-scan-device", "Scan device automatically", + "Scan all device nodes automatically until device open success.", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */ +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + /** + * GstV4l2Src:tbm-output + */ + g_object_class_install_property (gobject_class, PROP_TBM_OUTPUT, + g_param_spec_boolean ("tbm-output", "Enable TBM for output buffer", + "It works for only DMABUF mode.", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ + + /** * GstV4l2Src::prepare-format: * @v4l2src: the v4l2src instance * @fd: the file descriptor of the current device @@ -212,6 +329,9 @@ gst_v4l2src_init (GstV4l2Src * v4l2src) /* Avoid the slow probes */ v4l2src->v4l2object->skip_try_fmt_probes = TRUE; +#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE + v4l2src->v4l2object->auto_scan_device = TRUE; +#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */ gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (v4l2src), TRUE); @@ -236,6 +356,40 @@ gst_v4l2src_set_property (GObject * object, if (!gst_v4l2_object_set_property_helper (v4l2src->v4l2object, prop_id, value, pspec)) { switch (prop_id) { + case PROP_CROP_TOP: + v4l2src->crop_top = g_value_get_uint (value); + break; + case PROP_CROP_LEFT: + v4l2src->crop_left = g_value_get_uint (value); + break; + case PROP_CROP_BOTTOM: + v4l2src->crop_bottom = g_value_get_uint (value); + break; + case PROP_CROP_RIGHT: + v4l2src->crop_right = g_value_get_uint (value); + break; +#ifdef TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID + case PROP_CAMERA_ID: + g_free (v4l2src->v4l2object->videodev); + + v4l2src->camera_id = g_value_get_uint (value); + v4l2src->v4l2object->videodev = g_strdup_printf ("/dev/video%u", v4l2src->camera_id); + + GST_INFO_OBJECT (v4l2src, "videodev [%s]", v4l2src->v4l2object->videodev); + break; +#endif /* TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID */ +#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE + case PROP_AUTO_SCAN_DEVICE: + v4l2src->v4l2object->auto_scan_device = g_value_get_boolean (value); + GST_INFO_OBJECT (v4l2src, "auto scan device [%d]", v4l2src->v4l2object->auto_scan_device); + break; +#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */ +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + case PROP_TBM_OUTPUT: + v4l2src->v4l2object->tbm_output = g_value_get_boolean (value); + GST_INFO_OBJECT (v4l2src, "tbm output [%d]", v4l2src->v4l2object->tbm_output); + break; +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -244,6 +398,29 @@ gst_v4l2src_set_property (GObject * object, } static void +gst_v4l2src_set_rect_value (GValue * value, struct v4l2_rect *rect) +{ + GValue val = { 0 }; + + g_value_init (&val, G_TYPE_INT); + g_value_reset (value); + + g_value_set_int (&val, rect->left); + gst_value_array_append_value (value, &val); + + g_value_set_int (&val, rect->top); + gst_value_array_append_value (value, &val); + + g_value_set_int (&val, rect->width); + gst_value_array_append_value (value, &val); + + g_value_set_int (&val, rect->height); + gst_value_array_append_value (value, &val); + + g_value_unset (&val); +} + +static void gst_v4l2src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { @@ -252,6 +429,38 @@ gst_v4l2src_get_property (GObject * object, if (!gst_v4l2_object_get_property_helper (v4l2src->v4l2object, prop_id, value, pspec)) { switch (prop_id) { + case PROP_CROP_TOP: + g_value_set_uint (value, v4l2src->crop_top); + break; + case PROP_CROP_LEFT: + g_value_set_uint (value, v4l2src->crop_left); + break; + case PROP_CROP_BOTTOM: + g_value_set_uint (value, v4l2src->crop_bottom); + break; + case PROP_CROP_RIGHT: + g_value_set_uint (value, v4l2src->crop_right); + break; + case PROP_CROP_BOUNDS: + gst_v4l2src_set_rect_value (value, &v4l2src->crop_bounds); + break; +#ifdef TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID + case PROP_CAMERA_ID: + g_value_set_uint (value, v4l2src->camera_id); + break; +#endif /* TIZEN_FEATURE_V4L2SRC_SUPPORT_CAMERA_ID */ +#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE + case PROP_AUTO_SCAN_DEVICE: + GST_INFO_OBJECT (v4l2src, "auto scan device [%d]", v4l2src->v4l2object->auto_scan_device); + g_value_set_boolean (value, v4l2src->v4l2object->auto_scan_device); + break; +#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */ +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + case PROP_TBM_OUTPUT: + GST_INFO_OBJECT (v4l2src, "tbm output [%d]", v4l2src->v4l2object->tbm_output); + g_value_set_boolean (value, v4l2src->v4l2object->tbm_output); + break; +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -374,6 +583,23 @@ done: } static gboolean +gst_v4l2src_do_source_crop (GstV4l2Src * v4l2src) +{ + struct v4l2_rect def_crop; + + if (v4l2src->apply_crop_settings) + return gst_v4l2_object_set_crop (v4l2src->v4l2object, &v4l2src->crop_rect); + + /* If no crop setting is given, reset to the default. Resetting the default + * crop may fail if the device does not support cropping. This should not + * be considered an error. */ + if (gst_v4l2_object_get_crop_default (v4l2src->v4l2object, &def_crop)) + gst_v4l2_object_set_crop (v4l2src->v4l2object, &def_crop); + + return TRUE; +} + +static gboolean gst_v4l2src_set_format (GstV4l2Src * v4l2src, GstCaps * caps, GstV4l2Error * error) { @@ -388,6 +614,9 @@ gst_v4l2src_set_format (GstV4l2Src * v4l2src, GstCaps * caps, g_signal_emit (v4l2src, gst_v4l2_signals[SIGNAL_PRE_SET_FORMAT], 0, v4l2src->v4l2object->video_fd, caps); + if (!gst_v4l2src_do_source_crop (v4l2src)) + return FALSE; + return gst_v4l2_object_set_format (obj, caps, error); } @@ -497,6 +726,7 @@ gst_v4l2src_query_preferred_dv_timings (GstV4l2Src * v4l2src, GstV4l2Object *obj = v4l2src->v4l2object; struct v4l2_dv_timings dv_timings = { 0, }; const struct v4l2_bt_timings *bt = &dv_timings.bt; + gboolean not_streaming; gint tot_width, tot_height; gint gcd; @@ -523,7 +753,14 @@ gst_v4l2src_query_preferred_dv_timings (GstV4l2Src * v4l2src, /* If are are not streaming (e.g. we received source-change event), lock the * new timing immediatly so that TRY_FMT can properly work */ - if (!obj->pool || !GST_V4L2_BUFFER_POOL_IS_STREAMING (obj->pool)) { + { + GstBufferPool *obj_pool = gst_v4l2_object_get_buffer_pool (obj); + not_streaming = !obj_pool || !GST_V4L2_BUFFER_POOL_IS_STREAMING (obj_pool); + if (obj_pool) + gst_object_unref (obj_pool); + } + + if (not_streaming) { gst_v4l2_set_dv_timings (obj, &dv_timings); /* Setting a new DV timings invalidates the probed caps. */ gst_caps_replace (&obj->probed_caps, NULL); @@ -577,6 +814,53 @@ gst_v4l2src_query_preferred_size (GstV4l2Src * v4l2src, } static gboolean +gst_v4l2src_setup_source_crop (GstV4l2Src * v4l2src, + struct PreferredCapsInfo *pref) +{ + gint cropped_width, cropped_height; + struct v4l2_rect *crop_bounds = &v4l2src->crop_bounds; + + v4l2src->apply_crop_settings = FALSE; + + if (!gst_v4l2_object_get_crop_bounds (v4l2src->v4l2object, crop_bounds)) + return FALSE; + + g_object_notify (G_OBJECT (v4l2src), "crop-bounds"); + + cropped_width = crop_bounds->width - v4l2src->crop_left - v4l2src->crop_right; + cropped_height = + crop_bounds->height - v4l2src->crop_top - v4l2src->crop_bottom; + + if (v4l2src->crop_left < crop_bounds->left + || v4l2src->crop_top < crop_bounds->top + || cropped_width <= 0 || cropped_height <= 0) { + GST_WARNING_OBJECT (v4l2src, "Ignoring out of bounds crop region"); + return FALSE; + } + + if (cropped_width == crop_bounds->width + && cropped_height == crop_bounds->height) { + GST_DEBUG_OBJECT (v4l2src, + "No cropping requested, keep current preferred size"); + return FALSE; + } + + v4l2src->crop_rect.left = v4l2src->crop_left; + v4l2src->crop_rect.top = v4l2src->crop_top; + v4l2src->crop_rect.width = cropped_width; + v4l2src->crop_rect.height = cropped_height; + v4l2src->apply_crop_settings = TRUE; + + pref->width = cropped_width; + pref->height = cropped_height; + + GST_INFO_OBJECT (v4l2src, "Updated preferred capture size to %i x %i", + pref->width, pref->height); + + return TRUE; +} + +static gboolean gst_v4l2src_negotiate (GstBaseSrc * basesrc) { GstV4l2Src *v4l2src = GST_V4L2SRC (basesrc); @@ -596,6 +880,8 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc) * the caps enumeration. */ have_pref = gst_v4l2src_query_preferred_size (v4l2src, &pref); + have_pref |= gst_v4l2src_setup_source_crop (v4l2src, &pref); + /* first see what is possible on our source pad */ thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps); @@ -693,17 +979,23 @@ static gboolean gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) { GstV4l2Src *src = GST_V4L2SRC (bsrc); + GstBufferPool *bpool = gst_v4l2_object_get_buffer_pool (src->v4l2object); gboolean ret = TRUE; if (src->pending_set_fmt) { GstCaps *caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc)); GstV4l2Error error = GST_V4L2_ERROR_INIT; + /* Setting the format replaces the current pool */ + gst_clear_object (&bpool); + caps = gst_caps_make_writable (caps); ret = gst_v4l2src_set_format (src, caps, &error); if (ret) { - GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (src->v4l2object->pool); + GstV4l2BufferPool *pool; + bpool = gst_v4l2_object_get_buffer_pool (src->v4l2object); + pool = GST_V4L2_BUFFER_POOL (bpool); gst_v4l2_buffer_pool_enable_resolution_change (pool); } else { gst_v4l2_error (src, &error); @@ -711,7 +1003,7 @@ gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) gst_caps_unref (caps); src->pending_set_fmt = FALSE; - } else if (gst_buffer_pool_is_active (src->v4l2object->pool)) { + } else if (gst_buffer_pool_is_active (bpool)) { /* Trick basesrc into not deactivating the active pool. Renegotiating here * would otherwise turn off and on the camera. */ GstAllocator *allocator; @@ -737,6 +1029,8 @@ gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) gst_object_unref (pool); if (allocator) gst_object_unref (allocator); + if (bpool) + gst_object_unref (bpool); return GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query); } @@ -748,10 +1042,12 @@ gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) } if (ret) { - if (!gst_buffer_pool_set_active (src->v4l2object->pool, TRUE)) + if (!gst_buffer_pool_set_active (bpool, TRUE)) goto activate_failed; } + if (bpool) + gst_object_unref (bpool); return ret; activate_failed: @@ -759,6 +1055,8 @@ activate_failed: GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (_("Failed to allocate required memory.")), ("Buffer pool activation failed")); + if (bpool) + gst_object_unref (bpool); return FALSE; } } @@ -803,8 +1101,13 @@ gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query) min_latency /= 2; /* max latency is total duration of the frame buffer */ - if (obj->pool != NULL) - num_buffers = GST_V4L2_BUFFER_POOL_CAST (obj->pool)->max_latency; + { + GstBufferPool *obj_pool = gst_v4l2_object_get_buffer_pool (obj); + if (obj_pool != NULL) { + num_buffers = GST_V4L2_BUFFER_POOL_CAST (obj_pool)->max_latency; + gst_object_unref (obj_pool); + } + } if (num_buffers == 0) max_latency = -1; @@ -924,6 +1227,23 @@ gst_v4l2src_change_state (GstElement * element, GstStateChange transition) return ret; } +static gboolean +gst_v4l2src_handle_resolution_change (GstV4l2Src * v4l2src) +{ + GST_INFO_OBJECT (v4l2src, "Resolution change detected."); + + /* It is required to always cycle through streamoff, we also need to + * streamoff in order to allow locking a new DV_TIMING which will + * influence the output of TRY_FMT */ + gst_v4l2src_stop (GST_BASE_SRC (v4l2src)); + + /* Force renegotiation */ + v4l2src->renegotiation_adjust = v4l2src->offset + 1; + v4l2src->pending_set_fmt = TRUE; + + return gst_base_src_negotiate (GST_BASE_SRC (v4l2src)); +} + static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) { @@ -937,25 +1257,12 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) gboolean half_frame; do { - GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL_CAST (obj->pool); - ret = GST_BASE_SRC_CLASS (parent_class)->alloc (GST_BASE_SRC (src), 0, obj->info.size, buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) { if (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE) { - GST_INFO_OBJECT (v4l2src, "Resolution change detected."); - - /* It is required to always cycle through streamoff, we also need to - * streamoff in order to allow locking a new DV_TIMING which will - * influence the output of TRY_FMT */ - gst_v4l2src_stop (GST_BASE_SRC (src)); - - /* Force renegotiation */ - v4l2src->renegotiation_adjust = v4l2src->offset + 1; - v4l2src->pending_set_fmt = TRUE; - - if (!gst_base_src_negotiate (GST_BASE_SRC (src))) { + if (!gst_v4l2src_handle_resolution_change (v4l2src)) { ret = GST_FLOW_NOT_NEGOTIATED; goto error; } @@ -965,7 +1272,20 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) goto alloc_failed; } - ret = gst_v4l2_buffer_pool_process (pool, buf, NULL); + { + GstV4l2BufferPool *obj_pool = + GST_V4L2_BUFFER_POOL_CAST (gst_v4l2_object_get_buffer_pool (obj)); + ret = gst_v4l2_buffer_pool_process (obj_pool, buf, NULL); + if (obj_pool) + gst_object_unref (obj_pool); + + if (G_UNLIKELY (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE)) { + if (!gst_v4l2src_handle_resolution_change (v4l2src)) { + ret = GST_FLOW_NOT_NEGOTIATED; + goto error; + } + } + } } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER || ret == GST_V4L2_FLOW_RESOLUTION_CHANGE);