v4l2: Use single pass iterator for M2M probe
authorNicolas Dufresne <nicolas.dufresne@collabora.co.uk>
Sat, 3 May 2014 01:38:30 +0000 (21:38 -0400)
committerNicolas Dufresne <nicolas.dufresne@collabora.co.uk>
Sun, 4 May 2014 03:11:35 +0000 (23:11 -0400)
Instead of having each M2M class do their own probing, use the
GstV4l2Iterator and probe all devices in a single pass.

https://bugzilla.gnome.org/show_bug.cgi?id=727925

sys/v4l2/gstv4l2.c
sys/v4l2/gstv4l2object.c
sys/v4l2/gstv4l2videodec.c
sys/v4l2/gstv4l2videodec.h

index 7fc07c0..9ded33d 100644 (file)
 
 #include <gst/gst.h>
 
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "ext/videodev2.h"
+#include "v4l2-utils.h"
+
 #include "gstv4l2object.h"
 #include "gstv4l2src.h"
 #include "gstv4l2sink.h"
 
 /* used in v4l2_calls.c and v4l2src_calls.c */
 GST_DEBUG_CATEGORY (v4l2_debug);
+#define GST_CAT_DEFAULT v4l2_debug
+
+/* This is a minimalist probe, for speed, we only enumerate formats */
+static GstCaps *
+gst_v4l2_probe_template_caps (const gchar * device, gint video_fd,
+    enum v4l2_buf_type type)
+{
+  gint n;
+  struct v4l2_fmtdesc format;
+  GstCaps *caps;
+
+  GST_DEBUG ("Getting %s format enumerations", device);
+  caps = gst_caps_new_empty ();
+
+  for (n = 0;; n++) {
+    GstStructure *template;
+
+    memset (&format, 0, sizeof (format));
+
+    format.index = n;
+    format.type = type;
+
+    if (ioctl (video_fd, VIDIOC_ENUM_FMT, &format) < 0)
+      break;                    /* end of enumeration */
+
+    GST_LOG ("index:       %u", format.index);
+    GST_LOG ("type:        %d", format.type);
+    GST_LOG ("flags:       %08x", format.flags);
+    GST_LOG ("description: '%s'", format.description);
+    GST_LOG ("pixelformat: %" GST_FOURCC_FORMAT,
+        GST_FOURCC_ARGS (format.pixelformat));
+
+    template = gst_v4l2_object_v4l2fourcc_to_structure (format.pixelformat);
+
+    if (template)
+      gst_caps_append_structure (caps, template);
+  }
+
+  return gst_caps_simplify (caps);
+}
+
+static gboolean
+gst_v4l2_probe_and_register (GstPlugin * plugin)
+{
+  GstV4l2Iterator *it;
+  gint video_fd = -1;
+  struct v4l2_capability vcap;
+  gboolean ret = TRUE;
+
+  it = gst_v4l2_iterator_new ();
+
+  while (gst_v4l2_iterator_next (it)) {
+    GstCaps *src_caps, *sink_caps;
+    gchar *basename;
+
+    if (video_fd > 0)
+      close (video_fd);
+
+    video_fd = open (it->device_path, O_RDWR);
+    if (video_fd == -1) {
+      GST_DEBUG ("Failed to open %s: %s", it->device_path, g_strerror (errno));
+      continue;
+    }
+
+    memset (&vcap, 0, sizeof (vcap));
+
+    if (ioctl (video_fd, VIDIOC_QUERYCAP, &vcap) < 0) {
+      GST_DEBUG ("Failed to get device capabilities: %s", g_strerror (errno));
+      continue;
+    }
+
+    if (!((vcap.capabilities & (V4L2_CAP_VIDEO_M2M |
+                    V4L2_CAP_VIDEO_M2M_MPLANE)) ||
+            /* But legacy driver may expose both CAPTURE and OUTPUT */
+            ((vcap.capabilities &
+                    (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) &&
+                (vcap.capabilities &
+                    (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE)))))
+      continue;
+
+    GST_DEBUG ("Probing '%s' located at '%s'",
+        it->device_name ? it->device_name : (const gchar *) vcap.driver,
+        it->device_path);
+
+    /* get sink supported format (no MPLANE for codec) */
+    sink_caps = gst_caps_merge (gst_v4l2_probe_template_caps (it->device_path,
+            video_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT),
+        gst_v4l2_probe_template_caps (it->device_path, video_fd,
+            V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE));
+
+    /* get src supported format */
+    src_caps = gst_caps_merge (gst_v4l2_probe_template_caps (it->device_path,
+            video_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE),
+        gst_v4l2_probe_template_caps (it->device_path, video_fd,
+            V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE));
+
+    basename = g_path_get_basename (it->device_path);
+
+    if (gst_v4l2_is_video_dec (sink_caps, src_caps))
+      ret = gst_v4l2_video_dec_register (plugin, basename, it->device_path,
+          sink_caps, src_caps);
+    /* else if ( ... etc. */
+
+    gst_caps_unref (sink_caps);
+    gst_caps_unref (src_caps);
+    g_free (basename);
+
+    if (!ret)
+      break;
+  }
+
+  if (video_fd > 0)
+    close (video_fd);
+
+  gst_v4l2_iterator_free (it);
+
+  return ret;
+}
 
 static gboolean
 plugin_init (GstPlugin * plugin)
@@ -50,11 +177,10 @@ plugin_init (GstPlugin * plugin)
           GST_TYPE_V4L2SINK) ||
       !gst_element_register (plugin, "v4l2radio", GST_RANK_NONE,
           GST_TYPE_V4L2RADIO) ||
