v4l2src: Add new property for auto scan device feature
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / v4l2_calls.c
index 74b2e0a..137f399 100644 (file)
@@ -1,7 +1,7 @@
 /* GStreamer
  *
  * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
- *               2006 Edgard Lima <edgard.lima@indt.org.br>
+ *               2006 Edgard Lima <edgard.lima@gmail.com>
  *
  * v4l2_calls.c - generic V4L2 calls handling
  *
@@ -17,8 +17,8 @@
  *
  * 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.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+#include <glob.h>
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
 #ifdef __sun
 /* Needed on older Solaris Nevada builds (72 at least) */
 #include <stropts.h>
 #include <sys/ioccom.h>
 #endif
-#include "v4l2_calls.h"
+#include "gstv4l2object.h"
 #include "gstv4l2tuner.h"
-#if 0
-#include "gstv4l2xoverlay.h"
-#endif
 #include "gstv4l2colorbalance.h"
 
 #include "gstv4l2src.h"
 #include "gstv4l2sink.h"
+#include "gstv4l2videodec.h"
 
 #include "gst/gst-i18n-plugin.h"
 
-/* Those are ioctl calls */
-#ifndef V4L2_CID_HCENTER
-#define V4L2_CID_HCENTER V4L2_CID_HCENTER_DEPRECATED
-#endif
-#ifndef V4L2_CID_VCENTER
-#define V4L2_CID_VCENTER V4L2_CID_VCENTER_DEPRECATED
-#endif
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+enum {
+  V4L2_OPEN_ERROR = 0,
+  V4L2_OPEN_ERROR_STAT_FAILED,
+  V4L2_OPEN_ERROR_NO_DEVICE,
+  V4L2_OPEN_ERROR_NOT_OPEN,
+  V4L2_OPEN_ERROR_NOT_CAPTURE,
+  V4L2_OPEN_ERROR_NOT_OUTPUT
+};
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
 
 GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
 #define GST_CAT_DEFAULT v4l2_debug
@@ -66,7 +70,7 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
  *   get the device's capturing capabilities
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-gboolean
+static gboolean
 gst_v4l2_get_capabilities (GstV4l2Object * v4l2object)
 {
   GstElement *e;
@@ -78,14 +82,20 @@ gst_v4l2_get_capabilities (GstV4l2Object * v4l2object)
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &v4l2object->vcap) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP,
+          &v4l2object->vcap) < 0)
     goto cap_failed;
 
+  if (v4l2object->vcap.capabilities & V4L2_CAP_DEVICE_CAPS)
+    v4l2object->device_caps = v4l2object->vcap.device_caps;
+  else
+    v4l2object->device_caps = v4l2object->vcap.capabilities;
+
   GST_LOG_OBJECT (e, "driver:      '%s'", v4l2object->vcap.driver);
   GST_LOG_OBJECT (e, "card:        '%s'", v4l2object->vcap.card);
   GST_LOG_OBJECT (e, "bus_info:    '%s'", v4l2object->vcap.bus_info);
   GST_LOG_OBJECT (e, "version:     %08x", v4l2object->vcap.version);
-  GST_LOG_OBJECT (e, "capabilites: %08x", v4l2object->vcap.capabilities);
+  GST_LOG_OBJECT (e, "capabilites: %08x", v4l2object->device_caps);
 
   return TRUE;
 
@@ -100,6 +110,29 @@ cap_failed:
   }
 }
 
+/******************************************************
+ * The video4linux command line tool v4l2-ctrl
+ * normalises the names of the controls received from
+ * the kernel like:
+ *
+ *     "Exposure (absolute)" -> "exposure_absolute"
+ *
+ * We follow their lead here.  @name is modified
+ * in-place.
+ ******************************************************/
+static void
+gst_v4l2_normalise_control_name (gchar * name)
+{
+  int i, j;
+  for (i = 0, j = 0; name[j]; ++j) {
+    if (g_ascii_isalnum (name[j])) {
+      if (i > 0 && !g_ascii_isalnum (name[j - 1]))
+        name[i++] = '_';
+      name[i++] = g_ascii_tolower (name[j]);
+    }
+  }
+  name[i++] = '\0';
+}
 
 /******************************************************
  * gst_v4l2_empty_lists() and gst_v4l2_fill_lists():
@@ -109,7 +142,8 @@ cap_failed:
 static gboolean
 gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
 {
-  gint n;
+  gint n, next;
+  struct v4l2_queryctrl control = { 0, };
 
   GstElement *e;
 
@@ -128,8 +162,8 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
     memset (&input, 0, sizeof (input));
 
     input.index = n;
-    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
-      if (errno == EINVAL)
+    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
+      if (errno == EINVAL || errno == ENOTTY)
         break;                  /* end of enumeration */
       else {
         GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
@@ -145,7 +179,8 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
     GST_LOG_OBJECT (e, "   name:      '%s'", input.name);
     GST_LOG_OBJECT (e, "   type:      %08x", input.type);
     GST_LOG_OBJECT (e, "   audioset:  %08x", input.audioset);
-    GST_LOG_OBJECT (e, "   std:       %016x", (guint) input.std);
+    GST_LOG_OBJECT (e, "   std:       %016" G_GINT64_MODIFIER "x",
+        (guint64) input.std);
     GST_LOG_OBJECT (e, "   status:    %08x", input.status);
 
     v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL);
