mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 18:05:37 +00:00
v4l2: Clean up interlace support
Rather than try and guess interlace support as part of checking supported sizes, look for interlace support specifically in its own function. As a cleanup, use V4L2_FIELD_ANY when probing sizes, which should result in the driver doing the right thing. With my capture setup, this gets me the following sample caps: For 1080i resolution: video/x-raw, format=(string)YUY2, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)interleaved, framerate=(fraction){ 25/1, 30/1 } For 720p resolution: video/x-raw, format=(string)YUY2, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 50/1, 60/1 } For 576i/p resolution (both possible at the point of query): video/x-raw, format=(string)YUY2, width=(int)720, height=(int)576, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string){ progressive, interleaved }, framerate=(fraction){ 25/1, 50/1 } This, in turn, makes 576i work correctly; with the old code, the caps would be interlace-mode=progressive for interlaced video. https://bugzilla.gnome.org/show_bug.cgi?id=726194
This commit is contained in:
parent
130873c8fd
commit
02040d507c
3 changed files with 151 additions and 106 deletions
|
@ -1086,15 +1086,25 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer)
|
|||
#endif
|
||||
|
||||
/* set top/bottom field first if v4l2_buffer has the information */
|
||||
if (group->buffer.field == V4L2_FIELD_INTERLACED_TB) {
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
} else if (group->buffer.field == V4L2_FIELD_INTERLACED_BT) {
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
} else {
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
switch (group->buffer.field) {
|
||||
case V4L2_FIELD_NONE:
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
break;
|
||||
case V4L2_FIELD_INTERLACED_TB:
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
break;
|
||||
case V4L2_FIELD_INTERLACED_BT:
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
break;
|
||||
default:
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
|
||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
GST_FIXME_OBJECT (pool,
|
||||
"Unhandled enum v4l2_field %d - treating as progressive",
|
||||
group->buffer.field);
|
||||
}
|
||||
|
||||
if (GST_VIDEO_INFO_FORMAT (&obj->info) == GST_VIDEO_FORMAT_ENCODED) {
|
||||
|
|
|
@ -1562,7 +1562,7 @@ unsupported_format:
|
|||
|
||||
static gboolean
|
||||
gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
|
||||
guint32 pixelformat, gint * width, gint * height, gboolean * interlaced);
|
||||
guint32 pixelformat, gint * width, gint * height);
|
||||
|
||||
static void
|
||||
gst_v4l2_object_add_aspect_ratio (GstV4l2Object * v4l2object, GstStructure * s)
|
||||
|
@ -1625,6 +1625,121 @@ gst_v4l2src_value_simplify (GValue * val)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2_object_get_interlace_mode (enum v4l2_field field,
|
||||
GstVideoInterlaceMode * interlace_mode)
|
||||
{
|
||||
/* NB: If you add new return values, please fix mode_strings in
|
||||
* gst_v4l2_object_add_interlace_mode */
|
||||
switch (field) {
|
||||
case V4L2_FIELD_ANY:
|
||||
GST_ERROR
|
||||
("Driver bug detected - check driver with v4l2-compliance from http://git.linuxtv.org/v4l-utils.git\n");
|
||||
/* fallthrough */
|
||||
case V4L2_FIELD_NONE:
|
||||
*interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
|
||||
return TRUE;
|
||||
case V4L2_FIELD_INTERLACED:
|
||||
case V4L2_FIELD_INTERLACED_TB:
|
||||
case V4L2_FIELD_INTERLACED_BT:
|
||||
*interlace_mode = GST_VIDEO_INTERLACE_MODE_INTERLEAVED;
|
||||
return TRUE;
|
||||
default:
|
||||
GST_ERROR ("Unknown enum v4l2_field %d", field);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
gst_v4l2_object_try_fmt (GstV4l2Object * v4l2object,
|
||||
struct v4l2_format *try_fmt)
|
||||
{
|
||||
int fd = v4l2object->video_fd;
|
||||
struct v4l2_format fmt;
|
||||
int r;
|
||||
|
||||
memcpy (&fmt, try_fmt, sizeof (fmt));
|
||||
r = v4l2_ioctl (fd, VIDIOC_TRY_FMT, &fmt);
|
||||
|
||||
if (r < 0 && errno == ENOTTY) {
|
||||
/* The driver might not implement TRY_FMT, in which case we will try
|
||||
S_FMT to probe */
|
||||
if (GST_V4L2_IS_ACTIVE (v4l2object))
|
||||
goto error;
|
||||
|
||||
memcpy (&fmt, try_fmt, sizeof (fmt));
|
||||
r = v4l2_ioctl (fd, VIDIOC_S_FMT, &fmt);
|
||||
}
|
||||
memcpy (try_fmt, &fmt, sizeof (fmt));
|
||||
return r;
|
||||
|
||||
error:
|
||||
memcpy (try_fmt, &fmt, sizeof (fmt));
|
||||
GST_WARNING_OBJECT (v4l2object->element,
|
||||
"Unable to try format: %s", g_strerror (errno));
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_v4l2_object_add_interlace_mode (GstV4l2Object * v4l2object,
|
||||
GstStructure * s, guint32 width, guint32 height, guint32 pixelformat)
|
||||
{
|
||||
struct v4l2_format fmt;
|
||||
GValue interlace_formats = { 0, };
|
||||
GstVideoInterlaceMode interlace_mode;
|
||||
|
||||
const gchar *mode_strings[] = { "progressive",
|
||||
"interleaved",
|
||||
"mixed"
|
||||
};
|
||||
|
||||
if (!g_str_equal (gst_structure_get_name (s), "video/x-raw"))
|
||||
return;
|
||||
|
||||
if (v4l2object->never_interlaced) {
|
||||
gst_structure_set (s, "interlace-mode", G_TYPE_STRING, "progressive", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
g_value_init (&interlace_formats, GST_TYPE_LIST);
|
||||
|
||||
/* Try twice - once for NONE, once for INTERLACED. */
|
||||
memset (&fmt, 0, sizeof (fmt));
|
||||
fmt.type = v4l2object->type;
|
||||
fmt.fmt.pix.width = width;
|
||||
fmt.fmt.pix.height = height;
|
||||
fmt.fmt.pix.pixelformat = pixelformat;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_NONE;
|
||||
|
||||
if (gst_v4l2_object_try_fmt (v4l2object, &fmt) == 0 &&
|
||||
gst_v4l2_object_get_interlace_mode (fmt.fmt.pix.field, &interlace_mode)) {
|
||||
GValue interlace_enum = { 0, };
|
||||
g_value_init (&interlace_enum, G_TYPE_STRING);
|
||||
g_value_set_string (&interlace_enum, mode_strings[interlace_mode]);
|
||||
gst_value_list_append_value (&interlace_formats, &interlace_enum);
|
||||
}
|
||||
|
||||
memset (&fmt, 0, sizeof (fmt));
|
||||
fmt.type = v4l2object->type;
|
||||
fmt.fmt.pix.width = width;
|
||||
fmt.fmt.pix.height = height;
|
||||
fmt.fmt.pix.pixelformat = pixelformat;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
|
||||
|
||||
if (gst_v4l2_object_try_fmt (v4l2object, &fmt) == 0 &&
|
||||
gst_v4l2_object_get_interlace_mode (fmt.fmt.pix.field, &interlace_mode)) {
|
||||
GValue interlace_enum = { 0, };
|
||||
g_value_init (&interlace_enum, G_TYPE_STRING);
|
||||
g_value_set_string (&interlace_enum, mode_strings[interlace_mode]);
|
||||
gst_value_list_append_value (&interlace_formats, &interlace_enum);
|
||||
}
|
||||
|
||||
gst_v4l2src_value_simplify (&interlace_formats);
|
||||
gst_structure_set_value (s, "interlace-mode", &interlace_formats);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The frame interval enumeration code first appeared in Linux 2.6.19. */
|
||||
static GstStructure *
|
||||
gst_v4l2_object_probe_caps_for_format_and_size (GstV4l2Object * v4l2object,
|
||||
|
@ -1636,18 +1751,6 @@ gst_v4l2_object_probe_caps_for_format_and_size (GstV4l2Object * v4l2object,
|
|||
guint32 num, denom;
|
||||
GstStructure *s;
|
||||
GValue rates = { 0, };
|
||||
gboolean interlaced;
|
||||
gint int_width = width;
|
||||
gint int_height = height;
|
||||
|
||||
if (v4l2object->never_interlaced) {
|
||||
interlaced = FALSE;
|
||||
} else {
|
||||
/* Interlaced detection using VIDIOC_TRY/S_FMT */
|
||||
if (!gst_v4l2_object_get_nearest_size (v4l2object, pixelformat,
|
||||
&int_width, &int_height, &interlaced))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset (&ival, 0, sizeof (struct v4l2_frmivalenum));
|
||||
ival.index = 0;
|
||||
|
@ -1807,9 +1910,8 @@ return_data:
|
|||
gst_structure_set (s, "width", G_TYPE_INT, (gint) width,
|
||||
"height", G_TYPE_INT, (gint) height, NULL);
|
||||
gst_v4l2_object_add_aspect_ratio (v4l2object, s);
|
||||
if (g_str_equal (gst_structure_get_name (s), "video/x-raw"))
|
||||
gst_structure_set (s, "interlace-mode", G_TYPE_STRING,
|
||||
(interlaced ? "mixed" : "progressive"), NULL);
|
||||
gst_v4l2_object_add_interlace_mode (v4l2object, s, width, height,
|
||||
pixelformat);
|
||||
|
||||
if (G_IS_VALUE (&rates)) {
|
||||
gst_v4l2src_value_simplify (&rates);
|
||||
|
@ -2072,19 +2174,18 @@ unknown_type:
|
|||
default_frame_sizes:
|
||||
{
|
||||
gint min_w, max_w, min_h, max_h, fix_num = 0, fix_denom = 0;
|
||||
gboolean interlaced;
|
||||
|
||||
/* This code is for Linux < 2.6.19 */
|
||||
min_w = min_h = 1;
|
||||
max_w = max_h = GST_V4L2_MAX_SIZE;
|
||||
if (!gst_v4l2_object_get_nearest_size (v4l2object, pixelformat, &min_w,
|
||||
&min_h, &interlaced)) {
|
||||
&min_h)) {
|
||||
GST_WARNING_OBJECT (v4l2object->element,
|
||||
"Could not probe minimum capture size for pixelformat %"
|
||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (pixelformat));
|
||||
}
|
||||
if (!gst_v4l2_object_get_nearest_size (v4l2object, pixelformat, &max_w,
|
||||
&max_h, &interlaced)) {
|
||||
&max_h)) {
|
||||
GST_WARNING_OBJECT (v4l2object->element,
|
||||
"Could not probe maximum capture size for pixelformat %"
|
||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (pixelformat));
|
||||
|
@ -2130,9 +2231,9 @@ default_frame_sizes:
|
|||
else
|
||||
gst_structure_set (tmp, "height", GST_TYPE_INT_RANGE, min_h, max_h, NULL);
|
||||
|
||||
if (g_str_equal (gst_structure_get_name (tmp), "video/x-raw"))
|
||||
gst_structure_set (tmp, "interlace-mode", G_TYPE_STRING,
|
||||
(interlaced ? "mixed" : "progressive"), NULL);
|
||||
/* We could consider setting interlace mode from min and max. */
|
||||
gst_v4l2_object_add_interlace_mode (v4l2object, tmp, max_w, max_h,
|
||||
pixelformat);
|
||||
gst_v4l2_object_add_aspect_ratio (v4l2object, tmp);
|
||||
|
||||
gst_v4l2_object_update_and_append (v4l2object, pixelformat, ret, tmp);
|
||||
|
@ -2140,32 +2241,13 @@ default_frame_sizes:
|
|||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2_object_get_interlace (int field, gboolean * interlaced)
|
||||
{
|
||||
switch (field) {
|
||||
case V4L2_FIELD_ANY:
|
||||
case V4L2_FIELD_NONE:
|
||||
*interlaced = FALSE;
|
||||
return TRUE;
|
||||
case V4L2_FIELD_INTERLACED:
|
||||
case V4L2_FIELD_INTERLACED_TB:
|
||||
case V4L2_FIELD_INTERLACED_BT:
|
||||
*interlaced = TRUE;
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
|
||||
guint32 pixelformat, gint * width, gint * height, gboolean * interlaced)
|
||||
guint32 pixelformat, gint * width, gint * height)
|
||||
{
|
||||
struct v4l2_format fmt;
|
||||
int fd;
|
||||
int r;
|
||||
gboolean ret = FALSE;
|
||||
GstVideoInterlaceMode interlace_mode;
|
||||
|
||||
g_return_val_if_fail (width != NULL, FALSE);
|
||||
g_return_val_if_fail (height != NULL, FALSE);
|
||||
|
@ -2174,8 +2256,6 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
|
|||
"getting nearest size to %dx%d with format %" GST_FOURCC_FORMAT,
|
||||
*width, *height, GST_FOURCC_ARGS (pixelformat));
|
||||
|
||||
fd = v4l2object->video_fd;
|
||||
|
||||
memset (&fmt, 0, sizeof (struct v4l2_format));
|
||||
|
||||
/* get size delimiters */
|
||||
|
@ -2184,58 +2264,10 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
|
|||
fmt.fmt.pix.width = *width;
|
||||
fmt.fmt.pix.height = *height;
|
||||
fmt.fmt.pix.pixelformat = pixelformat;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_NONE;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
|
||||
r = v4l2_ioctl (fd, VIDIOC_TRY_FMT, &fmt);
|
||||
if ((r < 0 && errno == EINVAL) ||
|
||||
!gst_v4l2_object_get_interlace (fmt.fmt.pix.field, interlaced)) {
|
||||
/* try again with interlaced video */
|
||||
memset (&fmt, 0, sizeof (fmt));
|
||||
fmt.type = v4l2object->type;
|
||||
fmt.fmt.pix.width = *width;
|
||||
fmt.fmt.pix.height = *height;
|
||||
fmt.fmt.pix.pixelformat = pixelformat;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
|
||||
r = v4l2_ioctl (fd, VIDIOC_TRY_FMT, &fmt);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
/* The driver might not implement TRY_FMT, in which case we will try
|
||||
S_FMT to probe */
|
||||
if (errno != ENOTTY)
|
||||
goto error;
|
||||
|
||||
/* Only try S_FMT if we're not actively capturing yet, which we shouldn't
|
||||
be, because we're still probing */
|
||||
if (GST_V4L2_IS_ACTIVE (v4l2object))
|
||||
goto error;
|
||||
|
||||
GST_LOG_OBJECT (v4l2object->element,
|
||||
"Failed to probe size limit with VIDIOC_TRY_FMT, trying VIDIOC_S_FMT");
|
||||
|
||||
memset (&fmt, 0, sizeof (fmt));
|
||||
fmt.type = v4l2object->type;
|
||||
fmt.fmt.pix.width = *width;
|
||||
fmt.fmt.pix.height = *height;
|
||||
fmt.fmt.pix.pixelformat = pixelformat;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_NONE;
|
||||
|
||||
r = v4l2_ioctl (fd, VIDIOC_S_FMT, &fmt);
|
||||
if ((r < 0 && errno == EINVAL) ||
|
||||
!gst_v4l2_object_get_interlace (fmt.fmt.pix.field, interlaced)) {
|
||||
/* try again with interlaced video */
|
||||
memset (&fmt, 0, sizeof (fmt));
|
||||
fmt.type = v4l2object->type;
|
||||
fmt.fmt.pix.width = *width;
|
||||
fmt.fmt.pix.height = *height;
|
||||
fmt.fmt.pix.pixelformat = pixelformat;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
|
||||
r = v4l2_ioctl (fd, VIDIOC_S_FMT, &fmt);
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
goto error;
|
||||
}
|
||||
if (gst_v4l2_object_try_fmt (v4l2object, &fmt) < 0)
|
||||
goto error;
|
||||
|
||||
GST_LOG_OBJECT (v4l2object->element,
|
||||
"got nearest size %dx%d", fmt.fmt.pix.width, fmt.fmt.pix.height);
|
||||
|
@ -2243,7 +2275,7 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
|
|||
*width = fmt.fmt.pix.width;
|
||||
*height = fmt.fmt.pix.height;
|
||||
|
||||
if (!gst_v4l2_object_get_interlace (fmt.fmt.pix.field, interlaced)) {
|
||||
if (!gst_v4l2_object_get_interlace_mode (fmt.fmt.pix.field, &interlace_mode)) {
|
||||
GST_WARNING_OBJECT (v4l2object->element,
|
||||
"Unsupported field type for %" GST_FOURCC_FORMAT "@%ux%u: %u",
|
||||
GST_FOURCC_ARGS (pixelformat), *width, *height, fmt.fmt.pix.field);
|
||||
|
|
|
@ -274,6 +274,9 @@ gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
|
|||
|
||||
if (gst_structure_has_field (structure, "format"))
|
||||
gst_structure_fixate_field (structure, "format");
|
||||
|
||||
if (gst_structure_has_field (structure, "interlace-mode"))
|
||||
gst_structure_fixate_field (structure, "interlace-mode");
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (basesrc, "fixated caps %" GST_PTR_FORMAT, caps);
|
||||
|
|
Loading…
Reference in a new issue