summaryrefslogtreecommitdiff
path: root/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
diff options
context:
space:
mode:
Diffstat (limited to 'v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c')
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c445
1 files changed, 246 insertions, 199 deletions
diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
index 93bc67c7e..362ac1914 100644
--- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
+++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
@@ -64,11 +64,24 @@ static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = {
/* List of cams which need special flags */
static const struct v4lconvert_flags_info v4lconvert_flags[] = {
- { "SPC 200NC ", V4LCONVERT_UPSIDE_DOWN },
- { "SPC 300NC ", V4LCONVERT_UPSIDE_DOWN },
- { "USB Camera (0471:0325)", V4LCONVERT_UPSIDE_DOWN }, /* SPC200NC */
- { "USB Camera (0471:0326)", V4LCONVERT_UPSIDE_DOWN }, /* SPC300NC */
- { "USB Camera (093a:2476)", V4LCONVERT_UPSIDE_DOWN }, /* Genius E-M 112 */
+ { "SPC 200NC ", V4LCONVERT_ROTATE_180 },
+ { "SPC 300NC ", V4LCONVERT_ROTATE_180 },
+ { "USB Camera (0471:0325)", V4LCONVERT_ROTATE_180 }, /* SPC200NC */
+ { "USB Camera (0471:0326)", V4LCONVERT_ROTATE_180 }, /* SPC300NC */
+ { "USB Camera (093a:2476)", V4LCONVERT_ROTATE_180 }, /* Genius E-M 112 */
+};
+
+/* List of well known resolutions which we can get by cropping somewhat larger
+ resolutions */
+static const int v4lconvert_crop_res[][2] = {
+ /* low res VGA resolutions, can be made by software cropping SIF resolutions
+ for cam/drivers which do not support this in hardware */
+ { 320, 240 },
+ { 160, 120 },
+ /* Some CIF cams (with vv6410 sensor) have slightly larger then usual CIF
+ resolutions, make regular CIF resolutions available on these by sw crop */
+ { 352, 288 },
+ { 176, 144 },
};
struct v4lconvert_data *v4lconvert_create(int fd)
@@ -171,8 +184,7 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
return 0;
}
-/* See libv4lconvert.h for description of in / out parameters */
-int v4lconvert_try_format(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;
@@ -180,15 +192,6 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
unsigned int desired_pixfmt = dest_fmt->fmt.pix.pixelformat;
struct v4l2_format try_fmt, closest_fmt = { .type = 0 };
- /* Can we do conversion to the requested format & type? */
- if (!v4lconvert_supported_dst_format(desired_pixfmt) ||
- dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- int ret = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt);
- if (src_fmt)
- *src_fmt = *dest_fmt;
- return ret;
- }
-
for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) {
/* is this format supported? */
if (!(data->supported_src_formats & (1 << i)))
@@ -209,7 +212,7 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
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 > 176 || try_fmt.fmt.pix.height > 144) &&
+ ((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;
@@ -218,35 +221,92 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
}
}
- if (closest_fmt.type == 0) {
- int ret = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt);
+ if (closest_fmt.type == 0)
+ return -1;
+
+ *dest_fmt = closest_fmt;
+ if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt)
+ dest_fmt->fmt.pix.pixelformat = desired_pixfmt;
+ *src_fmt = closest_fmt;
+
+ return 0;
+}
+
+static void v4lconvert_fixup_fmt(struct v4l2_format *fmt)
+{
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * 3;
+ fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ fmt->fmt.pix.bytesperline = fmt->fmt.pix.width;
+ fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3 / 2;
+ break;
+ }
+}
+
+/* See libv4lconvert.h for description of in / out parameters */
+int v4lconvert_try_format(struct v4lconvert_data *data,
+ struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
+{
+ int i, result;
+ unsigned int desired_width = dest_fmt->fmt.pix.width;
+ unsigned int desired_height = dest_fmt->fmt.pix.height;
+ struct v4l2_format try_src, try_dest = *dest_fmt;
+
+ /* Can we do conversion to the requested format & type? */
+ if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
+ dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ v4lconvert_do_try_format(data, &try_dest, &try_src)) {
+ result = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt);
if (src_fmt)
*src_fmt = *dest_fmt;
- return ret;
+ return result;
}
- *dest_fmt = closest_fmt;
-
- /* Are we converting? */
- if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt) {
- dest_fmt->fmt.pix.pixelformat = desired_pixfmt;
- switch (dest_fmt->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- dest_fmt->fmt.pix.bytesperline = dest_fmt->fmt.pix.width * 3;
- dest_fmt->fmt.pix.sizeimage = dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height * 3;
- break;
- case V4L2_PIX_FMT_YUV420:
- dest_fmt->fmt.pix.bytesperline = dest_fmt->fmt.pix.width;
- dest_fmt->fmt.pix.sizeimage = (dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height * 3) / 2;
+ /* In case of a non exact resolution match, see if this is a resolution we
+ can support by cropping a slightly larger resolution to give the app
+ exactly what it asked for */
+ if (try_dest.fmt.pix.width != desired_width ||
+ try_dest.fmt.pix.height != desired_height) {
+ for (i = 0; i < ARRAY_SIZE(v4lconvert_crop_res); i++) {
+ if (v4lconvert_crop_res[i][0] == desired_width &&
+ v4lconvert_crop_res[i][1] == desired_height) {
+ struct v4l2_format try2_dest, try2_src;
+ /* Note these are chosen so that cropping to vga res just works for
+ vv6410 sensor cams, which have 356x292 and 180x148 */
+ unsigned int max_width = desired_width * 113 / 100;
+ unsigned int max_height = desired_height * 124 / 100;
+
+ try2_dest = *dest_fmt;
+ try2_dest.fmt.pix.width = max_width;
+ try2_dest.fmt.pix.height = max_height;
+ result = v4lconvert_do_try_format(data, &try2_dest, &try2_src);
+ if (result == 0 &&
+ try2_dest.fmt.pix.width >= desired_width &&
+ try2_dest.fmt.pix.width <= max_width &&
+ try2_dest.fmt.pix.height >= desired_height &&
+ try2_dest.fmt.pix.height <= max_height) {
+ /* Success! */
+ try2_dest.fmt.pix.width = desired_width;
+ try2_dest.fmt.pix.height = desired_height;
+ try_dest = try2_dest;
+ try_src = try2_src;
+ }
break;
+ }
}
}
+ /* Are we converting? */
+ if(memcmp(&try_src, &try_dest, sizeof(try_src)))
+ v4lconvert_fixup_fmt(&try_dest);
+
+ *dest_fmt = try_dest;
if (src_fmt)
- *src_fmt = closest_fmt;
+ *src_fmt = try_src;
return 0;
}
@@ -259,7 +319,7 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data,
if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt)))
return 1; /* Formats differ */
- if (!(data->flags & V4LCONVERT_UPSIDE_DOWN))
+ if (!(data->flags & (V4LCONVERT_ROTATE_90|V4LCONVERT_ROTATE_180)))
return 0; /* Formats identical and we don't need flip */
/* Formats are identical, but we need flip, do we support the dest_fmt? */
@@ -269,50 +329,16 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data,
return 1; /* Needs flip and thus conversion */
}
-int v4lconvert_convert(struct v4lconvert_data *data,
- const struct v4l2_format *src_fmt, /* in */
- const struct v4l2_format *dest_fmt, /* in */
- unsigned char *src, int src_size, unsigned char *_dest, int dest_size)
+static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
+ unsigned int src_pix_fmt, unsigned int dest_pix_fmt,
+ unsigned int width, unsigned int height,
+ unsigned char *src, int src_size, unsigned char *dest)
{
unsigned int header_width, header_height;
- int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
+ int result = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
unsigned char *components[3];
- unsigned char *dest = _dest;
-
- /* Special case when no conversion is needed */
- if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) {
- int to_copy = MIN(dest_size, src_size);
- memcpy(dest, src, to_copy);
- return to_copy;
- }
- /* sanity check, is the dest buffer large enough? */
- switch (dest_fmt->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- needed = dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3;
- break;
- case V4L2_PIX_FMT_YUV420:
- needed = (dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3) / 2;
- break;
- default:
- V4LCONVERT_ERR("Unknown dest format in conversion\n");
- errno = EINVAL;
- return -1;
- }
-
- if (dest_size < needed) {
- V4LCONVERT_ERR("destination buffer too small\n");
- errno = EFAULT;
- return -1;
- }
-
- if (data->flags & V4LCONVERT_UPSIDE_DOWN) {
- rotate = 180;
- dest = alloca(needed);
- }
-
- switch (src_fmt->fmt.pix.pixelformat) {
+ switch (src_pix_fmt) {
case V4L2_PIX_FMT_PJPG:
jpeg_flags |= TINYJPEG_FLAGS_PIXART_JPEG;
/* Fall through */
@@ -335,31 +361,29 @@ int v4lconvert_convert(struct v4lconvert_data *data,
}
tinyjpeg_get_size(data->jdec, &header_width, &header_height);
- if (header_width != dest_fmt->fmt.pix.width ||
- header_height != dest_fmt->fmt.pix.height) {
+ if (header_width != width || header_height != height) {
/* Check for (pixart) rotated JPEG */
- if (header_width == dest_fmt->fmt.pix.height ||
- header_height == dest_fmt->fmt.pix.width) {
- if (!rotate)
- dest = alloca(needed);
- rotate += 90;
+ if (header_width == height && header_height == width) {
+ if (!(data->flags & V4LCONVERT_ROTATE_90)) {
+ V4LCONVERT_ERR("JPEG needs 90 degree rotation");
+ data->flags |= V4LCONVERT_ROTATE_90;
+ errno = EAGAIN;
+ return -1;
+ }
} else {
V4LCONVERT_ERR("unexpected width / height in JPEG header"
- "expected: %ux%u, header: %ux%u\n",
- dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height,
- header_width, header_height);
+ "expected: %ux%u, header: %ux%u\n", width, height,
+ header_width, header_height);
errno = EIO;
return -1;
}
}
components[0] = dest;
- components[1] = components[0] + dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height;
- components[2] = components[1] + (dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height) / 4;
+ components[1] = components[0] + width * height;
+ components[2] = components[1] + width * height / 4;
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
tinyjpeg_set_components(data->jdec, components, 1);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_RGB24);
@@ -368,7 +392,7 @@ int v4lconvert_convert(struct v4lconvert_data *data,
tinyjpeg_set_components(data->jdec, components, 1);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_BGR24);
break;
- default:
+ case V4L2_PIX_FMT_YUV420:
tinyjpeg_set_components(data->jdec, components, 3);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P);
break;
@@ -379,7 +403,7 @@ int v4lconvert_convert(struct v4lconvert_data *data,
are best thrown away to avoid flashes in the video stream. Tell
the upper layer this is an intermediate fault and it should try
again with a new buffer by setting errno to EAGAIN */
- if (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_PJPG) {
+ if (src_pix_fmt == V4L2_PIX_FMT_PJPG) {
V4LCONVERT_ERR("Error decompressing JPEG: %s",
tinyjpeg_get_errorstring(data->jdec));
errno = EAGAIN;
@@ -398,18 +422,15 @@ int v4lconvert_convert(struct v4lconvert_data *data,
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_bayer_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
+ v4lconvert_bayer_to_rgb24(src, dest, width, height, src_pix_fmt);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_bayer_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
+ v4lconvert_bayer_to_bgr24(src, dest, width, height, src_pix_fmt);
break;
- default:
- v4lconvert_bayer_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_bayer_to_yuv420(src, dest, width, height, src_pix_fmt);
break;
}
break;
@@ -419,34 +440,27 @@ int v4lconvert_convert(struct v4lconvert_data *data,
case V4L2_PIX_FMT_SPCA505:
case V4L2_PIX_FMT_SPCA508:
{
- unsigned char tmpbuf[dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height *
- 3 / 2];
- unsigned char *my_dst = (dest_fmt->fmt.pix.pixelformat !=
- V4L2_PIX_FMT_YUV420) ? tmpbuf : dest;
+ unsigned char tmpbuf[width * height * 3 / 2];
+ unsigned char *d = (dest_pix_fmt != V4L2_PIX_FMT_YUV420) ? tmpbuf : dest;
- switch (src_fmt->fmt.pix.pixelformat) {
+ switch (src_pix_fmt) {
case V4L2_PIX_FMT_SPCA501:
- v4lconvert_spca501_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_spca501_to_yuv420(src, d, width, height);
break;
case V4L2_PIX_FMT_SPCA505:
- v4lconvert_spca505_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_spca505_to_yuv420(src, d, width, height);
break;
case V4L2_PIX_FMT_SPCA508:
- v4lconvert_spca508_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_spca508_to_yuv420(src, d, width, height);
break;
}
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yuv420_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_rgb24(tmpbuf, dest, width, height);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yuv420_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_bgr24(tmpbuf, dest, width, height);
break;
}
break;
@@ -457,60 +471,53 @@ int v4lconvert_convert(struct v4lconvert_data *data,
case V4L2_PIX_FMT_SN9C10X:
case V4L2_PIX_FMT_PAC207:
{
- unsigned char tmpbuf[dest_fmt->fmt.pix.width*dest_fmt->fmt.pix.height];
+ unsigned char tmpbuf[width * height];
unsigned int bayer_fmt = 0;
- switch (src_fmt->fmt.pix.pixelformat) {
+ switch (src_pix_fmt) {
case V4L2_PIX_FMT_SPCA561:
- v4lconvert_decode_spca561(src, tmpbuf, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_decode_spca561(src, tmpbuf, width, height);
bayer_fmt = V4L2_PIX_FMT_SGBRG8;
break;
case V4L2_PIX_FMT_SN9C10X:
- v4lconvert_decode_sn9c10x(src, tmpbuf, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_decode_sn9c10x(src, tmpbuf, width, height);
bayer_fmt = V4L2_PIX_FMT_SBGGR8;
break;
case V4L2_PIX_FMT_PAC207:
- v4lconvert_decode_pac207(src, tmpbuf, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_decode_pac207(src, tmpbuf, width, height);
bayer_fmt = V4L2_PIX_FMT_SBGGR8;
break;
}
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_bayer_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, bayer_fmt);
+ v4lconvert_bayer_to_rgb24(tmpbuf, dest, width, height, bayer_fmt);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_bayer_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, bayer_fmt);
+ v4lconvert_bayer_to_bgr24(tmpbuf, dest, width, height, bayer_fmt);
break;
- default:
- v4lconvert_bayer_to_yuv420(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, bayer_fmt);
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_bayer_to_yuv420(tmpbuf, dest, width, height, bayer_fmt);
break;
}
break;
}
case V4L2_PIX_FMT_RGB24:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_BGR24:
- v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_swap_rgb(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
printf("FIXME add rgb24 -> yuv420 conversion\n");
break;
}
break;
+
case V4L2_PIX_FMT_BGR24:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_swap_rgb(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
printf("FIXME add bgr24 -> yuv420 conversion\n");
@@ -519,48 +526,42 @@ int v4lconvert_convert(struct v4lconvert_data *data,
break;
case V4L2_PIX_FMT_YUV420:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yuv420_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_rgb24(src, dest, width,
+ height);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yuv420_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_bgr24(src, dest, width,
+ height);
break;
}
break;
case V4L2_PIX_FMT_YUYV:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yuyv_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuyv_to_rgb24(src, dest, width, height);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yuyv_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuyv_to_bgr24(src, dest, width, height);
break;
- default:
- v4lconvert_yuyv_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height);
break;
}
break;
case V4L2_PIX_FMT_YVYU:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yvyu_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yvyu_to_rgb24(src, dest, width, height);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yvyu_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yvyu_to_bgr24(src, dest, width, height);
break;
- default:
- v4lconvert_yvyu_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_yvyu_to_yuv420(src, dest, width, height);
break;
}
break;
@@ -570,47 +571,93 @@ int v4lconvert_convert(struct v4lconvert_data *data,
errno = EINVAL;
return -1;
}
+ return 0;
+}
- /* Note when rotating dest is our temporary buffer to which our conversion
- was done and _dest is the real dest! If the formats are identical no
- conversion has been done! */
- if (rotate && dest_fmt->fmt.pix.pixelformat == src_fmt->fmt.pix.pixelformat)
- dest = src;
+int v4lconvert_convert(struct v4lconvert_data *data,
+ const struct v4l2_format *src_fmt, /* in */
+ const struct v4l2_format *dest_fmt, /* in */
+ unsigned char *src, int src_size, unsigned char *dest, int dest_size)
+{
+ int res, dest_needed, temp_needed, convert = 0, rotate = 0, crop = 0;
+ unsigned char *convert_dest = dest, *rotate_src = src, *rotate_dest = dest;
+ unsigned char *crop_src = src;
+ struct v4l2_format my_src_fmt = *src_fmt;
- switch (rotate) {
- case 0:
- break;
- case 90:
- switch (dest_fmt->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- v4lconvert_rotate90_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- break;
- case V4L2_PIX_FMT_YUV420:
- v4lconvert_rotate90_yuv420(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- break;
- }
- break;
- case 180:
- switch (dest_fmt->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- v4lconvert_rotate180_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- break;
- case V4L2_PIX_FMT_YUV420:
- v4lconvert_rotate180_yuv420(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- break;
- }
- break;
- default:
- printf("FIXME add %d degrees rotation\n", rotate);
+ /* Special case when no conversion is needed */
+ if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) {
+ int to_copy = MIN(dest_size, src_size);
+ memcpy(dest, src, to_copy);
+ return to_copy;
}
- return needed;
+ /* sanity check, is the dest buffer large enough? */
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ dest_needed = dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3;
+ temp_needed = src_fmt->fmt.pix.width * src_fmt->fmt.pix.height * 3;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ dest_needed = dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3 / 2;
+ temp_needed = src_fmt->fmt.pix.width * src_fmt->fmt.pix.height * 3 / 2;
+ break;
+ default:
+ V4LCONVERT_ERR("Unknown dest format in conversion\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (dest_size < dest_needed) {
+ V4LCONVERT_ERR("destination buffer too small\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (dest_fmt->fmt.pix.pixelformat != src_fmt->fmt.pix.pixelformat)
+ convert = 1;
+
+ if (data->flags & V4LCONVERT_ROTATE_90)
+ rotate += 90;
+ if (data->flags & V4LCONVERT_ROTATE_180)
+ rotate += 180;
+
+ if (dest_fmt->fmt.pix.width != src_fmt->fmt.pix.width ||
+ dest_fmt->fmt.pix.height != src_fmt->fmt.pix.height)
+ crop = 1;
+
+ /* convert_pixfmt -> rotate -> crop, all steps are optional */
+ if (convert && (rotate || crop)) {
+ convert_dest = alloca(temp_needed);
+ rotate_src = crop_src = convert_dest;
+ }
+
+ if (rotate && crop) {
+ rotate_dest = alloca(temp_needed);
+ crop_src = rotate_dest;
+ }
+
+ if (convert) {
+ res = v4lconvert_convert_pixfmt(data, src_fmt->fmt.pix.pixelformat,
+ dest_fmt->fmt.pix.pixelformat,
+ src_fmt->fmt.pix.width, src_fmt->fmt.pix.height,
+ src, src_size, convert_dest);
+ if (res)
+ return res;
+
+ my_src_fmt.fmt.pix.pixelformat = dest_fmt->fmt.pix.pixelformat;
+ v4lconvert_fixup_fmt(&my_src_fmt);
+ }
+
+ if (rotate)
+ v4lconvert_rotate(rotate_src, rotate_dest,
+ my_src_fmt.fmt.pix.width, my_src_fmt.fmt.pix.height,
+ my_src_fmt.fmt.pix.pixelformat, rotate);
+
+ if (crop)
+ v4lconvert_crop(crop_src, dest, &my_src_fmt, dest_fmt);
+
+ return dest_needed;
}
const char *v4lconvert_get_error_message(struct v4lconvert_data *data)