@@ -161,7 +196,7 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
       channel->flags |= GST_TUNER_CHANNEL_FREQUENCY;
 
       vtun.index = input.tuner;
-      if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &vtun) < 0) {
+      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &vtun) < 0) {
         GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
             (_("Failed to get setting of tuner %d on device '%s'."),
                 input.tuner, v4l2object->videodev), GST_ERROR_SYSTEM);
@@ -202,9 +237,13 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
     standard.frameperiod.denominator = 0;
     standard.index = n;
 
-    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
+    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
       if (errno == EINVAL || errno == ENOTTY)
         break;                  /* end of enumeration */
+#ifdef ENODATA
+      else if (errno == ENODATA)
+        break;                  /* end of enumeration, as of Linux 3.7-rc1 */
+#endif
       else {
         GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
             (_("Failed to query norm on device '%s'."),
@@ -226,7 +265,7 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
         standard.frameperiod.denominator, standard.frameperiod.numerator);
     v4l2norm->index = standard.id;
 
-    GST_DEBUG_OBJECT (v4l2object->element, "index=%08x, label=%s",
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "index=%08x, label=%s",
         (unsigned int) v4l2norm->index, norm->label);
 
     v4l2object->norms = g_list_prepend (v4l2object->norms, (gpointer) norm);
@@ -236,11 +275,18 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
   GST_DEBUG_OBJECT (e, "  controls+menus");
 
   /* and lastly, controls+menus (if appropriate) */
-  for (n = V4L2_CID_BASE;; n++) {
-    struct v4l2_queryctrl control = { 0, };
+  next = V4L2_CTRL_FLAG_NEXT_CTRL;
+  n = 0;
+  control.id = next;
+
+  while (TRUE) {
     GstV4l2ColorBalanceChannel *v4l2channel;
     GstColorBalanceChannel *channel;
 
+    if (!next)
+      n++;
+
+  retry:
     /* when we reached the last official CID, continue with private CIDs */
     if (n == V4L2_CID_LASTP1) {
       GST_DEBUG_OBJECT (e, "checking private CIDs");
@@ -248,31 +294,71 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
     }
     GST_DEBUG_OBJECT (e, "checking control %08x", n);
 
-    control.id = n;
-    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
-      if (errno == EINVAL) {
+    control.id = n | next;
+    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_QUERYCTRL,
+            &control) < 0) {
+      if (next) {
+        if (n > 0) {
+          GST_DEBUG_OBJECT (e, "controls finished");
+          break;
+        } else {
+          GST_DEBUG_OBJECT (e, "V4L2_CTRL_FLAG_NEXT_CTRL not supported.");
+          next = 0;
+          n = V4L2_CID_BASE;
+          goto retry;
+        }
+      }
+      if (errno == EINVAL || errno == ENOTTY || errno == EIO || errno == ENOENT) {
         if (n < V4L2_CID_PRIVATE_BASE) {
           GST_DEBUG_OBJECT (e, "skipping control %08x", n);
           /* continue so that we also check private controls */
+          n = V4L2_CID_PRIVATE_BASE - 1;
           continue;
         } else {
           GST_DEBUG_OBJECT (e, "controls finished");
           break;
         }
       } else {
-        GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
-            (_("Failed getting controls attributes on device '%s'."),
-                v4l2object->videodev),
-            ("Failed querying control %d on device '%s'. (%d - %s)",
-                n, v4l2object->videodev, errno, strerror (errno)));
-        return FALSE;
+        GST_WARNING_OBJECT (e, "Failed querying control %d on device '%s'. "
+            "(%d - %s)", n, v4l2object->videodev, errno, strerror (errno));
+        continue;
       }
     }
+    /* bogus driver might mess with id in unexpected ways (e.g. set to 0), so
+     * make sure to simply try all if V4L2_CTRL_FLAG_NEXT_CTRL not supported */
+    if (next)
+      n = control.id;
     if (control.flags & V4L2_CTRL_FLAG_DISABLED) {
       GST_DEBUG_OBJECT (e, "skipping disabled control");
       continue;
     }
 
+    if (control.type == V4L2_CTRL_TYPE_CTRL_CLASS) {
+      GST_DEBUG_OBJECT (e, "starting control class '%s'", control.name);
+      continue;
+    }
+
+    switch (control.type) {
+      case V4L2_CTRL_TYPE_INTEGER:
+      case V4L2_CTRL_TYPE_BOOLEAN:
+      case V4L2_CTRL_TYPE_MENU:
+      case V4L2_CTRL_TYPE_INTEGER_MENU:
+      case V4L2_CTRL_TYPE_BITMASK:
+      case V4L2_CTRL_TYPE_BUTTON:{
+        control.name[31] = '\0';
+        gst_v4l2_normalise_control_name ((gchar *) control.name);
+        g_datalist_id_set_data (&v4l2object->controls,
+            g_quark_from_string ((const gchar *) control.name),
+            GINT_TO_POINTER (n));
+        break;
+      }
+      default:
+        GST_DEBUG_OBJECT (e,
+            "Control type for '%s' not supported for extra controls.",
+            control.name);
+        break;
+    }
+
     switch (n) {
       case V4L2_CID_BRIGHTNESS:
       case V4L2_CID_CONTRAST:
@@ -287,21 +373,13 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
       case V4L2_CID_EXPOSURE:
       case V4L2_CID_AUTOGAIN:
       case V4L2_CID_GAIN:
-#ifdef V4L2_CID_SHARPNESS
       case V4L2_CID_SHARPNESS:
-#endif
         /* we only handle these for now (why?) */
         break;
       case V4L2_CID_HFLIP:
       case V4L2_CID_VFLIP:
-      case V4L2_CID_HCENTER:
-      case V4L2_CID_VCENTER:
-#ifdef V4L2_CID_PAN_RESET
       case V4L2_CID_PAN_RESET:
-#endif
-#ifdef V4L2_CID_TILT_RESET
       case V4L2_CID_TILT_RESET:
-#endif
         /* not handled here, handled by VideoOrientation interface */
         control.id++;
         break;
@@ -311,8 +389,12 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
       case V4L2_CID_AUDIO_TREBLE:
       case V4L2_CID_AUDIO_MUTE:
       case V4L2_CID_AUDIO_LOUDNESS:
-        /* FIXME: We should implement GstMixer interface */
-        /* fall through */
+        /* FIXME: We should implement GstMixer interface instead */
+        /* but let's not be pedantic and make element more useful for now */
+        break;
+      case V4L2_CID_ALPHA_COMPONENT:
+        v4l2object->has_alpha_component = TRUE;
+        break;
       default:
         GST_DEBUG_OBJECT (e,
             "ControlID %s (%x) unhandled, FIXME", control.name, n);
@@ -339,7 +421,8 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
       menu.id = n;
       for (i = 0;; i++) {
         menu.index = i;
-        if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYMENU, &menu) < 0) {
+        if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_QUERYMENU,
+                &menu) < 0) {
           if (errno == EINVAL)
             break;              /* end of enumeration */
           else {
@@ -392,7 +475,7 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
 static void
 gst_v4l2_empty_lists (GstV4l2Object * v4l2object)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "deleting enumerations");
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "deleting enumerations");
 
   g_list_foreach (v4l2object->channels, (GFunc) g_object_unref, NULL);
   g_list_free (v4l2object->channels);
