summaryrefslogtreecommitdiff
path: root/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c
diff options
context:
space:
mode:
Diffstat (limited to 'v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c')
-rw-r--r--v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c272
1 files changed, 232 insertions, 40 deletions
diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c
index 6c6cb693d..4b48bfac4 100644
--- a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c
+++ b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c
@@ -9,7 +9,7 @@
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
@@ -30,6 +30,7 @@
/* 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, \
V4L2_PIX_FMT_BGR24, \
V4L2_PIX_FMT_YUV420
@@ -42,20 +43,31 @@ static const unsigned int supported_src_pixfmts[] = {
V4L2_PIX_FMT_SGRBG8,
V4L2_PIX_FMT_SRGGB8,
V4L2_PIX_FMT_SPCA501,
+ V4L2_PIX_FMT_SPCA505,
+ V4L2_PIX_FMT_SPCA508,
V4L2_PIX_FMT_SPCA561,
V4L2_PIX_FMT_SN9C10X,
V4L2_PIX_FMT_PAC207,
+ V4L2_PIX_FMT_PJPG,
};
static const unsigned int supported_dst_pixfmts[] = {
SUPPORTED_DST_PIXFMTS
};
+/* List of cams which need special flags */
+static const struct v4lconvert_flags_info v4lconvert_flags[] = {
+ { "USB Camera (0471:0325)", V4LCONVERT_UPSIDE_DOWN }, /* SPC200NC */
+ { "USB Camera (0471:0326)", V4LCONVERT_UPSIDE_DOWN }, /* SPC300NC */
+ { "SPC 200NC ", V4LCONVERT_UPSIDE_DOWN },
+ { "SPC 300NC ", V4LCONVERT_UPSIDE_DOWN },
+};
struct v4lconvert_data *v4lconvert_create(int fd)
{
int i, j;
struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data));
+ struct v4l2_capability cap;
if (!data)
return NULL;
@@ -81,6 +93,15 @@ struct v4lconvert_data *v4lconvert_create(int fd)
data->no_formats = i;
+ /* Check if this cam has any special flags */
+ if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) {
+ for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++)
+ if (!strcmp((const char *)v4lconvert_flags[i].card, (char *)cap.card)) {
+ data->flags = v4lconvert_flags[i].flags;
+ break;
+ }
+ }
+
return data;
}
@@ -168,7 +189,9 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
(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) {
+ if (size_diff < closest_fmt_size_diff ||
+ (size_diff == closest_fmt_size_diff &&
+ try_fmt.fmt.pix.pixelformat == desired_pixfmt)) {
closest_fmt_size_diff = size_diff;
closest_fmt = try_fmt;
}
@@ -189,6 +212,7 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
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 *
@@ -208,17 +232,42 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
return 0;
}
+/* Is conversion necessary ? */
+int v4lconvert_needs_conversion(struct v4lconvert_data *data,
+ const struct v4l2_format *src_fmt, /* in */
+ const struct v4l2_format *dest_fmt) /* in */
+{
+ int i;
+
+ if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt)))
+ return 1; /* Formats differ */
+
+ if (!(data->flags & V4LCONVERT_UPSIDE_DOWN))
+ return 0; /* Formats identical and we don't need flip */
+
+ /* Formats are identical, but we need flip, do we support the dest_fmt? */
+ for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++)
+ if (supported_dst_pixfmts[i] == dest_fmt->fmt.pix.pixelformat)
+ break;
+
+ if (i == ARRAY_SIZE(supported_dst_pixfmts))
+ return 0; /* Needs flip but we cannot do it :( */
+ else
+ 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)
+ unsigned char *src, int src_size, unsigned char *_dest, int dest_size)
{
unsigned int header_width, header_height;
- int result, needed;
+ int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
unsigned char *components[3];
+ unsigned char *dest = _dest;
/* Special case when no conversion is needed */
- if(!memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) {
+ 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;
@@ -226,6 +275,7 @@ int v4lconvert_convert(struct v4lconvert_data *data,
/* 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;
@@ -244,7 +294,15 @@ int v4lconvert_convert(struct v4lconvert_data *data,
return -1;
}
+ if (data->flags & V4LCONVERT_UPSIDE_DOWN) {
+ rotate = 180;
+ dest = alloca(needed);
+ }
+
switch (src_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_PJPG:
+ jpeg_flags |= TINYJPEG_FLAGS_PIXART_JPEG;
+ /* Fall through */
case V4L2_PIX_FMT_MJPEG:
case V4L2_PIX_FMT_JPEG:
if (!data->jdec) {
@@ -255,9 +313,7 @@ int v4lconvert_convert(struct v4lconvert_data *data,
return -1;
}
}
- tinyjpeg_set_flags(data->jdec,
- (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)?
- TINYJPEG_FLAGS_MJPEG_TABLE : 0);
+ tinyjpeg_set_flags(data->jdec, jpeg_flags);
if (tinyjpeg_parse_header(data->jdec, src, src_size)) {
V4LCONVERT_ERR("parsing JPEG header: %s\n",
tinyjpeg_get_errorstring(data->jdec));
@@ -266,13 +322,22 @@ 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) {
- V4LCONVERT_ERR("unexpected width / height in JPEG header\n");
- V4LCONVERT_ERR("expected: %dx%d, header: %ux%u\n",
- dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height,
- header_width, header_height);
- errno = EIO;
- return -1;
+ if (header_width != dest_fmt->fmt.pix.width ||
+ header_height != dest_fmt->fmt.pix.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;
+ } 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);
+ errno = EIO;
+ return -1;
+ }
}
components[0] = dest;
@@ -281,43 +346,98 @@ int v4lconvert_convert(struct v4lconvert_data *data,
components[2] = components[1] + (dest_fmt->fmt.pix.width *
dest_fmt->fmt.pix.height) / 4;
- if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) {
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ tinyjpeg_set_components(data->jdec, components, 1);
+ result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_RGB24);
+ break;
+ case V4L2_PIX_FMT_BGR24:
tinyjpeg_set_components(data->jdec, components, 1);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_BGR24);
- } else {
+ break;
+ default:
tinyjpeg_set_components(data->jdec, components, 3);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P);
+ break;
}
- /* If the JPEG header checked out ok and we get an error during actual
- decompression, log the error, but don't return an errorcode to the
- application, so that the user gets what we managed to decompress */
- if (result)
- fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s",
- tinyjpeg_get_errorstring(data->jdec));
-
+ if (result) {
+ /* Pixart webcam's seem to regulary generate corrupt frames, which
+ 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) {
+ V4LCONVERT_ERR("Error decompressing JPEG: %s",
+ tinyjpeg_get_errorstring(data->jdec));
+ errno = EAGAIN;
+ return -1;
+ } else {
+ /* If the JPEG header checked out ok and we get an error during actual
+ decompression, log the error, but don't return an errorcode to the
+ application, so that the user gets what we managed to decompress */
+ fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s",
+ tinyjpeg_get_errorstring(data->jdec));
+ }
+ }
break;
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
- if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ 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);
+ 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);
- else
+ break;
+ default:
v4lconvert_bayer_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
+ break;
+ }
break;
+ /* YUYV line by line formats */
case V4L2_PIX_FMT_SPCA501:
- if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
- v4lconvert_spca501_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- else
- v4lconvert_spca501_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ 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;
+
+ switch (src_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_SPCA501:
+ v4lconvert_spca501_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ case V4L2_PIX_FMT_SPCA505:
+ v4lconvert_spca505_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ case V4L2_PIX_FMT_SPCA508:
+ v4lconvert_spca508_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ }
+
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ v4lconvert_yuv420_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_yuv420_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ }
break;
+ }
/* compressed bayer formats */
case V4L2_PIX_FMT_SPCA561:
@@ -336,7 +456,7 @@ int v4lconvert_convert(struct v4lconvert_data *data,
case V4L2_PIX_FMT_SN9C10X:
v4lconvert_decode_sn9c10x(src, tmpbuf, dest_fmt->fmt.pix.width,
dest_fmt->fmt.pix.height);
- bayer_fmt = V4L2_PIX_FMT_SGBRG8;
+ bayer_fmt = V4L2_PIX_FMT_SBGGR8;
break;
case V4L2_PIX_FMT_PAC207:
v4lconvert_decode_pac207(src, tmpbuf, dest_fmt->fmt.pix.width,
@@ -345,24 +465,57 @@ int v4lconvert_convert(struct v4lconvert_data *data,
break;
}
- if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ v4lconvert_bayer_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.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);
- else
+ break;
+ default:
v4lconvert_bayer_to_yuv420(tmpbuf, dest, dest_fmt->fmt.pix.width,
dest_fmt->fmt.pix.height, bayer_fmt);
+ break;
+ }
break;
}
+ case V4L2_PIX_FMT_RGB24:
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ printf("FIXME add rgb24 -> yuv420 conversion\n");
+ break;
+ }
+ break;
case V4L2_PIX_FMT_BGR24:
- /* dest must be V4L2_PIX_FMT_YUV420 then */
- printf("FIXME add bgr24 -> yuv420 conversion\n");
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ printf("FIXME add bgr24 -> yuv420 conversion\n");
+ break;
+ }
break;
case V4L2_PIX_FMT_YUV420:
- /* dest must be V4L2_PIX_FMT_BGR24 then */
- v4lconvert_yuv420_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ v4lconvert_yuv420_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_yuv420_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
+ dest_fmt->fmt.pix.height);
+ break;
+ }
break;
default:
@@ -371,6 +524,45 @@ int v4lconvert_convert(struct v4lconvert_data *data,
return -1;
}
+ /* 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;
+
+ 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);
+ }
+
return needed;
}