-      !gst_v4l2_video_dec_register (plugin) ||
       !gst_device_monitor_register (plugin, "v4l2monitor",
           GST_RANK_PRIMARY, GST_TYPE_V4L2_DEVICE_MONITOR) ||
       /* etc. */
-      FALSE)
+      !gst_v4l2_probe_and_register (plugin))
     return FALSE;
 
 #ifdef ENABLE_NLS
index 4313c34..d07a37a 100644 (file)
@@ -1150,8 +1150,8 @@ gst_v4l2_object_v4l2fourcc_to_video_format (guint32 fourcc)
   return format;
 }
 
-GstStructure *
-gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc)
+static GstStructure *
+gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc)
 {
   GstStructure *structure = NULL;
 
@@ -1258,6 +1258,34 @@ gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc)
   return structure;
 }
 
+GstStructure *
+gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc)
+{
+  GstStructure *template;
+  gint i;
+
+  template = gst_v4l2_object_v4l2fourcc_to_bare_struct (fourcc);
+
+  if (template == NULL)
+    goto done;
+
+  for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) {
+    if (gst_v4l2_formats[i].format != fourcc)
+      continue;
+
+    if (gst_v4l2_formats[i].dimensions) {
+      gst_structure_set (template,
+          "width", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
+          "height", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
+          "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 100, 1, NULL);
+    }
+    break;
+  }
+
+done:
+  return template;
+}
+
 
 static GstCaps *
 gst_v4l2_object_get_caps_helper (GstV4L2FormatFlags flags)
@@ -1273,7 +1301,7 @@ gst_v4l2_object_get_caps_helper (GstV4L2FormatFlags flags)
       continue;
 
     structure =