@@ -405,6 +488,39 @@ gst_v4l2_empty_lists (GstV4l2Object * v4l2object)
   g_list_foreach (v4l2object->colors, (GFunc) g_object_unref, NULL);
   g_list_free (v4l2object->colors);
   v4l2object->colors = NULL;
+
+  g_datalist_clear (&v4l2object->controls);
+}
+
+static void
+gst_v4l2_adjust_buf_type (GstV4l2Object * v4l2object)
+{
+  /* when calling gst_v4l2_object_new the user decides the initial type
+   * so adjust it if multi-planar is supported
+   * the driver should make it exclusive. So the driver should
+   * not support both MPLANE and non-PLANE.
+   * Because even when using MPLANE it still possibles to use it
+   * in a contiguous manner. In this case the first v4l2 plane
+   * contains all the gst planes.
+   */
+  switch (v4l2object->type) {
+    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+      if (v4l2object->device_caps &
+          (V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_M2M_MPLANE)) {
+        GST_DEBUG ("adjust type to multi-planar output");
+        v4l2object->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+      }
+      break;
+    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+      if (v4l2object->device_caps &
+          (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_M2M_MPLANE)) {
+        GST_DEBUG ("adjust type to multi-planar capture");
+        v4l2object->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+      }
+      break;
+    default:
+      break;
+  }
 }
 
 /******************************************************
@@ -416,10 +532,20 @@ gboolean
 gst_v4l2_open (GstV4l2Object * v4l2object)
 {
   struct stat st;
-  int libv4l2_fd;
-  GstPollFD pollfd = GST_POLL_FD_INIT;
+  int libv4l2_fd = -1;
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+  int error_type = V4L2_OPEN_ERROR_STAT_FAILED;
+  int device_index = 0;
+  glob_t glob_buf;
+
+  memset (&glob_buf, 0x0, sizeof(glob_t));
 
-  GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s",
+  if (!v4l2object) {
+    GST_ERROR ("v4l2object is NULL");
+    return FALSE;
+  }
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "Trying to open device %s",
       v4l2object->videodev);
 
   GST_V4L2_CHECK_NOT_OPEN (v4l2object);
@@ -429,22 +555,52 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
   if (!v4l2object->videodev)
     v4l2object->videodev = g_strdup ("/dev/video");
 
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+  if (!v4l2object->videodev) {
+    GST_ERROR_OBJECT (v4l2object->element, "videodev is NULL");
+    return FALSE;
+  }
+
+CHECK_AGAIN:
+  /* check if it is a device */
+  if (stat (v4l2object->videodev, &st) == -1) {
+    error_type = V4L2_OPEN_ERROR_STAT_FAILED;
+    goto pre_error_check;
+  }
+
+  if (!S_ISCHR (st.st_mode)) {
+    error_type = V4L2_OPEN_ERROR_NO_DEVICE;
+    goto pre_error_check;
+  }
+#else /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
   /* check if it is a device */
   if (stat (v4l2object->videodev, &st) == -1)
     goto stat_failed;
 
   if (!S_ISCHR (st.st_mode))
     goto no_device;
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
 
   /* open the device */
   v4l2object->video_fd =
       open (v4l2object->videodev, O_RDWR /* | O_NONBLOCK */ );
 
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+  if (!GST_V4L2_IS_OPEN (v4l2object)) {
+    error_type = V4L2_OPEN_ERROR_NOT_OPEN;
+    goto pre_error_check;
+  }
+#else /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
   if (!GST_V4L2_IS_OPEN (v4l2object))
     goto not_open;
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
+
+#ifdef HAVE_LIBV4L2
+  if (v4l2object->fd_open)
+    libv4l2_fd = v4l2object->fd_open (v4l2object->video_fd,
+        V4L2_ENABLE_ENUM_FMT_EMULATION);
+#endif
 
