From aa74871080d93701523e8bc183b2a8432e9b61c5 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Fri, 2 May 2014 21:38:30 -0400 Subject: [PATCH] v4l2: Use single pass iterator for M2M probe 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 | 130 ++++++++++++++++++++++++++++++++++- sys/v4l2/gstv4l2object.c | 37 ++++++++-- sys/v4l2/gstv4l2videodec.c | 134 +++++++++---------------------------- sys/v4l2/gstv4l2videodec.h | 6 +- 4 files changed, 198 insertions(+), 109 deletions(-) diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c index 7fc07c0434..9ded33d919 100644 --- a/sys/v4l2/gstv4l2.c +++ b/sys/v4l2/gstv4l2.c @@ -29,6 +29,15 @@ #include +#include +#include +#include +#include +#include + +#include "ext/videodev2.h" +#include "v4l2-utils.h" + #include "gstv4l2object.h" #include "gstv4l2src.h" #include "gstv4l2sink.h" @@ -38,6 +47,124 @@ /* 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 diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index 4313c3468d..d07a37aa95 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -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; diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c index 2594e8cd36..d18b4b0b13 100644 --- a/sys/v4l2/gstv4l2videodec.c +++ b/sys/v4l2/gstv4l2videodec.c @@ -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; + gboolean ret = FALSE; - 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); - - 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; + GTypeQuery type_query; + GTypeInfo type_info = { 0, }; + GType type, subtype; + gchar *type_name; + GstV4l2VideoDecCData *cdata; - GST_DEBUG_CATEGORY_INIT (gst_v4l2_video_dec_debug, "v4l2videodec", 0, - "V4L2 Video Decoder"); + 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); - while (TRUE) { - GstCaps *src_caps, *sink_caps; - gint video_fd; + 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; - g_free (device); - device = g_strdup_printf ("/dev/video%d", ++i); + type_name = g_strdup_printf ("v4l2%sdec", basename); + subtype = g_type_register_static (type, type_name, &type_info, 0); - if (!g_file_test (device, G_FILE_TEST_EXISTS)) - break; + gst_element_register (plugin, type_name, GST_RANK_PRIMARY + 1, subtype); - 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); + g_free (type_name); return TRUE; } diff --git a/sys/v4l2/gstv4l2videodec.h b/sys/v4l2/gstv4l2videodec.h index 48750a0c7d..98aaa40446 100644 --- a/sys/v4l2/gstv4l2videodec.h +++ b/sys/v4l2/gstv4l2videodec.h @@ -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