Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / v4l2_calls.c
index cb10841..74b2e0a 100644 (file)
@@ -1,5 +1,9 @@
-/* G-Streamer generic V4L2 element - generic V4L2 calls handling
+/* GStreamer
+ *
  * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *               2006 Edgard Lima <edgard.lima@indt.org.br>
+ *
+ * v4l2_calls.c - generic V4L2 calls handling
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #include <sys/mman.h>
 #include <string.h>
 #include <errno.h>
+#include <unistd.h>
+#ifdef __sun
+/* Needed on older Solaris Nevada builds (72 at least) */
+#include <stropts.h>
+#include <sys/ioccom.h>
+#endif
 #include "v4l2_calls.h"
+#include "gstv4l2tuner.h"
+#if 0
+#include "gstv4l2xoverlay.h"
+#endif
+#include "gstv4l2colorbalance.h"
 
-#define DEBUG(format, args...) \
-       GST_DEBUG_ELEMENT(GST_CAT_PLUGIN_INFO, \
-               GST_ELEMENT(v4l2element), \
-               "V4L2: " format, ##args)
+#include "gstv4l2src.h"
+#include "gstv4l2sink.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
+
+GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
+#define GST_CAT_DEFAULT v4l2_debug
 
 /******************************************************
  * gst_v4l2_get_capabilities():
  *   get the device's capturing capabilities
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
-static gboolean
-gst_v4l2_get_capabilities (GstV4l2Element *v4l2element)
+gboolean
+gst_v4l2_get_capabilities (GstV4l2Object * v4l2object)
 {
-       DEBUG("getting capabilities");
-       GST_V4L2_CHECK_OPEN(v4l2element);
+  GstElement *e;
+
+  e = v4l2object->element;
+
+  GST_DEBUG_OBJECT (e, "getting capabilities");
+
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       if (ioctl(v4l2element->video_fd, VIDIOC_QUERYCAP, &(v4l2element->vcap)) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Error getting %s capabilities: %s",
-                       v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &v4l2object->vcap) < 0)
+    goto cap_failed;
 
-       return TRUE;
+  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);
+
+  return TRUE;
+
+  /* ERRORS */
+cap_failed:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Error getting capabilities for device '%s': "
+                "It isn't a v4l2 driver. Check if it is a v4l1 driver."),
+            v4l2object->videodev), GST_ERROR_SYSTEM);
+    return FALSE;
+  }
 }
 
 