-  libv4l2_fd = v4l2_fd_open (v4l2object->video_fd,
-      V4L2_ENABLE_ENUM_FMT_EMULATION);
   /* Note the v4l2_xxx functions are designed so that if they get passed an
      unknown fd, the will behave exactly as their regular xxx counterparts, so
      if v4l2_fd_open fails, we continue as normal (missing the libv4l2 custom
@@ -454,35 +610,141 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
   if (libv4l2_fd != -1)
     v4l2object->video_fd = libv4l2_fd;
 
-  v4l2object->can_poll_device = TRUE;
-
   /* get capabilities, error will be posted */
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+  if (!gst_v4l2_get_capabilities (v4l2object)) {
+    error_type = V4L2_OPEN_ERROR;
+    goto pre_error_check;
+  }
+
+  GST_INFO_OBJECT (v4l2object->element, "device_caps 0x%x", v4l2object->device_caps);
+
+  if (GST_IS_V4L2SRC (v4l2object->element) &&
+      (!(v4l2object->device_caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) ||
+       (v4l2object->device_caps & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) {
+    error_type = V4L2_OPEN_ERROR_NOT_CAPTURE;
+    goto pre_error_check;
+  }
+
+  if (GST_IS_V4L2SINK (v4l2object->element) &&
+      !(v4l2object->device_caps & (V4L2_CAP_VIDEO_OUTPUT |
+              V4L2_CAP_VIDEO_OUTPUT_MPLANE))) {
+    error_type = V4L2_OPEN_ERROR_NOT_OUTPUT;
+    goto pre_error_check;
+  }
+#else /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
   if (!gst_v4l2_get_capabilities (v4l2object))
     goto error;
 
   /* do we need to be a capture device? */
   if (GST_IS_V4L2SRC (v4l2object->element) &&
-      !(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+      !(v4l2object->device_caps & (V4L2_CAP_VIDEO_CAPTURE |
+              V4L2_CAP_VIDEO_CAPTURE_MPLANE)))
     goto not_capture;
 
   if (GST_IS_V4L2SINK (v4l2object->element) &&
-      !(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT))
+      !(v4l2object->device_caps & (V4L2_CAP_VIDEO_OUTPUT |
+              V4L2_CAP_VIDEO_OUTPUT_MPLANE)))
     goto not_output;
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
+
+  if (GST_IS_V4L2_VIDEO_DEC (v4l2object->element) &&
+      !GST_V4L2_IS_M2M (v4l2object->device_caps))
+    goto not_m2m;
+
+  gst_v4l2_adjust_buf_type (v4l2object);
 
   /* create enumerations, posts errors. */
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+  if (!gst_v4l2_fill_lists (v4l2object)) {
+    error_type = V4L2_OPEN_ERROR;
+    goto pre_error_check;
+  }
+#else /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
   if (!gst_v4l2_fill_lists (v4l2object))
     goto error;
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
 
-  GST_INFO_OBJECT (v4l2object->element,
+  GST_INFO_OBJECT (v4l2object->dbg_obj,
       "Opened device '%s' (%s) successfully",
       v4l2object->vcap.card, v4l2object->videodev);
 
-  pollfd.fd = v4l2object->video_fd;
-  gst_poll_add_fd (v4l2object->poll, &pollfd);
-  gst_poll_fd_ctl_read (v4l2object->poll, &pollfd, TRUE);
+  if (v4l2object->extra_controls)
+    gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls);
+
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+  globfree (&glob_buf);
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
+
+  /* UVC devices are never interlaced, and doing VIDIOC_TRY_FMT on them
+   * causes expensive and slow USB IO, so don't probe them for interlaced
+   */
+  if (!strcmp ((char *) v4l2object->vcap.driver, "uvcusb") ||
+      !strcmp ((char *) v4l2object->vcap.driver, "uvcvideo")) {
+    v4l2object->never_interlaced = TRUE;
+  }
 
   return TRUE;
 
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+pre_error_check:
+  {
+    if (v4l2object->auto_scan_device == FALSE) {
+      GST_WARNING_OBJECT (v4l2object->element, "auto scan device disabled");
+      goto error;
+    }
+
+    if (GST_IS_V4L2SRC (v4l2object->element) && glob_buf.gl_pathc == 0) {
+      if (glob("/dev/video*", 0, 0, &glob_buf) != 0) {
+        GST_WARNING_OBJECT (v4l2object->element, "glob failed");
+      }
+    }
+
+    if (glob_buf.gl_pathc > 0 && device_index < glob_buf.gl_pathc) {
+      if (v4l2object->videodev)
+        g_free (v4l2object->videodev);
+
+      v4l2object->videodev = g_strdup (glob_buf.gl_pathv[device_index]);
+      if (v4l2object->videodev) {
+        device_index++;
+
+        GST_INFO_OBJECT (v4l2object->element, "check device [%s]",
+          v4l2object->videodev);
+
+        if (GST_V4L2_IS_OPEN (v4l2object)) {
+          /* close device */
+          v4l2_close (v4l2object->video_fd);
+          v4l2object->video_fd = -1;
+        }
+        /* empty lists */
+        gst_v4l2_empty_lists (v4l2object);
+
+        goto CHECK_AGAIN;
+      } else {
+        GST_WARNING_OBJECT (v4l2object->element, "g_strdup failed [%s]",
+          glob_buf.gl_pathv[device_index]);
+      }
+    }
+
+    GST_WARNING_OBJECT (v4l2object->element, "error type : %d", error_type);
+
+    switch (error_type) {
+    case V4L2_OPEN_ERROR_STAT_FAILED:
+      goto stat_failed;
+    case V4L2_OPEN_ERROR_NO_DEVICE:
+      goto no_device;
+    case V4L2_OPEN_ERROR_NOT_OPEN:
+      goto not_open;
+    case V4L2_OPEN_ERROR_NOT_CAPTURE:
+      goto not_capture;
+    case V4L2_OPEN_ERROR_NOT_OUTPUT:
+      goto not_output;
+    default:
+      goto error;
+    }
+  }
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
+
   /* ERRORS */
 stat_failed:
   {
@@ -510,7 +772,7 @@ not_capture:
     GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND,
         (_("Device '%s' is not a capture device."),
             v4l2object->videodev),
-        ("Capabilities: 0x%x", v4l2object->vcap.capabilities));
+        ("Capabilities: 0x%x", v4l2object->device_caps));
     goto error;
   }
 not_output:
