From f054413dcae4d70337c017bdfc9d66d6fcf02dcb Mon Sep 17 00:00:00 2001 From: Theodore Kilgore Date: Fri, 16 Dec 2011 13:53:03 -0600 Subject: [PATCH] Improved JL2005BCD patch for libv4lcnvert Hans, The patch below supercedes the previous one I sent. Two particular items of interest: 1. The Bayer tiling question seems to be resolved. 2. White balance is enabled and seems to help a lot. I am not certain if one ought to enable a gamma setting as well, but things look pretty decent without it, seems to me. Other remarks: It is pretty clear that these cameras have an internal gain setting which relies upon internal feedback for self-correction. It works pretty well most of the time, but sometimes it overcorrects and thereby goes a little bit crazy. I have tried it out with a bunch of cameras now. I did not check individually which one is which, but I have a fairly large set of them which includes the B and C and D variants, and some of the fancy ones have the LCD previewer on the back but definitely do not send preview thumbnails in webcam mode. So all of them seem to work. I would dare to presume that your camera will work, too, whenever you get around to checking it. Patch below created by git diff from my recently downloaded git tree, as was the last one. Patch is Signed-off-by Theodore Kilgore --- include/linux/videodev2.h | 1 + lib/libv4lconvert/Makefile | 1 + lib/libv4lconvert/control/libv4lcontrol.c | 2 + lib/libv4lconvert/jl2005bcd.c | 211 ++++++++++++++++++++++++++++++ lib/libv4lconvert/libv4lconvert-priv.h | 4 + lib/libv4lconvert/libv4lconvert.c | 8 ++ 6 files changed, 227 insertions(+) create mode 100644 lib/libv4lconvert/jl2005bcd.c diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 9d14523..95fb72e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -401,6 +401,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ #define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_JL2005BCD v4l2_fourcc('J', 'L', '2', '0') /* compressed RGGB bayer */ #define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ diff --git a/lib/libv4lconvert/Makefile b/lib/libv4lconvert/Makefile index c9ca05d..7826438 100644 --- a/lib/libv4lconvert/Makefile +++ b/lib/libv4lconvert/Makefile @@ -10,6 +10,7 @@ override CPPFLAGS += -fPIC endif CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o sn9c20x.o pac207.o \ + jl2005bcd.o \ mr97310a.o flip.o crop.o jidctflt.o spca561-decompress.o \ rgbyuv.o sn9c2028-decomp.o spca501.o sq905c.o bayer.o hm12.o \ stv0680.o cpia1.o se401.o jpgl.o jpeg.o jpeg_memsrcdest.o \ diff --git a/lib/libv4lconvert/control/libv4lcontrol.c b/lib/libv4lconvert/control/libv4lcontrol.c index 1b38af3..7d11eb1 100644 --- a/lib/libv4lconvert/control/libv4lcontrol.c +++ b/lib/libv4lconvert/control/libv4lcontrol.c @@ -607,6 +607,8 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, /* Second: devices which should use some software processing by default */ + /* jl2005bcd devices */ + { 0x0979, 0x0227, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* sn9c101 / sn9c102 based devices (sonixb) */ { 0x0c45, 0x6011, 0, NULL, NULL, 0, 1500 }, /* OV6650, no WB needed */ { 0x0c45, 0x6019, 0, NULL, NULL, 0, 1500 }, /* OV7630, no WB needed */ diff --git a/lib/libv4lconvert/jl2005bcd.c b/lib/libv4lconvert/jl2005bcd.c new file mode 100644 index 0000000..f3dbf20 --- /dev/null +++ b/lib/libv4lconvert/jl2005bcd.c @@ -0,0 +1,211 @@ +/* jl2005bcd.c + * + * Converts raw output from Jeilin JL2005B/C/D to Bayer output. + * + * The code is based upon what is found in libgphoto2/camlibs/jl2005c, + * Copyright (c) 2010 Theodore Kilgore + * + * The decompression code used is + * Copyright (c) 2010 Hans de Goede + * + * 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 +#include + +#include "libv4lconvert-priv.h" +#include "jpeg_memsrcdest.h" +#include "libv4lsyscall-priv.h" + + +#define JPEG_HEADER_SIZE 338 +#define JPEG_HEIGHT_OFFSET 94 + +static int +find_eoi(const unsigned char *jpeg_data, int jpeg_data_idx, int jpeg_data_size) +{ + int i; + + for (i = jpeg_data_idx; i < (jpeg_data_size - 1); i++) + if (jpeg_data[i] == 0xff && jpeg_data[i + 1] == 0xd9) + break; + + if (i >= (jpeg_data_size - 1)) { + printf("AAI\n"); + return -1; + } + + return i + 2; /* + 2 -> Point to after EOI marker */ +} + + +int v4lconvert_decode_jl2005bcd(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dest, int width, int height) +{ + unsigned char jpeg_stripe[50000]; + int q; + struct jpeg_compress_struct cinfo; + struct jpeg_decompress_struct dinfo; + struct jpeg_error_mgr jcerr, jderr; + JOCTET *jpeg_header = NULL; + unsigned long jpeg_header_size = 0; + int i, x, y, x1, y1, jpeg_data_size, jpeg_data_idx, eoi, size; + + /* src_size had better be bigger than 16 */ + if (src_size < 16) + return 1; + + /* The first 16 bytes of src are a raw frame header */ + q = src[13] & 0x7f; + + if (height != src[4] << 3) { + printf("Height is %d, not %d\n", src[4] << 3, height); + return 1; + } + + if (width != src[5] << 3) { + printf("Width is %d, not %d\n", src[5] << 3, width); + return 1; + } + printf("quality is %d\n", q); + printf("size: %dx%d\n", width, height); + /* + * And the fun begins, first of all create a dummy jpeg, which we use + * to get the headers from to feed to libjpeg when decompressing the + * stripes. This way we can use libjpeg's quant table handling + * (and built in default huffman tables). + */ + cinfo.err = jpeg_std_error (&jcerr); + jpeg_create_compress (&cinfo); + jpeg_mem_dest (&cinfo, &jpeg_header, &jpeg_header_size); + cinfo.image_width = 16; + cinfo.image_height = 16; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults (&cinfo); + /* Make comp[0] (which will be green) 1x2 subsampled */ + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 2; + /* Make comp[1] and [2] use huffman table and quanttable 0, as all + * components use luminance settings with the jl2005c/d/e */ + cinfo.comp_info[1].quant_tbl_no = 0; + cinfo.comp_info[1].dc_tbl_no = 0; + cinfo.comp_info[1].ac_tbl_no = 0; + cinfo.comp_info[2].quant_tbl_no = 0; + cinfo.comp_info[2].dc_tbl_no = 0; + cinfo.comp_info[2].ac_tbl_no = 0; + /* Apply the quality setting from the header */ + if (q <= 0) + i = 5000; + else if (q <= 50) + i = 5000 / q; + else if (q <= 100) + i = 2 * (100 - q); + else + i = 0; + jpeg_set_linear_quality(&cinfo, i, TRUE); + + jpeg_start_compress (&cinfo, TRUE); + while( cinfo.next_scanline < cinfo.image_height ) { + JOCTET row[16 * 3]; + JSAMPROW row_pointer[1] = { row }; + jpeg_write_scanlines (&cinfo, row_pointer, 1); + } + jpeg_finish_compress (&cinfo); + jpeg_destroy_compress (&cinfo); + + JSAMPLE green[8 * 16]; + JSAMPLE red[8 * 8]; + JSAMPLE blue[8 * 8]; + JSAMPROW green_row_pointer[16]; + JSAMPROW red_row_pointer[8]; + JSAMPROW blue_row_pointer[8]; + + for (i = 0; i < 16; i++) + green_row_pointer[i] = green + i * 8; + + for (i = 0; i < 8; i++) { + red_row_pointer[i] = red + i * 8; + blue_row_pointer[i] = blue + i * 8; + } + + JSAMPARRAY samp_image[3] = { green_row_pointer, + red_row_pointer, + blue_row_pointer }; + + memcpy(jpeg_stripe, jpeg_header, JPEG_HEADER_SIZE); + jpeg_stripe[JPEG_HEIGHT_OFFSET ] = height >> 8; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 1] = height; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 2] = 0; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 3] = 8; + free (jpeg_header); + /* Get past the raw frame header. */ + src += 16; + jpeg_data_size = src_size - 16; + + jpeg_data_idx = 0; + + + dinfo.err = jpeg_std_error (&jderr); + jpeg_create_decompress (&dinfo); + for (x = 0; x < width; x += 16) { + eoi = find_eoi(src, jpeg_data_idx, jpeg_data_size); + if (eoi < 0) + return eoi; + + size = eoi - jpeg_data_idx; + if ((JPEG_HEADER_SIZE + size) > sizeof(jpeg_stripe)) { + printf("AAAIIIIII\n"); + return 1; + } + memcpy (jpeg_stripe + JPEG_HEADER_SIZE, + src + jpeg_data_idx, size); + + jpeg_mem_src (&dinfo, jpeg_stripe, JPEG_HEADER_SIZE + size); + jpeg_read_header (&dinfo, TRUE); + dinfo.raw_data_out = TRUE; +#if JPEG_LIB_VERSION >= 70 + dinfo.do_fancy_upsampling = FALSE; +#endif + jpeg_start_decompress (&dinfo); + for (y = 0; y < height; y += 16) { + jpeg_read_raw_data (&dinfo, samp_image, 16); + for (y1 = 0; y1 < 16; y1 += 2) { + for (x1 = 0; x1 < 16; x1 += 2) { + dest[((y + y1 + 0) * width + + x + x1 + 0)] + = red[y1 * 4 + x1 / 2]; + dest[((y + y1 + 0) * width + + x + x1 + 1)] + = green[y1 * 8 + x1 / 2]; + dest[((y + y1 + 1) * width + + x + x1 + 0)] + = green[y1 * 8 + 8 + x1 / 2]; + dest[((y + y1 + 1) * width + + x + x1 + 1)] + = blue[y1 * 4 + x1 / 2]; + } + } + } + jpeg_finish_decompress (&dinfo); + + /* Set jpeg_data_idx for the next stripe */ + jpeg_data_idx = (jpeg_data_idx + size + 0x0f) & ~0x0f; + } + jpeg_destroy_decompress(&dinfo); + return 0; +} diff --git a/lib/libv4lconvert/libv4lconvert-priv.h b/lib/libv4lconvert/libv4lconvert-priv.h index d183143..9852319 100644 --- a/lib/libv4lconvert/libv4lconvert-priv.h +++ b/lib/libv4lconvert/libv4lconvert-priv.h @@ -218,6 +218,10 @@ int v4lconvert_decode_mr97310a(struct v4lconvert_data *data, const unsigned char *src, int src_size, unsigned char *dst, int width, int height); +int v4lconvert_decode_jl2005bcd(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dest, int width, int height); + void v4lconvert_decode_sn9c2028(const unsigned char *src, unsigned char *dst, int width, int height); diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c index 399d73b..5e31d82 100644 --- a/lib/libv4lconvert/libv4lconvert.c +++ b/lib/libv4lconvert/libv4lconvert.c @@ -78,6 +78,7 @@ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = { { V4L2_PIX_FMT_SN9C2028, 0, 9, 9, 1 }, { V4L2_PIX_FMT_PAC207, 0, 9, 9, 1 }, { V4L2_PIX_FMT_MR97310A, 0, 9, 9, 1 }, + { V4L2_PIX_FMT_JL2005BCD, 0, 9, 9, 1 }, { V4L2_PIX_FMT_SQ905C, 0, 9, 9, 1 }, /* special */ { V4L2_PIX_FMT_SE401, 0, 8, 9, 1 }, @@ -591,6 +592,7 @@ static int v4lconvert_processing_needs_double_conversion( case V4L2_PIX_FMT_SN9C10X: case V4L2_PIX_FMT_PAC207: case V4L2_PIX_FMT_MR97310A: + case V4L2_PIX_FMT_JL2005BCD: case V4L2_PIX_FMT_SN9C2028: case V4L2_PIX_FMT_SQ905C: case V4L2_PIX_FMT_SBGGR8: @@ -787,6 +789,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, case V4L2_PIX_FMT_SN9C10X: case V4L2_PIX_FMT_PAC207: case V4L2_PIX_FMT_MR97310A: + case V4L2_PIX_FMT_JL2005BCD: case V4L2_PIX_FMT_SN9C2028: case V4L2_PIX_FMT_SQ905C: case V4L2_PIX_FMT_STV0680: { /* Not compressed but needs some shuffling */ @@ -825,6 +828,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, } tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; + case V4L2_PIX_FMT_JL2005BCD: + v4lconvert_decode_jl2005bcd(data, src, src_size, + tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; + break; case V4L2_PIX_FMT_SN9C2028: v4lconvert_decode_sn9c2028(src, tmpbuf, width, height); tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; -- 2.7.4