-        gst_v4l2_object_v4l2fourcc_to_structure (gst_v4l2_formats[i].format);
+        gst_v4l2_object_v4l2fourcc_to_bare_struct (gst_v4l2_formats[i].format);
     if (structure) {
       if (gst_v4l2_formats[i].dimensions) {
         gst_structure_set (structure,
@@ -2873,7 +2901,8 @@ gst_v4l2_object_get_caps (GstV4l2Object * v4l2object, GstCaps * filter)
 
       format = (struct v4l2_fmtdesc *) walk->data;
 
-      template = gst_v4l2_object_v4l2fourcc_to_structure (format->pixelformat);
+      template =
+          gst_v4l2_object_v4l2fourcc_to_bare_struct (format->pixelformat);
 
       if (template) {
         GstCaps *tmp;
index 2594e8c..d18b4b0 100644 (file)
@@ -720,6 +720,9 @@ gst_v4l2_video_dec_class_init (GstV4l2VideoDecClass * klass)
   gobject_class = (GObjectClass *) klass;
   video_decoder_class = (GstVideoDecoderClass *) klass;
 
+  GST_DEBUG_CATEGORY_INIT (gst_v4l2_video_dec_debug, "v4l2videodec", 0,
+      "V4L2 Video Decoder");
+
   gst_element_class_set_static_metadata (element_class,
       "V4L2 Video Decoder",
       "Codec/Decoder/Video",
@@ -794,121 +797,48 @@ gst_v4l2_video_dec_subclass_init (gpointer g_class, gpointer data)
 }
 
 /* Probing functions */
-static GstCaps *
-gst_v4l2_video_dec_probe_caps (gchar * device, gint video_fd,
-    enum v4l2_buf_type type, GstCaps * filter)
+gboolean
+gst_v4l2_is_video_dec (GstCaps * sink_caps, GstCaps * src_caps)
 {
-  gint n;
-  struct v4l2_fmtdesc format;
-  GstCaps *ret, *caps;
-
-  GST_DEBUG ("Getting %s format enumerations", device);
-  caps = gst_caps_new_empty ();
-
-  for (n = 0;; n++) {
-    GstStructure *template;
-
-    memset (&format, 0, sizeof (format));
-
-    format.index = n;
-    format.type = type;
-
-    if (v4l2_ioctl (video_fd, VIDIOC_ENUM_FMT, &format) < 0)
-      break;                    /* end of enumeration */
-
-    GST_LOG ("index:       %u", format.index);
-    GST_LOG ("type:        %d", format.type);
-    GST_LOG ("flags:       %08x", format.flags);
-    GST_LOG ("description: '%s'", format.description);
-    GST_LOG ("pixelformat: %" GST_FOURCC_FORMAT,
-        GST_FOURCC_ARGS (format.pixelformat));
-
-    template = gst_v4l2_object_v4l2fourcc_to_structure (format.pixelformat);
-
-    if (template)
-      gst_caps_append_structure (caps, template);
-  }
-
-  caps = gst_caps_simplify (caps);
+  gboolean ret = FALSE;
 
-  ret = gst_caps_intersect (filter, caps);
-  gst_caps_unref (filter);
-  gst_caps_unref (caps);
+  if (gst_caps_is_subset (sink_caps, gst_v4l2_object_get_codec_caps ())
+      && gst_caps_is_subset (src_caps, gst_v4l2_object_get_raw_caps ()))
+    ret = TRUE;
 
   return ret;
 }
 
 gboolean
-gst_v4l2_video_dec_register (GstPlugin * plugin)
+gst_v4l2_video_dec_register (GstPlugin * plugin, const gchar * basename,
+    const gchar * device_path, GstCaps * sink_caps, GstCaps * src_caps)
 {
-  gint i = -1;
-  gchar *device = NULL;
-
-  GST_DEBUG_CATEGORY_INIT (gst_v4l2_video_dec_debug, "v4l2videodec", 0,
-      "V4L2 Video Decoder");
-
-  while (TRUE) {
-    GstCaps *src_caps, *sink_caps;
-    gint video_fd;
-
-    g_free (device);
-    device = g_strdup_printf ("/dev/video%d", ++i);
-
-    if (!g_file_test (device, G_FILE_TEST_EXISTS))
-      break;
-
-    video_fd = open (device, O_RDWR);
-    if (video_fd == -1) {
-      GST_WARNING ("Failed to open %s", device);
-      continue;
-    }
-
-    /* get sink supported format (no MPLANE for codec) */
-    sink_caps = gst_v4l2_video_dec_probe_caps (device, video_fd,
-        V4L2_BUF_TYPE_VIDEO_OUTPUT, gst_v4l2_object_get_codec_caps ());
-
-    /* get src supported format */
-    src_caps = gst_caps_merge (gst_v4l2_video_dec_probe_caps (device, video_fd,
-            V4L2_BUF_TYPE_VIDEO_CAPTURE, gst_v4l2_object_get_raw_caps ()),
-        gst_v4l2_video_dec_probe_caps (device, video_fd,
-            V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
-            gst_v4l2_object_get_raw_caps ()));
-
-    if (!gst_caps_is_empty (sink_caps) && !gst_caps_is_empty (src_caps)) {
-      GTypeQuery type_query;
-      GTypeInfo type_info = { 0, };
-      GType type, subtype;
-      gchar *type_name;
-      GstV4l2VideoDecCData *cdata;
-
-      cdata = g_new0 (GstV4l2VideoDecCData, 1);
-      cdata->device = g_strdup (device);
-      cdata->sink_caps = gst_caps_ref (sink_caps);
-      cdata->src_caps = gst_caps_ref (src_caps);
-
-      type = gst_v4l2_video_dec_get_type ();
-      g_type_query (type, &type_query);
-      memset (&type_info, 0, sizeof (type_info));
-      type_info.class_size = type_query.class_size;
-      type_info.instance_size = type_query.instance_size;
-      type_info.class_init = gst_v4l2_video_dec_subclass_init;
-      type_info.class_data = cdata;
-      type_info.instance_init = gst_v4l2_video_dec_subinstance_init;
-
-      type_name = g_strdup_printf ("v4l2video%ddec", i);
-      subtype = g_type_register_static (type, type_name, &type_info, 0);
-
-      gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, subtype);
-
-      g_free (type_name);
-    }
-
-    close (video_fd);
-    gst_caps_unref (src_caps);
-    gst_caps_unref (sink_caps);
-  }
-
-  g_free (device);
+  GTypeQuery type_query;
+  GTypeInfo type_info = { 0, };
+  GType type, subtype;
+  gchar *type_name;
+  GstV4l2VideoDecCData *cdata;
+
+  cdata = g_new0 (GstV4l2VideoDecCData, 1);
+  cdata->device = g_strdup (device_path);
+  cdata->sink_caps = gst_caps_ref (sink_caps);
+  cdata->src_caps = gst_caps_ref (src_caps);
+
+  type = gst_v4l2_video_dec_get_type ();
+  g_type_query (type, &type_query);
+  memset (&type_info, 0, sizeof (type_info));
+  type_info.class_size = type_query.class_size;
+  type_info.instance_size = type_query.instance_size;
+  type_info.class_init = gst_v4l2_video_dec_subclass_init;
+  type_info.class_data = cdata;
+  type_info.instance_init = gst_v4l2_video_dec_subinstance_init;
+
+  type_name = g_strdup_printf ("v4l2%sdec", basename);
+  subtype = g_type_register_static (type, type_name, &type_info, 0);
+
+  gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, subtype);
+
+  g_free (type_name);
 
   return TRUE;
 }
index 48750a0..98aaa40 100644 (file)
@@ -78,7 +78,11 @@ struct _GstV4l2VideoDecClass
 
 GType gst_v4l2_video_dec_get_type (void);
 
-gboolean gst_v4l2_video_dec_register (GstPlugin * plugin);
+gboolean gst_v4l2_is_video_dec       (GstCaps * sink_caps, GstCaps * src_caps);
+gboolean gst_v4l2_video_dec_register (GstPlugin * plugin,
+                                      const gchar *basename,
+                                      const gchar *device_path,
+                                      GstCaps * sink_caps, GstCaps * src_caps);
 
 G_END_DECLS