@@ -64,239 +106,461 @@ gst_v4l2_get_capabilities (GstV4l2Element *v4l2element)
  *   fill/empty the lists of enumerations
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 static gboolean
-gst_v4l2_fill_lists (GstV4l2Element *v4l2element)
+gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
 {
-       gint n;
-
-       DEBUG("getting enumerations");
-       GST_V4L2_CHECK_OPEN(v4l2element);
-
-       /* and now, the inputs */
-       for (n=0;;n++) {
-               struct v4l2_input input, *inpptr;
-               input.index = n;
-               if (ioctl(v4l2element->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
-                       if (errno == EINVAL)
-                               break; /* end of enumeration */
-                       else {
-                               gst_element_error(GST_ELEMENT(v4l2element),
-                                       "Failed to get no. %d in input enumeration for %s: %s",
-                                       n, v4l2element->device, g_strerror(errno));
-                               return FALSE;
-                       }
-               }
-               inpptr = g_malloc(sizeof(input));
-               memcpy(inpptr, &input, sizeof(input));
-               v4l2element->inputs = g_list_append(v4l2element->inputs, inpptr);
-       }
-
-       /* outputs */
-       for (n=0;;n++) {
-               struct v4l2_output output, *outptr;
-               output.index = n;
-               if (ioctl(v4l2element->video_fd, VIDIOC_ENUMOUTPUT, &output) < 0) {
-                       if (errno == EINVAL)
-                               break; /* end of enumeration */
-                       else {
-                               gst_element_error(GST_ELEMENT(v4l2element),
-                                       "Failed to get no. %d in output enumeration for %s: %s",
-                                       n, v4l2element->device, g_strerror(errno));
-                               return FALSE;
-                       }
-               }
-               outptr = g_malloc(sizeof(output));
-               memcpy(outptr, &output, sizeof(output));
-               v4l2element->outputs = g_list_append(v4l2element->outputs, outptr);
-       }
-
-       /* norms... */
-       for (n=0;;n++) {
-               struct v4l2_standard standard, *stdptr;
-               standard.index = n;
-               if (ioctl(v4l2element->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
-                       if (errno == EINVAL)
-                               break; /* end of enumeration */
-                       else {
-                               gst_element_error(GST_ELEMENT(v4l2element),
-                                       "Failed to get no. %d in norm enumeration for %s: %s",
-                                       n, v4l2element->device, g_strerror(errno));
-                               return FALSE;
-                       }
-               }
-               stdptr = g_malloc(sizeof(standard));
-               memcpy(stdptr, &standard, sizeof(standard));
-               v4l2element->norms = g_list_append(v4l2element->norms, stdptr);
-       }
-
-       /* and lastly, controls+menus (if appropriate) */
-       for (n=V4L2_CID_BASE;;n++) {
-               struct v4l2_queryctrl control, *ctrlptr;
-               GList *menus = NULL;
-               /* hacky... */
-               if (n == V4L2_CID_LASTP1)
-                       n = V4L2_CID_PRIVATE_BASE;
-               control.id = n;
-               if (ioctl(v4l2element->video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
-                       if (errno == EINVAL) {
-                               if (n < V4L2_CID_PRIVATE_BASE)
-                                       continue;
-                               else
-                                       break;
-                       } else {
-                               gst_element_error(GST_ELEMENT(v4l2element),
-                                       "Failed to get no. %d in control enumeration for %s: %s",
-                                       n, v4l2element->device, g_strerror(errno));
-                               return FALSE;
-                       }
-               }
-               if (control.flags & V4L2_CTRL_FLAG_DISABLED)
-                       continue;
-               ctrlptr = g_malloc(sizeof(control));
-               memcpy(ctrlptr, &control, sizeof(control));
-               v4l2element->controls = g_list_append(v4l2element->controls, ctrlptr);
-               if (control.type == V4L2_CTRL_TYPE_MENU) {
-                       struct v4l2_querymenu menu, *mptr;
-                       int i;
-                       menu.id = n;
-                       for (i=0;;i++) {
-                               menu.index = i;
-                               if (ioctl(v4l2element->video_fd, VIDIOC_QUERYMENU, &menu) < 0) {
-                                       if (errno == EINVAL)
-                                               break; /* end of enumeration */
-                                       else {
-                                               gst_element_error(GST_ELEMENT(v4l2element),
-                                                       "Failed to get no. %d in menu %d enumeration for %s: %s",
-                                                       i, n, v4l2element->device, g_strerror(errno));
-                                               return FALSE;
-                                       }
-                               }
-                               mptr = g_malloc(sizeof(menu));
-                               memcpy(mptr, &menu, sizeof(menu));
-                               menus = g_list_append(menus, mptr);
-                       }
-               }
-               v4l2element->menus = g_list_append(v4l2element->menus, menus);
-       }
-
-       return TRUE;
+  gint n;
+
+  GstElement *e;
+
+  e = v4l2object->element;
+
+  GST_DEBUG_OBJECT (e, "getting enumerations");
+  GST_V4L2_CHECK_OPEN (v4l2object);
+
+  GST_DEBUG_OBJECT (e, "  channels");
+  /* and now, the channels */
+  for (n = 0;; n++) {
+    struct v4l2_input input;
+    GstV4l2TunerChannel *v4l2channel;
+    GstTunerChannel *channel;
+
+    memset (&input, 0, sizeof (input));
+
+    input.index = n;
+    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
+      if (errno == EINVAL)
+        break;                  /* end of enumeration */
+      else {
+        GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+            (_("Failed to query attributes of input %d in device %s"),
+                n, v4l2object->videodev),
+            ("Failed to get %d in input enumeration for %s. (%d - %s)",
+                n, v4l2object->videodev, errno, strerror (errno)));
+        return FALSE;
+      }
+    }
+
+    GST_LOG_OBJECT (e, "   index:     %d", input.index);
+    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, "   status:    %08x", input.status);
+
+    v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL);
+    channel = GST_TUNER_CHANNEL (v4l2channel);
+    channel->label = g_strdup ((const gchar *) input.name);
+    channel->flags = GST_TUNER_CHANNEL_INPUT;
+    v4l2channel->index = n;
+
+    if (input.type == V4L2_INPUT_TYPE_TUNER) {
+      struct v4l2_tuner vtun;
+
+      v4l2channel->tuner = input.tuner;
+      channel->flags |= GST_TUNER_CHANNEL_FREQUENCY;
+
+      vtun.index = input.tuner;
+      if (v4l2_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);
+        g_object_unref (G_OBJECT (channel));
+        return FALSE;
+      }
+
+      channel->freq_multiplicator =
+          62.5 * ((vtun.capability & V4L2_TUNER_CAP_LOW) ? 1 : 1000);
+      channel->min_frequency = vtun.rangelow * channel->freq_multiplicator;
+      channel->max_frequency = vtun.rangehigh * channel->freq_multiplicator;
+      channel->min_signal = 0;
+      channel->max_signal = 0xffff;
+    }
+    if (input.audioset) {
+      /* we take the first. We don't care for
+       * the others for now */
+      while (!(input.audioset & (1 << v4l2channel->audio)))
+        v4l2channel->audio++;
+      channel->flags |= GST_TUNER_CHANNEL_AUDIO;
+    }
+
+    v4l2object->channels =
+        g_list_prepend (v4l2object->channels, (gpointer) channel);
+  }
+  v4l2object->channels = g_list_reverse (v4l2object->channels);
+
+  GST_DEBUG_OBJECT (e, "  norms");
+  /* norms... */
+  for (n = 0;; n++) {
+    struct v4l2_standard standard = { 0, };
+    GstV4l2TunerNorm *v4l2norm;
+
+    GstTunerNorm *norm;
+
+    /* fill in defaults */
+    standard.frameperiod.numerator = 1;
+    standard.frameperiod.denominator = 0;
+    standard.index = n;
+
+    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
+      if (errno == EINVAL || errno == ENOTTY)
+        break;                  /* end of enumeration */
+      else {
+        GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+            (_("Failed to query norm on device '%s'."),
+                v4l2object->videodev),
+            ("Failed to get attributes for norm %d on devide '%s'. (%d - %s)",
+                n, v4l2object->videodev, errno, strerror (errno)));
+        return FALSE;
+      }
+    }
+
+    GST_DEBUG_OBJECT (e, "    '%s', fps: %d / %d",
+        standard.name, standard.frameperiod.denominator,
+        standard.frameperiod.numerator);
+
+    v4l2norm = g_object_new (GST_TYPE_V4L2_TUNER_NORM, NULL);
+    norm = GST_TUNER_NORM (v4l2norm);
+    norm->label = g_strdup ((const gchar *) standard.name);
+    gst_value_set_fraction (&norm->framerate,
+        standard.frameperiod.denominator, standard.frameperiod.numerator);
+    v4l2norm->index = standard.id;
+
+    GST_DEBUG_OBJECT (v4l2object->element, "index=%08x, label=%s",
+        (unsigned int) v4l2norm->index, norm->label);
+
+    v4l2object->norms = g_list_prepend (v4l2object->norms, (gpointer) norm);
+  }
+  v4l2object->norms = g_list_reverse (v4l2object->norms);
+
+  GST_DEBUG_OBJECT (e, "  controls+menus");
+
+  /* and lastly, controls+menus (if appropriate) */
+  for (n = V4L2_CID_BASE;; n++) {
+    struct v4l2_queryctrl control = { 0, };
+    GstV4l2ColorBalanceChannel *v4l2channel;
+    GstColorBalanceChannel *channel;
+
+    /* when we reached the last official CID, continue with private CIDs */
+    if (n == V4L2_CID_LASTP1) {
+      GST_DEBUG_OBJECT (e, "checking private CIDs");
+      n = V4L2_CID_PRIVATE_BASE;
+    }
+    GST_DEBUG_OBJECT (e, "checking control %08x", n);
+
+    control.id = n;
+    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
+      if (errno == EINVAL) {
+        if (n < V4L2_CID_PRIVATE_BASE) {
+          GST_DEBUG_OBJECT (e, "skipping control %08x", n);
+          /* continue so that we also check private controls */
+          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;
+      }
+    }
+    if (control.flags & V4L2_CTRL_FLAG_DISABLED) {
+      GST_DEBUG_OBJECT (e, "skipping disabled control");
+      continue;
+    }
+
+    switch (n) {
+      case V4L2_CID_BRIGHTNESS:
+      case V4L2_CID_CONTRAST:
+      case V4L2_CID_SATURATION:
+      case V4L2_CID_HUE:
+      case V4L2_CID_BLACK_LEVEL:
+      case V4L2_CID_AUTO_WHITE_BALANCE:
+      case V4L2_CID_DO_WHITE_BALANCE:
+      case V4L2_CID_RED_BALANCE:
+      case V4L2_CID_BLUE_BALANCE:
+      case V4L2_CID_GAMMA:
+      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;
+      case V4L2_CID_AUDIO_VOLUME:
+      case V4L2_CID_AUDIO_BALANCE:
+      case V4L2_CID_AUDIO_BASS:
+      case V4L2_CID_AUDIO_TREBLE:
+      case V4L2_CID_AUDIO_MUTE:
+      case V4L2_CID_AUDIO_LOUDNESS:
+        /* FIXME: We should implement GstMixer interface */
+        /* fall through */
+      default:
+        GST_DEBUG_OBJECT (e,
+            "ControlID %s (%x) unhandled, FIXME", control.name, n);
+        control.id++;
+        break;
+    }
+    if (n != control.id)
+      continue;
+
+    GST_DEBUG_OBJECT (e, "Adding ControlID %s (%x)", control.name, n);
+    v4l2channel = g_object_new (GST_TYPE_V4L2_COLOR_BALANCE_CHANNEL, NULL);
+    channel = GST_COLOR_BALANCE_CHANNEL (v4l2channel);
+    channel->label = g_strdup ((const gchar *) control.name);
+    v4l2channel->id = n;
+
+#if 0
+    /* FIXME: it will be need just when handling private controls
+     *(currently none of base controls are of this type) */
+    if (control.type == V4L2_CTRL_TYPE_MENU) {
+      struct v4l2_querymenu menu, *mptr;
+
+      int i;
+
+      menu.id = n;
+      for (i = 0;; i++) {
+        menu.index = i;
+        if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYMENU, &menu) < 0) {
+          if (errno == EINVAL)
+            break;              /* end of enumeration */
+          else {
+            GST_ELEMENT_ERROR (e, RESOURCE, SETTINGS,
+                (_("Failed getting controls attributes on device '%s'."),
+                    v4l2object->videodev),
+                ("Failed to get %d in menu enumeration for %s. (%d - %s)",
+                    n, v4l2object->videodev, errno, strerror (errno)));
+            return FALSE;
+          }
+        }
+        mptr = g_malloc (sizeof (menu));
+        memcpy (mptr, &menu, sizeof (menu));
+        menus = g_list_append (menus, mptr);
+      }
+    }
+    v4l2object->menus = g_list_append (v4l2object->menus, menus);
+#endif
+
+    switch (control.type) {
+      case V4L2_CTRL_TYPE_INTEGER:
+        channel->min_value = control.minimum;
+        channel->max_value = control.maximum;
+        break;
+      case V4L2_CTRL_TYPE_BOOLEAN:
+        channel->min_value = FALSE;
+        channel->max_value = TRUE;
+        break;
+      default:
+        /* FIXME we should find out how to handle V4L2_CTRL_TYPE_BUTTON.
+           BUTTON controls like V4L2_CID_DO_WHITE_BALANCE can just be set (1) or
+           unset (0), but can't be queried */
+        GST_DEBUG_OBJECT (e,
+            "Control with non supported type %s (%x), type=%d",
+            control.name, n, control.type);
+        channel->min_value = channel->max_value = 0;
+        break;
+    }
+
+    v4l2object->colors =
+        g_list_prepend (v4l2object->colors, (gpointer) channel);
+  }
+  v4l2object->colors = g_list_reverse (v4l2object->colors);
+
+  GST_DEBUG_OBJECT (e, "done");
+  return TRUE;
 }
 
 
 static void