@@ -518,19 +780,72 @@ not_output:
     GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND,
         (_("Device '%s' is not a output device."),
             v4l2object->videodev),
-        ("Capabilities: 0x%x", v4l2object->vcap.capabilities));
+        ("Capabilities: 0x%x", v4l2object->device_caps));
+    goto error;
+  }
+not_m2m:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND,
+        (_("Device '%s' is not a M2M device."),
+            v4l2object->videodev),
+        ("Capabilities: 0x%x", v4l2object->device_caps));
     goto error;
   }
 error:
   {
     if (GST_V4L2_IS_OPEN (v4l2object)) {
       /* close device */
-      v4l2_close (v4l2object->video_fd);
+      v4l2object->close (v4l2object->video_fd);
       v4l2object->video_fd = -1;
     }
     /* empty lists */
     gst_v4l2_empty_lists (v4l2object);
 
+#ifdef TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE
+    globfree (&glob_buf);
+#endif /* TIZEN_FEATURE_V4L2SRC_AUTO_SCAN_DEVICE_NODE */
+
+    return FALSE;
+  }
+}
+
+gboolean
+gst_v4l2_dup (GstV4l2Object * v4l2object, GstV4l2Object * other)
+{
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "Trying to dup device %s",
+      other->videodev);
+
+  GST_V4L2_CHECK_OPEN (other);
+  GST_V4L2_CHECK_NOT_OPEN (v4l2object);
+  GST_V4L2_CHECK_NOT_ACTIVE (other);
+  GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
+
+  v4l2object->vcap = other->vcap;
+  v4l2object->device_caps = other->device_caps;
+  gst_v4l2_adjust_buf_type (v4l2object);
+
+  v4l2object->video_fd = v4l2object->dup (other->video_fd);
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    goto not_open;
+
+  g_free (v4l2object->videodev);
+  v4l2object->videodev = g_strdup (other->videodev);
+
+  GST_INFO_OBJECT (v4l2object->dbg_obj,
+      "Cloned device '%s' (%s) successfully",
+      v4l2object->vcap.card, v4l2object->videodev);
+
+  v4l2object->never_interlaced = other->never_interlaced;
+  v4l2object->no_initial_format = other->no_initial_format;
+
+  return TRUE;
+
+not_open:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ_WRITE,
+        (_("Could not dup device '%s' for reading and writing."),
+            v4l2object->videodev), GST_ERROR_SYSTEM);
+
     return FALSE;
   }
 }
