Merge branch 'upstream/1.22.7' into tizen_gst_1.22.7
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-good / sys / v4l2 / gstv4l2src.c
index d00f7a8..3d3a4b6 100644 (file)
@@ -61,7 +61,7 @@
 #include "gstv4l2tuner.h"
 #include "gstv4l2vidorient.h"
 
-#include "gst/gst-i18n-plugin.h"
+#include <glib/gi18n-lib.h>
 
 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 ('<x, y, width, height>').",
+          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);