-gst_v4l2_empty_lists (GstV4l2Element *v4l2element)
+gst_v4l2_empty_lists (GstV4l2Object * v4l2object)
 {
-       DEBUG("deleting enumerations");
-
-       /* empty lists */
-       while (g_list_length(v4l2element->inputs) > 0) {
-               gpointer data = g_list_nth_data(v4l2element->inputs, 0);
-               v4l2element->inputs = g_list_remove(v4l2element->inputs, data);
-               g_free(data);
-       }
-       while (g_list_length(v4l2element->outputs) > 0) {
-               gpointer data = g_list_nth_data(v4l2element->outputs, 0);
-               v4l2element->outputs = g_list_remove(v4l2element->outputs, data);
-               g_free(data);
-       }
-       while (g_list_length(v4l2element->norms) > 0) {
-               gpointer data = g_list_nth_data(v4l2element->norms, 0);
-               v4l2element->norms = g_list_remove(v4l2element->norms, data);
-               g_free(data);
-       }
-       while (g_list_length(v4l2element->controls) > 0) {
-               gpointer data = g_list_nth_data(v4l2element->controls, 0);
-               v4l2element->controls = g_list_remove(v4l2element->controls, data);
-               g_free(data);
-       }
-       v4l2element->menus = g_list_remove_all(v4l2element->menus, NULL);
-       while (g_list_length(v4l2element->menus) > 0) {
-               GList *items = (GList *) g_list_nth_data(v4l2element->menus, 0);
-               v4l2element->inputs = g_list_remove(v4l2element->inputs, items);
-               while (g_list_length(items) > 0) {
-                       gpointer data = g_list_nth_data(v4l2element->menus, 0);
-                       items = g_list_remove(items, data);
-                       g_free(data);
-               }
-       }
-}
+  GST_DEBUG_OBJECT (v4l2object->element, "deleting enumerations");
 