@@ -544,17 +859,14 @@ error:
 gboolean
 gst_v4l2_close (GstV4l2Object * v4l2object)
 {
-  GstPollFD pollfd = GST_POLL_FD_INIT;
-  GST_DEBUG_OBJECT (v4l2object->element, "Trying to close %s",
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "Trying to close %s",
       v4l2object->videodev);
 
   GST_V4L2_CHECK_OPEN (v4l2object);
   GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
 
   /* close device */
-  v4l2_close (v4l2object->video_fd);
-  pollfd.fd = v4l2object->video_fd;
-  gst_poll_remove_fd (v4l2object->poll, &pollfd);
+  v4l2object->close (v4l2object->video_fd);
   v4l2object->video_fd = -1;
 
   /* empty lists */
@@ -572,12 +884,12 @@ gst_v4l2_close (GstV4l2Object * v4l2object)
 gboolean
 gst_v4l2_get_norm (GstV4l2Object * v4l2object, v4l2_std_id * norm)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "getting norm");
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "getting norm");
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_STD, norm) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_STD, norm) < 0)
     goto std_failed;
 
   return TRUE;
@@ -600,13 +912,13 @@ std_failed:
 gboolean
 gst_v4l2_set_norm (GstV4l2Object * v4l2object, v4l2_std_id norm)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "trying to set norm to "
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "trying to set norm to "
       "%" G_GINT64_MODIFIER "x", (guint64) norm);
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_STD, &norm) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_STD, &norm) < 0)
     goto std_failed;
 
   return TRUE;
