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
This commit is contained in:
Nicolas Dufresne 2014-05-02 21:38:30 -04:00
parent 726fc3b524
commit aa74871080
4 changed files with 198 additions and 109 deletions

View file

@ -29,6 +29,15 @@
#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"
@ -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

View 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;

View 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;
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;
}

View 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