+  g_list_foreach (v4l2object->channels, (GFunc) g_object_unref, NULL);
+  g_list_free (v4l2object->channels);
+  v4l2object->channels = NULL;
+
+  g_list_foreach (v4l2object->norms, (GFunc) g_object_unref, NULL);
+  g_list_free (v4l2object->norms);
+  v4l2object->norms = NULL;
+
+  g_list_foreach (v4l2object->colors, (GFunc) g_object_unref, NULL);
+  g_list_free (v4l2object->colors);
+  v4l2object->colors = NULL;
+}
 
 /******************************************************
  * gst_v4l2_open():
- *   open the video device (v4l2element->device)
+ *   open the video device (v4l2object->videodev)
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_open (GstV4l2Element *v4l2element)
+gst_v4l2_open (GstV4l2Object * v4l2object)
 {
-       DEBUG("Trying to open device %s", v4l2element->device);
-       GST_V4L2_CHECK_NOT_OPEN(v4l2element);
-       GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
-
-       /* be sure we have a device */
-       if (!v4l2element->device)
-               v4l2element->device = g_strdup("/dev/video");
-
-       /* open the device */
-       v4l2element->video_fd = open(v4l2element->device, O_RDWR);
-       if (!GST_V4L2_IS_OPEN(v4l2element)) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to open device %s: %s",
-                       v4l2element->device, g_strerror(errno));
-               goto error;
-       }
-
-       /* get capabilities */
-       if (!gst_v4l2_get_capabilities(v4l2element)) {
-               goto error;
-       }
-
-       /* create enumerations */
-       if (!gst_v4l2_fill_lists(v4l2element))
-               goto error;
-
-       gst_info("Opened device '%s' (%s) successfully\n",
-               v4l2element->vcap.card, v4l2element->device);
-
-       return TRUE;
-
+  struct stat st;
+  int libv4l2_fd;
+  GstPollFD pollfd = GST_POLL_FD_INIT;
+
+  GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s",
+      v4l2object->videodev);
+
+  GST_V4L2_CHECK_NOT_OPEN (v4l2object);
+  GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
+
+  /* be sure we have a device */
+  if (!v4l2object->videodev)
+    v4l2object->videodev = g_strdup ("/dev/video");
+
+  /* check if it is a device */
+  if (stat (v4l2object->videodev, &st) == -1)
+    goto stat_failed;
+
+  if (!S_ISCHR (st.st_mode))
+    goto no_device;
+
+  /* open the device */
+  v4l2object->video_fd =
+      open (v4l2object->videodev, O_RDWR /* | O_NONBLOCK */ );
+
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    goto not_open;
+
+  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
+     cam format to normal formats conversion). Chances are big we will still
+     fail then though, as normally v4l2_fd_open only fails if the device is not
+     a v4l2 device. */
+  if (libv4l2_fd != -1)
+    v4l2object->video_fd = libv4l2_fd;
+
+  v4l2object->can_poll_device = TRUE;
+
+  /* get capabilities, error will be posted */
+  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))
+    goto not_capture;
+
+  if (GST_IS_V4L2SINK (v4l2object->element) &&
+      !(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT))
+    goto not_output;
+
+  /* create enumerations, posts errors. */
+  if (!gst_v4l2_fill_lists (v4l2object))
+    goto error;
+
+  GST_INFO_OBJECT (v4l2object->element,
+      "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);
+
+  return TRUE;
+
+  /* ERRORS */
+stat_failed:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND,
+        (_("Cannot identify device '%s'."), v4l2object->videodev),
+        GST_ERROR_SYSTEM);
+    goto error;
+  }
+no_device:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND,
+        (_("This isn't a device '%s'."), v4l2object->videodev),
+        GST_ERROR_SYSTEM);
+    goto error;
+  }
+not_open:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ_WRITE,
+        (_("Could not open device '%s' for reading and writing."),
+            v4l2object->videodev), GST_ERROR_SYSTEM);
+    goto error;
+  }
+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));
+    goto error;
+  }
+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));
+    goto error;
+  }
 error:
-       if (GST_V4L2_IS_OPEN(v4l2element)) {
-               /* close device */
-               close(v4l2element->video_fd);
-               v4l2element->video_fd = -1;
-       }
-       /* empty lists */
-       gst_v4l2_empty_lists(v4l2element);
-
-       return FALSE;
+  {
+    if (GST_V4L2_IS_OPEN (v4l2object)) {
+      /* close device */
+      v4l2_close (v4l2object->video_fd);
+      v4l2object->video_fd = -1;
+    }
+    /* empty lists */
+    gst_v4l2_empty_lists (v4l2object);
+
+    return FALSE;
+  }
 }
 
 
 /******************************************************
  * gst_v4l2_close():
- *   close the video device (v4l2element->video_fd)
+ *   close the video device (v4l2object->video_fd)
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_close (GstV4l2Element *v4l2element)
+gst_v4l2_close (GstV4l2Object * v4l2object)
 {
-       DEBUG("Trying to close %s", v4l2element->device);
-       GST_V4L2_CHECK_OPEN(v4l2element);
-       GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+  GstPollFD pollfd = GST_POLL_FD_INIT;
+  GST_DEBUG_OBJECT (v4l2object->element, "Trying to close %s",
+      v4l2object->videodev);
 
-       /* close device */
-       close(v4l2element->video_fd);
-       v4l2element->video_fd = -1;
+  GST_V4L2_CHECK_OPEN (v4l2object);
+  GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
 
