Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / v4l2_calls.c
index 9618845..74b2e0a 100644 (file)
 #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"
 
 #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
@@ -51,14 +69,24 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
 gboolean
 gst_v4l2_get_capabilities (GstV4l2Object * v4l2object)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "getting capabilities");
+  GstElement *e;
+
+  e = v4l2object->element;
+
+  GST_DEBUG_OBJECT (e, "getting capabilities");
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &v4l2object->vcap) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCAP, &v4l2object->vcap) < 0)
     goto cap_failed;
 
+  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 */
@@ -83,35 +111,49 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
 {
   gint n;
 
-  GST_DEBUG_OBJECT (v4l2object->element, "getting enumerations");
+  GstElement *e;
+
+  e = v4l2object->element;
+
+  GST_DEBUG_OBJECT (e, "getting enumerations");
   GST_V4L2_CHECK_OPEN (v4l2object);
 
-  GST_DEBUG_OBJECT (v4l2object->element, "  channels");
+  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 (ioctl (v4l2object->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
+    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
       if (errno == EINVAL)
         break;                  /* end of enumeration */
       else {
-        GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
-            (_("Failed to get %d in channel enumeration for %s."),
-                n, v4l2object->videodev), GST_ERROR_SYSTEM);
+        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_DEBUG_OBJECT (v4l2object->element, "    '%s'", input.name);
+    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;
 
@@ -119,13 +161,14 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
       channel->flags |= GST_TUNER_CHANNEL_FREQUENCY;
 
       vtun.index = input.tuner;
-      if (ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &vtun) < 0) {
-        GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
-            (_("Failed to get tuner %d settings on %s."),
+      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;
@@ -142,32 +185,37 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
     }
 
     v4l2object->channels =
-        g_list_append (v4l2object->channels, (gpointer) channel);
+        g_list_prepend (v4l2object->channels, (gpointer) channel);
   }
+  v4l2object->channels = g_list_reverse (v4l2object->channels);
 
-  GST_DEBUG_OBJECT (v4l2object->element, "  norms");
+  GST_DEBUG_OBJECT (e, "  norms");
   /* norms... */
   for (n = 0;; n++) {
-    struct v4l2_standard standard;
+    struct v4l2_standard standard = { 0, };
     GstV4l2TunerNorm *v4l2norm;
+
     GstTunerNorm *norm;
 
     /* fill in defaults */
-    standard.frameperiod.denominator = 0;
     standard.frameperiod.numerator = 1;
+    standard.frameperiod.denominator = 0;
     standard.index = n;
-    if (ioctl (v4l2object->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
-      if (errno == EINVAL)
+
+    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
+      if (errno == EINVAL || errno == ENOTTY)
         break;                  /* end of enumeration */
       else {
-        GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
-            (_("Failed to get %d in norm enumeration for %s."),
-                n, v4l2object->videodev), GST_ERROR_SYSTEM);
+        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 (v4l2object->element, "    '%s', fps: %d/%d",
+    GST_DEBUG_OBJECT (e, "    '%s', fps: %d / %d",
         standard.name, standard.frameperiod.denominator,
         standard.frameperiod.numerator);
 
@@ -178,42 +226,52 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
         standard.frameperiod.denominator, standard.frameperiod.numerator);
     v4l2norm->index = standard.id;
 
-    v4l2object->norms = g_list_append (v4l2object->norms, (gpointer) norm);
+    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");
 
-  GST_DEBUG_OBJECT (v4l2object->element, "  controls+menus");
   /* and lastly, controls+menus (if appropriate) */
   for (n = V4L2_CID_BASE;; n++) {
-    struct v4l2_queryctrl control;
+    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 (v4l2object->element, "checking private CIDs");
+      GST_DEBUG_OBJECT (e, "checking private CIDs");
       n = V4L2_CID_PRIVATE_BASE;
-      /* FIXME: We are still not handling private controls. We need a new GstInterface
-         to export those controls */
-      break;
     }
+    GST_DEBUG_OBJECT (e, "checking control %08x", n);
 
     control.id = n;
-    if (ioctl (v4l2object->video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
+    if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
       if (errno == EINVAL) {
-        if (n < V4L2_CID_PRIVATE_BASE)
+        if (n < V4L2_CID_PRIVATE_BASE) {
+          GST_DEBUG_OBJECT (e, "skipping control %08x", n);
           /* continue so that we also check private controls */
           continue;
-        else
+        } else {
+          GST_DEBUG_OBJECT (e, "controls finished");
           break;
+        }
       } else {
-        GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
-            (_("Failed to get %d in control enumeration for %s."),
-                n, v4l2object->videodev), GST_ERROR_SYSTEM);
+        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)
+    if (control.flags & V4L2_CTRL_FLAG_DISABLED) {
+      GST_DEBUG_OBJECT (e, "skipping disabled control");
       continue;
+    }
 
     switch (n) {
       case V4L2_CID_BRIGHTNESS:
@@ -229,12 +287,21 @@ 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;
@@ -247,7 +314,7 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
         /* FIXME: We should implement GstMixer interface */
         /* fall through */
       default:
-        GST_DEBUG_OBJECT (v4l2object->element,
+        GST_DEBUG_OBJECT (e,
             "ControlID %s (%x) unhandled, FIXME", control.name, n);
         control.id++;
         break;
@@ -255,29 +322,32 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
     if (n != control.id)
       continue;
 
-    GST_DEBUG_OBJECT (v4l2object->element,
-        "Adding ControlID %s (%x)", control.name, n);
+    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 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 (ioctl (v4l2object->video_fd, VIDIOC_QUERYMENU, &menu) < 0) {
+        if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_QUERYMENU, &menu) < 0) {
           if (errno == EINVAL)
             break;              /* end of enumeration */
           else {
-            GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
-                (_("Failed to get %d in menu enumeration for %s"),
-                    n, v4l2object->videodev), GST_ERROR_SYSTEM);
+            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;
           }
         }
@@ -302,17 +372,19 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
         /* 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 (v4l2object->element,
+        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_append (v4l2object->colors, (gpointer) channel);
+    v4l2object->colors =
+        g_list_prepend (v4l2object->colors, (gpointer) channel);
   }
+  v4l2object->colors = g_list_reverse (v4l2object->colors);
 
-  GST_DEBUG_OBJECT (v4l2object->element, "done");
+  GST_DEBUG_OBJECT (e, "done");
   return TRUE;
 }
 
@@ -344,6 +416,8 @@ gboolean
 gst_v4l2_open (GstV4l2Object * v4l2object)
 {
   struct stat st;
+  int libv4l2_fd;
+  GstPollFD pollfd = GST_POLL_FD_INIT;
 
   GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s",
       v4l2object->videodev);
@@ -369,15 +443,32 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
   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) &&
+  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;
@@ -386,6 +477,10 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
       "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 */
@@ -406,14 +501,22 @@ no_device:
 not_open:
   {
     GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ_WRITE,
-        (_("Could not open device \"%s\" for reading and writing."),
+        (_("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."),
+        (_("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;
@@ -422,7 +525,7 @@ error:
   {
     if (GST_V4L2_IS_OPEN (v4l2object)) {
       /* close device */
-      close (v4l2object->video_fd);
+      v4l2_close (v4l2object->video_fd);
       v4l2object->video_fd = -1;
     }
     /* empty lists */
@@ -441,6 +544,7 @@ error:
 gboolean
 gst_v4l2_close (GstV4l2Object * v4l2object)
 {
+  GstPollFD pollfd = GST_POLL_FD_INIT;
   GST_DEBUG_OBJECT (v4l2object->element, "Trying to close %s",
       v4l2object->videodev);
 
@@ -448,7 +552,9 @@ gst_v4l2_close (GstV4l2Object * v4l2object)
   GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
 
   /* close device */
-  close (v4l2object->video_fd);
+  v4l2_close (v4l2object->video_fd);
+  pollfd.fd = v4l2object->video_fd;
+  gst_poll_remove_fd (v4l2object->poll, &pollfd);
   v4l2object->video_fd = -1;
 
   /* empty lists */
@@ -471,7 +577,7 @@ gst_v4l2_get_norm (GstV4l2Object * v4l2object, v4l2_std_id * norm)
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (ioctl (v4l2object->video_fd, VIDIOC_G_STD, norm) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_STD, norm) < 0)
     goto std_failed;
 
   return TRUE;
@@ -494,12 +600,13 @@ std_failed:
 gboolean
 gst_v4l2_set_norm (GstV4l2Object * v4l2object, v4l2_std_id norm)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "trying to set norm to %llx", norm);
+  GST_DEBUG_OBJECT (v4l2object->element, "trying to set norm to "
+      "%" G_GINT64_MODIFIER "x", (guint64) norm);
 
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (ioctl (v4l2object->video_fd, VIDIOC_S_STD, &norm) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_STD, &norm) < 0)
     goto std_failed;
 
   return TRUE;
@@ -508,8 +615,8 @@ gst_v4l2_set_norm (GstV4l2Object * v4l2object, v4l2_std_id norm)
 std_failed:
   {
     GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to set norm 0x%llx for device %s: %s."),
-            norm, v4l2object->videodev), GST_ERROR_SYSTEM);
+        (_("Failed to set norm for device '%s'."),
+            v4l2object->videodev), GST_ERROR_SYSTEM);
     return FALSE;
   }
 }
@@ -523,7 +630,8 @@ gboolean
 gst_v4l2_get_frequency (GstV4l2Object * v4l2object,
     gint tunernum, gulong * frequency)
 {
-  struct v4l2_frequency freq;
+  struct v4l2_frequency freq = { 0, };
+
   GstTunerChannel *channel;
 
   GST_DEBUG_OBJECT (v4l2object->element, "getting current tuner frequency");
@@ -534,7 +642,7 @@ gst_v4l2_get_frequency (GstV4l2Object * v4l2object,
   channel = gst_tuner_get_channel (GST_TUNER (v4l2object->element));
 
   freq.tuner = tunernum;
-  if (ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0)
     goto freq_failed;
 
   *frequency = freq.frequency * channel->freq_multiplicator;
@@ -545,7 +653,7 @@ gst_v4l2_get_frequency (GstV4l2Object * v4l2object,
 freq_failed:
   {
     GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to get current tuner frequency for device %s."),
+        (_("Failed to get current tuner frequency for device '%s'."),
             v4l2object->videodev), GST_ERROR_SYSTEM);
     return FALSE;
   }
@@ -561,7 +669,8 @@ gboolean
 gst_v4l2_set_frequency (GstV4l2Object * v4l2object,
     gint tunernum, gulong frequency)
 {
-  struct v4l2_frequency freq;
+  struct v4l2_frequency freq = { 0, };
+
   GstTunerChannel *channel;
 
   GST_DEBUG_OBJECT (v4l2object->element,
@@ -574,10 +683,10 @@ gst_v4l2_set_frequency (GstV4l2Object * v4l2object,
 
   freq.tuner = tunernum;
   /* fill in type - ignore error */
-  ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq);
+  v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_FREQUENCY, &freq);
   freq.frequency = frequency / channel->freq_multiplicator;
 
-  if (ioctl (v4l2object->video_fd, VIDIOC_S_FREQUENCY, &freq) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_FREQUENCY, &freq) < 0)
     goto freq_failed;
 
   return TRUE;
@@ -586,7 +695,7 @@ gst_v4l2_set_frequency (GstV4l2Object * v4l2object,
 freq_failed:
   {
     GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to set current tuner frequency for device %s to %lu."),
+        (_("Failed to set current tuner frequency for device '%s' to %lu Hz."),
             v4l2object->videodev, frequency), GST_ERROR_SYSTEM);
     return FALSE;
   }
@@ -601,7 +710,7 @@ gboolean
 gst_v4l2_signal_strength (GstV4l2Object * v4l2object,
     gint tunernum, gulong * signal_strength)
 {
-  struct v4l2_tuner tuner;
+  struct v4l2_tuner tuner = { 0, };
 
   GST_DEBUG_OBJECT (v4l2object->element, "trying to get signal strength");
 
@@ -609,7 +718,7 @@ gst_v4l2_signal_strength (GstV4l2Object * v4l2object,
     return FALSE;
 
   tuner.index = tunernum;
-  if (ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &tuner) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_TUNER, &tuner) < 0)
     goto tuner_failed;
 
   *signal_strength = tuner.signal;
@@ -620,7 +729,7 @@ gst_v4l2_signal_strength (GstV4l2Object * v4l2object,
 tuner_failed:
   {
     GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to get signal strength for device %s."),
+        (_("Failed to get signal strength for device '%s'."),
             v4l2object->videodev), GST_ERROR_SYSTEM);
     return FALSE;
   }
@@ -635,7 +744,7 @@ gboolean
 gst_v4l2_get_attribute (GstV4l2Object * v4l2object,
     int attribute_num, int *value)
 {
-  struct v4l2_control control;
+  struct v4l2_control control = { 0, };
 
   GST_DEBUG_OBJECT (v4l2object->element, "getting value of attribute %d",
       attribute_num);
@@ -645,7 +754,7 @@ gst_v4l2_get_attribute (GstV4l2Object * v4l2object,
 
   control.id = attribute_num;
 
-  if (ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
     goto ctrl_failed;
 
   *value = control.value;
@@ -656,7 +765,7 @@ gst_v4l2_get_attribute (GstV4l2Object * v4l2object,
 ctrl_failed:
   {
     GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to get value for control %d on device %s."),
+        (_("Failed to get value for control %d on device '%s'."),
             attribute_num, v4l2object->videodev), GST_ERROR_SYSTEM);
     return FALSE;
   }
@@ -672,7 +781,7 @@ gboolean
 gst_v4l2_set_attribute (GstV4l2Object * v4l2object,
     int attribute_num, const int value)
 {
-  struct v4l2_control control;
+  struct v4l2_control control = { 0, };
 
   GST_DEBUG_OBJECT (v4l2object->element, "setting value of attribute %d to %d",
       attribute_num, value);
@@ -682,7 +791,7 @@ gst_v4l2_set_attribute (GstV4l2Object * v4l2object,
 
   control.id = attribute_num;
   control.value = value;
-  if (ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0)
     goto ctrl_failed;
 
   return TRUE;
@@ -691,7 +800,7 @@ gst_v4l2_set_attribute (GstV4l2Object * v4l2object,
 ctrl_failed:
   {
     GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS,
-        (_("Failed to set value %d for control %d on device %s."),
+        (_("Failed to set value %d for control %d on device '%s'."),
             value, attribute_num, v4l2object->videodev), GST_ERROR_SYSTEM);
     return FALSE;
   }
@@ -707,21 +816,25 @@ gst_v4l2_get_input (GstV4l2Object * v4l2object, gint * input)
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (ioctl (v4l2object->video_fd, VIDIOC_G_INPUT, &n) < 0)
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_INPUT, &n) < 0)
     goto input_failed;
 
   *input = n;
 
+  GST_DEBUG_OBJECT (v4l2object->element, "input: %d", n);
+
   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 get current input on device %s."),
-            v4l2object->videodev), GST_ERROR_SYSTEM);
-    return FALSE;
+        (_("Failed to get current input on device '%s'. May be it is a radio device"), v4l2object->videodev), GST_ERROR_SYSTEM);
   }
+  return FALSE;
 }
 
 gboolean
@@ -732,17 +845,77 @@ gst_v4l2_set_input (GstV4l2Object * v4l2object, gint input)
   if (!GST_V4L2_IS_OPEN (v4l2object))
     return FALSE;
 
-  if (ioctl (v4l2object->video_fd, VIDIOC_S_INPUT, &input) < 0)
+  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;
+}
+
+gboolean
+gst_v4l2_get_output (GstV4l2Object * v4l2object, gint * output)
+{
+  gint n;
+
+  GST_DEBUG_OBJECT (v4l2object->element, "trying to get output");
+
+  if (!GST_V4L2_IS_OPEN (v4l2object))
+    return FALSE;
+
+  if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_G_OUTPUT, &n) < 0)
+    goto output_failed;
+
+  *output = n;
+
+  GST_DEBUG_OBJECT (v4l2object->element, "output: %d", n);
+
+  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 get current output on device '%s'. May be it is a radio device"), v4l2object->videodev), GST_ERROR_SYSTEM);
+  }
+  return FALSE;
+}
+
+gboolean
+gst_v4l2_set_output (GstV4l2Object * v4l2object, gint output)
+{
+  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;
 }