aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2011-05-09 10:14:32 +0200
committerHans de Goede <hdegoede@redhat.com>2011-05-09 20:03:29 +0200
commit1840fb7a9f87d3c94b9f812f338c5658af6f4281 (patch)
tree6cb9fb0c74ff774e612e0e054129a07e4d4e8b44
parentff9a3b22c71a8cfd5074b1ae4089232976776555 (diff)
libv4l: rewrite src format selection algorithm
When multiple src formats are available for the best resolution match we need to decide which one to use. The old algorithm was rather crude: - it depended on the formats being sorted in a certain way in the source formats array - it did not really take bus bandwidth / fps into account other then always preferring compressed formats except for resolutions of quarter CIF and lower - it did not differentiate between converting to YUV420 or to RGB32, while some src formats are clearly a better match for one then for the other This rewritten src format ranking algorithm takes all of the above into account. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--TODO8
-rw-r--r--lib/include/libv4lconvert.h5
-rw-r--r--lib/libv4lconvert/libv4lconvert-priv.h14
-rw-r--r--lib/libv4lconvert/libv4lconvert.c228
4 files changed, 170 insertions, 85 deletions
diff --git a/TODO b/TODO
index 20de6bf3..f44a11e7 100644
--- a/TODO
+++ b/TODO
@@ -1,15 +1,13 @@
libv4l todo:
------------
+-libv4lconvert: v4lconvert_do_try_format should always prefer smaller then
+ requested resolutions over bigger then requested ones
+
-add support for setting / getting the number of read buffers
-add code to v4l2_read to not return frames more then say 5 seconds old
--add support for libv4l1 for non pure capture (combined capture and overlay)
- devices so that atleast CGMBUF emulation (but no conversion, as thats
- impossible for overlays) can be done, so that it will no longer be
- necessary to implement CGMBUF in the kernel for each driver.
-
-take the possibility of pitch != width into account everywhere
-make updating of parameters happen based on time elapsed rather then
diff --git a/lib/include/libv4lconvert.h b/lib/include/libv4lconvert.h
index 02642909..2b82ea81 100644
--- a/lib/include/libv4lconvert.h
+++ b/lib/include/libv4lconvert.h
@@ -115,6 +115,11 @@ LIBV4L_PUBLIC int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data,
/* Is the passed in pixelformat supported as destination format? */
LIBV4L_PUBLIC int v4lconvert_supported_dst_format(unsigned int pixelformat);
+/* Get/set the no fps libv4lconvert uses to decide if a compressed format
+ must be used as src fmt to stay within the bus bandwidth */
+LIBV4L_PUBLIC int v4lconvert_get_fps(struct v4lconvert_data *data);
+LIBV4L_PUBLIC void v4lconvert_set_fps(struct v4lconvert_data *data, int fps);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/lib/libv4lconvert/libv4lconvert-priv.h b/lib/libv4lconvert/libv4lconvert-priv.h
index d81b3795..c804d3c3 100644
--- a/lib/libv4lconvert/libv4lconvert-priv.h
+++ b/lib/libv4lconvert/libv4lconvert-priv.h
@@ -39,11 +39,6 @@
/* Card flags */
#define V4LCONVERT_IS_UVC 0x01
-/* Pixformat flags */
-#define V4LCONVERT_COMPRESSED 0x01 /* Compressed format */
-#define V4LCONVERT_NEEDS_CONVERSION 0x02 /* Apps likely wont know this */
-#define V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION 0x03
-
struct v4lconvert_data {
int fd;
int flags; /* bitfield */
@@ -54,6 +49,8 @@ struct v4lconvert_data {
struct jdec_private *jdec;
struct v4l2_frmsizeenum framesizes[V4LCONVERT_MAX_FRAMESIZES];
unsigned int no_framesizes;
+ int bandwidth;
+ int fps;
int convert1_buf_size;
int convert2_buf_size;
int rotate90_buf_size;
@@ -80,8 +77,11 @@ struct v4lconvert_data {
};
struct v4lconvert_pixfmt {
- unsigned int fmt;
- int flags;
+ unsigned int fmt; /* v4l2 fourcc */
+ int bpp; /* bits per pixel, 0 for compressed formats */
+ int rgb_rank; /* rank for converting to rgb32 / bgr32 */
+ int yuv_rank; /* rank for converting to yuv420 / yvu420 */
+ int needs_conversion;
};
void v4lconvert_fixup_fmt(struct v4l2_format *fmt);
diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c
index a468e2f3..7caa7585 100644
--- a/lib/libv4lconvert/libv4lconvert.c
+++ b/lib/libv4lconvert/libv4lconvert.c
@@ -28,52 +28,59 @@
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
+ unsigned int pixelformat, int index);
+
/* Note for proper functioning of v4lconvert_enum_fmt the first entries in
supported_src_pixfmts must match with the entries in supported_dst_pixfmts */
#define SUPPORTED_DST_PIXFMTS \
-{ V4L2_PIX_FMT_RGB24, 0 }, \
-{ V4L2_PIX_FMT_BGR24, 0 }, \
-{ V4L2_PIX_FMT_YUV420, 0 }, \
-{ V4L2_PIX_FMT_YVU420, 0 }
+ /* fourcc bpp rgb yuv needs */ \
+ /* rank rank conversion */ \
+ { V4L2_PIX_FMT_RGB24, 24, 1, 5, 0 }, \
+ { V4L2_PIX_FMT_BGR24, 24, 1, 5, 0 }, \
+ { V4L2_PIX_FMT_YUV420, 12, 6, 1, 0 }, \
+ { V4L2_PIX_FMT_YVU420, 12, 6, 1, 0 }
-static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
- unsigned int pixelformat, int index);
-
-/* Note uncompressed formats must go first so that they are prefered by
- v4lconvert_try_format for low resolutions */
static const struct v4lconvert_pixfmt supported_src_pixfmts[] = {
SUPPORTED_DST_PIXFMTS,
- { V4L2_PIX_FMT_GREY, 0 },
- { V4L2_PIX_FMT_YUYV, 0 },
- { V4L2_PIX_FMT_YVYU, 0 },
- { V4L2_PIX_FMT_UYVY, 0 },
- { V4L2_PIX_FMT_RGB565, 0 },
- { V4L2_PIX_FMT_SN9C20X_I420, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SBGGR8, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SGBRG8, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SGRBG8, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SRGGB8, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_STV0680, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SPCA501, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SPCA505, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SPCA508, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_CIT_YYVYUY, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_KONICA420, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_M420, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_CPIA1, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_HM12, V4LCONVERT_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SN9C2028, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_MR97310A, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_SQ905C, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_JPGL, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_OV511, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
- { V4L2_PIX_FMT_OV518, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+ /* packed rgb formats */
+ { V4L2_PIX_FMT_RGB565, 16, 4, 6, 0 },
+ /* yuv 4:2:2 formats */
+ { V4L2_PIX_FMT_YUYV, 16, 5, 4, 0 },
+ { V4L2_PIX_FMT_YVYU, 16, 5, 4, 0 },
+ { V4L2_PIX_FMT_UYVY, 16, 5, 4, 0 },
+ /* yuv 4:2:0 formats */
+ { V4L2_PIX_FMT_SPCA501, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_SPCA505, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_SPCA508, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_CIT_YYVYUY, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_KONICA420, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_SN9C20X_I420, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_M420, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_HM12, 12, 6, 3, 1 },
+ { V4L2_PIX_FMT_CPIA1, 0, 6, 3, 1 },
+ /* JPEG and variants */
+ { V4L2_PIX_FMT_MJPEG, 0, 7, 7, 0 },
+ { V4L2_PIX_FMT_JPEG, 0, 7, 7, 0 },
+ { V4L2_PIX_FMT_PJPG, 0, 7, 7, 1 },
+ { V4L2_PIX_FMT_JPGL, 0, 7, 7, 1 },
+ { V4L2_PIX_FMT_OV511, 0, 7, 7, 1 },
+ { V4L2_PIX_FMT_OV518, 0, 7, 7, 1 },
+ /* uncompressed bayer */
+ { V4L2_PIX_FMT_SBGGR8, 8, 8, 8, 1 },
+ { V4L2_PIX_FMT_SGBRG8, 8, 8, 8, 1 },
+ { V4L2_PIX_FMT_SGRBG8, 8, 8, 8, 1 },
+ { V4L2_PIX_FMT_SRGGB8, 8, 8, 8, 1 },
+ { V4L2_PIX_FMT_STV0680, 8, 8, 8, 1 },
+ /* compressed bayer */
+ { V4L2_PIX_FMT_SPCA561, 0, 9, 9, 1 },
+ { V4L2_PIX_FMT_SN9C10X, 0, 9, 9, 1 },
+ { V4L2_PIX_FMT_SN9C2028, 0, 9, 9, 1 },
+ { V4L2_PIX_FMT_PAC207, 0, 9, 9, 1 },
+ { V4L2_PIX_FMT_MR97310A, 0, 9, 9, 1 },
+ { V4L2_PIX_FMT_SQ905C, 0, 9, 9, 1 },
+ /* grey formats */
+ { V4L2_PIX_FMT_GREY, 8, 20, 20, 0 },
};
static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = {
@@ -110,6 +117,9 @@ struct v4lconvert_data *v4lconvert_create(int fd)
data->fd = fd;
data->decompress_pid = -1;
+ /* Default to usb 2 bandwidth and 30 fps for now */
+ data->bandwidth = 3 * 1024 * 8000; /* 3 * 1024 byte packets * 8000 microframes */
+ data->fps = 30;
/* Check supported formats */
for (i = 0; ; i++) {
@@ -121,15 +131,15 @@ struct v4lconvert_data *v4lconvert_create(int fd)
break;
for (j = 0; j < ARRAY_SIZE(supported_src_pixfmts); j++)
- if (fmt.pixelformat == supported_src_pixfmts[j].fmt) {
- data->supported_src_formats |= 1 << j;
- v4lconvert_get_framesizes(data, fmt.pixelformat, j);
- if (!(supported_src_pixfmts[j].flags & V4LCONVERT_NEEDS_CONVERSION))
- always_needs_conversion = 0;
+ if (fmt.pixelformat == supported_src_pixfmts[j].fmt)
break;
- }
- if (j == ARRAY_SIZE(supported_src_pixfmts))
+ if (j < ARRAY_SIZE(supported_src_pixfmts)) {
+ data->supported_src_formats |= 1 << j;
+ v4lconvert_get_framesizes(data, fmt.pixelformat, j);
+ if (!supported_src_pixfmts[j].needs_conversion)
+ always_needs_conversion = 0;
+ } else
always_needs_conversion = 0;
}
@@ -238,6 +248,58 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
return 0;
}
+/* This function returns a value to rank (sort) source format by preference
+ when multiple source formats are available for a certain resolution, the
+ source format for which this function returns the lowest value wins.
+
+ This function uses the rgb_rank resp. yuv_rank values as a base when
+ converting to rgb32 resp. yuv420. The initial ranks range from 1 - 10,
+ the initial rank purely expresses the CPU cost of doing the conversion, the
+ ranking algorithm will give a penalty of 10 points if
+ (width * height * fps * bpp / 8) > bandwidth
+ thus disqualifying a src format which causes the bandwidth to be exceeded,
+ except when all of them cause this.
+
+ Note grey scale formats start at 20 rather then 1-10, because we want to
+ never autoselect them, unless they are the only choice */
+static int v4lconvert_get_rank(struct v4lconvert_data *data,
+ int src_index, int src_width, int src_height,
+ unsigned int dest_pixelformat)
+{
+ int needed, rank = 0;
+
+ switch (dest_pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ rank = supported_src_pixfmts[src_index].rgb_rank;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ rank = supported_src_pixfmts[src_index].yuv_rank;
+ break;
+ }
+
+ /* So that if both rgb32 and bgr32 are supported, or both yuv420 and
+ yvu420 the right one wins */
+ if (supported_src_pixfmts[src_index].fmt == dest_pixelformat)
+ rank--;
+
+ /* check bandwidth needed */
+ needed = src_width * src_height * data->fps *
+ supported_src_pixfmts[src_index].bpp / 8;
+ if (needed > data->bandwidth)
+ rank += 10;
+#if 0
+ printf("ranked: %c%c%c%c for %dx%d @ %d fps, needed: %d, bandwidth: %d, rank: %d\n",
+ supported_src_pixfmts[src_index].fmt & 0xff,
+ (supported_src_pixfmts[src_index].fmt >> 8) & 0xff,
+ (supported_src_pixfmts[src_index].fmt >> 16) & 0xff,
+ supported_src_pixfmts[src_index].fmt >> 24, src_width,
+ src_height, data->fps, needed, data->bandwidth, rank);
+#endif
+ return rank;
+}
+
/* Find out what format to use based on the (cached) results of enum
framesizes instead of doing a zillion try_fmt calls. This function
currently is intended for use with UVC cams only. This is esp.
@@ -245,10 +307,11 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
static int v4lconvert_do_try_format_uvc(struct v4lconvert_data *data,
struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
- int i;
+ int i, rank;
unsigned int closest_fmt_size_diff = -1;
int best_framesize = 0;/* Just use the first format if no small enough one */
int best_format = 0;
+ int best_rank = 100;
for (i = 0; i < data->no_framesizes; i++) {
if (data->framesizes[i].discrete.width <= dest_fmt->fmt.pix.width &&
@@ -272,12 +335,16 @@ static int v4lconvert_do_try_format_uvc(struct v4lconvert_data *data,
if (!(data->framesizes[best_framesize].pixel_format & (1 << i)))
continue;
- if (!best_format ||
- supported_src_pixfmts[i].fmt == dest_fmt->fmt.pix.pixelformat ||
- ((data->framesizes[best_framesize].discrete.width > 180 ||
- data->framesizes[best_framesize].discrete.height > 148) &&
- (supported_src_pixfmts[i].flags & V4LCONVERT_COMPRESSED)))
+ /* Note the hardcoded use of discrete is based on this function
+ only getting called for uvc devices */
+ rank = v4lconvert_get_rank(data, i,
+ data->framesizes[best_framesize].discrete.width,
+ data->framesizes[best_framesize].discrete.height,
+ dest_fmt->fmt.pix.pixelformat);
+ if (rank < best_rank) {
+ best_rank = rank;
best_format = supported_src_pixfmts[i].fmt;
+ }
}
dest_fmt->fmt.pix.width = data->framesizes[best_framesize].discrete.width;
@@ -299,8 +366,8 @@ static int v4lconvert_do_try_format_uvc(struct v4lconvert_data *data,
static int v4lconvert_do_try_format(struct v4lconvert_data *data,
struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
- int i;
- unsigned int closest_fmt_size_diff = -1;
+ int i, size_x_diff, size_y_diff, rank, best_rank = 0;
+ unsigned int size_diff, closest_fmt_size_diff = -1;
unsigned int desired_pixfmt = dest_fmt->fmt.pix.pixelformat;
struct v4l2_format try_fmt, closest_fmt = { .type = 0 };
@@ -314,25 +381,30 @@ static int v4lconvert_do_try_format(struct v4lconvert_data *data,
try_fmt = *dest_fmt;
try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;
+ if (SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, &try_fmt))
+ continue;
- if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, &try_fmt)) {
- if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) {
- int size_x_diff = abs((int)try_fmt.fmt.pix.width -
- (int)dest_fmt->fmt.pix.width);
- int size_y_diff = abs((int)try_fmt.fmt.pix.height -
- (int)dest_fmt->fmt.pix.height);
- unsigned int size_diff = size_x_diff * size_x_diff +
- size_y_diff * size_y_diff;
-
- if (size_diff < closest_fmt_size_diff ||
- (size_diff == closest_fmt_size_diff &&
- (supported_src_pixfmts[i].fmt == desired_pixfmt ||
- ((try_fmt.fmt.pix.width > 180 || try_fmt.fmt.pix.height > 148) &&
- (supported_src_pixfmts[i].flags & V4LCONVERT_COMPRESSED))))) {
- closest_fmt_size_diff = size_diff;
- closest_fmt = try_fmt;
- }
- }
+ if (try_fmt.fmt.pix.pixelformat !=
+ supported_src_pixfmts[i].fmt)
+ continue;
+
+ /* Did we get a better match then before? */
+ size_x_diff = (int)try_fmt.fmt.pix.width -
+ (int)dest_fmt->fmt.pix.width;
+ size_y_diff = (int)try_fmt.fmt.pix.height -
+ (int)dest_fmt->fmt.pix.height;
+ size_diff = size_x_diff * size_x_diff +
+ size_y_diff * size_y_diff;
+
+ rank = v4lconvert_get_rank(data, i,
+ try_fmt.fmt.pix.width,
+ try_fmt.fmt.pix.height,
+ desired_pixfmt);
+ if (size_diff < closest_fmt_size_diff ||
+ (size_diff == closest_fmt_size_diff && rank < best_rank)) {
+ closest_fmt = try_fmt;
+ closest_fmt_size_diff = size_diff;
+ best_rank = rank;
}
}
@@ -1429,3 +1501,13 @@ int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, void *arg)
{
return v4lcontrol_vidioc_s_ctrl(data->control, arg);
}
+
+int v4lconvert_get_fps(struct v4lconvert_data *data)
+{
+ return data->fps;
+}
+
+void v4lconvert_set_fps(struct v4lconvert_data *data, int fps)
+{
+ data->fps = fps;
+}

Privacy Policy