-       /* empty lists */
-       gst_v4l2_empty_lists(v4l2element);
+  /* close device */
+  v4l2_close (v4l2object->video_fd);
+  pollfd.fd = v4l2object->video_fd;
+  gst_poll_remove_fd (v4l2object->poll, &pollfd);
+  v4l2object->video_fd = -1;
 
-       return TRUE;
+  /* empty lists */
+  gst_v4l2_empty_lists (v4l2object);
+
+  return TRUE;
 }
 
 
@@ -305,554 +569,353 @@ gst_v4l2_close (GstV4l2Element *v4l2element)
  *   Get the norm of the current device
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_get_norm (GstV4l2Element *v4l2element,
-                   gint           *norm)
+gst_v4l2_get_norm (GstV4l2Object * v4l2object, v4l2_std_id * norm)
 {
-       v4l2_std_id std_id;
-       gint n;
-
-       DEBUG("getting norm");
-       GST_V4L2_CHECK_OPEN(v4l2element);
-
-       if (ioctl(v4l2element->video_fd, VIDIOC_G_STD, &std_id) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to get the current norm for device %s: %s",
-                       v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
-
-       /* try to find out what norm number this actually is */
-       for (n=0;n<g_list_length(v4l2element->norms);n++) {
-               struct v4l2_standard *stdptr = (struct v4l2_standard *) g_list_nth_data(v4l2element->norms, n);
-               if (stdptr->id == std_id) {
-                       *norm = n;
-                       return TRUE;
-               }
-       }
-
-       gst_element_error(GST_ELEMENT(v4l2element),
-               "Failed to find norm '%llu' in our list of available norms for device %s",
-               std_id, v4l2element->device);
-       return FALSE;
-}
-
-
-/******************************************************
- * gst_v4l2_set_norm()
- *   Set the norm of the current device
- * return value: TRUE on success, FALSE on error
- ******************************************************/
-
-gboolean
-gst_v4l2_set_norm (GstV4l2Element *v4l2element,
-                   gint            norm)
-{
-       struct v4l2_standard *standard;
-
-       DEBUG("trying to set norm to %d", norm);
-       GST_V4L2_CHECK_OPEN(v4l2element);
-       GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
-
-       if (norm < 0 || norm >= g_list_length(v4l2element->norms)) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Invalid norm number %d (%d-%d)",
-                       norm, 0, g_list_length(v4l2element->norms));
-               return FALSE;
-       }
-
-       standard = (struct v4l2_standard *) g_list_nth_data(v4l2element->norms, norm);
-
-       if (ioctl(v4l2element->video_fd, VIDIOC_S_STD, &standard->id) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to set norm '%s' (%llu) for device %s: %s",
-                       standard->name, standard->id, v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
+  GST_DEBUG_OBJECT (v4l2object->element, "getting norm");
 
-       return TRUE;
-}
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_STD, norm) < 0)
+    goto std_failed;
 
-/******************************************************
- * gst_v4l2_get_norm_names()
- *   Get the list of available norms
- * return value: the list
- ******************************************************/
-
-GList *
-gst_v4l2_get_norm_names (GstV4l2Element *v4l2element)
-{
-       GList *names = NULL;
-       gint n;
+  return TRUE;
 
-       DEBUG("getting a list of norm names");
-
-       for (n=0;n<g_list_length(v4l2element->norms);n++) {
-               struct v4l2_standard *standard = (struct v4l2_standard *) g_list_nth_data(v4l2element->norms, n);
-               names = g_list_append(names, g_strdup(standard->name));
-       }
-
-       return names;
+  /* ERRORS */
+std_failed:
+  {
+    GST_DEBUG ("Failed to get the current norm for device %s",
+        v4l2object->videodev);
+    return FALSE;
+  }
 }
 
 
 /******************************************************
- * gst_v4l2_get_input()
- *   Get the input of the current device
+ * gst_v4l2_set_norm()
+ *   Set the norm of the current device
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_get_input (GstV4l2Element *v4l2element,
-                    gint           *input)
+gst_v4l2_set_norm (GstV4l2Object * v4l2object, v4l2_std_id norm)
 {
-       gint n;
+  GST_DEBUG_OBJECT (v4l2object->element, "trying to set norm to "
+      "%" G_GINT64_MODIFIER "x", (guint64) norm);
 
-       DEBUG("trying to get input");
-       GST_V4L2_CHECK_OPEN(v4l2element);
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       if (ioctl(v4l2element->video_fd, VIDIOC_G_INPUT, &n) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to get current input on device %s: %s",
-                       v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_STD, &norm) < 0)
+    goto std_failed;
 
-       *input = n;
+  return TRUE;
 
-       return TRUE;
+  /* ERRORS */
+std_failed:
+  {
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to set norm for device '%s'."),
+            v4l2object->videodev), GST_ERROR_SYSTEM);
+    return FALSE;
+  }
 }
 
-
 /******************************************************
- * gst_v4l2_set_input()
- *   Set the input of the current device
+ * gst_v4l2_get_frequency():
+ *   get the current frequency
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_set_input (GstV4l2Element *v4l2element,
-                    gint            input)
+gst_v4l2_get_frequency (GstV4l2Object * v4l2object,
+    gint tunernum, gulong * frequency)
 {
-       DEBUG("trying to set input to %d", input);
-       GST_V4L2_CHECK_OPEN(v4l2element);
-       GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
-
-       if (input < 0 || input >= g_list_length(v4l2element->inputs)) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Invalid input number %d (%d-%d)",
-                       input, 0, g_list_length(v4l2element->inputs));
-               return FALSE;
-       }
-
-       if (ioctl(v4l2element->video_fd, VIDIOC_S_INPUT, &input) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to set input %d on device %s: %s",
-                       input, v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
-
-       return TRUE;
-}
+  struct v4l2_frequency freq = { 0, };
 
+  GstTunerChannel *channel;
 
-/******************************************************
- * gst_v4l2_get_input_names()
- *   Get the list of available input channels
- * return value: the list
- ******************************************************/
+  GST_DEBUG_OBJECT (v4l2object->element, "getting current tuner frequency");
 