@@ -634,7 +946,7 @@ gst_v4l2_get_frequency (GstV4l2Object * v4l2object,
 
   GstTunerChannel *channel;
 
-  GST_DEBUG_OBJECT (v4l2object->element, "getting current tuner frequency");
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "getting current tuner frequency");
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
@@ -642,7 +954,7 @@ gst_v4l2_get_frequency (GstV4l2Object * v4l2object,
   channel = gst_tuner_get_channel (GST_TUNER (v4l2object->element));
 
   freq.tuner = tunernum;
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0)
     goto freq_failed;
 
   *frequency = freq.frequency * channel->freq_multiplicator;
@@ -673,7 +985,7 @@ gst_v4l2_set_frequency (GstV4l2Object * v4l2object,
 
   GstTunerChannel *channel;
 
-  GST_DEBUG_OBJECT (v4l2object->element,
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj,
       "setting current tuner frequency to %lu", frequency);
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
@@ -683,10 +995,10 @@ gst_v4l2_set_frequency (GstV4l2Object * v4l2object,
 
   freq.tuner = tunernum;
   /* fill in type - ignore error */
-  v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq);
+  (void) v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq);
   freq.frequency = frequency / channel->freq_multiplicator;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_FREQUENCY, &freq) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_FREQUENCY, &freq) < 0)
     goto freq_failed;
 
   return TRUE;
@@ -712,13 +1024,13 @@ gst_v4l2_signal_strength (GstV4l2Object * v4l2object,
 {
   struct v4l2_tuner tuner = { 0, };
 
-  GST_DEBUG_OBJECT (v4l2object->element, "trying to get signal strength");
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "trying to get signal strength");
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
   tuner.index = tunernum;
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &tuner) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &tuner) < 0)
     goto tuner_failed;
 
   *signal_strength = tuner.signal;
@@ -746,7 +1058,7 @@ gst_v4l2_get_attribute (GstV4l2Object * v4l2object,
 {
   struct v4l2_control control = { 0, };
 
-  GST_DEBUG_OBJECT (v4l2object->element, "getting value of attribute %d",
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "getting value of attribute %d",
       attribute_num);
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
@@ -754,7 +1066,7 @@ gst_v4l2_get_attribute (GstV4l2Object * v4l2object,
 
   control.id = attribute_num;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
     goto ctrl_failed;
 
   *value = control.value;
@@ -764,9 +1076,9 @@ gst_v4l2_get_attribute (GstV4l2Object * v4l2object,
   /* ERRORS */
 ctrl_failed:
   {
-    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to get value for control %d on device '%s'."),
-            attribute_num, v4l2object->videodev), GST_ERROR_SYSTEM);
+    GST_WARNING_OBJECT (v4l2object,
+        _("Failed to get value for control %d on device '%s'."),
+        attribute_num, v4l2object->videodev);
     return FALSE;
   }
 }
@@ -783,7 +1095,7 @@ gst_v4l2_set_attribute (GstV4l2Object * v4l2object,
 {
   struct v4l2_control control = { 0, };
 
-  GST_DEBUG_OBJECT (v4l2object->element, "setting value of attribute %d to %d",
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "setting value of attribute %d to %d",
       attribute_num, value);
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
@@ -791,7 +1103,7 @@ gst_v4l2_set_attribute (GstV4l2Object * v4l2object,
 
   control.id = attribute_num;
   control.value = value;
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0)
     goto ctrl_failed;
 
   return TRUE;
@@ -799,35 +1111,83 @@ gst_v4l2_set_attribute (GstV4l2Object * v4l2object,
   /* ERRORS */
 ctrl_failed:
   {
-    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to set value %d for control %d on device '%s'."),
-            value, attribute_num, v4l2object->videodev), GST_ERROR_SYSTEM);
+    GST_WARNING_OBJECT (v4l2object,
+        _("Failed to set value %d for control %d on device '%s'."),
+        value, attribute_num, v4l2object->videodev);
     return FALSE;
   }
 }
 
+static gboolean
+set_control (GQuark field_id, const GValue * value, gpointer user_data)
+{
+  GstV4l2Object *v4l2object = user_data;
+  GQuark normalised_field_id;
+  gpointer *d;
+
+  /* 32 bytes is the maximum size for a control name according to v4l2 */
+  gchar name[32];
+
+  /* Backwards compatibility: in the past GStreamer would normalise strings in
+     a subtly different way to v4l2-ctl.  e.g. the kernel's "Focus (absolute)"
+     would become "focus__absolute_" whereas now it becomes "focus_absolute".
+     Please remove the following in GStreamer 1.5 for 1.6 */
+  strncpy (name, g_quark_to_string (field_id), sizeof (name));
+  name[31] = '\0';
+  gst_v4l2_normalise_control_name (name);
+  normalised_field_id = g_quark_from_string (name);
+  if (normalised_field_id != field_id)
+    g_warning ("In GStreamer 1.4 the way V4L2 control names were normalised "
+        "changed.  Instead of setting \"%s\" please use \"%s\".  The former is "
+        "deprecated and will be removed in a future version of GStreamer",
+        g_quark_to_string (field_id), name);
+  field_id = normalised_field_id;
+
+  d = g_datalist_id_get_data (&v4l2object->controls, field_id);
+  if (!d) {
+    GST_WARNING_OBJECT (v4l2object,
+        "Control '%s' does not exist or has an unsupported type.",
+        g_quark_to_string (field_id));
+    return TRUE;
+  }
+  if (!G_VALUE_HOLDS (value, G_TYPE_INT)) {
+    GST_WARNING_OBJECT (v4l2object,
+        "'int' value expected for control '%s'.", g_quark_to_string (field_id));
+    return TRUE;
+  }
+  gst_v4l2_set_attribute (v4l2object, GPOINTER_TO_INT (d),
+      g_value_get_int (value));
+  return TRUE;
+}
+
+gboolean
+gst_v4l2_set_controls (GstV4l2Object * v4l2object, GstStructure * controls)
+{
+  return gst_structure_foreach (controls, set_control, v4l2object);
+}
+
 gboolean
 gst_v4l2_get_input (GstV4l2Object * v4l2object, gint * input)
 {
   gint n;
 
-  GST_DEBUG_OBJECT (v4l2object->element, "trying to get input");
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "trying to get input");
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_INPUT, &n) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_INPUT, &n) < 0)
     goto input_failed;
 
   *input = n;
 
