summaryrefslogtreecommitdiff
path: root/v4l2-apps/libv4l/libv4lconvert
diff options
context:
space:
mode:
Diffstat (limited to 'v4l2-apps/libv4l/libv4lconvert')
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/Makefile7
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/bayer.c17
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/crop.c168
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/flip.c51
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/hm12.c159
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h90
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c839
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/mr97310a.c172
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/rgbyuv.c197
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/sn9c20x.c137
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/spca501.c36
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/sq905c.c217
12 files changed, 1778 insertions, 312 deletions
diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile
index 641d19d6e..f779011b4 100644
--- a/v4l2-apps/libv4l/libv4lconvert/Makefile
+++ b/v4l2-apps/libv4l/libv4lconvert/Makefile
@@ -10,8 +10,9 @@ CONVERT_LIB = libv4lconvert.so
override CPPFLAGS += -fPIC
endif
-CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o flip.o \
- jidctflt.o spca561-decompress.o rgbyuv.o spca501.o bayer.o
+CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o sn9c20x.o pac207.o \
+ mr97310a.o flip.o crop.o jidctflt.o spca561-decompress.o \
+ rgbyuv.o spca501.o sq905c.o bayer.o hm12.o
TARGETS = $(CONVERT_LIB) libv4lconvert.pc
INCLUDES = ../include/libv4lconvert.h
@@ -57,7 +58,7 @@ endif
install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig
clean::
- rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~
+ rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~ *.orig *.rej
%.o: %.c
$(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $<
diff --git a/v4l2-apps/libv4l/libv4lconvert/bayer.c b/v4l2-apps/libv4l/libv4lconvert/bayer.c
index ca7bb486f..033ee2724 100644
--- a/v4l2-apps/libv4l/libv4lconvert/bayer.c
+++ b/v4l2-apps/libv4l/libv4lconvert/bayer.c
@@ -433,16 +433,23 @@ static void v4lconvert_border_bayer_line_to_y(
}
}
-void v4lconvert_bayer_to_yuv420(const unsigned char *bayer,
- unsigned char *yuv, int width, int height, unsigned int pixfmt)
+void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv,
+ int width, int height, unsigned int src_pixfmt, int yvu)
{
int blue_line = 0, start_with_green = 0, x, y;
unsigned char *ydst = yuv;
- unsigned char *udst = yuv + width * height;
- unsigned char *vdst = udst + width * height / 4;
+ unsigned char *udst, *vdst;
+
+ if (yvu) {
+ vdst = yuv + width * height;
+ udst = vdst + width * height / 4;
+ } else {
+ udst = yuv + width * height;
+ vdst = udst + width * height / 4;
+ }
/* First calculate the u and v planes 2x2 pixels at a time */
- switch (pixfmt) {
+ switch (src_pixfmt) {
case V4L2_PIX_FMT_SBGGR8:
for (y = 0; y < height; y += 2) {
for (x = 0; x < width; x += 2) {
diff --git a/v4l2-apps/libv4l/libv4lconvert/crop.c b/v4l2-apps/libv4l/libv4lconvert/crop.c
new file mode 100644
index 000000000..4294fbeaf
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/crop.c
@@ -0,0 +1,168 @@
+/*
+
+# RGB and YUV crop routines
+
+# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# 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 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
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#include <string.h>
+#include "libv4lconvert-priv.h"
+
+
+static void v4lconvert_reduceandcrop_rgbbgr24(
+ unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x, y;
+ int startx = src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width;
+ int starty = src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height;
+
+ src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx;
+
+ for (y = 0; y < dest_fmt->fmt.pix.height; y++) {
+ unsigned char *mysrc = src;
+ for (x = 0; x < dest_fmt->fmt.pix.width; x++) {
+ *(dest++) = *(mysrc++);
+ *(dest++) = *(mysrc++);
+ *(dest++) = *(mysrc++);
+ mysrc += 3; /* skip one pixel */
+ }
+ src += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */
+ }
+}
+
+static void v4lconvert_crop_rgbbgr24(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x;
+ int startx = (src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2;
+ int starty = (src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2;
+
+ src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx;
+
+ for (x = 0; x < dest_fmt->fmt.pix.height; x++) {
+ memcpy(dest, src, dest_fmt->fmt.pix.width * 3);
+ src += src_fmt->fmt.pix.bytesperline;
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+}
+
+static void v4lconvert_reduceandcrop_yuv420(
+ unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x,y;
+ int dest_height_half = dest_fmt->fmt.pix.height / 2;
+ int dest_width_half = dest_fmt->fmt.pix.width / 2;
+ int startx = src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width;
+ int starty = src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height;
+ unsigned char *mysrc, *mysrc2;
+
+ /* Y */
+ mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx;
+ for (y = 0; y < dest_fmt->fmt.pix.height; y++){
+ mysrc2 = mysrc;
+ for (x = 0; x < dest_fmt->fmt.pix.width; x++){
+ *(dest++) = *mysrc2;
+ mysrc2 += 2; /* skip one pixel */
+ }
+ mysrc += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */
+ }
+
+ /* U */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline +
+ (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (y = 0; y < dest_height_half; y++){
+ mysrc2 = mysrc;
+ for (x = 0; x < dest_width_half; x++){
+ *(dest++) = *mysrc2;
+ mysrc2 += 2; /* skip one pixel */
+ }
+ mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */
+ }
+
+ /* V */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4
+ + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (y = 0; y < dest_height_half; y++){
+ mysrc2 = mysrc;
+ for (x = 0; x < dest_width_half; x++){
+ *(dest++) = *mysrc2;
+ mysrc2 += 2; /* skip one pixel */
+ }
+ mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */
+ }
+}
+
+static void v4lconvert_crop_yuv420(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x;
+ int startx = (src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2;
+ int starty = (src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2;
+ unsigned char *mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx;
+
+ /* Y */
+ for (x = 0; x < dest_fmt->fmt.pix.height; x++) {
+ memcpy(dest, mysrc, dest_fmt->fmt.pix.width);
+ mysrc += src_fmt->fmt.pix.bytesperline;
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+
+ /* U */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline +
+ (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) {
+ memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2);
+ mysrc += src_fmt->fmt.pix.bytesperline / 2;
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+
+ /* V */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4
+ + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) {
+ memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2);
+ mysrc += src_fmt->fmt.pix.bytesperline / 2;
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+}
+
+void v4lconvert_crop(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width &&
+ src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height)
+ v4lconvert_reduceandcrop_rgbbgr24(src, dest, src_fmt, dest_fmt);
+ else
+ v4lconvert_crop_rgbbgr24(src, dest, src_fmt, dest_fmt);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width &&
+ src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height)
+ v4lconvert_reduceandcrop_yuv420(src, dest, src_fmt, dest_fmt);
+ else
+ v4lconvert_crop_yuv420(src, dest, src_fmt, dest_fmt);
+
+ break;
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/flip.c b/v4l2-apps/libv4l/libv4lconvert/flip.c
index cd3468a89..f47afde72 100644
--- a/v4l2-apps/libv4l/libv4lconvert/flip.c
+++ b/v4l2-apps/libv4l/libv4lconvert/flip.c
@@ -22,8 +22,8 @@
#include "libv4lconvert-priv.h"
-void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int width, int height)
+static void v4lconvert_rotate180_rgbbgr24(const unsigned char *src,
+ unsigned char *dst, int width, int height)
{
int i;
@@ -38,8 +38,8 @@ void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst,
}
}
-void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+static void v4lconvert_rotate180_yuv420(const unsigned char *src,
+ unsigned char *dst, int width, int height)
{
int i;
@@ -59,8 +59,8 @@ void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst,
*dst++ = *src--;
}
-void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight)
+static void v4lconvert_rotate90_rgbbgr24(const unsigned char *src,
+ unsigned char *dst, int destwidth, int destheight)
{
int x, y;
#define srcwidth destheight
@@ -75,8 +75,8 @@ void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst,
}
}
-void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight)
+static void v4lconvert_rotate90_yuv420(const unsigned char *src,
+ unsigned char *dst, int destwidth, int destheight)
{
int x, y;
@@ -105,3 +105,38 @@ void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst,
*dst++ = src[offset];
}
}
+
+void v4lconvert_rotate(unsigned char *src, unsigned char *dest,
+ int width, int height, unsigned int pix_fmt, int rotate)
+{
+ switch (rotate) {
+ case 0:
+ break;
+ case 90:
+ switch (pix_fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_rotate90_rgbbgr24(src, dest, width, height);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_rotate90_yuv420(src, dest, width, height);
+ break;
+ }
+ break;
+ case 180:
+ switch (pix_fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_rotate180_rgbbgr24(src, dest, width, height);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_rotate180_yuv420(src, dest, width, height);
+ break;
+ }
+ break;
+ default:
+ printf("FIXME add %d degrees rotation\n", rotate);
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/hm12.c b/v4l2-apps/libv4l/libv4lconvert/hm12.c
new file mode 100644
index 000000000..f711627b4
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/hm12.c
@@ -0,0 +1,159 @@
+/*
+
+cx2341x HM12 conversion routines
+
+(C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+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 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
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+#include "libv4lconvert-priv.h"
+#include <string.h>
+
+/* The HM12 format is used in the Conexant cx23415/6/8 MPEG encoder devices.
+ It is a macroblock format with separate Y and UV planes, each plane
+ consisting of 16x16 values. All lines are always 720 bytes long. If the
+ width of the image is less than 720, then the remainder is padding.
+
+ The height has to be a multiple of 32 in order to get correct chroma
+ values.
+
+ It is basically a by-product of the MPEG encoding inside the device,
+ which is available for raw video as a 'bonus feature'.
+ */
+
+#define CLIP(color) \
+ (unsigned char)(((color) > 0xff) ? 0xff : (((color) < 0) ? 0 : (color)))
+
+static const int stride = 720;
+
+static void v4lconvert_hm12_to_rgb(const unsigned char *src, unsigned char *dest,
+ int width, int height, int rgb)
+{
+ unsigned int y, x, i, j;
+ const unsigned char *y_base = src;
+ const unsigned char *uv_base = src + stride * height;
+ const unsigned char *src_y;
+ const unsigned char *src_uv;
+ int mb_size = 256;
+ int r = rgb ? 0 : 2;
+ int b = 2 - r;
+
+ for (y = 0; y < height; y += 16) {
+ int mb_y = (y / 16) * (stride / 16);
+ int mb_uv = (y / 32) * (stride / 16);
+ int maxy = (height - y < 16 ? height - y : 16);
+
+ for (x = 0; x < width; x += 16, mb_y++, mb_uv++) {
+ int maxx = (width - x < 16 ? width - x : 16);
+
+ src_y = y_base + mb_y * mb_size;
+ src_uv = uv_base + mb_uv * mb_size;
+
+ if (y & 0x10)
+ src_uv += mb_size / 2;
+
+ for (i = 0; i < maxy; i++) {
+ int idx = (x + (y + i) * width) * 3;
+
+ for (j = 0; j < maxx; j++) {
+ int y = src_y[j];
+ int u = src_uv[j & ~1];
+ int v = src_uv[j | 1];
+ int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
+ int rg = (((u - 128) << 1) + (u - 128) +
+ ((v - 128) << 2) + ((v - 128) << 1)) >> 3;
+ int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
+
+ dest[idx+r] = CLIP(y + v1);
+ dest[idx+1] = CLIP(y - rg);
+ dest[idx+b] = CLIP(y + u1);
+ idx += 3;
+ }
+ src_y += 16;
+ if (i & 1)
+ src_uv += 16;
+ }
+ }
+ }
+}
+
+void v4lconvert_hm12_to_rgb24(const unsigned char *src, unsigned char *dest,
+ int width, int height)
+{
+ v4lconvert_hm12_to_rgb(src, dest, width, height, 1);
+}
+
+void v4lconvert_hm12_to_bgr24(const unsigned char *src, unsigned char *dest,
+ int width, int height)
+{
+ v4lconvert_hm12_to_rgb(src, dest, width, height, 0);
+}
+
+static void de_macro_uv(unsigned char *dstu, unsigned char *dstv,
+ const unsigned char *src, int w, int h)
+{
+ unsigned int y, x, i, j;
+
+ for (y = 0; y < h; y += 16) {
+ for (x = 0; x < w; x += 8) {
+ const unsigned char *src_uv = src + y * stride + x * 32;
+ int maxy = (h - y < 16 ? h - y : 16);
+ int maxx = (w - x < 8 ? w - x : 8);
+
+ for (i = 0; i < maxy; i++) {
+ int idx = x + (y + i) * w;
+
+ for (j = 0; j < maxx; j++) {
+ dstu[idx+j] = src_uv[2 * j];
+ dstv[idx+j] = src_uv[2 * j + 1];
+ }
+ src_uv += 16;
+ }
+ }
+ }
+}
+
+static void de_macro_y(unsigned char *dst, const unsigned char *src,
+ int w, int h)
+{
+ unsigned int y, x, i;
+
+ for (y = 0; y < h; y += 16) {
+ for (x = 0; x < w; x += 16) {
+ const unsigned char *src_y = src + y * stride + x * 16;
+ int maxy = (h - y < 16 ? h - y : 16);
+ int maxx = (w - x < 16 ? w - x : 16);
+
+ for (i = 0; i < maxy; i++) {
+ memcpy(dst + x + (y + i) * w, src_y, maxx);
+ src_y += 16;
+ }
+ }
+ }
+}
+
+void v4lconvert_hm12_to_yuv420(const unsigned char *src, unsigned char *dest,
+ int width, int height, int yvu)
+{
+ de_macro_y(dest, src, width, height);
+ dest += width * height;
+ src += stride * height;
+ if (yvu)
+ de_macro_uv(dest + width * height / 4, dest, src, width / 2, height / 2);
+ else
+ de_macro_uv(dest, dest + width * height / 4, src, width / 2, height / 2);
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h
index 0c4eff6ce..5ce7bde3b 100644
--- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h
+++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h
@@ -43,6 +43,14 @@
#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7')
#endif
+#ifndef V4L2_PIX_FMT_MR97310A
+#define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M','3','1','0')
+#endif
+
+#ifndef V4L2_PIX_FMT_SQ905C
+#define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C')
+#endif
+
#ifndef V4L2_PIX_FMT_PJPG
#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G')
#endif
@@ -63,6 +71,14 @@
#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U')
#endif
+#ifndef V4L2_PIX_FMT_HM12
+#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2')
+#endif
+
+#ifndef V4L2_PIX_FMT_SN9C20X_I420
+#define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0')
+#endif
+
#define V4LCONVERT_ERROR_MSG_SIZE 256
#define V4LCONVERT_MAX_FRAMESIZES 16
@@ -71,7 +87,9 @@
"v4l-convert: error " __VA_ARGS__)
/* Card flags */
-#define V4LCONVERT_UPSIDE_DOWN 0x01
+#define V4LCONVERT_ROTATE_90 0x01
+#define V4LCONVERT_ROTATE_180 0x02
+#define V4LCONVERT_IS_UVC 0x04
/* Pixformat flags */
#define V4LCONVERT_COMPRESSED 0x01
@@ -85,10 +103,20 @@ struct v4lconvert_data {
struct jdec_private *jdec;
struct v4l2_frmsizeenum framesizes[V4LCONVERT_MAX_FRAMESIZES];
unsigned int no_framesizes;
+ int convert_buf_size;
+ int rotate_buf_size;
+ int convert_pixfmt_buf_size;
+ unsigned char *convert_buf;
+ unsigned char *rotate_buf;
+ unsigned char *convert_pixfmt_buf;
};
struct v4lconvert_flags_info {
- const char *card;
+ unsigned short vendor_id;
+ unsigned short product_id;
+/* We could also use the USB manufacturer and product strings some devices have
+ const char *manufacturer;
+ const char *product; */
int flags;
};
@@ -97,11 +125,14 @@ struct v4lconvert_pixfmt {
int flags;
};
+void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, int bgr, int yvu);
+
void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dst,
int width, int height);
@@ -110,7 +141,7 @@ void v4lconvert_yuyv_to_bgr24(const unsigned char *src, unsigned char *dst,
int width, int height);
void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst,
int width, int height);
@@ -118,20 +149,32 @@ void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst,
void v4lconvert_yvyu_to_bgr24(const unsigned char *src, unsigned char *dst,
int width, int height);
-void v4lconvert_yvyu_to_yuv420(const unsigned char *src, unsigned char *dst,
+void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dst,
+ int width, int height);
+
+void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dst,
int width, int height);
+void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dst,
+ int width, int height, int yvu);
+
void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst,
int width, int height);
+void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dst,
+ const struct v4l2_format *src_fmt);
+
void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
+
+void v4lconvert_sn9c20x_to_yuv420(const unsigned char *src, unsigned char *dst,
+ int width, int height, int yvu);
void v4lconvert_decode_spca561(const unsigned char *src, unsigned char *dst,
int width, int height);
@@ -142,25 +185,34 @@ void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst,
void v4lconvert_decode_pac207(const unsigned char *src, unsigned char *dst,
int width, int height);
+void v4lconvert_decode_mr97310a(const unsigned char *src, unsigned char *dst,
+ int width, int height);
+
+void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst,
+ int width, int height);
+
void v4lconvert_bayer_to_rgb24(const unsigned char *bayer,
unsigned char *rgb, int width, int height, unsigned int pixfmt);
void v4lconvert_bayer_to_bgr24(const unsigned char *bayer,
unsigned char *rgb, int width, int height, unsigned int pixfmt);
-void v4lconvert_bayer_to_yuv420(const unsigned char *bayer,
- unsigned char *yuv, int width, int height, unsigned int pixfmt);
+void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv,
+ int width, int height, unsigned int src_pixfmt, int yvu);
-void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight);
+void v4lconvert_hm12_to_rgb24(const unsigned char *src,
+ unsigned char *dst, int width, int height);
-void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight);
+void v4lconvert_hm12_to_bgr24(const unsigned char *src,
+ unsigned char *dst, int width, int height);
-void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int width, int height);
+void v4lconvert_hm12_to_yuv420(const unsigned char *src,
+ unsigned char *dst, int width, int height, int yvu);
-void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+void v4lconvert_rotate(unsigned char *src, unsigned char *dest,
+ int width, int height, unsigned int pix_fmt, int rotate);
+
+void v4lconvert_crop(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt);
#endif
diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
index 93bc67c7e..1204e8ef2 100644
--- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
+++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
@@ -21,6 +21,8 @@
#include <stdlib.h>
#include <syscall.h>
#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include "libv4lconvert.h"
#include "libv4lconvert-priv.h"
@@ -32,30 +34,36 @@
#define SUPPORTED_DST_PIXFMTS \
{ V4L2_PIX_FMT_RGB24, 0 }, \
{ V4L2_PIX_FMT_BGR24, 0 }, \
- { V4L2_PIX_FMT_YUV420, 0 }
+ { V4L2_PIX_FMT_YUV420, 0 }, \
+ { V4L2_PIX_FMT_YVU420, 0 }
static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
- unsigned int pixelformat);
+ 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_YUYV, 0 },
- { V4L2_PIX_FMT_YVYU, 0 },
- { V4L2_PIX_FMT_SBGGR8, 0 },
- { V4L2_PIX_FMT_SGBRG8, 0 },
- { V4L2_PIX_FMT_SGRBG8, 0 },
- { V4L2_PIX_FMT_SRGGB8, 0 },
- { V4L2_PIX_FMT_SPCA501, 0 },
- { V4L2_PIX_FMT_SPCA505, 0 },
- { V4L2_PIX_FMT_SPCA508, 0 },
- { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_YUYV, 0 },
+ { V4L2_PIX_FMT_YVYU, 0 },
+ { V4L2_PIX_FMT_UYVY, 0 },
+ { V4L2_PIX_FMT_SN9C20X_I420, 0 },
+ { V4L2_PIX_FMT_SBGGR8, 0 },
+ { V4L2_PIX_FMT_SGBRG8, 0 },
+ { V4L2_PIX_FMT_SGRBG8, 0 },
+ { V4L2_PIX_FMT_SRGGB8, 0 },
+ { V4L2_PIX_FMT_SPCA501, 0 },
+ { V4L2_PIX_FMT_SPCA505, 0 },
+ { V4L2_PIX_FMT_SPCA508, 0 },
+ { V4L2_PIX_FMT_HM12, 0 },
+ { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_MR97310A, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_SQ905C, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED },
};
static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = {
@@ -64,13 +72,65 @@ 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 */
+ { 0x0471, 0x0325, V4LCONVERT_ROTATE_180 }, /* Philips SPC200NC */
+ { 0x0471, 0x0326, V4LCONVERT_ROTATE_180 }, /* Philips SPC300NC */
+ { 0x0471, 0x032d, V4LCONVERT_ROTATE_180 }, /* Philips SPC210NC */
+ { 0x093a, 0x2476, 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 },
+};
+
+static int v4lconvert_get_flags(int fd)
+{
+ struct stat st;
+ FILE *f;
+ char sysfs_name[512];
+ unsigned short vendor_id = 0;
+ unsigned short product_id = 0;
+ int i;
+
+ if (fstat(fd, &st) || !S_ISCHR(st.st_mode))
+ return 0; /* Should never happen */
+
+ /* Get product ID */
+ snprintf(sysfs_name, sizeof(sysfs_name),
+ "/sys/class/video4linux/video%d/device/idVendor",
+ minor(st.st_rdev));
+ f = fopen(sysfs_name, "r");
+ if (!f)
+ return 0; /* Not an USB device (or no sysfs) */
+ i = fscanf(f, "%hx", &vendor_id);
+ fclose(f);
+
+ /* Get product ID */
+ snprintf(sysfs_name, sizeof(sysfs_name),
+ "/sys/class/video4linux/video%d/device/idProduct",
+ minor(st.st_rdev));
+ f = fopen(sysfs_name, "r");
+ if (!f)
+ return 0; /* Should never happen */
+ i = fscanf(f, "%hx", &product_id);
+ fclose(f);
+
+ for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++)
+ if (v4lconvert_flags[i].vendor_id == vendor_id &&
+ v4lconvert_flags[i].product_id == product_id)
+ return v4lconvert_flags[i].flags;
+
+ return 0;
+}
+
struct v4lconvert_data *v4lconvert_create(int fd)
{
int i, j;
@@ -81,7 +141,6 @@ struct v4lconvert_data *v4lconvert_create(int fd)
return NULL;
data->fd = fd;
- data->jdec = NULL;
/* Check supported formats */
for (i = 0; ; i++) {
@@ -95,21 +154,18 @@ struct v4lconvert_data *v4lconvert_create(int fd)
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);
break;
}
-
- v4lconvert_get_framesizes(data, fmt.pixelformat);
}
data->no_formats = i;
/* Check if this cam has any special flags */
+ data->flags = v4lconvert_get_flags(data->fd);
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;
- }
+ if (!strcmp((char *)cap.driver, "uvcvideo"))
+ data->flags |= V4LCONVERT_IS_UVC;
}
return data;
@@ -122,6 +178,9 @@ void v4lconvert_destroy(struct v4lconvert_data *data)
tinyjpeg_set_components(data->jdec, comps, 3);
tinyjpeg_free(data->jdec);
}
+ free(data->convert_buf);
+ free(data->rotate_buf);
+ free(data->convert_pixfmt_buf);
free(data);
}
@@ -171,8 +230,65 @@ 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,
+/* 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.
+ important for UVC based cams as doing try_fmt there actually causes I/O */
+static int v4lconvert_do_try_format_uvc(struct v4lconvert_data *data,
+ struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
+{
+ int i;
+ 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;
+
+ for (i = 0; i < data->no_framesizes; i++) {
+ if (data->framesizes[i].discrete.width <= dest_fmt->fmt.pix.width &&
+ data->framesizes[i].discrete.height <= dest_fmt->fmt.pix.height) {
+ int size_x_diff = dest_fmt->fmt.pix.width -
+ data->framesizes[i].discrete.width;
+ int size_y_diff = dest_fmt->fmt.pix.height -
+ data->framesizes[i].discrete.height;
+ unsigned int size_diff = size_x_diff * size_x_diff +
+ size_y_diff * size_y_diff;
+
+ if (size_diff < closest_fmt_size_diff) {
+ closest_fmt_size_diff = size_diff;
+ best_framesize = i;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) {
+ /* is this format supported? */
+ 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)))
+ best_format = supported_src_pixfmts[i].fmt;
+ }
+
+ dest_fmt->fmt.pix.width = data->framesizes[best_framesize].discrete.width;
+ dest_fmt->fmt.pix.height = data->framesizes[best_framesize].discrete.height;
+ dest_fmt->fmt.pix.field = V4L2_FIELD_NONE; /* UVC has no fields */
+ /* Not pretty, the pwc driver doesn't fill these in try_fmt either though,
+ so we should be able to get away with this. */
+ dest_fmt->fmt.pix.bytesperline = 0;
+ dest_fmt->fmt.pix.sizeimage = 0;
+ dest_fmt->fmt.pix.colorspace = 0;
+ dest_fmt->fmt.pix.priv = 0;
+
+ *src_fmt = *dest_fmt;
+ src_fmt->fmt.pix.pixelformat = best_format;
+
+ return 0;
+}
+
+static int v4lconvert_do_try_format(struct v4lconvert_data *data,
struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
int i;
@@ -180,14 +296,8 @@ 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;
- }
+ if (data->flags & V4LCONVERT_IS_UVC)
+ return v4lconvert_do_try_format_uvc(data, dest_fmt, src_fmt);
for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) {
/* is this format supported? */
@@ -209,7 +319,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 +328,96 @@ 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:
+ case V4L2_PIX_FMT_YVU420:
+ 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 well known
+ resolution some apps are hardcoded too and try to give the app what it
+ asked for by cropping a slightly larger resolution */
+ 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_src, try2_dest = *dest_fmt;
+
+ /* Note these are chosen so that cropping to vga res just works for
+ vv6410 sensor cams, which have 356x292 and 180x148 */
+ try2_dest.fmt.pix.width = desired_width * 113 / 100;
+ try2_dest.fmt.pix.height = desired_height * 124 / 100;
+ 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 <= desired_width * 5 / 4 &&
+ try2_dest.fmt.pix.height >= desired_height &&
+ try2_dest.fmt.pix.height <= desired_height * 5 / 4) ||
+ (try2_dest.fmt.pix.width >= desired_width * 2 &&
+ try2_dest.fmt.pix.width <= desired_width * 5 / 2 &&
+ try2_dest.fmt.pix.height >= desired_height &&
+ try2_dest.fmt.pix.height <= desired_height * 5 / 2))) {
+ /* 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(try_src.fmt.pix.width != try_dest.fmt.pix.width ||
+ try_src.fmt.pix.height != try_dest.fmt.pix.height ||
+ try_src.fmt.pix.pixelformat != try_dest.fmt.pix.pixelformat)
+ v4lconvert_fixup_fmt(&try_dest);
+
+ *dest_fmt = try_dest;
if (src_fmt)
- *src_fmt = closest_fmt;
+ *src_fmt = try_src;
return 0;
}
@@ -256,10 +427,12 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data,
const struct v4l2_format *src_fmt, /* in */
const struct v4l2_format *dest_fmt) /* in */
{
- if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt)))
+ if(src_fmt->fmt.pix.width != dest_fmt->fmt.pix.width ||
+ src_fmt->fmt.pix.height != dest_fmt->fmt.pix.height ||
+ src_fmt->fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat)
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 +442,35 @@ 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 unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data,
+ int needed, unsigned char **buf, int *buf_size)
{
- unsigned int header_width, header_height;
- 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 (!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 (*buf_size < needed) {
+ free(*buf);
+ *buf = malloc(needed);
+ if (*buf == NULL) {
+ *buf_size = 0;
+ V4LCONVERT_ERR("could not allocate memory\n");
+ errno = ENOMEM;
+ return NULL;
+ }
+ *buf_size = needed;
}
+ return *buf;
+}
- if (data->flags & V4LCONVERT_UPSIDE_DOWN) {
- rotate = 180;
- dest = alloca(needed);
- }
+static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
+ unsigned char *src, int src_size, unsigned char *dest,
+ const struct v4l2_format *src_fmt, unsigned int dest_pix_fmt)
+{
+ unsigned int header_width, header_height;
+ int result = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
+ unsigned char *components[3];
+ unsigned int src_pix_fmt = src_fmt->fmt.pix.pixelformat;
+ unsigned int width = src_fmt->fmt.pix.width;
+ unsigned int height = src_fmt->fmt.pix.height;
- 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 +493,27 @@ 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\n");
+ 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;
- 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 +522,15 @@ 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:
+ components[1] = components[0] + width * height;
+ components[2] = components[1] + width * height / 4;
+ tinyjpeg_set_components(data->jdec, components, 3);
+ result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ components[2] = components[0] + width * height;
+ components[1] = components[2] + width * height / 4;
tinyjpeg_set_components(data->jdec, components, 3);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P);
break;
@@ -379,8 +541,8 @@ 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) {
- V4LCONVERT_ERR("Error decompressing JPEG: %s",
+ if (src_pix_fmt == V4L2_PIX_FMT_PJPG) {
+ V4LCONVERT_ERR("decompressing JPEG: %s",
tinyjpeg_get_errorstring(data->jdec));
errno = EAGAIN;
return -1;
@@ -394,223 +556,357 @@ int v4lconvert_convert(struct v4lconvert_data *data,
}
break;
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- 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);
- 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 */
+ /* Custom cam specific YUV formats */
case V4L2_PIX_FMT_SPCA501:
case V4L2_PIX_FMT_SPCA505:
case V4L2_PIX_FMT_SPCA508:
+ case V4L2_PIX_FMT_SN9C20X_I420:
{
- 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 *d;
+ int yvu = 0;
+
+ if (dest_pix_fmt != V4L2_PIX_FMT_YUV420 &&
+ dest_pix_fmt != V4L2_PIX_FMT_YVU420) {
+ d = v4lconvert_alloc_buffer(data, width * height * 3 / 2,
+ &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size);
+ if (!d)
+ return -1;
+ } else
+ d = dest;
- switch (src_fmt->fmt.pix.pixelformat) {
+ if (dest_pix_fmt == V4L2_PIX_FMT_YVU420)
+ yvu = 1;
+
+ 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, yvu);
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, yvu);
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, yvu);
+ break;
+ case V4L2_PIX_FMT_SN9C20X_I420:
+ v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu);
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(data->convert_pixfmt_buf, dest, width,
+ height, yvu);
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(data->convert_pixfmt_buf, dest, width,
+ height, yvu);
break;
}
break;
}
+ /* Conexant cx2341x raw video macroblock format */
+ case V4L2_PIX_FMT_HM12:
+ switch (dest_pix_fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ v4lconvert_hm12_to_rgb24(src, dest, width, height);
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_hm12_to_bgr24(src, dest, width, height);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_hm12_to_yuv420(src, dest, width, height, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_hm12_to_yuv420(src, dest, width, height, 1);
+ break;
+ }
+ break;
+
/* compressed bayer formats */
case V4L2_PIX_FMT_SPCA561:
case V4L2_PIX_FMT_SN9C10X:
case V4L2_PIX_FMT_PAC207:
+ case V4L2_PIX_FMT_MR97310A:
+ case V4L2_PIX_FMT_SQ905C:
{
- unsigned char tmpbuf[dest_fmt->fmt.pix.width*dest_fmt->fmt.pix.height];
- unsigned int bayer_fmt = 0;
+ unsigned char *tmpbuf;
+
+ tmpbuf = v4lconvert_alloc_buffer(data, width * height,
+ &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size);
+ if (!tmpbuf)
+ return -1;
- 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);
- bayer_fmt = V4L2_PIX_FMT_SGBRG8;
+ v4lconvert_decode_spca561(src, tmpbuf, width, height);
+ src_pix_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);
- bayer_fmt = V4L2_PIX_FMT_SBGGR8;
+ v4lconvert_decode_sn9c10x(src, tmpbuf, width, height);
+ src_pix_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);
- bayer_fmt = V4L2_PIX_FMT_SBGGR8;
+ v4lconvert_decode_pac207(src, tmpbuf, width, height);
+ src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+ break;
+ case V4L2_PIX_FMT_MR97310A:
+ v4lconvert_decode_mr97310a(src, tmpbuf, width, height);
+ src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+ break;
+ case V4L2_PIX_FMT_SQ905C:
+ v4lconvert_decode_sq905c(src, tmpbuf, width, height);
+ src_pix_fmt = V4L2_PIX_FMT_SRGGB8;
break;
}
+ src = tmpbuf;
+ /* Deliberate fall through to raw bayer fmt code! */
+ }
- switch (dest_fmt->fmt.pix.pixelformat) {
+ /* Raw bayer formats */
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ 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(src, dest, width, height, src_pix_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(src, dest, width, height, src_pix_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(src, dest, width, height, src_pix_fmt, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_bayer_to_yuv420(src, dest, width, height, src_pix_fmt, 1);
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");
+ v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 1);
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");
+ v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 1);
break;
}
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, 0);
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, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_swap_uv(src, dest, src_fmt);
break;
}
break;
- case V4L2_PIX_FMT_YUYV:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YVU420:
+ 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_yuv420_to_rgb24(src, dest, width,
+ height, 1);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yuyv_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_bgr24(src, dest, width,
+ height, 1);
break;
- default:
- v4lconvert_yuyv_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_swap_uv(src, dest, src_fmt);
break;
}
break;
- case V4L2_PIX_FMT_YVYU:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ 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_yuyv_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_yuyv_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_yuyv_to_yuv420(src, dest, width, height, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height, 1);
break;
}
break;
- default:
- V4LCONVERT_ERR("Unknown src format in conversion\n");
- errno = EINVAL;
- 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_YVYU:
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
+ v4lconvert_yvyu_to_rgb24(src, dest, width, height);
+ break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_rotate90_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yvyu_to_bgr24(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
- v4lconvert_rotate90_yuv420(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ /* Note we use yuyv_to_yuv420 not v4lconvert_yvyu_to_yuv420,
+ with the last argument reversed to make it have as we want */
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height, 1);
break;
- }
- break;
- case 180:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height, 0);
+ break;
+ }
+ break;
+
+ case V4L2_PIX_FMT_UYVY:
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
+ v4lconvert_uyvy_to_rgb24(src, dest, width, height);
+ break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_rotate180_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_uyvy_to_bgr24(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
- v4lconvert_rotate180_yuv420(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_uyvy_to_yuv420(src, dest, width, height, 0);
break;
- }
- break;
- default:
- printf("FIXME add %d degrees rotation\n", rotate);
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_uyvy_to_yuv420(src, dest, width, height, 1);
+ break;
+ }
+ break;
+
+ default:
+ V4LCONVERT_ERR("Unknown src format in conversion\n");
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+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;
+ struct v4l2_format my_dest_fmt = *dest_fmt;
+
+ /* 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;
+ }
+
+ /* When field is V4L2_FIELD_ALTERNATE, each buffer only contains half the
+ lines */
+ if (my_src_fmt.fmt.pix.field == V4L2_FIELD_ALTERNATE) {
+ my_src_fmt.fmt.pix.height /= 2;
+ my_dest_fmt.fmt.pix.height /= 2;
+ }
+
+ /* sanity check, is the dest buffer large enough? */
+ switch (my_dest_fmt.fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3;
+ temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ dest_needed =
+ my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3 / 2;
+ temp_needed =
+ my_src_fmt.fmt.pix.width * my_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 (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat)
+ convert = 1;
+
+ if (data->flags & V4LCONVERT_ROTATE_90)
+ rotate += 90;
+ if (data->flags & V4LCONVERT_ROTATE_180)
+ rotate += 180;
+
+ if (my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width ||
+ my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height)
+ crop = 1;
+
+ /* convert_pixfmt -> rotate -> crop, all steps are optional */
+ if (convert && (rotate || crop)) {
+ convert_dest = v4lconvert_alloc_buffer(data, temp_needed,
+ &data->convert_buf, &data->convert_buf_size);
+ if (!convert_dest)
+ return -1;
+
+ rotate_src = crop_src = convert_dest;
}
- return needed;
+ if (rotate && crop) {
+ rotate_dest = v4lconvert_alloc_buffer(data, temp_needed,
+ &data->rotate_buf, &data->rotate_buf_size);
+ if (!rotate_dest)
+ return -1;
+
+ crop_src = rotate_dest;
+ }
+
+ if (convert) {
+ res = v4lconvert_convert_pixfmt(data, src, src_size, convert_dest,
+ &my_src_fmt,
+ my_dest_fmt.fmt.pix.pixelformat);
+ if (res)
+ return res;
+
+ my_src_fmt.fmt.pix.pixelformat = my_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, &my_dest_fmt);
+
+ return dest_needed;
}
const char *v4lconvert_get_error_message(struct v4lconvert_data *data)
@@ -619,7 +915,7 @@ const char *v4lconvert_get_error_message(struct v4lconvert_data *data)
}
static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
- unsigned int pixelformat)
+ unsigned int pixelformat, int index)
{
int i, j, match;
struct v4l2_frmsizeenum frmsize = { .pixel_format = pixelformat };
@@ -631,7 +927,7 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
/* We got a framesize, check we don't have the same one already */
match = 0;
- for (j = 0; j < data->no_framesizes && !match; j++) {
+ for (j = 0; j < data->no_framesizes; j++) {
if (frmsize.type != data->framesizes[j].type)
continue;
@@ -648,6 +944,8 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
match = 1;
break;
}
+ if (match)
+ break;
}
/* Add this framesize if it is not already in our list */
if (!match) {
@@ -657,6 +955,9 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
return;
}
data->framesizes[data->no_framesizes].type = frmsize.type;
+ /* We use the pixel_format member to store a bitmask of all
+ supported src_formats which can do this size */
+ data->framesizes[data->no_framesizes].pixel_format = 1 << index;
switch(frmsize.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
data->framesizes[data->no_framesizes].discrete = frmsize.discrete;
@@ -668,6 +969,8 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
}
data->no_framesizes++;
}
+ else
+ data->framesizes[j].pixel_format |= 1 << index;
}
}
@@ -702,8 +1005,12 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
int res;
struct v4l2_format src_fmt, dest_fmt;
- if (!v4lconvert_supported_dst_format(frmival->pixel_format))
- return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+ if (!v4lconvert_supported_dst_format(frmival->pixel_format)) {
+ res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+ if (res)
+ V4LCONVERT_ERR("%s\n", strerror(errno));
+ return res;
+ }
/* Check which format we will be using to convert to frmival->pixel_format */
memset(&dest_fmt, 0, sizeof(dest_fmt));
@@ -711,13 +1018,30 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
dest_fmt.fmt.pix.pixelformat = frmival->pixel_format;
dest_fmt.fmt.pix.width = frmival->width;
dest_fmt.fmt.pix.height = frmival->height;
- if ((res = v4lconvert_try_format(data, &dest_fmt, &src_fmt)))
+ if ((res = v4lconvert_try_format(data, &dest_fmt, &src_fmt))) {
+ if (res)
+ V4LCONVERT_ERR("trying format: %s\n", strerror(errno));
return res;
+ }
/* Check the requested format is supported exactly as requested */
if (dest_fmt.fmt.pix.pixelformat != frmival->pixel_format ||
dest_fmt.fmt.pix.width != frmival->width ||
dest_fmt.fmt.pix.height != frmival->height) {
+ int frmival_pixformat = frmival->pixel_format;
+ int dest_pixformat = dest_fmt.fmt.pix.pixelformat;
+ V4LCONVERT_ERR("Could not find matching framesize for: %c%c%c%c %dx%d "
+ "closest match: %c%c%c%c %dx%d\n",
+ frmival_pixformat & 0xff,
+ (frmival_pixformat >> 8) & 0xff,
+ (frmival_pixformat >> 16) & 0xff,
+ frmival_pixformat >> 24,
+ frmival->width, frmival->height,
+ dest_pixformat & 0xff,
+ (dest_pixformat >> 8) & 0xff,
+ (dest_pixformat >> 16) & 0xff,
+ dest_pixformat >> 24,
+ dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height);
errno = EINVAL;
return -1;
}
@@ -727,6 +1051,23 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
frmival->width = src_fmt.fmt.pix.width;
frmival->height = src_fmt.fmt.pix.height;
res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+ if (res) {
+ int dest_pixfmt = dest_fmt.fmt.pix.pixelformat;
+ int src_pixfmt = src_fmt.fmt.pix.pixelformat;
+ V4LCONVERT_ERR("Could not enum frameival index: %d for: %c%c%c%c %dx%d "
+ "using src: %c%c%c%c %dx%d, error: %s\n",
+ frmival->index,
+ dest_pixfmt & 0xff,
+ (dest_pixfmt >> 8) & 0xff,
+ (dest_pixfmt >> 16) & 0xff,
+ dest_pixfmt >> 24,
+ dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height,
+ src_pixfmt & 0xff,
+ (src_pixfmt >> 8) & 0xff,
+ (src_pixfmt >> 16) & 0xff,
+ src_pixfmt >> 24,
+ src_fmt.fmt.pix.width, src_fmt.fmt.pix.height, strerror(errno));
+ }
/* Restore the requested format in the frmival struct */
frmival->pixel_format = dest_fmt.fmt.pix.pixelformat;
diff --git a/v4l2-apps/libv4l/libv4lconvert/mr97310a.c b/v4l2-apps/libv4l/libv4lconvert/mr97310a.c
new file mode 100644
index 000000000..e6ce94b29
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/mr97310a.c
@@ -0,0 +1,172 @@
+/*
+ * MR97310A decoder
+ *
+ * Copyright (C) 2004 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "libv4lconvert-priv.h"
+
+#define CLIP(x) ((x)<0?0:((x)>0xff)?0xff:(x))
+
+/* FIXME not threadsafe */
+static int decoder_initialized = 0;
+
+static struct {
+ unsigned char is_abs;
+ unsigned char len;
+ signed char val;
+} table[256];
+
+static void init_mr97310a_decoder(void)
+{
+ int i;
+ int is_abs, val, len;
+
+ for (i = 0; i < 256; ++i) {
+ is_abs = 0;
+ val = 0;
+ len = 0;
+ if ((i & 0x80) == 0) {
+ /* code 0 */
+ val = 0;
+ len = 1;
+ } else if ((i & 0xe0) == 0xc0) {
+ /* code 110 */
+ val = -3;
+ len = 3;
+ } else if ((i & 0xe0) == 0xa0) {
+ /* code 101 */
+ val = +3;
+ len = 3;
+ } else if ((i & 0xf0) == 0x80) {
+ /* code 1000 */
+ val = +7;
+ len = 4;
+ } else if ((i & 0xf0) == 0x90) {
+ /* code 1001 */
+ val = -7;
+ len = 4;
+ } else if ((i & 0xf0) == 0xf0) {
+ /* code 1111 */
+ val = -15;
+ len = 4;
+ } else if ((i & 0xf8) == 0xe0) {
+ /* code 11100 */
+ val = +15;
+ len = 5;
+ } else if ((i & 0xf8) == 0xe8) {
+ /* code 11101xxxxx */
+ is_abs = 1;
+ val = 0; /* value is calculated later */
+ len = 5;
+ }
+ table[i].is_abs = is_abs;
+ table[i].val = val;
+ table[i].len = len;
+ }
+ decoder_initialized = 1;
+}
+
+static inline unsigned char get_byte(const unsigned char *inp,
+ unsigned int bitpos)
+{
+ const unsigned char *addr;
+ addr = inp + (bitpos >> 3);
+ return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7)));
+}
+
+void v4lconvert_decode_mr97310a(const unsigned char *inp, unsigned char *outp,
+ int width, int height)
+{
+ int row, col;
+ int val;
+ int bitpos;
+ unsigned char code;
+ unsigned char lp, tp, tlp, trp;
+
+ if (!decoder_initialized)
+ init_mr97310a_decoder();
+
+ /* remove the header */
+ inp += 12;
+
+ bitpos = 0;
+
+ /* main decoding loop */
+ for (row = 0; row < height; ++row) {
+ col = 0;
+
+ /* first two pixels in first two rows are stored as raw 8-bit */
+ if (row < 2) {
+ code = get_byte(inp, bitpos);
+ bitpos += 8;
+ *outp++ = code;
+
+ code = get_byte(inp, bitpos);
+ bitpos += 8;
+ *outp++ = code;
+
+ col += 2;
+ }
+
+ while (col < width) {
+ /* get bitcode */
+ code = get_byte(inp, bitpos);
+ /* update bit position */
+ bitpos += table[code].len;
+
+ /* calculate pixel value */
+ if (table[code].is_abs) {
+ /* get 5 more bits and use them as absolute value */
+ code = get_byte(inp, bitpos);
+ val = (code & 0xf8);
+ bitpos += 5;
+
+ } else {
+ /* value is relative to top or left pixel */
+ val = table[code].val;
+ lp = outp[-2];
+ if (row > 1) {
+ tlp = outp[-2*width-2];
+ tp = outp[-2*width];
+ trp = outp[-2*width+2];
+ }
+ if (row < 2) {
+ /* top row: relative to left pixel */
+ val += lp;
+ } else if (col < 2) {
+ /* left column: relative to top pixel */
+ /* initial estimate */
+ val += (2*tp + 2*trp + 1)/4;
+ } else if (col > width - 3) {
+ /* left column: relative to top pixel */
+ val += (2*tp + 2*tlp + 1)/4;
+ /* main area: average of left and top pixel */
+ } else {
+ /* initial estimate for predictor */
+ val += (2*lp + tp + trp + 1)/4;
+ }
+ }
+ /* store pixel */
+ *outp++ = CLIP(val);
+ ++col;
+ }
+ }
+
+ return;
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c
index 0f26b227a..00706be9d 100644
--- a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c
+++ b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c
@@ -20,13 +20,65 @@
*/
+#include <string.h>
#include "libv4lconvert-priv.h"
-#define RGB2YUV(r,g,b,y,u,v) \
- (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15); \
+#define RGB2Y(r,g,b,y) \
+ (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15)
+
+#define RGB2UV(r,g,b,u,v) \
(u) = (( -4878*(r) - 9578*(g) + 14456*(b) + 4210688) >> 15); \
(v) = (( 14456*(r) - 12105*(g) - 2351*(b) + 4210688) >> 15)
+void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, int bgr, int yvu)
+{
+ int x, y;
+ unsigned char *udest, *vdest;
+
+ /* Y */
+ for (y = 0; y < src_fmt->fmt.pix.height; y++) {
+ for (x = 0; x < src_fmt->fmt.pix.width; x++) {
+ if (bgr) {
+ RGB2Y(src[2], src[1], src[0], *dest++);
+ } else {
+ RGB2Y(src[0], src[1], src[2], *dest++);
+ }
+ src += 3;
+ }
+ src += src_fmt->fmt.pix.bytesperline - 3 * src_fmt->fmt.pix.width;
+ }
+ src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline;
+
+ /* U + V */
+ if (yvu) {
+ vdest = dest;
+ udest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4;
+ } else {
+ udest = dest;
+ vdest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4;
+ }
+
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ for (x = 0; x < src_fmt->fmt.pix.width / 2; x++) {
+ int avg_src[3];
+ avg_src[0] = (src[0] + src[3] + src[src_fmt->fmt.pix.bytesperline] +
+ src[src_fmt->fmt.pix.bytesperline + 3]) / 4;
+ avg_src[1] = (src[1] + src[4] + src[src_fmt->fmt.pix.bytesperline + 1] +
+ src[src_fmt->fmt.pix.bytesperline + 4]) / 4;
+ avg_src[2] = (src[2] + src[5] + src[src_fmt->fmt.pix.bytesperline + 2] +
+ src[src_fmt->fmt.pix.bytesperline + 5]) / 4;
+ if (bgr) {
+ RGB2UV(avg_src[2], avg_src[1], avg_src[0], *udest++, *vdest++);
+ } else {
+ RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++);
+ }
+ src += 6;
+ }
+ src += 2 * src_fmt->fmt.pix.bytesperline - 3 * src_fmt->fmt.pix.width;
+ }
+}
+
#define YUV2R(y, u, v) ({ \
int r = (y) + ((((v)-128)*1436) >> 10); r > 255 ? 255 : r < 0 ? 0 : r; })
#define YUV2G(y, u, v) ({ \
@@ -37,13 +89,20 @@
#define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))
void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
const unsigned char *ysrc = src;
- const unsigned char *usrc = src + width * height;
- const unsigned char *vsrc = usrc + (width * height) / 4;
+ const unsigned char *usrc, *vsrc;
+
+ if (yvu) {
+ vsrc = src + width * height;
+ usrc = vsrc + (width * height) / 4;
+ } else {
+ usrc = src + width * height;
+ vsrc = usrc + (width * height) / 4;
+ }
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
@@ -84,13 +143,20 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
}
void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
const unsigned char *ysrc = src;
- const unsigned char *usrc = src + width * height;
- const unsigned char *vsrc = usrc + (width * height) / 4;
+ const unsigned char *usrc, *vsrc;
+
+ if (yvu) {
+ vsrc = src + width * height;
+ usrc = vsrc + (width * height) / 4;
+ } else {
+ usrc = src + width * height;
+ vsrc = usrc + (width * height) / 4;
+ }
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
@@ -183,11 +249,11 @@ void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dest,
}
void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int yvu)
{
int i, j;
const unsigned char *src1;
- unsigned char *vdest;
+ unsigned char *udest, *vdest;
/* copy the Y values */
src1 = src;
@@ -202,10 +268,16 @@ void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest,
/* copy the U and V values */
src++; /* point to V */
src1 = src + width * 2; /* next line */
- vdest = dest + width * height / 4;
+ if (yvu) {
+ vdest = dest;
+ udest = dest + width * height / 4;
+ } else {
+ udest = dest;
+ vdest = dest + width * height / 4;
+ }
for (i = 0; i < height; i += 2) {
for (j = 0; j < width; j += 2) {
- *dest++ = ((int) src[0] + src1[0]) / 2; /* U */
+ *udest++ = ((int) src[0] + src1[0]) / 2; /* U */
*vdest++ = ((int) src[2] + src1[2]) / 2; /* V */
src += 4;
src1 += 4;
@@ -267,19 +339,71 @@ void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dest,
}
}
-void v4lconvert_yvyu_to_yuv420(const unsigned char *src, unsigned char *dest,
+void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dest,
+ int width, int height)
+{
+ int j;
+
+ while (--height >= 0) {
+ for (j = 0; j < width; j += 2) {
+ int u = src[0];
+ int v = src[2];
+ int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
+ int rg = (((u - 128) << 1) + (u - 128) +
+ ((v - 128) << 2) + ((v - 128) << 1)) >> 3;
+ int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
+
+ *dest++ = CLIP(src[1] + u1);
+ *dest++ = CLIP(src[1] - rg);
+ *dest++ = CLIP(src[1] + v1);
+
+ *dest++ = CLIP(src[3] + u1);
+ *dest++ = CLIP(src[3] - rg);
+ *dest++ = CLIP(src[3] + v1);
+ src += 4;
+ }
+ }
+}
+
+void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dest,
int width, int height)
{
+ int j;
+
+ while (--height >= 0) {
+ for (j = 0; j < width; j += 2) {
+ int u = src[0];
+ int v = src[2];
+ int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
+ int rg = (((u - 128) << 1) + (u - 128) +
+ ((v - 128) << 2) + ((v - 128) << 1)) >> 3;
+ int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
+
+ *dest++ = CLIP(src[1] + v1);
+ *dest++ = CLIP(src[1] - rg);
+ *dest++ = CLIP(src[1] + u1);
+
+ *dest++ = CLIP(src[3] + v1);
+ *dest++ = CLIP(src[3] - rg);
+ *dest++ = CLIP(src[3] + u1);
+ src += 4;
+ }
+ }
+}
+
+void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dest,
+ int width, int height, int yvu)
+{
int i, j;
const unsigned char *src1;
- unsigned char *vdest;
+ unsigned char *udest, *vdest;
/* copy the Y values */
src1 = src;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
- *dest++ = src1[0];
- *dest++ = src1[2];
+ *dest++ = src1[1];
+ *dest++ = src1[3];
src1 += 4;
}
}
@@ -287,11 +411,17 @@ void v4lconvert_yvyu_to_yuv420(const unsigned char *src, unsigned char *dest,
/* copy the U and V values */
src++; /* point to V */
src1 = src + width * 2; /* next line */
- vdest = dest + width * height / 4;
+ if (yvu) {
+ vdest = dest;
+ udest = dest + width * height / 4;
+ } else {
+ udest = dest;
+ vdest = dest + width * height / 4;
+ }
for (i = 0; i < height; i += 2) {
for (j = 0; j < width; j += 2) {
- *dest++ = ((int) src[2] + src1[2]) / 2; /* U */
- *vdest++ = ((int) src[0] + src1[0]) / 2; /* V */
+ *udest++ = ((int) src[0] + src1[0]) / 2; /* U */
+ *vdest++ = ((int) src[2] + src1[2]) / 2; /* V */
src += 4;
src1 += 4;
}
@@ -314,3 +444,32 @@ void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst,
*dst++ = tmp0;
}
}
+
+void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt)
+{
+ int y;
+
+ /* Copy Y */
+ for (y = 0; y < src_fmt->fmt.pix.height; y++) {
+ memcpy(dest, src, src_fmt->fmt.pix.width);
+ dest += src_fmt->fmt.pix.width;
+ src += src_fmt->fmt.pix.bytesperline;
+ }
+
+ /* Copy component 2 */
+ src += src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 4;
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ memcpy(dest, src, src_fmt->fmt.pix.width / 2);
+ dest += src_fmt->fmt.pix.width / 2;
+ src += src_fmt->fmt.pix.bytesperline / 2;
+ }
+
+ /* Copy component 1 */
+ src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 2;
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ memcpy(dest, src, src_fmt->fmt.pix.width / 2);
+ dest += src_fmt->fmt.pix.width / 2;
+ src += src_fmt->fmt.pix.bytesperline / 2;
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c b/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c
new file mode 100644
index 000000000..19951300f
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c
@@ -0,0 +1,137 @@
+/*
+ * Sonix SN9C20X decoder
+ * Vasily Khoruzhick, (C) 2008-2009
+ * Algorithm based on Java code written by Jens on microdia google group
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Note this code was originally licensed under the GNU GPL instead of the
+ * GNU LGPL, its license has been changed by its author.
+ */
+
+#include "libv4lconvert-priv.h"
+
+#define DO_SANITY_CHECKS 0
+
+static const int UVTranslate[32] = {0, 1, 2, 3,
+ 8, 9, 10, 11,
+ 16, 17, 18, 19,
+ 24, 25, 26, 27,
+ 4, 5, 6, 7,
+ 12, 13, 14, 15,
+ 20, 21, 22, 23,
+ 28, 29, 30, 31};
+
+static const int Y_coords_624x[128][2] = {
+{ 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 4, 0}, { 5, 0}, { 6, 0}, { 7, 0},
+{ 0, 1}, { 1, 1}, { 2, 1}, { 3, 1}, { 4, 1}, { 5, 1}, { 6, 1}, { 7, 1},
+{ 0, 2}, { 1, 2}, { 2, 2}, { 3, 2}, { 4, 2}, { 5, 2}, { 6, 2}, { 7, 2},
+{ 0, 3}, { 1, 3}, { 2, 3}, { 3, 3}, { 4, 3}, { 5, 3}, { 6, 3}, { 7, 3},
+
+{ 0, 4}, { 1, 4}, { 2, 4}, { 3, 4}, { 4, 4}, { 5, 4}, { 6, 4}, { 7, 4},
+{ 0, 5}, { 1, 5}, { 2, 5}, { 3, 5}, { 4, 5}, { 5, 5}, { 6, 5}, { 7, 5},
+{ 0, 6}, { 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6}, { 7, 6},
+{ 0, 7}, { 1, 7}, { 2, 7}, { 3, 7}, { 4, 7}, { 5, 7}, { 6, 7}, { 7, 7},
+
+{ 8, 0}, { 9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0},
+{ 8, 1}, { 9, 1}, {10, 1}, {11, 1}, {12, 1}, {13, 1}, {14, 1}, {15, 1},
+{ 8, 2}, { 9, 2}, {10, 2}, {11, 2}, {12, 2}, {13, 2}, {14, 2}, {15, 2},
+{ 8, 3}, { 9, 3}, {10, 3}, {11, 3}, {12, 3}, {13, 3}, {14, 3}, {15, 3},
+
+{ 8, 4}, { 9, 4}, {10, 4}, {11, 4}, {12, 4}, {13, 4}, {14, 4}, {15, 4},
+{ 8, 5}, { 9, 5}, {10, 5}, {11, 5}, {12, 5}, {13, 5}, {14, 5}, {15, 5},
+{ 8, 6}, { 9, 6}, {10, 6}, {11, 6}, {12, 6}, {13, 6}, {14, 6}, {15, 6},
+{ 8, 7}, { 9, 7}, {10, 7}, {11, 7}, {12, 7}, {13, 7}, {14, 7}, {15, 7}
+};
+
+static void do_write_u(const unsigned char *buf, unsigned char *ptr,
+ int i, int j)
+{
+ *ptr = buf[i + 128 + j];
+}
+
+static void do_write_v(const unsigned char *buf, unsigned char *ptr,
+ int i, int j)
+{
+ *ptr = buf[i + 160 + j];
+}
+
+void v4lconvert_sn9c20x_to_yuv420(const unsigned char *raw, unsigned char *i420,
+ int width, int height, int yvu)
+{
+ int i = 0, x = 0, y = 0, j, relX, relY, x_div2, y_div2;
+ const unsigned char *buf = raw;
+ unsigned char *ptr;
+ int frame_size = width * height;
+ int frame_size_div2 = frame_size >> 1;
+ int frame_size_div4 = frame_size >> 2;
+ int width_div2 = width >> 1;
+ int height_div2 = height >> 1;
+ void (*do_write_uv1)(const unsigned char *buf, unsigned char *ptr, int i,
+ int j) = NULL;
+ void (*do_write_uv2)(const unsigned char *buf, unsigned char *ptr, int i,
+ int j) = NULL;
+
+ if (yvu) {
+ do_write_uv1 = do_write_v;
+ do_write_uv2 = do_write_u;
+ }
+ else {
+ do_write_uv1 = do_write_u;
+ do_write_uv2 = do_write_v;
+ }
+
+ while (i < (frame_size + frame_size_div2)) {
+ for (j = 0; j < 128; j++) {
+ relX = x + Y_coords_624x[j][0];
+ relY = y + Y_coords_624x[j][1];
+
+#if (DO_SANITY_CHECKS==1)
+ if ((relX < width) && (relY < height)) {
+#endif
+ ptr = i420 + relY * width + relX;
+ *ptr = buf[i + j];
+#if (DO_SANITY_CHECKS==1)
+ }
+#endif
+
+ }
+ x_div2 = x >> 1;
+ y_div2 = y >> 1;
+ for (j = 0; j < 32; j++) {
+ relX = (x_div2) + (j & 0x07);
+ relY = (y_div2) + (j >> 3);
+
+#if (DO_SANITY_CHECKS==1)
+ if ((relX < width_div2) && (relY < height_div2)) {
+#endif
+ ptr = i420 + frame_size +
+ relY * width_div2 + relX;
+ do_write_uv1(buf, ptr, i, j);
+ ptr += frame_size_div4;
+ do_write_uv2(buf, ptr, i, j);
+#if (DO_SANITY_CHECKS==1)
+ }
+#endif
+ }
+
+ i += 192;
+ x += 16;
+ if (x >= width) {
+ x = 0;
+ y += 8;
+ }
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/spca501.c b/v4l2-apps/libv4l/libv4lconvert/spca501.c
index 9157629e3..f491512e3 100644
--- a/v4l2-apps/libv4l/libv4lconvert/spca501.c
+++ b/v4l2-apps/libv4l/libv4lconvert/spca501.c
@@ -20,7 +20,7 @@
/* YUYV per line */
void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
unsigned long *lsrc = (unsigned long *)src;
@@ -34,7 +34,10 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line U */
- ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
@@ -48,7 +51,10 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line V */
- ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
@@ -58,7 +64,7 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
/* YYUV per line */
void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
unsigned long *lsrc = (unsigned long *)src;
@@ -72,14 +78,20 @@ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line U */
- ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
}
/* -128 - 127 --> 0 - 255 and copy 1 line V */
- ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
@@ -89,7 +101,7 @@ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
/* YUVY per line */
void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
unsigned long *lsrc = (unsigned long *)src;
@@ -103,14 +115,20 @@ void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line U */
- ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
}
/* -128 - 127 --> 0 - 255 and copy 1 line V */
- ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
diff --git a/v4l2-apps/libv4l/libv4lconvert/sq905c.c b/v4l2-apps/libv4l/libv4lconvert/sq905c.c
new file mode 100644
index 000000000..a73b4da93
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/sq905c.c
@@ -0,0 +1,217 @@
+/*
+ * sq905c.c
+ *
+ * Here is the decompression function for the SQ905C cameras. The functions
+ * used are adapted from the libgphoto2 functions for the same cameras,
+ * which was
+ * Copyright (c) 2005 and 2007 Theodore Kilgore <kilgota@auburn.edu>
+ * This version for libv4lconvert is
+ * Copyright (c) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+
+#include "libv4lconvert-priv.h"
+
+
+#define CLIP(x) ((x) < 0 ? 0 : ((x) > 0xff) ? 0xff : (x))
+
+
+static int
+sq905c_first_decompress(unsigned char *output, const unsigned char *input,
+ unsigned int outputsize)
+{
+ unsigned char parity = 0;
+ unsigned char nibble_to_keep[2];
+ unsigned char temp1 = 0, temp2 = 0;
+ unsigned char input_byte;
+ unsigned char lookup = 0;
+ unsigned int i = 0;
+ unsigned int bytes_used = 0;
+ unsigned int bytes_done = 0;
+ unsigned int bit_counter = 8;
+ unsigned int cycles = 0;
+ int table[9] = { -1, 0, 2, 6, 0x0e, 0x0e, 0x0e, 0x0e, 0xfb};
+ unsigned char lookup_table[16]
+ = {0, 2, 6, 0x0e, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb};
+ unsigned char translator[16] = {8, 7, 9, 6, 10, 11, 12, 13,
+ 14, 15, 5, 4, 3, 2, 1, 0};
+
+ nibble_to_keep[0] = 0;
+ nibble_to_keep[1] = 0;
+
+ while (bytes_done < outputsize) {
+ while (parity < 2) {
+ while (lookup > table[cycles]) {
+ if (bit_counter == 8) {
+ input_byte = input[bytes_used];
+ bytes_used++;
+ temp1 = input_byte;
+ bit_counter = 0;
+ }
+ input_byte = temp1;
+ temp2 = (temp2 << 1) & 0xFF;
+ input_byte = input_byte >> 7;
+ temp2 = temp2 | input_byte;
+ temp1 = (temp1 << 1) & 0xFF;
+ bit_counter++;
+ cycles++;
+ if (cycles > 9)
+ return -1;
+ lookup = temp2 & 0xff;
+ }
+ temp2 = 0;
+ for (i = 0; i < 17; i++) {
+ if (lookup == lookup_table[i]) {
+ nibble_to_keep[parity] = translator[i];
+ break;
+ }
+ if (i == 16)
+ return -1;
+ }
+ cycles = 0;
+ parity++;
+ }
+ output[bytes_done] = (nibble_to_keep[0]<<4)|nibble_to_keep[1];
+ bytes_done++;
+ parity = 0;
+ }
+ return 0;
+}
+
+static int
+sq905c_second_decompress(unsigned char *uncomp, unsigned char *in,
+ int width, int height)
+{
+ int diff = 0;
+ int tempval = 0;
+ int i, m, parity;
+ unsigned char delta_left = 0;
+ unsigned char delta_right = 0;
+ int input_counter = 0;
+ int delta_table[] = {-144, -110, -77, -53, -35, -21, -11, -3,
+ 2, 10, 20, 34, 52, 76, 110, 144};
+ unsigned char *templine_red;
+ unsigned char *templine_green;
+ unsigned char *templine_blue;
+ templine_red = malloc(width);
+ if (!templine_red) {
+ free(templine_red);
+ return -1;
+ }
+ for (i = 0; i < width; i++)
+ templine_red[i] = 0x80;
+ templine_green = malloc(width);
+ if (!templine_green) {
+ free(templine_green);
+ return -1;
+ }
+ for (i = 0; i < width; i++)
+ templine_green[i] = 0x80;
+ templine_blue = malloc(width);
+ if (!templine_blue) {
+ free(templine_blue);
+ return -1;
+ }
+ for (i = 0; i < width; i++)
+ templine_blue[i] = 0x80;
+ for (m = 0; m < height/2; m++) {
+ /* First we do an even-numbered line */
+ for (i = 0; i < width/2; i++) {
+ parity = i&1;
+ delta_right = in[input_counter] & 0x0f;
+ delta_left = (in[input_counter]>>4)&0xff;
+ input_counter++;
+ /* left pixel (red) */
+ diff = delta_table[delta_left];
+ if (!i)
+ tempval = templine_red[0] + diff;
+ else
+ tempval = (templine_red[i]
+ + uncomp[2*m*width+2*i-2])/2 + diff;
+ tempval = CLIP(tempval);
+ uncomp[2*m*width+2*i] = tempval;
+ templine_red[i] = tempval;
+ /* right pixel (green) */
+ diff = delta_table[delta_right];
+ if (!i)
+ tempval = templine_green[1] + diff;
+ else if (2*i == width - 2)
+ tempval = (templine_green[i]
+ + uncomp[2*m*width+2*i-1])/2
+ + diff;
+ else
+ tempval = (templine_green[i+1]
+ + uncomp[2*m*width+2*i-1])/2
+ + diff;
+ tempval = CLIP(tempval);
+ uncomp[2*m*width+2*i+1] = tempval;
+ templine_green[i] = tempval;
+ }
+ /* then an odd-numbered line */
+ for (i = 0; i < width/2; i++) {
+ delta_right = in[input_counter] & 0x0f;
+ delta_left = (in[input_counter]>>4) & 0xff;
+ input_counter++;
+ /* left pixel (green) */
+ diff = delta_table[delta_left];
+ if (!i)
+ tempval = templine_green[0] + diff;
+ else
+ tempval = (templine_green[i]
+ + uncomp[(2*m+1)*width+2*i-2])/2
+ + diff;
+ tempval = CLIP(tempval);
+ uncomp[(2*m+1)*width+2*i] = tempval;
+ templine_green[i] = tempval;
+ /* right pixel (blue) */
+ diff = delta_table[delta_right];
+ if (!i)
+ tempval = templine_blue[0] + diff;
+ else
+ tempval = (templine_blue[i]
+ + uncomp[(2*m+1)*width+2*i-1])/2
+ + diff;
+ tempval = CLIP(tempval);
+ uncomp[(2*m+1)*width+2*i+1] = tempval;
+ templine_blue[i] = tempval;
+ }
+ }
+ free(templine_green);
+ free(templine_red);
+ free(templine_blue);
+ return 0;
+}
+
+void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst,
+ int width, int height)
+{
+ int size;
+ unsigned char *temp_data;
+ const unsigned char *raw;
+ /* here we get rid of the 0x50 bytes of header in src. */
+ raw = src + 0x50;
+ size = width*height/2;
+ temp_data = malloc(size);
+ if (!temp_data)
+ goto out;
+ sq905c_first_decompress(temp_data, raw, size);
+ sq905c_second_decompress(dst, temp_data, width, height);
+out:
+ free(temp_data);
+}