-GList *
-gst_v4l2_get_input_names (GstV4l2Element *v4l2element)
-{
-       GList *names = NULL;
-       gint n;
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       DEBUG("getting a list of input names");
+  channel = gst_tuner_get_channel (GST_TUNER (v4l2object->element));
 
-       for (n=0;n<g_list_length(v4l2element->inputs);n++) {
-               struct v4l2_input *input = (struct v4l2_input *) g_list_nth_data(v4l2element->inputs, n);
-               names = g_list_append(names, g_strdup(input->name));
-       }
+  freq.tuner = tunernum;
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0)
+    goto freq_failed;
 
-       return names;
-}
-
-
-/******************************************************
- * gst_v4l2_get_output()
- *   Get the output of the current device
- * return value: TRUE on success, FALSE on error
- ******************************************************/
-
-gboolean
-gst_v4l2_get_output (GstV4l2Element *v4l2element,
-                     gint           *output)
-{
-       gint n;
+  *frequency = freq.frequency * channel->freq_multiplicator;
 
-       DEBUG("trying to get output");
-       GST_V4L2_CHECK_OPEN(v4l2element);
+  return TRUE;
 
-       if (ioctl(v4l2element->video_fd, VIDIOC_G_OUTPUT, &n) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to get current output on device %s: %s",
-                       v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
-
-       *output = n;
-
-       return TRUE;
+  /* ERRORS */
+freq_failed:
+  {
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to get current tuner frequency for device '%s'."),
+            v4l2object->videodev), GST_ERROR_SYSTEM);
+    return FALSE;
+  }
 }
 
 
 /******************************************************
- * gst_v4l2_set_output()
- *   Set the output of the current device
+ * gst_v4l2_set_frequency():
+ *   set frequency
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_set_output (GstV4l2Element *v4l2element,
-                     gint            output)
-{
-       DEBUG("trying to set output to %d", output);
-       GST_V4L2_CHECK_OPEN(v4l2element);
-       GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
-
-       if (output < 0 || output >= g_list_length(v4l2element->outputs)) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Invalid output number %d (%d-%d)",
-                       output, 0, g_list_length(v4l2element->outputs));
-               return FALSE;
-       }
-
-       if (ioctl(v4l2element->video_fd, VIDIOC_S_OUTPUT, &output) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to set output %d on device %s: %s",
-                       output, v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-
-/******************************************************
- * gst_v4l2_get_output_names()
- *   Get the list of available output channels
- * return value: the list, or NULL on error
- ******************************************************/
-
-GList *
-gst_v4l2_get_output_names (GstV4l2Element *v4l2element)
+gst_v4l2_set_frequency (GstV4l2Object * v4l2object,
+    gint tunernum, gulong frequency)
 {
-       GList *names = NULL;
-       gint n;
+  struct v4l2_frequency freq = { 0, };
 
-       DEBUG("getting a list of output names");
+  GstTunerChannel *channel;
 
-       for (n=0;n<g_list_length(v4l2element->outputs);n++) {
-               struct v4l2_output *output = (struct v4l2_output *) g_list_nth_data(v4l2element->outputs, n);
-               names = g_list_append(names, g_strdup(output->name));
-       }
-
-       return names;
-}
+  GST_DEBUG_OBJECT (v4l2object->element,
+      "setting current tuner frequency to %lu", frequency);
 
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-/******************************************************
- * gst_v4l_has_tuner():
- *   Check whether the device has a tuner
- * return value: TRUE if it has a tuner, else FALSE
- ******************************************************/
-
-gint
-gst_v4l2_has_tuner (GstV4l2Element *v4l2element,
-                    gint           *tuner_num)
-{
-       gint input_num;
-       struct v4l2_input *input;
+  channel = gst_tuner_get_channel (GST_TUNER (v4l2object->element));
 
-       DEBUG("detecting whether device has a tuner");
-       GST_V4L2_CHECK_OPEN(v4l2element);
+  freq.tuner = tunernum;
+  /* fill in type - ignore error */
+  v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq);
+  freq.frequency = frequency / channel->freq_multiplicator;
 
-       if (!gst_v4l2_get_input(v4l2element, &input_num))
-               return FALSE;
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_FREQUENCY, &freq) < 0)
+    goto freq_failed;
 
-       input = (struct v4l2_input *) g_list_nth_data(v4l2element->inputs, input_num);
+  return TRUE;
 
-       if (input->type == V4L2_INPUT_TYPE_TUNER &&
-           v4l2element->vcap.capabilities & V4L2_CAP_TUNER) {
-               *tuner_num = input->tuner;
-               return TRUE;
-       }
-       return FALSE;
+  /* ERRORS */
+freq_failed:
+  {
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to set current tuner frequency for device '%s' to %lu Hz."),
+            v4l2object->videodev, frequency), GST_ERROR_SYSTEM);
+    return FALSE;
+  }
 }
 