-  GST_DEBUG_OBJECT (v4l2object->element, "input: %d", n);
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "input: %d", n);
 
   return TRUE;
 
   /* ERRORS */
 input_failed:
-  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+  if (v4l2object->device_caps & V4L2_CAP_TUNER) {
     /* only give a warning message if driver actually claims to have tuner
      * support
      */
@@ -840,19 +1200,19 @@ input_failed:
 gboolean
 gst_v4l2_set_input (GstV4l2Object * v4l2object, gint input)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "trying to set input to %d", input);
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "trying to set input to %d", input);
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_INPUT, &input) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_INPUT, &input) < 0)
     goto input_failed;
 
   return TRUE;
 
   /* ERRORS */
 input_failed:
-  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+  if (v4l2object->device_caps & V4L2_CAP_TUNER) {
     /* only give a warning message if driver actually claims to have tuner
      * support
      */
@@ -868,23 +1228,23 @@ gst_v4l2_get_output (GstV4l2Object * v4l2object, gint * output)
 {
   gint n;
 
-  GST_DEBUG_OBJECT (v4l2object->element, "trying to get output");
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "trying to get output");
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_OUTPUT, &n) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_OUTPUT, &n) < 0)
     goto output_failed;
 
   *output = n;
 
-  GST_DEBUG_OBJECT (v4l2object->element, "output: %d", n);
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "output: %d", n);
 
   return TRUE;
 
   /* ERRORS */
 output_failed:
-  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+  if (v4l2object->device_caps & V4L2_CAP_TUNER) {
     /* only give a warning message if driver actually claims to have tuner
      * support
      */
@@ -897,19 +1257,19 @@ output_failed:
 gboolean
 gst_v4l2_set_output (GstV4l2Object * v4l2object, gint output)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "trying to set output to %d", output);
+  GST_DEBUG_OBJECT (v4l2object->dbg_obj, "trying to set output to %d", output);
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_OUTPUT, &output) < 0)
+  if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_OUTPUT, &output) < 0)
     goto output_failed;
 
   return TRUE;
 
   /* ERRORS */
 output_failed:
-  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+  if (v4l2object->device_caps & V4L2_CAP_TUNER) {
     /* only give a warning message if driver actually claims to have tuner
      * support
      */