-
 /******************************************************
- * gst_v4l_get_frequency():
- *   get the current frequency
+ * gst_v4l2_signal_strength():
+ *   get the strength of the signal on the current input
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_get_frequency (GstV4l2Element *v4l2element,
-                        gulong         *frequency)
+gst_v4l2_signal_strength (GstV4l2Object * v4l2object,
+    gint tunernum, gulong * signal_strength)
 {
-       struct v4l2_frequency freq;
+  struct v4l2_tuner tuner = { 0, };
 
-       DEBUG("getting current tuner frequency");
-       GST_V4L2_CHECK_OPEN(v4l2element);
+  GST_DEBUG_OBJECT (v4l2object->element, "trying to get signal strength");
 
-       if (!gst_v4l2_has_tuner(v4l2element, &freq.tuner))
-               return FALSE;
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       freq.type = 0;
+  tuner.index = tunernum;
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &tuner) < 0)
+    goto tuner_failed;
 
-       if (ioctl(v4l2element->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to get current tuner frequency for device %s: %s",
-                       v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
+  *signal_strength = tuner.signal;
 
-       *frequency = freq.frequency;
+  return TRUE;
 
-       return TRUE;
+  /* ERRORS */
+tuner_failed:
+  {
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to get signal strength for device '%s'."),
+            v4l2object->videodev), GST_ERROR_SYSTEM);
+    return FALSE;
+  }
 }
 
-
 /******************************************************
- * gst_v4l_set_frequency():
- *   set frequency
+ * gst_v4l2_get_attribute():
+ *   try to get the value of one specific attribute
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_set_frequency (GstV4l2Element *v4l2element,
-                        gulong          frequency)
+gst_v4l2_get_attribute (GstV4l2Object * v4l2object,
+    int attribute_num, int *value)
 {
-       struct v4l2_frequency freq;
+  struct v4l2_control control = { 0, };
 
-       DEBUG("setting current tuner frequency to %lu", frequency);
-       GST_V4L2_CHECK_OPEN(v4l2element);
-       GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+  GST_DEBUG_OBJECT (v4l2object->element, "getting value of attribute %d",
+      attribute_num);
 
-       if (!gst_v4l2_has_tuner(v4l2element, &freq.tuner))
-               return FALSE;
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       freq.frequency = frequency;
-       freq.type = 0;
+  control.id = attribute_num;
 
-       if (ioctl(v4l2element->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to set tuner frequency to %lu for device %s: %s",
-                       frequency, v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
+    goto ctrl_failed;
 
-       return TRUE;
+  *value = control.value;
+
+  return TRUE;
+
+  /* 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);
+    return FALSE;
+  }
 }
 
 
 /******************************************************
- * gst_v4l_signal_strength():
- *   get the strength of the signal on the current input
+ * gst_v4l2_set_attribute():
+ *   try to set the value of one specific attribute
  * return value: TRUE on success, FALSE on error
  ******************************************************/
-
 gboolean
-gst_v4l2_signal_strength (GstV4l2Element *v4l2element,
-                          gulong         *signal_strength)
+gst_v4l2_set_attribute (GstV4l2Object * v4l2object,
+    int attribute_num, const int value)
 {
-       struct v4l2_tuner tuner;
+  struct v4l2_control control = { 0, };
 
-       DEBUG("trying to get signal strength");
-       GST_V4L2_CHECK_OPEN(v4l2element);
+  GST_DEBUG_OBJECT (v4l2object->element, "setting value of attribute %d to %d",
+      attribute_num, value);
 
-       if (ioctl(v4l2element->video_fd, VIDIOC_G_TUNER, &tuner) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to get signal strength for device %s: %s",
-                       v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       *signal_strength = tuner.signal;
-
-       return TRUE;
-}
+  control.id = attribute_num;
+  control.value = value;
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0)
+    goto ctrl_failed;
 
+  return TRUE;
 
-/******************************************************
- * gst_v4l_has_audio():
- *   Check whether the device has audio capabilities
- * return value: TRUE if it has a tuner, else FALSE
- ******************************************************/
+  /* 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);
+    return FALSE;
+  }
+}
 
 gboolean
-gst_v4l2_has_audio (GstV4l2Element *v4l2element)
+gst_v4l2_get_input (GstV4l2Object * v4l2object, gint * input)
 {
-       gint input_num;
-       struct v4l2_input *input;
+  gint n;
 
-       DEBUG("detecting whether device has audio");
-       GST_V4L2_CHECK_OPEN(v4l2element);
+  GST_DEBUG_OBJECT (v4l2object->element, "trying to get input");
 
-       if (!gst_v4l2_get_input(v4l2element, &input_num))
-               return FALSE;
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       input = (struct v4l2_input *) g_list_nth_data(v4l2element->inputs, input_num);
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_INPUT, &n) < 0)
+    goto input_failed;
 
-       return (input->audioset != 0);
-}
+  *input = n;
 
+  GST_DEBUG_OBJECT (v4l2object->element, "input: %d", n);
 
-/******************************************************
- * gst_v4l_get_attributes():
- *   get a list of attributes available on this device
- * return value: the list
- ******************************************************/
+  return TRUE;
 
-GList *
-gst_v4l2_get_attributes        (GstV4l2Element *v4l2element)
-{
-       gint i;
-       GList *list = NULL;
-
-       DEBUG("getting a list of available attributes");
-
-       for (i=0;i<g_list_length(v4l2element->controls);i++) {
-               struct v4l2_queryctrl *control = (struct v4l2_queryctrl *) g_list_nth_data(v4l2element->controls, i);
-               GstV4l2Attribute* attribute = g_malloc(sizeof(GstV4l2Attribute));
-               attribute->name = g_strdup(control->name);
-               attribute->index = i;
-               attribute->list_items = NULL;
-               attribute->val_type = control->type;
-               if (control->type == V4L2_CTRL_TYPE_MENU) {
-                       /* list items */
-                       gint n;
-                       GList *menus = (GList *) g_list_nth_data(v4l2element->menus, i);
-                       for (n=0;n<g_list_length(menus);n++) {
-                               struct v4l2_querymenu *menu = g_list_nth_data(menus, n);
-                               attribute->list_items = g_list_append(attribute->list_items,
-                                                                       g_strdup(menu->name));
-                       }
-               }
-               switch (control->id) {
-                       case V4L2_CID_BRIGHTNESS:
-                       case V4L2_CID_CONTRAST:
-                       case V4L2_CID_SATURATION:
-                       case V4L2_CID_HUE:
-                       case V4L2_CID_BLACK_LEVEL:
-                       case V4L2_CID_AUTO_WHITE_BALANCE:
-                       case V4L2_CID_DO_WHITE_BALANCE:
-                       case V4L2_CID_RED_BALANCE:
-                       case V4L2_CID_BLUE_BALANCE:
-                       case V4L2_CID_GAMMA:
-                       case V4L2_CID_EXPOSURE:
-                       case V4L2_CID_AUTOGAIN:
-                       case V4L2_CID_GAIN:
-                       case V4L2_CID_HFLIP:
-                       case V4L2_CID_VFLIP:
-                       case V4L2_CID_HCENTER:
-                       case V4L2_CID_VCENTER:
-                               attribute->type = GST_V4L2_ATTRIBUTE_TYPE_VIDEO;
-                               break;
-                       case V4L2_CID_AUDIO_VOLUME:
-                       case V4L2_CID_AUDIO_BALANCE:
-                       case V4L2_CID_AUDIO_BASS:
-                       case V4L2_CID_AUDIO_TREBLE:
-                       case V4L2_CID_AUDIO_MUTE:
-                       case V4L2_CID_AUDIO_LOUDNESS:
-                               attribute->type = GST_V4L2_ATTRIBUTE_TYPE_AUDIO;
-                               break;
-                       default:
-                               attribute->type = GST_V4L2_ATTRIBUTE_TYPE_OTHER;
-                               break;
-               }
-               gst_v4l2_get_attribute(v4l2element, i, &attribute->value);
-               attribute->min = control->minimum;
-               attribute->max = control->maximum;
-       }
-
-       return list;
+  /* ERRORS */
+input_failed:
+  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+    /* only give a warning message if driver actually claims to have tuner
+     * support
+     */
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to get current input on device '%s'. May be it is a radio device"), v4l2object->videodev), GST_ERROR_SYSTEM);
+  }
+  return FALSE;
 }
 
-
-/******************************************************
- * gst_v4l_get_attribute():
- *   try to get the value of one specific attribute
- * return value: TRUE on success, FALSE on error
- ******************************************************/
-
 gboolean
-gst_v4l2_get_attribute (GstV4l2Element *v4l2element,
-                         gint            attribute_num,
-                         gint           *value)
+gst_v4l2_set_input (GstV4l2Object * v4l2object, gint input)
 {
-       struct v4l2_control control;
+  GST_DEBUG_OBJECT (v4l2object->element, "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)
+    goto input_failed;
+
+  return TRUE;
+
+  /* ERRORS */
+input_failed:
+  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+    /* only give a warning message if driver actually claims to have tuner
+     * support
+     */
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to set input %d on device %s."),
+            input, v4l2object->videodev), GST_ERROR_SYSTEM);
+  }
+  return FALSE;
+}
 
-       DEBUG("getting value of attribute %d", attribute_num);
-       GST_V4L2_CHECK_OPEN(v4l2element);
+gboolean
+gst_v4l2_get_output (GstV4l2Object * v4l2object, gint * output)
+{
+  gint n;
 
-       if (attribute_num < 0 || attribute_num >= g_list_length(v4l2element->controls)) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Invalid control ID %d", attribute_num);
-               return FALSE;
-       }
+  GST_DEBUG_OBJECT (v4l2object->element, "trying to get output");
 
-       control.id = attribute_num;
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
 
-       if (ioctl(v4l2element->video_fd, VIDIOC_G_CTRL, &control) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to get value for control %d on device %s: %s",
-                       attribute_num, v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_OUTPUT, &n) < 0)
+    goto output_failed;
 
-       *value = control.value;
+  *output = n;
 
-       return TRUE;
-}
+  GST_DEBUG_OBJECT (v4l2object->element, "output: %d", n);
 
+  return TRUE;
 
-/******************************************************
- * gst_v4l_set_attribute():
- *   try to set the value of one specific attribute
- * return value: TRUE on success, FALSE on error
- ******************************************************/
+  /* ERRORS */
+output_failed:
+  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+    /* only give a warning message if driver actually claims to have tuner
+     * support
+     */
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to get current output on device '%s'. May be it is a radio device"), v4l2object->videodev), GST_ERROR_SYSTEM);
+  }
+  return FALSE;
+}
 
 gboolean
-gst_v4l2_set_attribute (GstV4l2Element *v4l2element,
-                         gint            attribute_num,
-                         gint            value)
+gst_v4l2_set_output (GstV4l2Object * v4l2object, gint output)
 {
-       struct v4l2_control control;
-
-       DEBUG("setting value of attribute %d to %d", attribute_num, value);
-       GST_V4L2_CHECK_OPEN(v4l2element);
-
-       if (attribute_num < 0 || attribute_num >= g_list_length(v4l2element->controls)) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Invalid control ID %d", attribute_num);
-               return FALSE;
-       }
-
-       control.id = attribute_num;
-       control.value = value;
-
-       if (ioctl(v4l2element->video_fd, VIDIOC_S_CTRL, &control) < 0) {
-               gst_element_error(GST_ELEMENT(v4l2element),
-                       "Failed to set value %d for control %d on device %s: %s",
-                       value, attribute_num, v4l2element->device, g_strerror(errno));
-               return FALSE;
-       }
-
-       return TRUE;
+  GST_DEBUG_OBJECT (v4l2object->element, "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)
+    goto output_failed;
+
+  return TRUE;
+
+  /* ERRORS */
+output_failed:
+  if (v4l2object->vcap.capabilities & V4L2_CAP_TUNER) {
+    /* only give a warning message if driver actually claims to have tuner
+     * support
+     */
+    GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
+        (_("Failed to set output %d on device %s."),
+            output, v4l2object->videodev), GST_ERROR_SYSTEM);
+  }
+  return FALSE;
 }
-