Changes of Revision 2
[-] [+] | Changed | libv4l.spec |
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/ChangeLog ^ |
@@ -1,3 +1,77 @@ +libv4l-0.6.0 +------------ +* Recognize disabled controls and replace with fake equivalents where + available +* Add support for decompressing ov511 and ov518 "JPEG", by piping data through + an external helper as I've failed to contact Mark W. McClelland to get + permission to relicense the code. If you know a working email address for + Mark W. McClelland, please let me know. +* Add tons of laptop models to the upside down devices table +* Support for rgb565 source format by Mauro Carvalho Chehab +* Many bug fixes (see the mercurial tree for details) +* Improved pac207 decompression code to also support higher compression + modes of the pac207, which enables us to use higher framerates. + Many many thanks to Bertrik Sikken for figuring the decompression out! + +libv4l-0.5.99 +------------- +* Link libv4lconvert with -lm for powf by Gregor Jasny +* Fix black screen on devices with hardware gamma control +* Fix crash with devices on which we do not emulate fake controls +* Add a patch by Hans Petter Selasky <hselasky@freebsd.org>, which should + lead to allowing use of libv4l (and the Linux webcam drivers ported + to userspace usb drivers) on FreeBSD, this is a work in progress + +libv4l-0.5.98 +------------- +* Add software gamma correction +* Add software auto gain / exposure +* Add support for separate vflipping and hflipping +* Add fake controls controlling the software h- and v-flipping +* Add ability to determine upside down cams based on DMI info +* Add the capability to provide 320x240 to apps if the cam can only + do 320x232 (some zc3xx cams) by adding black borders +* Rewrite video processing code to make it easier to add more video filters + (and with little extra processing cost). As part of this the normalize + filter has been removed as it wasn't functioning satisfactory anyways +* Support V4L2_CTRL_FLAG_NEXT_CTRL for fake controls by Adam Baker +* Some makefile improvements by Gregor Jasny +* Various small bugfixes and tweaks +* The V4L2_ENABLE_ENUM_FMT_EMULATION v4l2_fd_open flag is obsolete, libv4l2 + now *always* reports emulated formats through the ENUM_FMT ioctl + +libv4l-0.5.97 +------------- +* As the version number shows this is a beta release of the 0.6.x series, + the big change here is the addition of video processing to libv4l + currently this only does whitebalance and normalizing (which turns out + to be useless for most cams) but the basic framework for doing video + processing, and being able to control it through fake v4l2 controls using + for example v4l2ucp is there. + The initial version of this code was written by 3 of my computer science + students: Elmar Kleijn, Sjoerd Piepenbrink and Radjnies Bhansingh +* Currently whitebalancing gets enabled based on USB-ID's and it only gets + enabled for Pixart webcam's. You can force it being enabled with other + webcams by setting the environment variable LIBV4LCONTROL_CONTROLS, this + sets a bitmask enabling certain v4l2 controls which control the video + processing set it to 15 to enable both whitebalancing and normalize. You + can then change the settings using a v4l2 control panel like v4l2ucp +* Only report / allow supported destination formats in enum_fmt / try_fmt / + g_fmt / s_fmt when processing, rotating or flipping. +* Some applications / libs (*cough* gstreamer *cough*) will not work + correctly with planar YUV formats when the width is not a multiple of 8, + so crop widths which are not a multiple of 8 to the nearest multiple of 8 + when converting to planar YUV +* Add dependency generation to libv4l by: Gilles Gigan <gilles.gigan@gmail.com> +* Add support to use orientation from VIDIOC_ENUMINPUT by: + Adam Baker <linux@baker-net.org.uk> +* sn9c20x cams have occasional bad jpeg frames, drop these to avoid the + flickering effect they cause, by: Brian Johnson <brijohn@gmail.com> +* adjust libv4l's upside down cam detection to also work with devices + which have the usb interface as parent instead of the usb device +* fix libv4l upside down detection for the new v4l minor numbering scheme +* fix reading outside of the source memory when doing yuv420->rgb conversion + libv4l-0.5.9 ------------ * Add support for MR97310A decompression by Kyle Guinn <elyk03@gmail.com> | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/Makefile ^ |
@@ -1,5 +1,5 @@ LIB_RELEASE=0 -V4L2_LIB_VERSION=$(LIB_RELEASE).5.9 +V4L2_LIB_VERSION=$(LIB_RELEASE).6.0 all install: $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ @@ -7,7 +7,7 @@ $(MAKE) -C libv4l1 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ clean: - rm -f *~ include/*~ + rm -f *~ include/*~ DEADJOE include/DEADJOE $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ $(MAKE) -C libv4l2 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ $(MAKE) -C libv4l1 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/README ^ |
@@ -15,14 +15,26 @@ libv4lconvert ------------- -libv4lconvert offers functions to convert from any (known) pixelformat -to V4l2_PIX_FMT_BGR24 or V4l2_PIX_FMT_YUV420. +libv4lconvert started as a library to convert from any (known) pixelformat to +V4l2_PIX_FMT_BGR24, RGB24, YUV420 or YVU420. -Currently the following source formats are supported: -jpeg, mjpeg, bayer (all 4 variants: bggr, rggb, gbrg, grbg), -spca501 (chip specific yuv 420 with interlaced components), -spca561 (chip specific compressed gbrg bayer) -For more details on the v4lconvert_ functions see libv4lconvert.h . +The list of know source formats is large and continually growing, so instead +of keeping an (almost always outdated) list here in the README, I refer you +to the source, see the list of defines at the top of +libv4lconvert/libv4lconvert.c for the full list. +For more details on the v4lconvert_ functions see libv4lconvert.h. + +Later on libv4lconvert was expanded to also be able to do various video +processing functions improve webcam video quality on a software basis. So +the name no longer 100% covers the functionality. The video processing is +split in to 2 parts, libv4lconvert/control and libv4lconvert/processing. + +The control part is used to offer video controls which can be used to control +the video processing functions made available by libv4lconvert/processing. +These controls are stored application wide (untill reboot) by using a +persistent shared memory object. + +libv4lconvert/processing offers the actual video processing functionality. libv4l1 | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/TODO ^ |
@@ -8,3 +8,8 @@ necessary to implement CGMBUF in the kernel for each driver. -take the possibility of pitch != width into account everywhere + +-make updating of parameters happen based on time elapsed rather then + frames + +-get standardized CID for AUTOGAIN_TARGET upstream and switch to that | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/include/libv4l1.h ^ |
@@ -63,7 +63,7 @@ LIBV4L_PUBLIC int v4l1_ioctl (int fd, unsigned long int request, ...); LIBV4L_PUBLIC ssize_t v4l1_read (int fd, void* buffer, size_t n); LIBV4L_PUBLIC void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset); + int64_t offset); LIBV4L_PUBLIC int v4l1_munmap(void *_start, size_t length); #ifdef __cplusplus | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/include/libv4l2.h ^ |
@@ -21,6 +21,7 @@ #include <stdio.h> #include <unistd.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -41,18 +42,12 @@ format which is not supported by the cam, but is supported by libv4lconvert, then the try_fmt / set_fmt will succeed as if the cam supports the format and on dqbuf / read the data will be converted for you and returned in - the request format. + the request format. enum_fmt will also report support for the formats to + which conversion is possible. Another difference is that you can make v4l2_read() calls even on devices which do not support the regular read() method. - Note that libv4l2 normally does not interfere with enum_fmt, so enum_fmt - will still return the actual formats the hardware supports, and not any - formats which may be emulated on top of that. If you pass the - V4L2_ENABLE_ENUM_FMT_EMULATION flag to v4l2_fd_open (as the v4l2convert.so - wrapper does) then enum_fmt will also report support for the formats to - which conversion is possible. - Note the device name passed to v4l2_open must be of a video4linux2 device, if it is anything else (including a video4linux1 device), v4l2_open will fail. @@ -64,7 +59,7 @@ LIBV4L_PUBLIC int v4l2_ioctl (int fd, unsigned long int request, ...); LIBV4L_PUBLIC ssize_t v4l2_read (int fd, void* buffer, size_t n); LIBV4L_PUBLIC void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset); + int64_t offset); LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length); @@ -89,11 +84,14 @@ /* Flags for v4l2_fd_open's v4l2_flags argument */ -/* Disable all format conversion done by libv4l2 (reduces libv4l2 functionality - to offering v4l2_read() even on devices which don't implement read()) */ +/* Disable all format conversion done by libv4l2, this includes the software + whitebalance, gamma correction, flipping, etc. libv4lconvert does. Use this + if you want raw frame data, but still want the additional error checks and + the read() emulation libv4l2 offers. */ #define V4L2_DISABLE_CONVERSION 0x01 -/* Report not only real but also emulated formats with the ENUM_FMT ioctl */ -#define V4L2_ENABLE_ENUM_FMT_EMULATION 02 +/* This flag is *OBSOLETE*, since version 0.5.98 libv4l *always* reports + emulated formats to ENUM_FMT, except when conversion is disabled. */ +#define V4L2_ENABLE_ENUM_FMT_EMULATION 0x02 /* v4l2_fd_open: open an already opened fd for further use through v4l2lib and possibly modify libv4l2's default behavior through the | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/include/libv4lconvert.h ^ |
@@ -21,10 +21,21 @@ /* These headers are not needed by us, but by linux/videodev2.h, which is broken on some systems and doesn't include them itself :( */ + +#ifdef linux #include <sys/time.h> #include <linux/types.h> #include <linux/ioctl.h> +#endif + +#ifdef __FreeBSD__ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#endif + /* end broken header workaround includes */ + #include <linux/videodev2.h> #ifdef __cplusplus @@ -42,18 +53,30 @@ LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd); LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data); +/* When doing flipping / rotating / video-processing, only supported + destination formats can be used (as flipping / rotating / video-processing + is not supported on other formats). This function can be used to query + if that is the case. */ +LIBV4L_PUBLIC int v4lconvert_supported_dst_fmt_only( + struct v4lconvert_data *data); + /* With regards to dest_fmt just like VIDIOC_TRY_FMT, except that the try format will succeed and return the requested V4L2_PIX_FMT_foo in dest_fmt if the cam has a format from which v4lconvert can convert to dest_fmt. The real format to which the cam should be set is returned through src_fmt - when not NULL. */ + when not NULL. + Note that just like the real VIDIOC_TRY_FMT this function will change the + dest_fmt when not supported. This includes changing it to a supported + destination format when trying a native format of the camera and + v4lconvert_supported_dst_fmt_only() returns true. */ LIBV4L_PUBLIC int v4lconvert_try_format(struct v4lconvert_data *data, struct v4l2_format *dest_fmt, /* in / out */ struct v4l2_format *src_fmt /* out */ ); -/* Just like VIDIOC_ENUM_FMT, except that the emulated formats are added at - the end of the list */ +/* Like VIDIOC_ENUM_FMT, but the emulated formats are added at the end of the + list, except if flipping / processing is active for the device, then only + supported destination formats are listed */ LIBV4L_PUBLIC int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt); /* Is conversion necessary or can the app use the data directly? */ @@ -81,6 +104,17 @@ LIBV4L_PUBLIC int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, struct v4l2_frmivalenum *frmival); +/* Pass calls to query, get and set video controls to the libv4lcontrol class */ +LIBV4L_PUBLIC int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, + void *arg); + +/* Is the passed in pixelformat supported as destination format ? */ +LIBV4L_PUBLIC int v4lconvert_supported_dst_format(unsigned int pixelformat); + #ifdef __cplusplus } #endif /* __cplusplus */ | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l1/Makefile ^ |
@@ -13,9 +13,10 @@ ifeq ($(LINKTYPE),static) V4L1_LIB = libv4l1.a +V4L1_DEPS = $(V4L1_OBJS) else V4L1_LIB = libv4l1.so -V4L1_OBJS += ../libv4l2/libv4l2.so +V4L1_DEPS += $(V4L1_OBJS) ../libv4l2/libv4l2.so TARGETS += $(V4L1COMPAT) override CPPFLAGS += -fPIC endif @@ -34,8 +35,9 @@ all: $(TARGETS) +-include $(V4L1_OBJS:.o=.d) -$(V4L1_LIB): $(V4L1_OBJS) +$(V4L1_LIB): $(V4L1_DEPS) $(V4L1COMPAT): $(V4L1COMPAT_O) $(V4L1_LIB) @@ -69,10 +71,10 @@ install -m 644 libv4l1.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4l1.pc log *~ *.orig *.rej + rm -f *.a *.so* *.o *.d libv4l1.pc log *~ *.orig *.rej DEADJOE %.o: %.c - $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< + $(CC) -Wp,-MMD,"$*.d",-MQ,"$@",-MP -c $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l1/libv4l1-priv.h ^ |
@@ -22,14 +22,7 @@ #include <stdio.h> #include <pthread.h> -/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ -#ifdef __NR_mmap2 -#define SYS_mmap2 __NR_mmap2 -#define MMAP2_PAGE_SHIFT 12 -#else -#define SYS_mmap2 SYS_mmap -#define MMAP2_PAGE_SHIFT 0 -#endif +#include "../libv4lconvert/libv4lsyscall-priv.h" #define V4L1_MAX_DEVICES 16 #define V4L1_NO_FRAMES 4 | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l1/libv4l1.c ^ |
@@ -44,18 +44,12 @@ #include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <syscall.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -#include <linux/ioctl.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev.h> #include <linux/videodev2.h> #include <libv4l2.h> @@ -155,8 +149,10 @@ /* Do we need to change the resolution / format ? */ if (width == devices[index].width && height == devices[index].height && - v4l2_pixfmt == devices[index].v4l2_pixfmt) + v4l2_pixfmt == devices[index].v4l2_pixfmt) { + devices[index].v4l1_pal = v4l1_pal; return 0; + } /* Get current settings, apply our changes and try the new setting */ if ((result = v4l2_ioctl(devices[index].fd, VIDIOC_G_FMT, &fmt2))) { @@ -223,14 +219,14 @@ for (i = 0; ; i++) { fmtdesc2.index = i; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_ENUM_FMT, &fmtdesc2)) + if (v4l2_ioctl(devices[index].fd, VIDIOC_ENUM_FMT, &fmtdesc2)) break; fmt2->fmt.pix.pixelformat = fmtdesc2.pixelformat; fmt2->fmt.pix.width = 48; fmt2->fmt.pix.height = 32; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { + if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { if (fmt2->fmt.pix.width < devices[index].min_width) devices[index].min_width = fmt2->fmt.pix.width; if (fmt2->fmt.pix.height < devices[index].min_height) @@ -241,7 +237,7 @@ fmt2->fmt.pix.width = 100000; fmt2->fmt.pix.height = 100000; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { + if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { if (fmt2->fmt.pix.width > devices[index].max_width) devices[index].max_width = fmt2->fmt.pix.width; if (fmt2->fmt.pix.height > devices[index].max_height) @@ -278,11 +274,12 @@ va_start (ap, oflag); mode = va_arg (ap, mode_t); - fd = syscall(SYS_open, file, oflag, mode); + fd = SYS_OPEN(file, oflag, mode); va_end(ap); } else - fd = syscall(SYS_open, file, oflag); + fd = SYS_OPEN(file, oflag, 0); + /* end of original open code */ if (fd == -1 || !v4l_device) @@ -290,7 +287,7 @@ /* check that this is an v4l2 device, no need to emulate v4l1 on a v4l1 device */ - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap2)) + if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap2)) return fd; /* IMPROVEME */ @@ -308,21 +305,20 @@ if (!v4l2_log_file) v4l2_log_file = v4l1_log_file; - /* Get initial width, height and pixelformat */ - fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (syscall(SYS_ioctl, fd, VIDIOC_G_FMT, &fmt2)) { + /* Register with libv4l2, as we use that todo format conversion and read() + emulation for us */ + if (v4l2_fd_open(fd, 0) == -1) { int saved_err = errno; - V4L1_LOG_ERR("getting pixformat: %s\n", strerror(errno)); - syscall(SYS_close, fd); + SYS_CLOSE(fd); errno = saved_err; return -1; } - /* Register with libv4l2, as we use that todo format conversion and read() - emulation for us */ - if (v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION) == -1) { + /* Get initial width, height and pixelformat */ + fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(fd, VIDIOC_G_FMT, &fmt2)) { int saved_err = errno; - syscall(SYS_close, fd); + SYS_CLOSE(fd); errno = saved_err; return -1; } @@ -399,7 +395,7 @@ int index, result; if ((index = v4l1_get_index(fd)) == -1) - return syscall(SYS_close, fd); + return SYS_CLOSE(fd); /* Abuse stream_lock to stop 2 closes from racing and trying to free the resources twice */ @@ -417,7 +413,7 @@ V4L1_LOG("v4l1 capture buffer still mapped: %d times on close()\n", devices[index].v4l1_frame_buf_map_count); else - syscall(SYS_munmap, devices[index].v4l1_frame_pointer, + SYS_MUNMAP(devices[index].v4l1_frame_pointer, V4L1_NO_FRAMES * V4L1_FRAME_BUF_SIZE); devices[index].v4l1_frame_pointer = MAP_FAILED; } @@ -446,7 +442,6 @@ return v4l2_dup(fd); } - int v4l1_ioctl (int fd, unsigned long int request, ...) { void *arg; @@ -458,7 +453,7 @@ va_end (ap); if ((index = v4l1_get_index(fd)) == -1) - return syscall(SYS_ioctl, fd, request, arg); + return SYS_IOCTL(fd, request, arg); /* Appearantly the kernel and / or glibc ignore the 32 most significant bits when long = 64 bits, and some applications pass an int holding the req to @@ -485,7 +480,7 @@ { struct video_capability *cap = arg; - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); /* override kernel v4l1 compat min / max size with our own more accurate values */ @@ -547,24 +542,27 @@ break; case VIDIOCSWIN: + case VIDIOCGWIN: { struct video_window *win = arg; devices[index].flags |= V4L1_PIX_SIZE_TOUCHED; - result = v4l1_set_format(index, win->width, win->height, -1, 1); + if (request == VIDIOCSWIN) + result = v4l1_set_format(index, win->width, win->height, -1, 1); + else + result = 0; + if (result == 0) { + win->x = 0; + win->y = 0; win->width = devices[index].width; win->height = devices[index].height; + win->flags = 0; } } break; - case VIDIOCGWIN: - devices[index].flags |= V4L1_PIX_SIZE_TOUCHED; - result = syscall(SYS_ioctl, fd, request, arg); - break; - case VIDIOCGCHAN: { struct v4l2_input input2; @@ -572,7 +570,7 @@ if ((devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) && (devices[index].flags & V4L1_SUPPORTS_ENUMSTD)) { - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); break; } @@ -614,7 +612,7 @@ struct video_channel *chan = arg; if ((devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) && (devices[index].flags & V4L1_SUPPORTS_ENUMSTD)) { - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); break; } /* In case of no ENUMSTD support, ignore the norm member of the @@ -650,7 +648,7 @@ } if (devices[index].v4l1_frame_pointer == MAP_FAILED) { - devices[index].v4l1_frame_pointer = (void *)syscall(SYS_mmap2, NULL, + devices[index].v4l1_frame_pointer = (void *)SYS_MMAP(NULL, (size_t)mbuf->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); @@ -747,7 +745,7 @@ ssize_t result; if ((index = v4l1_get_index(fd)) == -1) - return syscall(SYS_read, fd, buffer, n); + return SYS_READ(fd, buffer, n); pthread_mutex_lock(&devices[index].stream_lock); result = v4l2_read(fd, buffer, n); @@ -758,7 +756,7 @@ void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset) + int64_t offset) { int index; void *result; | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l1/log.c ^ |
@@ -18,12 +18,7 @@ #include <stdio.h> #include <stdlib.h> -#include <linux/ioctl.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev.h> #include "libv4l1-priv.h" | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l1/v4l1compat.c ^ |
@@ -25,6 +25,7 @@ #include <stdarg.h> #include <fcntl.h> #include <libv4l1.h> +#include "../libv4lconvert/libv4lsyscall-priv.h" /* for __off_t */ #include <sys/ioctl.h> #include <sys/mman.h> @@ -61,6 +62,7 @@ return fd; } +#ifdef linux LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) { int fd; @@ -81,6 +83,7 @@ return fd; } +#endif LIBV4L_PUBLIC int close(int fd) { return v4l1_close(fd); @@ -114,11 +117,13 @@ return v4l1_mmap(start, length, prot, flags, fd, offset); } +#ifdef linux LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l1_mmap(start, length, prot, flags, fd, offset); } +#endif LIBV4L_PUBLIC int munmap(void *start, size_t length) { | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l2/Makefile ^ |
@@ -13,9 +13,10 @@ ifeq ($(LINKTYPE),static) V4L2_LIB = libv4l2.a +V4L2_DEPS = $(V4L2_OBJS) else V4L2_LIB = libv4l2.so -V4L2_OBJS += ../libv4lconvert/libv4lconvert.so +V4L2_DEPS += $(V4L2_OBJS) ../libv4lconvert/libv4lconvert.so TARGETS += $(V4L2CONVERT) override CPPFLAGS += -fPIC endif @@ -34,7 +35,9 @@ all: $(TARGETS) -$(V4L2_LIB): $(V4L2_OBJS) +-include $(V4L2_OBJS:.o=.d) + +$(V4L2_LIB): $(V4L2_DEPS) $(V4L2CONVERT): $(V4L2CONVERT_O) $(V4L2_LIB) @@ -68,10 +71,10 @@ install -m 644 libv4l2.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4l2.pc log *~ *.orig *.rej + rm -f *.a *.so* *.o *.d libv4l2.pc log *~ *.orig *.rej DEADJOE %.o: %.c - $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< + $(CC) -Wp,-MMD,"$*.d",-MQ,"$@",-MP -c $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) @@ -79,4 +82,3 @@ %.a: $(AR) cqs $@ $^ - | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l2/libv4l2-priv.h ^ |
@@ -23,14 +23,7 @@ #include <pthread.h> #include <libv4lconvert.h> /* includes videodev2.h for us */ -/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ -#ifdef __NR_mmap2 -#define SYS_mmap2 __NR_mmap2 -#define MMAP2_PAGE_SHIFT 12 -#else -#define SYS_mmap2 SYS_mmap -#define MMAP2_PAGE_SHIFT 0 -#endif +#include "../libv4lconvert/libv4lsyscall-priv.h" #define V4L2_MAX_DEVICES 16 /* Warning when making this larger the frame_queued and frame_mapped members of | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l2/libv4l2.c ^ |
@@ -59,7 +59,6 @@ #include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <syscall.h> #include <fcntl.h> #include <string.h> #include <unistd.h> @@ -77,6 +76,7 @@ #define V4L2_STREAM_CONTROLLED_BY_READ 0x0400 #define V4L2_SUPPORTS_READ 0x0800 #define V4L2_IS_UVC 0x1000 +#define V4L2_STREAM_TOUCHED 0x2000 #define V4L2_MMAP_OFFSET_MAGIC 0xABCDEF00u @@ -99,7 +99,7 @@ devices[index].nreadbuffers; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){ + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){ int saved_err = errno; V4L2_LOG_ERR("requesting %u buffers: %s\n", req.count, strerror(errno)); errno = saved_err; @@ -126,7 +126,7 @@ req.count = 0; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; - if(syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req) < 0) + if(SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req) < 0) return; devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES); @@ -147,7 +147,7 @@ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf); + result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf); if (result) { int saved_err = errno; V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno)); @@ -155,9 +155,9 @@ break; } - devices[index].frame_pointers[i] = (void *)syscall(SYS_mmap2, NULL, + devices[index].frame_pointers[i] = (void *)SYS_MMAP(NULL, (size_t)buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, devices[index].fd, - (__off_t)(buf.m.offset >> MMAP2_PAGE_SHIFT)); + buf.m.offset); if (devices[index].frame_pointers[i] == MAP_FAILED) { int saved_err = errno; V4L2_LOG_ERR("mmapping buffer %u: %s\n", i, strerror(errno)); @@ -181,7 +181,7 @@ /* unmap the buffers */ for (i = 0; i < devices[index].no_frames; i++) { if (devices[index].frame_pointers[i] != MAP_FAILED) { - syscall(SYS_munmap, devices[index].frame_pointers[i], + SYS_MUNMAP(devices[index].frame_pointers[i], devices[index].frame_sizes[i]); devices[index].frame_pointers[i] = MAP_FAILED; V4L2_LOG("unmapped buffer %u\n", i); @@ -195,7 +195,7 @@ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (!(devices[index].flags & V4L2_STREAMON)) { - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMON, + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMON, &type))) { int saved_err = errno; V4L2_LOG_ERR("turning on stream: %s\n", strerror(errno)); @@ -214,7 +214,7 @@ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (devices[index].flags & V4L2_STREAMON) { - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMOFF, + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMOFF, &type))) { int saved_err = errno; V4L2_LOG_ERR("turning off stream: %s\n", strerror(errno)); @@ -242,7 +242,7 @@ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = buffer_index; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, &buf))) { + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, &buf))) { int saved_err = errno; V4L2_LOG_ERR("queuing buf %d: %s\n", buffer_index, strerror(errno)); errno = saved_err; @@ -264,7 +264,7 @@ return result; do { - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf))) { + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf))) { if (errno != EAGAIN) { int saved_err = errno; V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); @@ -364,12 +364,20 @@ return 0; } +static int v4l2_needs_conversion(int index) +{ + if (!(devices[index].flags & V4L2_DISABLE_CONVERSION)) + return v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt); + + return 0; +} + static int v4l2_buffers_mapped(int index) { unsigned int i; - if (devices[index].src_fmt.fmt.pix.pixelformat == - devices[index].dest_fmt.fmt.pix.pixelformat) { + if (!v4l2_needs_conversion(index)) { /* Normal (no conversion) mode */ struct v4l2_buffer buf; @@ -377,7 +385,7 @@ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf)) { + if (SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf)) { int saved_err = errno; V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno)); errno = saved_err; @@ -413,12 +421,12 @@ va_start (ap, oflag); mode = va_arg (ap, mode_t); - fd = syscall(SYS_open, file, oflag, mode); + fd = SYS_OPEN(file, oflag, mode); va_end(ap); } else - fd = syscall(SYS_open, file, oflag); + fd = SYS_OPEN(file, oflag, 0); /* end of original open code */ if (fd == -1) @@ -426,7 +434,7 @@ if (v4l2_fd_open(fd, 0) == -1) { int saved_err = errno; - syscall(SYS_close, fd); + SYS_CLOSE(fd); errno = saved_err; return -1; } @@ -448,7 +456,7 @@ v4l2_log_file = fopen(lfname, "w"); /* check that this is an v4l2 device */ - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap)) { + if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) { int saved_err = errno; V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno)); errno = saved_err; @@ -463,7 +471,7 @@ /* Get current cam format */ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (syscall(SYS_ioctl, fd, VIDIOC_G_FMT, &fmt)) { + if (SYS_IOCTL(fd, VIDIOC_G_FMT, &fmt)) { int saved_err = errno; V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno)); errno = saved_err; @@ -499,6 +507,16 @@ devices[index].src_fmt = fmt; devices[index].dest_fmt = fmt; + /* When a user does a try_fmt with the current dest_fmt and the dest_fmt + is a supported one we will align the resulution (see try_fmt for why). + Do the same here now, so that a try_fmt on the result of a get_fmt done + immediately after open leaves the fmt unchanged. */ + if (v4lconvert_supported_dst_format( + devices[index].dest_fmt.fmt.pix.pixelformat)) { + devices[index].dest_fmt.fmt.pix.width &= ~7; + devices[index].dest_fmt.fmt.pix.height &= ~1; + } + pthread_mutex_init(&devices[index].stream_lock, NULL); devices[index].no_frames = 0; @@ -544,7 +562,7 @@ int index, result; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_close, fd); + return SYS_CLOSE(fd); /* Abuse stream_lock to stop 2 closes from racing and trying to free the resources twice */ @@ -558,15 +576,15 @@ /* Free resources */ v4l2_unmap_buffers(index); - v4lconvert_destroy(devices[index].convert); if (devices[index].convert_mmap_buf != MAP_FAILED) { if (v4l2_buffers_mapped(index)) V4L2_LOG_WARN("v4l2 mmap buffers still mapped on close()\n"); else - syscall(SYS_munmap, devices[index].convert_mmap_buf, + SYS_MUNMAP(devices[index].convert_mmap_buf, devices[index].no_frames * V4L2_FRAME_BUF_SIZE); devices[index].convert_mmap_buf = MAP_FAILED; } + v4lconvert_destroy(devices[index].convert); /* Remove the fd from our list of managed fds before closing it, because as soon as we've done the actual close the fd maybe returned by an open in @@ -576,7 +594,7 @@ /* Since we've marked the fd as no longer used, and freed the resources, redo the close in case it was interrupted */ do { - result = syscall(SYS_close, fd); + result = SYS_CLOSE(fd); } while (result == -1 && errno == EINTR); V4L2_LOG("close: %d\n", fd); @@ -613,7 +631,7 @@ /* We may change from convert to non conversion mode and v4l2_unrequest_read_buffers may change the no_frames, so free the convert mmap buffer */ - syscall(SYS_munmap, devices[index].convert_mmap_buf, + SYS_MUNMAP(devices[index].convert_mmap_buf, devices[index].no_frames * V4L2_FRAME_BUF_SIZE); devices[index].convert_mmap_buf = MAP_FAILED; @@ -659,7 +677,7 @@ { void *arg; va_list ap; - int result, converting, index, saved_err; + int result, index, saved_err; int is_capture_request = 0, stream_needs_locking = 0; va_start (ap, request); @@ -667,7 +685,7 @@ va_end (ap); if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_ioctl, fd, request, arg); + return SYS_IOCTL(fd, request, arg); /* Appearantly the kernel and / or glibc ignore the 32 most significant bits when long = 64 bits, and some applications pass an int holding the req to @@ -676,21 +694,28 @@ /* Is this a capture request and do we need to take the stream lock? */ switch (request) { + case VIDIOC_QUERYCTRL: + case VIDIOC_G_CTRL: + case VIDIOC_S_CTRL: + if (!(devices[index].flags & V4L2_DISABLE_CONVERSION)) + is_capture_request = 1; + break; case VIDIOC_QUERYCAP: is_capture_request = 1; break; case VIDIOC_ENUM_FMT: if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION)) + !(devices[index].flags & V4L2_DISABLE_CONVERSION)) is_capture_request = 1; break; case VIDIOC_ENUM_FRAMESIZES: case VIDIOC_ENUM_FRAMEINTERVALS: - if (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION) + if (!(devices[index].flags & V4L2_DISABLE_CONVERSION)) is_capture_request = 1; break; case VIDIOC_TRY_FMT: - if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + !(devices[index].flags & V4L2_DISABLE_CONVERSION)) is_capture_request = 1; break; case VIDIOC_S_FMT: @@ -724,7 +749,7 @@ } if (!is_capture_request) { - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); saved_err = errno; v4l2_log_ioctl(request, arg, result); errno = saved_err; @@ -732,18 +757,48 @@ } - if (stream_needs_locking) + if (stream_needs_locking) { pthread_mutex_lock(&devices[index].stream_lock); - - converting = v4lconvert_needs_conversion(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt); + /* If this is the first stream related ioctl, and we should only allow + libv4lconvert supported destination formats (so that it can do flipping, + processing, etc.) and the current destination format is not supported, + try setting the format to RGB24 (which is a supported dest. format). */ + if (!(devices[index].flags & V4L2_STREAM_TOUCHED) && + !(devices[index].flags & V4L2_DISABLE_CONVERSION) && + v4lconvert_supported_dst_fmt_only(devices[index].convert) && + !v4lconvert_supported_dst_format( + devices[index].dest_fmt.fmt.pix.pixelformat)) { + struct v4l2_format fmt = devices[index].dest_fmt; + + V4L2_LOG("Setting pixelformat to RGB24 (supported_dst_fmt_only)"); + devices[index].flags |= V4L2_STREAM_TOUCHED; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + pthread_mutex_unlock(&devices[index].stream_lock); + v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt); + pthread_mutex_lock(&devices[index].stream_lock); + V4L2_LOG("Done setting pixelformat (supported_dst_fmt_only)"); + } + devices[index].flags |= V4L2_STREAM_TOUCHED; + } switch (request) { + case VIDIOC_QUERYCTRL: + result = v4lconvert_vidioc_queryctrl(devices[index].convert, arg); + break; + + case VIDIOC_G_CTRL: + result = v4lconvert_vidioc_g_ctrl(devices[index].convert, arg); + break; + + case VIDIOC_S_CTRL: + result = v4lconvert_vidioc_s_ctrl(devices[index].convert, arg); + break; + case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = arg; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYCAP, cap); + result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYCAP, cap); if (result == 0) /* We always support read() as we fake it using mmap mode */ cap->capabilities |= V4L2_CAP_READWRITE; @@ -794,7 +849,7 @@ } if (devices[index].flags & V4L2_DISABLE_CONVERSION) { - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, + result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT, dest_fmt); src_fmt = *dest_fmt; } else { @@ -834,7 +889,7 @@ break; req_pix_fmt = src_fmt.fmt.pix; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT, &src_fmt); + result = SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT, &src_fmt); if (result) { saved_err = errno; V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno)); @@ -884,7 +939,7 @@ if (req->count > V4L2_MAX_NO_FRAMES) req->count = V4L2_MAX_NO_FRAMES; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, req); + result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, req); if (result < 0) break; result = 0; /* some drivers return the number of buffers on success */ @@ -904,8 +959,8 @@ /* Do a real query even when converting to let the driver fill in things like buf->field */ - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, buf); - if (result || !converting) + result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, buf); + if (result || !v4l2_needs_conversion(index)) break; buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; @@ -923,11 +978,11 @@ break; /* With some drivers the buffers must be mapped before queuing */ - if (converting) + if (v4l2_needs_conversion(index)) if ((result = v4l2_map_buffers(index))) break; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, arg); + result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, arg); break; case VIDIOC_DQBUF: @@ -938,8 +993,8 @@ if ((result = v4l2_deactivate_read_stream(index))) break; - if (!converting) { - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); + if (!v4l2_needs_conversion(index)) { + result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf); if (result) { int saved_err = errno; V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); @@ -952,7 +1007,7 @@ but we need the buffer _now_ to write our converted data to it! */ if (devices[index].convert_mmap_buf == MAP_FAILED) { - devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, NULL, + devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL, (size_t)( devices[index].no_frames * V4L2_FRAME_BUF_SIZE), @@ -997,7 +1052,7 @@ break; default: - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); } if (stream_needs_locking) @@ -1018,16 +1073,15 @@ struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_read, fd, dest, n); + return SYS_READ(fd, dest, n); pthread_mutex_lock(&devices[index].stream_lock); /* When not converting and the device supports read let the kernel handle it */ if ((devices[index].flags & V4L2_SUPPORTS_READ) && - !v4lconvert_needs_conversion(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt)) { - result = syscall(SYS_read, fd, dest, n); + !v4l2_needs_conversion(index)) { + result = SYS_READ(fd, dest, n); goto leave; } @@ -1056,7 +1110,7 @@ } void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset) + int64_t offset) { int index; unsigned int buffer_index; @@ -1076,8 +1130,7 @@ return MAP_FAILED; } - return (void *)syscall(SYS_mmap2, start, length, prot, flags, fd, - (__off_t)(offset >> MMAP2_PAGE_SHIFT)); + return (void *)SYS_MMAP(start, length, prot, flags, fd, offset); } pthread_mutex_lock(&devices[index].stream_lock); @@ -1085,15 +1138,14 @@ buffer_index = offset & 0xff; if (buffer_index >= devices[index].no_frames || /* Got magic offset and not converting ?? */ - !v4lconvert_needs_conversion(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt)) { + !v4l2_needs_conversion(index)) { errno = EINVAL; result = MAP_FAILED; goto leave; } if (devices[index].convert_mmap_buf == MAP_FAILED) { - devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, NULL, + devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL, (size_t)( devices[index].no_frames * V4L2_FRAME_BUF_SIZE), @@ -1166,7 +1218,7 @@ V4L2_LOG("v4l2 unknown munmap %p, %d\n", start, (int)length); - return syscall(SYS_munmap, _start, length); + return SYS_MUNMAP(_start, length); } /* Misc utility functions */ @@ -1176,7 +1228,7 @@ struct v4l2_control ctrl = { .id = cid }; int result; - if ((result = syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl))) + if ((result = SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl))) return result; if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) && @@ -1187,7 +1239,7 @@ ctrl.value = (value * (qctrl.maximum - qctrl.minimum) + 32767) / 65535 + qctrl.minimum; - result = syscall(SYS_ioctl, fd, VIDIOC_S_CTRL, &ctrl); + result = SYS_IOCTL(fd, VIDIOC_S_CTRL, &ctrl); } return result; @@ -1198,13 +1250,13 @@ struct v4l2_queryctrl qctrl = { .id = cid }; struct v4l2_control ctrl = { .id = cid }; - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl)) + if (SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl)) return 0; if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) return 0; - if (syscall(SYS_ioctl, fd, VIDIOC_G_CTRL, &ctrl)) + if (SYS_IOCTL(fd, VIDIOC_G_CTRL, &ctrl)) return 0; return ((ctrl.value - qctrl.minimum) * 65535 + | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l2/log.c ^ |
@@ -1,5 +1,7 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (C) 2008 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008 Hans de Goede <hdegoede@redhat.com> # 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 @@ -20,12 +22,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> -#include <linux/ioctl.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev2.h> #include "libv4l2.h" #include "libv4l2-priv.h" @@ -161,7 +158,7 @@ struct v4l2_frmsizeenum *frmsize = arg; int pixfmt = frmsize->pixel_format; - fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c", + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c\n", frmsize->index, pixfmt & 0xff, (pixfmt >> 8) & 0xff, @@ -169,12 +166,12 @@ pixfmt >> 24); switch (frmsize->type) { case V4L2_FRMSIZE_TYPE_DISCRETE: - fprintf(v4l2_log_file, " %ux%u\n", frmsize->discrete.width, + fprintf(v4l2_log_file, " %ux%u\n", frmsize->discrete.width, frmsize->discrete.height); break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: case V4L2_FRMSIZE_TYPE_STEPWISE: - fprintf(v4l2_log_file, " %ux%u -> %ux%u\n", + fprintf(v4l2_log_file, " %ux%u -> %ux%u\n", frmsize->stepwise.min_width, frmsize->stepwise.min_height, frmsize->stepwise.max_width, frmsize->stepwise.max_height); break; @@ -186,7 +183,7 @@ struct v4l2_frmivalenum *frmival = arg; int pixfmt = frmival->pixel_format; - fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c %ux%u: ", + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c %ux%u:\n", frmival->index, pixfmt & 0xff, (pixfmt >> 8) & 0xff, @@ -196,12 +193,12 @@ frmival->height); switch (frmival->type) { case V4L2_FRMIVAL_TYPE_DISCRETE: - fprintf(v4l2_log_file, "%u/%u\n", frmival->discrete.numerator, + fprintf(v4l2_log_file, " %u/%u\n", frmival->discrete.numerator, frmival->discrete.denominator); break; case V4L2_FRMIVAL_TYPE_CONTINUOUS: case V4L2_FRMIVAL_TYPE_STEPWISE: - fprintf(v4l2_log_file, "%u/%u -> %u/%u\n", + fprintf(v4l2_log_file, " %u/%u -> %u/%u\n", frmival->stepwise.min.numerator, frmival->stepwise.min.denominator, frmival->stepwise.max.numerator, | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4l2/v4l2convert.c ^ |
@@ -24,17 +24,11 @@ #include <stdarg.h> #include <stdlib.h> -#include <syscall.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <sys/mman.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -#include <linux/ioctl.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev2.h> #include <libv4l2.h> @@ -72,18 +66,18 @@ va_start (ap, oflag); mode = va_arg (ap, mode_t); - fd = syscall(SYS_open, file, oflag, mode); + fd = SYS_OPEN(file, oflag, mode); va_end(ap); } else - fd = syscall(SYS_open, file, oflag); + fd = SYS_OPEN(file, oflag, 0); /* end of original open code */ if (fd == -1 || !v4l_device) return fd; /* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */ - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap)) + if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) return fd; /* libv4l2 only adds functionality to capture capable devices */ @@ -92,11 +86,12 @@ /* Try to Register with libv4l2 (in case of failure pass the fd to the application as is) */ - v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION); + v4l2_fd_open(fd, 0); return fd; } +#ifdef linux LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) { int fd; @@ -119,6 +114,7 @@ return fd; } +#endif LIBV4L_PUBLIC int close(int fd) { @@ -153,11 +149,13 @@ return v4l2_mmap(start, length, prot, flags, fd, offset); } +#ifdef linux LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } +#endif LIBV4L_PUBLIC int munmap(void *start, size_t length) { | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4lconvert/Makefile ^ |
@@ -3,6 +3,8 @@ CFLAGS := -g -O1 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes +LIBS_libv4lconvert = -lrt -lm + ifeq ($(LINKTYPE),static) CONVERT_LIB = libv4lconvert.a else @@ -12,8 +14,11 @@ 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 + rgbyuv.o spca501.o sq905c.o bayer.o hm12.o helper.o \ + control/libv4lcontrol.o processing/libv4lprocessing.o \ + processing/whitebalance.o processing/autogain.o \ + processing/gamma.o +TARGETS = $(CONVERT_LIB) libv4lconvert.pc ov511-decomp ov518-decomp INCLUDES = ../include/libv4lconvert.h ifeq ($(LIB_RELEASE),) @@ -28,8 +33,12 @@ LIBDIR = $(PREFIX)/lib endif +override CPPFLAGS += -DLIBDIR=\"$(LIBDIR)\" + all: $(TARGETS) +-include $(CONVERT_OBJS:.o=.d) + $(CONVERT_LIB): $(CONVERT_OBJS) libv4lconvert.pc: @@ -40,32 +49,37 @@ @echo 'Description: v4l format conversion library' >> libv4lconvert.pc @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4lconvert.pc @echo 'Libs: -L$${libdir} -lv4lconvert' >> libv4lconvert.pc + @echo 'Libs.private: -lrt -lm' >> libv4lconvert.pc @echo 'Cflags: -I$${prefix}/include' >> libv4lconvert.pc install: all mkdir -p $(DESTDIR)$(PREFIX)/include install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include - mkdir -p $(DESTDIR)$(LIBDIR) + mkdir -p $(DESTDIR)$(LIBDIR)/libv4l ifeq ($(LINKTYPE),static) - mkdir -p $(DESTDIR)$(LIBDIR) install -m 644 $(CONVERT_LIB) $(DESTDIR)$(LIBDIR) else install -m 755 $(CONVERT_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR) cd $(DESTDIR)$(LIBDIR) && \ ln -f -s $(CONVERT_LIB).$(LIB_RELEASE) $(CONVERT_LIB) endif + install -m 755 *-decomp $(DESTDIR)$(LIBDIR)/libv4l mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~ *.orig *.rej + rm -f *.a *.so* *.o *.d */*.o */*.d libv4lconvert.pc log *~ */*~ + rm -f *.orig *.rej */*.orig */*.rej DEADJOE */DEADJOE *-decomp %.o: %.c - $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< + $(CC) -Wp,-MMD,"$*.d",-MQ,"$@",-MP -c $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: - $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) ln -f -s $@.$(LIB_RELEASE) $@ %.a: $(AR) cqs $@ $^ + +ov511-decomp: ov511-decomp.o +ov518-decomp: ov518-decomp.o | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/control ^ |
+(directory) | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/control/libv4lcontrol-priv.h ^ |
@@ -0,0 +1,58 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 +*/ + +#ifndef __LIBV4LCONTROL_PRIV_H +#define __LIBV4LCONTROL_PRIV_H + +#define V4LCONTROL_SHM_SIZE 4096 + +#define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01 + +struct v4lcontrol_flags_info; + +struct v4lcontrol_data { + int fd; /* Device fd */ + int flags; /* Flags for this device */ + int priv_flags; /* Internal use only flags */ + int controls; /* Which controls to use for this device */ + unsigned int *shm_values; /* shared memory control value store */ + unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */ + const struct v4lcontrol_flags_info *flags_info; +}; + +struct v4lcontrol_flags_info { + unsigned short vendor_id; + unsigned short product_id; + unsigned short product_mask; + const char *dmi_board_vendor; + const char *dmi_board_name; +/* We could also use the USB manufacturer and product strings some devices have + const char *manufacturer; + const char *product; */ + int flags; + int default_gamma; +/* Some seldom used dmi strings (for notebooks with bogus info in the board + entries, but usefull info elsewhere). We keep this at the end as to not + polute the initalizers for the normal case. */ + const char *dmi_system_version; +}; + +#endif | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/control/libv4lcontrol.c ^ |
@@ -0,0 +1,562 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "libv4lcontrol.h" +#include "libv4lcontrol-priv.h" +#include "../libv4lsyscall-priv.h" +#include <linux/videodev2.h> + +#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) + +/* Workaround these potentially missing from videodev2.h */ +#ifndef V4L2_IN_ST_HFLIP +#define V4L2_IN_ST_HFLIP 0x00000010 /* Frames are flipped horizontally */ +#endif + +#ifndef V4L2_IN_ST_VFLIP +#define V4L2_IN_ST_VFLIP 0x00000020 /* Frames are flipped vertically */ +#endif + + +/* List of cams which need special flags */ +static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { +/* First: Upside down devices */ + /* Philips SPC200NC */ + { 0x0471, 0x0325, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Philips SPC300NC */ + { 0x0471, 0x0326, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Philips SPC210NC */ + { 0x0471, 0x032d, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Genius E-M 112 (also want whitebalance by default) */ + { 0x093a, 0x2476, 0, NULL, NULL, + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED|V4LCONTROL_WANTS_WB, 1500 }, + /* Laptops */ + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vn ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N51Vg ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "F7SR ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "W7S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "X55SR ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "X55SV ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* These 2 PACKARD BELL's are Asus notebook in disguise */ + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG45", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG46", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "K40IJ ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "N10J ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Note no whitespace padding for board vendor, this is not a typo */ + { 0x064e, 0xa111, 0, "ASUSTeK Computer Inc.", "F5RL ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Another Asus laptop in disguise */ + { 0x064e, 0xa111, 0, "PEGATRON CORPORATION ", "F5SR ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc. ", "N20A ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x090c, 0xe370, 0, "ASUSTeK Computer Inc. ", "U6S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Ke ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Sa ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Sr ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* The G1S has been seen with 2 different board name strings */ + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "G1S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "G1S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "G1Sn ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "G2S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0, "Lenovo IdeaPad U330" }, + +/* Second: devices which should use some software processing by default */ + /* Pac207 based devices */ + { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x093a, 0x2460, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x145f, 0x013a, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x2001, 0xf115, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + /* Pac7302 based devices */ + { 0x093a, 0x2620, 0x0f, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG|V4LCONTROL_WANTS_WB }, + /* Pac7311 based devices */ + { 0x093a, 0x2600, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* sq905 devices */ + { 0x2770, 0x9120, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* spca561 revison 12a devices */ + { 0x041e, 0x403b, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x046d, 0x0928, 7, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + /* logitech quickcam express stv06xx + pb0100 */ + { 0x046d, 0x0840, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* logitech quickcam messenger variants, st6422 */ + { 0x046d, 0x08f0, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08f5, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08f6, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08da, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, +}; + +static const struct v4l2_queryctrl fake_controls[]; + +static void v4lcontrol_init_flags(struct v4lcontrol_data *data) +{ + struct stat st; + FILE *f; + char sysfs_name[512]; + unsigned short vendor_id = 0; + unsigned short product_id = 0; + char dmi_board_vendor[512] = ""; + char dmi_board_name[512]= ""; + char dmi_system_version[512]= ""; + int i, minor; + char c, *s, buf[32]; + struct v4l2_input input; + + if ((SYS_IOCTL(data->fd, VIDIOC_G_INPUT, &input.index) == 0) && + (SYS_IOCTL(data->fd, VIDIOC_ENUMINPUT, &input) == 0)) { + if (input.status & V4L2_IN_ST_HFLIP) + data->flags |= V4LCONTROL_HFLIPPED; + if (input.status & V4L2_IN_ST_VFLIP) + data->flags |= V4LCONTROL_VFLIPPED; + } + + if (fstat(data->fd, &st) || !S_ISCHR(st.st_mode)) { + return; /* Should never happen */ + } + + /* <Sigh> find ourselve in sysfs */ + for (i = 0; i < 256; i++) { + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/dev", i); + f = fopen(sysfs_name, "r"); + if (!f) + continue; + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (s && sscanf(buf, "%*d:%d%c", &minor, &c) == 2 && c == '\n' && + minor == minor(st.st_rdev)) + break; + } + if (i == 256) + return; /* Not found, sysfs not mounted? */ + + /* Get vendor and product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/device/modalias", i); + f = fopen(sysfs_name, "r"); + if (f) { + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || + sscanf(s, "usb:v%4hxp%4hx%c", &vendor_id, &product_id, &c) != 3 || + c != 'd') + return; /* Not an USB device */ + } else { + /* Try again assuming the device link points to the usb + device instead of the usb interface (bug in older versions + of gspca) */ + + /* Get product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/device/idVendor", i); + f = fopen(sysfs_name, "r"); + if (!f) + return; /* Not an USB device (or no sysfs) */ + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "%04hx%c", &vendor_id, &c) != 2 || c != '\n') + return; /* Should never happen */ + + /* Get product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/device/idProduct", i); + f = fopen(sysfs_name, "r"); + if (!f) + return; /* Should never happen */ + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "%04hx%c", &product_id, &c) != 2 || c != '\n') + return; /* Should never happen */ + } + + /* Get DMI board vendor and name */ + f = fopen("/sys/devices/virtual/dmi/id/board_vendor", "r"); + if (f) { + s = fgets(dmi_board_vendor, sizeof(dmi_board_vendor), f); + if (s) + s[strlen(s) - 1] = 0; + fclose(f); + } + + f = fopen("/sys/devices/virtual/dmi/id/board_name", "r"); + if (f) { + s = fgets(dmi_board_name, sizeof(dmi_board_name), f); + if (s) + s[strlen(s) - 1] = 0; + fclose(f); + } + + f = fopen("/sys/devices/virtual/dmi/id/product_version", "r"); + if (f) { + s = fgets(dmi_system_version, sizeof(dmi_system_version), f); + if (s) + s[strlen(s) - 1] = 0; + fclose(f); + } + + for (i = 0; i < ARRAY_SIZE(v4lcontrol_flags); i++) + if (v4lcontrol_flags[i].vendor_id == vendor_id && + v4lcontrol_flags[i].product_id == + (product_id & ~v4lcontrol_flags[i].product_mask) && + (v4lcontrol_flags[i].dmi_board_vendor == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_vendor, dmi_board_vendor)) && + (v4lcontrol_flags[i].dmi_board_name == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_name, dmi_board_name)) && + (v4lcontrol_flags[i].dmi_system_version == NULL || + !strcmp(v4lcontrol_flags[i].dmi_system_version, dmi_system_version))) { + data->flags |= v4lcontrol_flags[i].flags; + data->flags_info = &v4lcontrol_flags[i]; + break; + } +} + +struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) +{ + int shm_fd; + int i, rc, init = 0; + char *s, shm_name[256]; + struct v4l2_capability cap; + struct v4l2_queryctrl ctrl; + + struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data)); + + if (!data) + return NULL; + + data->fd = fd; + + v4lcontrol_init_flags(data); + + /* Allow overriding through environment */ + if ((s = getenv("LIBV4LCONTROL_FLAGS"))) + data->flags = strtol(s, NULL, 0); + + ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl) == 0) + data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL; + + /* If the device always needs conversion, we can add fake controls at no cost + (no cost when not activated by the user that is) */ + if (always_needs_conversion || v4lcontrol_needs_conversion(data)) { + for (i = 0; i < V4LCONTROL_AUTO_ENABLE_COUNT; i++) { + ctrl.id = fake_controls[i].id; + rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl); + if (rc == -1 || (rc == 0 && (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))) + data->controls |= 1 << i; + } + } + + if (data->flags & V4LCONTROL_WANTS_AUTOGAIN) + data->controls |= 1 << V4LCONTROL_AUTOGAIN | + 1 << V4LCONTROL_AUTOGAIN_TARGET; + + /* Allow overriding through environment */ + if ((s = getenv("LIBV4LCONTROL_CONTROLS"))) + data->controls = strtol(s, NULL, 0); + + if (data->controls == 0) + return data; /* No need to create a shared memory segment */ + + SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap); + snprintf(shm_name, 256, "/%s:%s", cap.bus_info, cap.card); + + /* / is not allowed inside shm names */ + for (i = 1; shm_name[i]; i++) + if (shm_name[i] == '/') + shm_name[i] = '-'; + + /* Open the shared memory object identified by shm_name */ + if ((shm_fd = shm_open(shm_name, (O_CREAT | O_EXCL | O_RDWR), + (S_IREAD | S_IWRITE))) >= 0) + init = 1; + else if ((shm_fd = shm_open(shm_name, O_RDWR, (S_IREAD | S_IWRITE))) < 0) + goto error; + + /* Set the shared memory size */ + ftruncate(shm_fd, V4LCONTROL_SHM_SIZE); + + /* Retreive a pointer to the shm object */ + data->shm_values = mmap(NULL, V4LCONTROL_SHM_SIZE, (PROT_READ | PROT_WRITE), + MAP_SHARED, shm_fd, 0); + close(shm_fd); + + if (data->shm_values == MAP_FAILED) + goto error; + + if (init) { + /* Initialize the new shm object we created */ + memset(data->shm_values, 0, sizeof(V4LCONTROL_SHM_SIZE)); + + for (i = 0; i < V4LCONTROL_COUNT; i++) + data->shm_values[i] = fake_controls[i].default_value; + + if (data->flags & V4LCONTROL_WANTS_WB) + data->shm_values[V4LCONTROL_WHITEBALANCE] = 1; + + if (data->flags_info && data->flags_info->default_gamma) + data->shm_values[V4LCONTROL_GAMMA] = data->flags_info->default_gamma; + } + + return data; + +error: + free(data); + return NULL; +} + +void v4lcontrol_destroy(struct v4lcontrol_data *data) +{ + if (data->controls) + munmap(data->shm_values, V4LCONTROL_SHM_SIZE); + free(data); +} + +/* FIXME get better CID's for normalize */ +static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = { +{ + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Whitebalance (software)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 +}, +{ + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal flip (sw)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 +}, +{ + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip (sw)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 +}, +{ + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma (software)", + .minimum = 500, /* == 0.5 */ + .maximum = 3000, /* == 3.0 */ + .step = 1, + .default_value = 1000, /* == 1.0 */ + .flags = 0 +}, +{}, /* Dummy place holder for V4LCONTROL_AUTO_ENABLE_COUNT */ +{ + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain (software)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0 +}, +{ + .id = V4L2_CTRL_CLASS_USER + 0x2000, /* FIXME */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto Gain target", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 100, + .flags = 0 +}, +}; + +static void v4lcontrol_copy_queryctrl(struct v4lcontrol_data *data, + struct v4l2_queryctrl *ctrl, int i) +{ + memcpy(ctrl, &fake_controls[i], sizeof(struct v4l2_queryctrl)); + + /* Hmm, not pretty */ + if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE && + (data->flags & V4LCONTROL_WANTS_WB)) + ctrl->default_value = 1; + + if (ctrl->id == V4L2_CID_GAMMA && data->flags_info && + data->flags_info->default_gamma) + ctrl->default_value = data->flags_info->default_gamma; +} + +int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_queryctrl *ctrl = arg; + int retval; + __u32 orig_id=ctrl->id; + + /* if we have an exact match return it */ + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + v4lcontrol_copy_queryctrl(data, ctrl, i); + return 0; + } + + /* find out what the kernel driver would respond. */ + retval = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, arg); + + if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) && + (orig_id & V4L2_CTRL_FLAG_NEXT_CTRL)) { + /* If the hardware has no more controls check if we still have any + fake controls with a higher id then the hardware's highest */ + if (retval) + ctrl->id = V4L2_CTRL_FLAG_NEXT_CTRL; + + /* If any of our controls have an id > orig_id but less than + ctrl->id then return that control instead. Note we do not + break when we have a match, but keep iterating, so that + we end up with the fake ctrl with the lowest CID > orig_id. */ + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + (fake_controls[i].id > (orig_id & ~V4L2_CTRL_FLAG_NEXT_CTRL)) && + (fake_controls[i].id <= ctrl->id)) { + v4lcontrol_copy_queryctrl(data, ctrl, i); + retval = 0; + } + } + + return retval; +} + +int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_control *ctrl = arg; + + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + ctrl->value = data->shm_values[i]; + return 0; + } + + return SYS_IOCTL(data->fd, VIDIOC_G_CTRL, arg); +} + +int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_control *ctrl = arg; + + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + if (ctrl->value > fake_controls[i].maximum || + ctrl->value < fake_controls[i].minimum) { + errno = EINVAL; + return -1; + } + + data->shm_values[i] = ctrl->value; + return 0; + } + + return SYS_IOCTL(data->fd, VIDIOC_S_CTRL, arg); +} + +int v4lcontrol_get_flags(struct v4lcontrol_data *data) +{ + return data->flags; +} + +int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl) +{ + if (data->controls & (1 << ctrl)) { + /* Special case for devices with flipped input */ + if ((ctrl == V4LCONTROL_HFLIP && (data->flags & V4LCONTROL_HFLIPPED)) || + (ctrl == V4LCONTROL_VFLIP && (data->flags & V4LCONTROL_VFLIPPED))) + return !data->shm_values[ctrl]; + + return data->shm_values[ctrl]; + } + + return 0; +} + +int v4lcontrol_controls_changed(struct v4lcontrol_data *data) +{ + int res; + + if (!data->controls) + return 0; + + res = memcmp(data->shm_values, data->old_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + memcpy(data->old_values, data->shm_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + return res; +} + +/* See the comment about this in libv4lconvert.h */ +int v4lcontrol_needs_conversion(struct v4lcontrol_data *data) { + return data->flags || data->controls; +} | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/control/libv4lcontrol.h ^ |
@@ -0,0 +1,70 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 +*/ + +#ifndef __LIBV4LCONTROL_H +#define __LIBV4LCONTROL_H + +/* Flags */ +#define V4LCONTROL_HFLIPPED 0x01 +#define V4LCONTROL_VFLIPPED 0x02 +#define V4LCONTROL_ROTATED_90_JPEG 0x04 +#define V4LCONTROL_WANTS_WB 0x08 +#define V4LCONTROL_WANTS_AUTOGAIN 0x10 + +/* Masks */ +#define V4LCONTROL_WANTS_WB_AUTOGAIN (V4LCONTROL_WANTS_WB | V4LCONTROL_WANTS_AUTOGAIN) + +/* Controls */ +enum { + V4LCONTROL_WHITEBALANCE, + V4LCONTROL_HFLIP, + V4LCONTROL_VFLIP, + V4LCONTROL_GAMMA, + /* All fake controls above here are auto enabled when not present in hw */ + V4LCONTROL_AUTO_ENABLE_COUNT, + V4LCONTROL_AUTOGAIN, + V4LCONTROL_AUTOGAIN_TARGET, + V4LCONTROL_COUNT + }; + +struct v4lcontrol_data; + +struct v4lcontrol_data* v4lcontrol_create(int fd, int always_needs_conversion); +void v4lcontrol_destroy(struct v4lcontrol_data *data); + +/* Functions used by v4lprocessing to get the control state */ +int v4lcontrol_get_flags(struct v4lcontrol_data *data); +int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl); +/* Check if the controls have changed since the last time this function + was called */ +int v4lcontrol_controls_changed(struct v4lcontrol_data *data); +/* Check if we must go through the conversion path (and thus alloc conversion + buffers, etc. in libv4l2). Note this always return 1 if we *may* need + rotate90 / flipping / processing, as if we actually need this may change + on the fly while the stream is active. */ +int v4lcontrol_needs_conversion(struct v4lcontrol_data *data); + +/* Functions used by v4lconvert to pass vidioc calls from libv4l2 */ +int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg); + +#endif | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4lconvert/crop.c ^ |
@@ -69,8 +69,8 @@ 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; + int startx = (src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width) & ~1; + int starty = (src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height) & ~1; unsigned char *mysrc, *mysrc2; /* Y */ @@ -113,8 +113,8 @@ 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; + int startx = ((src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2) & ~1; + int starty = ((src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2) & ~1; unsigned char *mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx; /* Y */ @@ -143,26 +143,146 @@ } } +/* Ok, so this is not really cropping, but more the reverse, whatever */ +static void v4lconvert_add_border_rgbbgr24( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int y; + int borderx = (dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2; + int bordery = (dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2; + + for (y = 0; y < bordery; y++) { + memset(dest, 0, dest_fmt->fmt.pix.width * 3); + dest += dest_fmt->fmt.pix.bytesperline; + } + + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + memset(dest, 0, borderx * 3); + dest += borderx * 3; + + memcpy(dest, src, src_fmt->fmt.pix.width * 3); + src += src_fmt->fmt.pix.bytesperline; + dest += src_fmt->fmt.pix.width * 3; + + memset(dest, 0, borderx * 3); + dest += dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width) * 3; + } + + for (y = 0; y < bordery; y++) { + memset(dest, 0, dest_fmt->fmt.pix.width * 3); + dest += dest_fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_add_border_yuv420( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int y; + int borderx = ((dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2) & ~1; + int bordery = ((dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2) & ~1; + + /* Y */ + for (y = 0; y < bordery; y++) { + memset(dest, 16, dest_fmt->fmt.pix.width); + dest += dest_fmt->fmt.pix.bytesperline; + } + + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + memset(dest, 16, borderx); + dest += borderx; + + memcpy(dest, src, src_fmt->fmt.pix.width); + src += src_fmt->fmt.pix.bytesperline; + dest += src_fmt->fmt.pix.width; + + memset(dest, 16, borderx); + dest += dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width); + } + + for (y = 0; y < bordery; y++) { + memset(dest, 16, dest_fmt->fmt.pix.width); + dest += dest_fmt->fmt.pix.bytesperline; + } + + /* U */ + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memset(dest, 128, borderx / 2); + dest += borderx / 2; + + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + src += src_fmt->fmt.pix.bytesperline / 2; + dest += src_fmt->fmt.pix.width / 2; + + memset(dest, 128, borderx / 2); + dest += (dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width)) / 2; + } + + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + /* V */ + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memset(dest, 128, borderx / 2); + dest += borderx / 2; + + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + src += src_fmt->fmt.pix.bytesperline / 2; + dest += src_fmt->fmt.pix.width / 2; + + memset(dest, 128, borderx / 2); + dest += (dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width)) / 2; + } + + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 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) + if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height) + v4lconvert_add_border_rgbbgr24(src, dest, src_fmt, dest_fmt); + else 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) + if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height) + v4lconvert_add_border_yuv420(src, dest, src_fmt, dest_fmt); + else 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; } } | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4lconvert/flip.c ^ |
@@ -20,8 +20,100 @@ */ +#include <string.h> #include "libv4lconvert-priv.h" +static void v4lconvert_vflip_rgbbgr24(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int y; + + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline; + for (y = 0; y < fmt->fmt.pix.height; y++) { + src -= fmt->fmt.pix.bytesperline; + memcpy(dest, src, fmt->fmt.pix.width * 3); + dest += fmt->fmt.pix.width * 3; + } +} + +static void v4lconvert_vflip_yuv420(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int y; + + /* First flip the Y plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline; + for (y = 0; y < fmt->fmt.pix.height; y++) { + src -= fmt->fmt.pix.bytesperline; + memcpy(dest, src, fmt->fmt.pix.width); + dest += fmt->fmt.pix.width; + } + + /* Now flip the U plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline * 5 / 4; + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src -= fmt->fmt.pix.bytesperline / 2; + memcpy(dest, src, fmt->fmt.pix.width / 2); + dest += fmt->fmt.pix.width / 2; + } + + /* Last flip the V plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 2; + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src -= fmt->fmt.pix.bytesperline / 2; + memcpy(dest, src, fmt->fmt.pix.width / 2); + dest += fmt->fmt.pix.width / 2; + } +} + +static void v4lconvert_hflip_rgbbgr24(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int x, y; + + for (y = 0; y < fmt->fmt.pix.height; y++) { + src += fmt->fmt.pix.width * 3; + for (x = 0; x < fmt->fmt.pix.width; x++) { + src -= 3; + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest += 3; + } + src += fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_hflip_yuv420(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int x, y; + + /* First flip the Y plane */ + for (y = 0; y < fmt->fmt.pix.height; y++) { + src += fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline; + } + + /* Now flip the U plane */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src += fmt->fmt.pix.width / 2; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline / 2; + } + + /* Last flip the V plane */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src += fmt->fmt.pix.width / 2; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline / 2; + } +} + static void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, int width, int height) { @@ -106,37 +198,69 @@ } } -void v4lconvert_rotate(unsigned char *src, unsigned char *dest, - int width, int height, unsigned int pix_fmt, int rotate) +void v4lconvert_rotate90(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int tmp; + + tmp = fmt->fmt.pix.width; + fmt->fmt.pix.width = fmt->fmt.pix.height; + fmt->fmt.pix.height = tmp; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate90_rgbbgr24(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_rotate90_yuv420(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + } +} + +void v4lconvert_flip(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt, int hflip, int vflip) { - switch (rotate) { - case 0: - break; - case 90: - switch (pix_fmt) { + if (vflip && hflip) { + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate180_rgbbgr24(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_rotate180_yuv420(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + } + } else if (hflip) { + switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: - v4lconvert_rotate90_rgbbgr24(src, dest, width, height); + v4lconvert_hflip_rgbbgr24(src, dest, fmt); break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - v4lconvert_rotate90_yuv420(src, dest, width, height); + v4lconvert_hflip_yuv420(src, dest, fmt); break; } - break; - case 180: - switch (pix_fmt) { + } else if (vflip) { + switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: - v4lconvert_rotate180_rgbbgr24(src, dest, width, height); + v4lconvert_vflip_rgbbgr24(src, dest, fmt); break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - v4lconvert_rotate180_yuv420(src, dest, width, height); + v4lconvert_vflip_yuv420(src, dest, fmt); break; } - break; - default: - printf("FIXME add %d degrees rotation\n", rotate); } + + /* Our newly written data has no padding */ + v4lconvert_fixup_fmt(fmt); } | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/helper-funcs.h ^ |
@@ -0,0 +1,79 @@ +/* Utility functions for decompression helpers + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +static int v4lconvert_helper_write(int fd, const void *b, size_t count, + char *progname) +{ + const unsigned char *buf = b; + size_t ret, written = 0; + + while (written < count) { + ret = write(fd, buf + written, count - written); + if (ret == -1) { + if (errno == EINTR) + continue; + + if (errno == EPIPE) /* Main program has quited */ + exit(0); + + fprintf(stderr, "%s: error writing: %s\n", progname, strerror(errno)); + return -1; + } + written += ret; + } + + return 0; +} + +static int v4lconvert_helper_read(int fd, void *b, size_t count, + char *progname) +{ + unsigned char *buf = b; + size_t ret, r = 0; + + while (r < count) { + ret = read(fd, buf + r, count - r); + if (ret == -1) { + if (errno == EINTR) + continue; + + fprintf(stderr, "%s: error reading: %s\n", progname, strerror(errno)); + return -1; + } + if (ret == 0) /* EOF */ + exit(0); + + r += ret; + } + + return 0; +} | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/helper.c ^ |
@@ -0,0 +1,224 @@ +/* +# (C) 2009 Hans de Goede <hdegoede@redhat.com> + +# 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <sys/wait.h> +#include "libv4lconvert-priv.h" + +#define READ_END 0 +#define WRITE_END 1 + +/* <sigh> Unfortunately I've failed in contact some Authors of decompression + code of out of tree drivers. So I've no permission to relicense their code + their code from GPL to LGPL. To work around this, these decompression + algorithms are put in separate executables and we pipe data through these + to decompress. + + The "protocol" is very simple: + + From libv4l to the helper the following is send: + int width + int height + int flags + int data length + unsigned char[] data (data length long) + + From the helper to libv4l the following is send: + int data length (-1 in case of a decompression error) + unsigned char[] data (not present when a decompression error happened) +*/ + +static int v4lconvert_helper_start(struct v4lconvert_data *data, + const char *helper) +{ + if (pipe(data->decompress_in_pipe)) { + V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno)); + goto error; + } + + if (pipe(data->decompress_out_pipe)) { + V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno)); + goto error_close_in_pipe; + } + + data->decompress_pid = fork(); + if (data->decompress_pid == -1) { + V4LCONVERT_ERR("with helper fork: %s\n", strerror(errno)); + goto error_close_out_pipe; + } + + if (data->decompress_pid == 0) { + /* We are the child */ + + /* Closed unused read / write end of the pipes */ + close(data->decompress_out_pipe[WRITE_END]); + close(data->decompress_in_pipe[READ_END]); + + /* Connect stdin / out to the pipes */ + if (dup2(data->decompress_out_pipe[READ_END], STDIN_FILENO) == -1) { + perror("libv4lconvert: error with helper dup2"); + exit(1); + } + if (dup2(data->decompress_in_pipe[WRITE_END], STDOUT_FILENO) == -1) { + perror("libv4lconvert: error with helper dup2"); + exit(1); + } + + /* And execute the helper */ + execl(helper, helper, NULL); + + /* We should never get here */ + perror("libv4lconvert: error starting helper"); + exit(1); + } else { + /* Closed unused read / write end of the pipes */ + close(data->decompress_out_pipe[READ_END]); + close(data->decompress_in_pipe[WRITE_END]); + } + + return 0; + +error_close_out_pipe: + close(data->decompress_out_pipe[READ_END]); + close(data->decompress_out_pipe[WRITE_END]); +error_close_in_pipe: + close(data->decompress_in_pipe[READ_END]); + close(data->decompress_in_pipe[WRITE_END]); +error: + return -1; +} + +/* IMPROVE ME: we could block SIGPIPE here using pthread_sigmask() + and then in case of EPIPE consume the signal using + sigtimedwait (we need to check if a blocked signal wasn't present + before the write, otherwise we will consume that) and + after consuming the signal try to restart the helper. + + Note we currently do not do this, as SIGPIPE only happens if the + decompressor crashes, which in case of an embedded decompressor + would mean end of program, so by not handling SIGPIPE we treat + external decompressors identical. */ +static int v4lconvert_helper_write(struct v4lconvert_data *data, + const void *b, size_t count) +{ + const unsigned char *buf = b; + const int *i = b; + size_t ret, written = 0; + + while (written < count) { + ret = write(data->decompress_out_pipe[WRITE_END], buf + written, + count - written); + if (ret == -1) { + if (errno == EINTR) + continue; + + V4LCONVERT_ERR("writing to helper: %s\n", strerror(errno)); + return -1; + } + written += ret; + } + + return 0; +} + +static int v4lconvert_helper_read(struct v4lconvert_data *data, void *b, + size_t count) +{ + unsigned char *buf = b; + size_t ret, r = 0; + + while (r < count) { + ret = read(data->decompress_in_pipe[READ_END], buf + r, count - r); + if (ret == -1) { + if (errno == EINTR) + continue; + + V4LCONVERT_ERR("reading from helper: %s\n", strerror(errno)); + return -1; + } + if (ret == 0) { + V4LCONVERT_ERR("reading from helper: unexpected EOF\n"); + return -1; + } + r += ret; + } + + return 0; +} + +int v4lconvert_helper_decompress(struct v4lconvert_data *data, + const char *helper, const unsigned char *src, int src_size, + unsigned char *dest, int dest_size, int width, int height, int flags) +{ + int r; + + if (data->decompress_pid == -1) { + if (v4lconvert_helper_start(data, helper)) + return -1; + } + + if (v4lconvert_helper_write(data, &width, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &height, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &flags, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &src_size, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, src, src_size)) + return -1; + + if (v4lconvert_helper_read(data, &r, sizeof(int))) + return -1; + + if (r < 0) { + V4LCONVERT_ERR("decompressing frame data\n"); + return -1; + } + + if (dest_size < r) { + V4LCONVERT_ERR("destination buffer to small\n"); + return -1; + } + + return v4lconvert_helper_read(data, dest, r); +} + +void v4lconvert_helper_cleanup(struct v4lconvert_data *data) +{ + int status; + + if (data->decompress_pid != -1) { + kill(data->decompress_pid, SIGTERM); + waitpid(data->decompress_pid, &status, 0); + + close(data->decompress_out_pipe[WRITE_END]); + close(data->decompress_in_pipe[READ_END]); + + data->decompress_pid = -1; + } +} | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4lconvert/libv4lconvert-priv.h ^ |
@@ -20,9 +20,14 @@ #define __LIBV4LCONVERT_PRIV_H #include <stdio.h> +#include <sys/types.h> #include "libv4lconvert.h" +#include "control/libv4lcontrol.h" +#include "processing/libv4lprocessing.h" #include "tinyjpeg.h" +/* Workaround these potentially missing from videodev2.h */ + #ifndef V4L2_PIX_FMT_SPCA501 #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ #endif @@ -79,6 +84,16 @@ #define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') #endif +#ifndef V4L2_PIX_FMT_OV511 +#define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') +#endif + +#ifndef V4L2_PIX_FMT_OV518 +#define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ +#endif + +#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) + #define V4LCONVERT_ERROR_MSG_SIZE 256 #define V4LCONVERT_MAX_FRAMESIZES 16 @@ -87,37 +102,40 @@ "v4l-convert: error " __VA_ARGS__) /* Card flags */ -#define V4LCONVERT_ROTATE_90 0x01 -#define V4LCONVERT_ROTATE_180 0x02 -#define V4LCONVERT_IS_UVC 0x04 +#define V4LCONVERT_IS_UVC 0x01 +#define V4LCONVERT_IS_SN9C20X 0x02 /* Pixformat flags */ -#define V4LCONVERT_COMPRESSED 0x01 +#define V4LCONVERT_COMPRESSED 0x01 /* Compressed format */ +#define V4LCONVERT_NEEDS_CONVERSION 0x02 /* Apps likely wont know this */ struct v4lconvert_data { int fd; int flags; /* bitfield */ + int control_flags; /* bitfield */ int supported_src_formats; /* bitfield */ unsigned int no_formats; char error_msg[V4LCONVERT_ERROR_MSG_SIZE]; struct jdec_private *jdec; struct v4l2_frmsizeenum framesizes[V4LCONVERT_MAX_FRAMESIZES]; unsigned int no_framesizes; - int convert_buf_size; - int rotate_buf_size; + int convert1_buf_size; + int convert2_buf_size; + int rotate90_buf_size; + int flip_buf_size; int convert_pixfmt_buf_size; - unsigned char *convert_buf; - unsigned char *rotate_buf; + unsigned char *convert1_buf; + unsigned char *convert2_buf; + unsigned char *rotate90_buf; + unsigned char *flip_buf; unsigned char *convert_pixfmt_buf; -}; + struct v4lcontrol_data *control; + struct v4lprocessing_data *processing; -struct v4lconvert_flags_info { - 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; + /* Data for external decompression helpers code */ + pid_t decompress_pid; + int decompress_in_pipe[2]; /* Data from helper to us */ + int decompress_out_pipe[2]; /* Data from us to helper */ }; struct v4lconvert_pixfmt { @@ -125,6 +143,8 @@ int flags; }; +void v4lconvert_fixup_fmt(struct v4l2_format *fmt); + void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest, const struct v4l2_format *src_fmt, int bgr, int yvu); @@ -164,6 +184,15 @@ void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dst, const struct v4l2_format *src_fmt); +void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height); + +void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height); + +void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int yvu); + void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height, int yvu); @@ -182,7 +211,8 @@ void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst, int width, int height); -void v4lconvert_decode_pac207(const unsigned char *src, unsigned char *dst, +int v4lconvert_decode_pac207(struct v4lconvert_data *data, + const unsigned char *inp, int src_size, unsigned char *outp, int width, int height); void v4lconvert_decode_mr97310a(const unsigned char *src, unsigned char *dst, @@ -209,10 +239,19 @@ void v4lconvert_hm12_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height, int yvu); -void v4lconvert_rotate(unsigned char *src, unsigned char *dest, - int width, int height, unsigned int pix_fmt, int rotate); +void v4lconvert_rotate90(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt); + +void v4lconvert_flip(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt, int hflip, int vflip); void v4lconvert_crop(unsigned char *src, unsigned char *dest, const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt); +int v4lconvert_helper_decompress(struct v4lconvert_data *data, + const char *helper, const unsigned char *src, int src_size, + unsigned char *dest, int dest_size, int width, int height, int command); + +void v4lconvert_helper_cleanup(struct v4lconvert_data *data); + #endif | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4lconvert/libv4lconvert.c ^ |
@@ -19,15 +19,14 @@ #include <errno.h> #include <string.h> #include <stdlib.h> -#include <syscall.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include "libv4lconvert.h" #include "libv4lconvert-priv.h" +#include "libv4lsyscall-priv.h" #define MIN(a,b) (((a)<(b))?(a):(b)) -#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) /* Note for proper functioning of v4lconvert_enum_fmt the first entries in supported_src_pixfmts must match with the entries in supported_dst_pixfmts */ @@ -47,15 +46,16 @@ { 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_RGB565, 0 }, + { V4L2_PIX_FMT_SN9C20X_I420, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SBGGR8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SGBRG8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SGRBG8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SRGGB8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SPCA501, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SPCA505, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SPCA508, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_HM12, V4LCONVERT_NEEDS_CONVERSION }, { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED }, @@ -64,20 +64,14 @@ { V4L2_PIX_FMT_MR97310A, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_SQ905C, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_OV511, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_OV518, V4LCONVERT_COMPRESSED }, }; static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = { SUPPORTED_DST_PIXFMTS }; -/* List of cams which need special flags */ -static const struct v4lconvert_flags_info v4lconvert_flags[] = { - { 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] = { @@ -91,56 +85,21 @@ { 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; struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data)); struct v4l2_capability cap; + /* This keeps tracks of devices which have only formats for which apps + most likely will need conversion and we can thus safely add software + processing controls without a performance impact. */ + int always_needs_conversion = 1; if (!data) return NULL; data->fd = fd; + data->decompress_pid = -1; /* Check supported formats */ for (i = 0; ; i++) { @@ -148,24 +107,47 @@ fmt.index = i; - if (syscall(SYS_ioctl, fd, VIDIOC_ENUM_FMT, &fmt)) + if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, &fmt)) break; for (j = 0; j < ARRAY_SIZE(supported_src_pixfmts); j++) if (fmt.pixelformat == supported_src_pixfmts[j].fmt) { data->supported_src_formats |= 1 << j; v4lconvert_get_framesizes(data, fmt.pixelformat, j); + if (!supported_src_pixfmts[j].flags) + always_needs_conversion = 0; break; } + + if (j == ARRAY_SIZE(supported_src_pixfmts)) + always_needs_conversion = 0; } 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) { + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP, &cap) == 0) { if (!strcmp((char *)cap.driver, "uvcvideo")) data->flags |= V4LCONVERT_IS_UVC; + else if (!strcmp((char *)cap.driver, "sn9c20x")) + data->flags |= V4LCONVERT_IS_SN9C20X; + + if ((cap.capabilities & 0xff) & ~V4L2_CAP_VIDEO_CAPTURE) + always_needs_conversion = 0; + } + + data->control = v4lcontrol_create(fd, always_needs_conversion); + if (!data->control) { + free(data); + return NULL; + } + data->control_flags = v4lcontrol_get_flags(data->control); + + data->processing = v4lprocessing_create(fd, data->control); + if (!data->processing) { + v4lcontrol_destroy(data->control); + free(data); + return NULL; } return data; @@ -173,18 +155,23 @@ void v4lconvert_destroy(struct v4lconvert_data *data) { + v4lprocessing_destroy(data->processing); + v4lcontrol_destroy(data->control); if (data->jdec) { unsigned char *comps[3] = { NULL, NULL, NULL }; tinyjpeg_set_components(data->jdec, comps, 3); tinyjpeg_free(data->jdec); } - free(data->convert_buf); - free(data->rotate_buf); + v4lconvert_helper_cleanup(data); + free(data->convert1_buf); + free(data->convert2_buf); + free(data->rotate90_buf); + free(data->flip_buf); free(data->convert_pixfmt_buf); free(data); } -static int v4lconvert_supported_dst_format(unsigned int pixelformat) +int v4lconvert_supported_dst_format(unsigned int pixelformat) { int i; @@ -195,6 +182,12 @@ return i != ARRAY_SIZE(supported_dst_pixfmts); } +int v4lconvert_supported_dst_fmt_only(struct v4lconvert_data *data) +{ + return v4lcontrol_needs_conversion(data->control) && + data->supported_src_formats; +} + /* See libv4lconvert.h for description of in / out parameters */ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt) { @@ -202,17 +195,22 @@ unsigned int faked_fmts[ARRAY_SIZE(supported_dst_pixfmts)]; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fmt->index < data->no_formats || - !data->supported_src_formats) - return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FMT, fmt); + (!v4lconvert_supported_dst_fmt_only(data) && + fmt->index < data->no_formats)) + return SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, fmt); for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++) - if (!(data->supported_src_formats & (1 << i))) { + if (v4lconvert_supported_dst_fmt_only(data) || + !(data->supported_src_formats & (1 << i))) { faked_fmts[no_faked_fmts] = supported_dst_pixfmts[i].fmt; no_faked_fmts++; } - i = fmt->index - data->no_formats; + if (!v4lconvert_supported_dst_fmt_only(data)) + i = fmt->index - data->no_formats; + else + i = fmt->index; + if (i >= no_faked_fmts) { errno = EINVAL; return -1; @@ -307,7 +305,7 @@ try_fmt = *dest_fmt; try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt; - if (!syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, &try_fmt)) + if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, &try_fmt)) { if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) { int size_x_diff = abs((int)try_fmt.fmt.pix.width - @@ -339,7 +337,7 @@ return 0; } -static void v4lconvert_fixup_fmt(struct v4l2_format *fmt) +void v4lconvert_fixup_fmt(struct v4l2_format *fmt) { switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: @@ -362,27 +360,58 @@ 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; + struct v4l2_format try_src, try_dest, try2_src, try2_dest; + + if (dest_fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + v4lconvert_supported_dst_fmt_only(data) && + !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) + dest_fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + + 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); + result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt); if (src_fmt) *src_fmt = *dest_fmt; return result; } + /* In case of a non exact resolution match, try again with a slightly larger + resolution as some weird devices are not able to crop of the number of + extra (border) pixels most sensors have compared to standard resolutions, + which we will then just crop of in software */ + if (try_dest.fmt.pix.width != desired_width || + try_dest.fmt.pix.height != desired_height) { + try2_dest = *dest_fmt; + try2_dest.fmt.pix.width = desired_width + 7; + try2_dest.fmt.pix.height = desired_height + 1; + 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 + 7 && + try2_dest.fmt.pix.height >= desired_height && + try2_dest.fmt.pix.height <= desired_height + 1) { + /* Success! */ + try2_dest.fmt.pix.width = desired_width; + try2_dest.fmt.pix.height = desired_height; + try_dest = try2_dest; + try_src = try2_src; + } + } + /* 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 */ + asked for by cropping a slightly larger resolution or adding a small + black border to a slightly smaller 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; + 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 */ @@ -390,13 +419,20 @@ 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 && + (/* Add a small black border of max 16 pixels */ + (try2_dest.fmt.pix.width >= desired_width - 16 && + try2_dest.fmt.pix.width <= desired_width && + try2_dest.fmt.pix.height >= desired_height - 16 && + try2_dest.fmt.pix.height <= desired_height) || + /* Standard cropping to max 80% of actual width / height */ + (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) || + /* Downscale 2x + cropping to max 80% of actual width / height */ (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 * 2 && try2_dest.fmt.pix.height <= desired_height * 5 / 2))) { /* Success! */ try2_dest.fmt.pix.width = desired_width; @@ -409,7 +445,15 @@ } } - /* Are we converting? */ + /* Some applications / libs (*cough* gstreamer *cough*) will not work + correctly with planar YUV formats when the width is not a multiple of 8 + or the height is not a multiple of 2. With RGB formats these apps require + the width to be a multiple of 4. We apply the same rounding to all + formats to not end up with 2 close but different resolutions. */ + try_dest.fmt.pix.width &= ~7; + try_dest.fmt.pix.height &= ~1; + + /* Are we converting / cropping ? */ 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) @@ -427,19 +471,40 @@ const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt) /* in */ { - 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_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? */ - if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) - return 0; /* Needs flip but we cannot do it :( */ - else - return 1; /* Needs flip and thus conversion */ + 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 || + (v4lcontrol_needs_conversion(data->control) && + v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat))) + return 1; + + return 0; +} + +static int v4lconvert_processing_needs_double_conversion( + unsigned int src_pix_fmt, unsigned int dest_pix_fmt) +{ + switch (src_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + 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: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + return 0; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return 0; + } + + return 1; } static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data, @@ -460,15 +525,15 @@ } 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 char *src, int src_size, unsigned char *dest, int dest_size, + struct v4l2_format *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; + unsigned int src_pix_fmt = fmt->fmt.pix.pixelformat; + unsigned int width = fmt->fmt.pix.width; + unsigned int height = fmt->fmt.pix.height; switch (src_pix_fmt) { case V4L2_PIX_FMT_PJPG: @@ -496,12 +561,14 @@ if (header_width != width || header_height != height) { /* Check for (pixart) rotated JPEG */ 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; + if (!(data->control_flags & V4LCONTROL_ROTATED_90_JPEG)) { + V4LCONVERT_ERR("JPEG needs 90° rotation, please report " + "this to <hdegoede@redhat.com>\n"); + errno = EIO; return -1; } + fmt->fmt.pix.width = header_width; + fmt->fmt.pix.height = header_height; } else { V4LCONVERT_ERR("unexpected width / height in JPEG header" "expected: %ux%u, header: %ux%u\n", width, height, @@ -509,6 +576,13 @@ errno = EIO; return -1; } + } else if ((data->control_flags & V4LCONTROL_ROTATED_90_JPEG)) { + fprintf(stderr, "libv4lconvert: expected 90° rotated JPEG, but got " + "normal JPEG, please report this to <hdegoede@redhat.com>\n"); + V4LCONVERT_ERR("expected 90° rotated JPEG, but got normal JPEG\n"); + errno = EAGAIN; + data->control_flags &= ~V4LCONTROL_ROTATED_90_JPEG; + return -1; } components[0] = dest; @@ -541,7 +615,8 @@ 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_pix_fmt == V4L2_PIX_FMT_PJPG) { + if (src_pix_fmt == V4L2_PIX_FMT_PJPG || + data->flags & V4LCONVERT_IS_SN9C20X) { V4LCONVERT_ERR("decompressing JPEG: %s", tinyjpeg_get_errorstring(data->jdec)); errno = EAGAIN; @@ -561,8 +636,11 @@ case V4L2_PIX_FMT_SPCA505: case V4L2_PIX_FMT_SPCA508: case V4L2_PIX_FMT_SN9C20X_I420: + case V4L2_PIX_FMT_OV511: + case V4L2_PIX_FMT_OV518: { unsigned char *d; + int d_size; int yvu = 0; if (dest_pix_fmt != V4L2_PIX_FMT_YUV420 && @@ -571,8 +649,11 @@ &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); if (!d) return -1; - } else + d_size = width * height * 3 / 2; + } else { d = dest; + d_size = dest_size; + } if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) yvu = 1; @@ -590,6 +671,22 @@ case V4L2_PIX_FMT_SN9C20X_I420: v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu); break; + case V4L2_PIX_FMT_OV511: + if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov511-decomp", + src, src_size, d, d_size, width, height, yvu)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + break; + case V4L2_PIX_FMT_OV518: + if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov518-decomp", + src, src_size, d, d_size, width, height, yvu)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + break; } switch (dest_pix_fmt) { @@ -631,6 +728,7 @@ case V4L2_PIX_FMT_SQ905C: { unsigned char *tmpbuf; + struct v4l2_format tmpfmt = *fmt; tmpbuf = v4lconvert_alloc_buffer(data, width * height, &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); @@ -640,27 +738,38 @@ switch (src_pix_fmt) { case V4L2_PIX_FMT_SPCA561: v4lconvert_decode_spca561(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SGBRG8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG8; break; case V4L2_PIX_FMT_SN9C10X: v4lconvert_decode_sn9c10x(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SBGGR8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_PAC207: - v4lconvert_decode_pac207(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SBGGR8; + if (v4lconvert_decode_pac207(data, src, src_size, tmpbuf, + width, height)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_MR97310A: v4lconvert_decode_mr97310a(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SBGGR8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_SQ905C: v4lconvert_decode_sq905c(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SRGGB8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; break; } - src = tmpbuf; + /* Do processing on the tmp buffer, because doing it on bayer data is + cheaper, and bayer == rgb and our dest_fmt may be yuv */ + tmpfmt.fmt.pix.bytesperline = width; + tmpfmt.fmt.pix.sizeimage = width * height; + v4lprocessing_processing(data->processing, tmpbuf, &tmpfmt); /* Deliberate fall through to raw bayer fmt code! */ + src_pix_fmt = tmpfmt.fmt.pix.pixelformat; + src = tmpbuf; } /* Raw bayer formats */ @@ -684,16 +793,33 @@ } break; + case V4L2_PIX_FMT_RGB565: + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_rgb565_to_rgb24(src, dest, width, height); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_rgb565_to_bgr24(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb565_to_yuv420(src, dest, fmt, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb565_to_yuv420(src, dest, fmt, 1); + break; + } + break; + case V4L2_PIX_FMT_RGB24: switch (dest_pix_fmt) { case V4L2_PIX_FMT_BGR24: v4lconvert_swap_rgb(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 0); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0); break; case V4L2_PIX_FMT_YVU420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 1); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1); break; } break; @@ -704,10 +830,10 @@ v4lconvert_swap_rgb(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 0); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0); break; case V4L2_PIX_FMT_YVU420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 1); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1); break; } break; @@ -723,7 +849,7 @@ height, 0); break; case V4L2_PIX_FMT_YVU420: - v4lconvert_swap_uv(src, dest, src_fmt); + v4lconvert_swap_uv(src, dest, fmt); break; } break; @@ -739,7 +865,7 @@ height, 1); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_swap_uv(src, dest, src_fmt); + v4lconvert_swap_uv(src, dest, fmt); break; } break; @@ -802,6 +928,10 @@ errno = EINVAL; return -1; } + + fmt->fmt.pix.pixelformat = dest_pix_fmt; + v4lconvert_fixup_fmt(fmt); + return 0; } @@ -810,14 +940,31 @@ 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; + int res, dest_needed, temp_needed, processing, convert = 0; + int rotate90, vflip, hflip, crop; + unsigned char *convert1_dest = dest; + int convert1_dest_size = dest_size; + unsigned char *convert2_src = src, *convert2_dest = dest; + int convert2_dest_size = dest_size; + unsigned char *rotate90_src = src, *rotate90_dest = dest; + unsigned char *flip_src = src, *flip_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)) { + processing = v4lprocessing_pre_processing(data->processing); + rotate90 = data->control_flags & V4LCONTROL_ROTATED_90_JPEG; + hflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_HFLIP); + vflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_VFLIP); + crop = my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width || + my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height; + + if (/* If no conversion/processing is needed */ + (src_fmt->fmt.pix.pixelformat == dest_fmt->fmt.pix.pixelformat && + !processing && !rotate90 && !hflip && !vflip && !crop) || + /* or if we should do processing/rotating/flipping but the app tries to + use the native cam format, we just return an unprocessed frame copy */ + !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) { int to_copy = MIN(dest_size, src_size); memcpy(dest, src, to_copy); return to_copy; @@ -856,52 +1003,96 @@ return -1; } - if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat) + + /* Sometimes we need foo -> rgb -> bar as video processing (whitebalance, + etc.) can only be done on rgb data */ + if (processing && v4lconvert_processing_needs_double_conversion( + my_src_fmt.fmt.pix.pixelformat, + my_dest_fmt.fmt.pix.pixelformat)) + convert = 2; + else 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) + /* convert_pixfmt (only if convert == 2) -> processing -> convert_pixfmt -> + rotate -> flip -> crop, all steps are optional */ + if (convert == 2) { + convert1_dest = v4lconvert_alloc_buffer(data, + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3, + &data->convert1_buf, &data->convert1_buf_size); + if (!convert1_dest) + return -1; + + convert1_dest_size = + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; + convert2_src = convert1_dest; + } + + if (convert && (rotate90 || hflip || vflip || crop)) { + convert2_dest = v4lconvert_alloc_buffer(data, temp_needed, + &data->convert2_buf, &data->convert2_buf_size); + if (!convert2_dest) + return -1; + + convert2_dest_size = temp_needed; + rotate90_src = flip_src = crop_src = convert2_dest; + } + + if (rotate90 && (hflip || vflip || crop)) { + rotate90_dest = v4lconvert_alloc_buffer(data, temp_needed, + &data->rotate90_buf, &data->rotate90_buf_size); + if (!rotate90_dest) return -1; - rotate_src = crop_src = convert_dest; + flip_src = crop_src = rotate90_dest; } - if (rotate && crop) { - rotate_dest = v4lconvert_alloc_buffer(data, temp_needed, - &data->rotate_buf, &data->rotate_buf_size); - if (!rotate_dest) + if ((vflip || hflip) && crop) { + flip_dest = v4lconvert_alloc_buffer(data, temp_needed, &data->flip_buf, + &data->flip_buf_size); + if (!flip_dest) return -1; - crop_src = rotate_dest; + crop_src = flip_dest; } + /* Done setting sources / dest and allocating intermediate buffers, + real conversion / processing / ... starts here. */ + if (convert == 2) { + res = v4lconvert_convert_pixfmt(data, src, src_size, + convert1_dest, convert1_dest_size, + &my_src_fmt, + V4L2_PIX_FMT_RGB24); + if (res) + return res; + + src_size = my_src_fmt.fmt.pix.sizeimage; + } + + if (processing) + v4lprocessing_processing(data->processing, convert2_src, &my_src_fmt); + if (convert) { - res = v4lconvert_convert_pixfmt(data, src, src_size, convert_dest, + res = v4lconvert_convert_pixfmt(data, convert2_src, src_size, + convert2_dest, convert2_dest_size, &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); + src_size = my_src_fmt.fmt.pix.sizeimage; + + /* We call processing here again in case the source format was not + rgb, but the dest is. v4lprocessing checks it self it only actually + does the processing once per frame. */ + if (processing) + v4lprocessing_processing(data->processing, convert2_dest, &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 (rotate90) + v4lconvert_rotate90(rotate90_src, rotate90_dest, &my_src_fmt); + + if (hflip || vflip) + v4lconvert_flip(flip_src, flip_dest, &my_src_fmt, hflip, vflip); if (crop) v4lconvert_crop(crop_src, dest, &my_src_fmt, &my_dest_fmt); @@ -922,7 +1113,7 @@ for (i = 0; ; i++) { frmsize.index = i; - if (syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) + if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) break; /* We got a framesize, check we don't have the same one already */ @@ -977,8 +1168,13 @@ int v4lconvert_enum_framesizes(struct v4lconvert_data *data, struct v4l2_frmsizeenum *frmsize) { - if (!v4lconvert_supported_dst_format(frmsize->pixel_format)) - return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize); + if (!v4lconvert_supported_dst_format(frmsize->pixel_format)) { + if (v4lconvert_supported_dst_fmt_only(data)) { + errno = EINVAL; + return -1; + } + return SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize); + } if (frmsize->index >= data->no_framesizes) { errno = EINVAL; @@ -989,6 +1185,9 @@ switch(frmsize->type) { case V4L2_FRMSIZE_TYPE_DISCRETE: frmsize->discrete = data->framesizes[frmsize->index].discrete; + /* Apply the same rounding algorithm as v4lconvert_try_format */ + frmsize->discrete.width &= ~7; + frmsize->discrete.height &= ~1; break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: case V4L2_FRMSIZE_TYPE_STEPWISE: @@ -1006,7 +1205,11 @@ struct v4l2_format src_fmt, dest_fmt; if (!v4lconvert_supported_dst_format(frmival->pixel_format)) { - res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); + if (v4lconvert_supported_dst_fmt_only(data)) { + errno = EINVAL; + return -1; + } + res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); if (res) V4LCONVERT_ERR("%s\n", strerror(errno)); return res; @@ -1050,7 +1253,7 @@ frmival->pixel_format = src_fmt.fmt.pix.pixelformat; frmival->width = src_fmt.fmt.pix.width; frmival->height = src_fmt.fmt.pix.height; - res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); + res = 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; @@ -1076,3 +1279,15 @@ return res; } + +int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data, void *arg) { + return v4lcontrol_vidioc_queryctrl(data->control, arg); +} + +int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data, void *arg) { + return v4lcontrol_vidioc_g_ctrl(data->control, arg); +} + +int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, void *arg){ + return v4lcontrol_vidioc_s_ctrl(data->control, arg); +} | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/libv4lsyscall-priv.h ^ |
@@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2009 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The following file allows for having the complete V4L stack and + * hardware drivers in userland. + */ + +#ifndef _LIBV4LSYSCALL_PRIV_H_ +#define _LIBV4LSYSCALL_PRIV_H_ + +/* Some of these headers are not needed by us, but by linux/videodev2.h, + which is broken on some systems and doesn't include them itself :( */ + +#ifdef linux +#include <sys/time.h> +#include <syscall.h> +#include <linux/types.h> +#include <linux/ioctl.h> +/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ +#ifdef __NR_mmap2 +#define SYS_mmap2 __NR_mmap2 +#define MMAP2_PAGE_SHIFT 12 +#else +#define SYS_mmap2 SYS_mmap +#define MMAP2_PAGE_SHIFT 0 +#endif +#endif + +#ifdef __FreeBSD__ +#include <sys/time.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#define _IOC_NR(cmd) ((cmd) & 0xFF) +#define _IOC_TYPE(cmd) IOCGROUP(cmd) +#define _IOC_SIZE(cmd) IOCPARM_LEN(cmd) +#define MAP_ANONYMOUS MAP_ANON +#define SYS_mmap2 SYS_mmap +#define MMAP2_PAGE_SHIFT 0 +typedef off_t __off_t; +#endif + +#undef SYS_OPEN +#undef SYS_CLOSE +#undef SYS_IOCTL +#undef SYS_READ +#undef SYS_WRITE +#undef SYS_MMAP +#undef SYS_MUNMAP + +#ifndef CONFIG_SYS_WRAPPER + +#define SYS_OPEN(file, oflag, mode) \ + syscall(SYS_open, (const char *)(file), (int)(oflag), (mode_t)(mode)) +#define SYS_CLOSE(fd) \ + syscall(SYS_close, (int)(fd)) +#define SYS_IOCTL(fd, cmd, arg) \ + syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg)) +#define SYS_READ(fd, buf, len) \ + syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len)); +#define SYS_WRITE(fd, buf, len) \ + syscall(SYS_write, (int)(fd), (void *)(buf), (size_t)(len)); +#define SYS_MMAP(addr, len, prot, flags, fd, off) \ + syscall(SYS_mmap2, (void *)(addr), (size_t)(len), \ + (int)(prot), (int)(flags), (int)(fd), (__off_t)((off) >> MMAP2_PAGE_SHIFT)) +#define SYS_MUNMAP(addr, len) \ + syscall(SYS_munmap, (void *)(addr), (size_t)(len)) + +#else + +int v4lx_open_wrapper(const char *, int, int); +int v4lx_close_wrapper(int); +int v4lx_ioctl_wrapper(int, unsigned long, void *); +int v4lx_read_wrapper(int, void *, size_t); +int v4lx_write_wrapper(int, void *, size_t); +void *v4lx_mmap_wrapper(void *, size_t, int, int, int, off_t); +int v4lx_munmap_wrapper(void *, size_t); + +#define SYS_OPEN(...) v4lx_open_wrapper(__VA_ARGS__) +#define SYS_CLOSE(...) v4lx_close_wrapper(__VA_ARGS__) +#define SYS_IOCTL(...) v4lx_ioctl_wrapper(__VA_ARGS__) +#define SYS_READ(...) v4lx_read_wrapper(__VA_ARGS__) +#define SYS_WRITE(...) v4lx_write_wrapper(__VA_ARGS__) +#define SYS_MMAP(...) v4lx_mmap_wrapper(__VA_ARGS__) +#define SYS_MUNMAP(...) v4lx_munmap_wrapper(__VA_ARGS__) + +#endif + +#endif /* _LIBV4LSYSCALL_PRIV_H_ */ | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/ov511-decomp.c ^ |
@@ -0,0 +1,666 @@ +/* We would like to embed this inside libv4l, but we cannot as I've failed + to contact Mark W. McClelland to get permission to relicense this, + so this lives in an external (GPL licensed) helper */ + +/* OV511 Decompression Support Module + * + * Copyright (c) 1999-2003 Mark W. McClelland. All rights reserved. + * http://alpha.dyndns.org/ov511/ + * + * Original decompression code Copyright 1998-2000 OmniVision Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + */ + +#include <string.h> +#include <unistd.h> +#include "helper-funcs.h" + +/****************************************************************************** + * Decompression Functions + ******************************************************************************/ + +static void +DecompressYHI(unsigned char *pIn, + unsigned char *pOut, + int *iIn, /* in/out */ + int *iOut, /* in/out */ + const int w, + const int YUVFlag) +{ + short ZigZag[64]; + int temp[64]; + int Zcnt_Flag = 0; + int Num8_Flag = 0; + int in_pos = *iIn; + int out_pos = *iOut; + int tmp, tmp1, tmp2, tmp3; + unsigned char header, ZTable[64]; + short tmpl, tmph, half_byte, idx, count; + unsigned long ZigZag_length = 0, ZT_length, i, j; + short DeZigZag[64]; + + const short a = 11584; + const short b = 16068; + const short c = 15136; + const short d = 13624; + const short e = 9104; + const short f = 6270; + const short g = 3196; + + int out_idx; + + /* Take off every 'Zig' */ + for (i = 0; i < 64; i++) { + ZigZag[i] = 0; + } + + /***************************** + * Read in the Y header byte * + *****************************/ + + header = pIn[in_pos]; + in_pos++; + + ZigZag_length = header & 0x3f; + ZigZag_length = ZigZag_length + 1; + + Num8_Flag = header & 0x40; + Zcnt_Flag = header & 0x80; + + /************************* + * Read in the Y content * + *************************/ + + if (Zcnt_Flag == 0) { /* Without Zero Table read contents directly */ + /* Read in ZigZag[0] */ + ZigZag[0] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl<<8; + ZigZag[0] = ZigZag[0] | tmph; + ZigZag[0] = ZigZag[0]<<4; + ZigZag[0] = ZigZag[0]>>4; + + if (Num8_Flag) { /* 8 Bits */ + for (i = 1; i < ZigZag_length; i++) { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + ZigZag[i] = ZigZag[i]>>8; + } + } else { /* 12 bits and has no Zero Table */ + idx = 1; + half_byte = 0; + for (i = 1; i < ZigZag_length; i++) { + if (half_byte == 0) { + ZigZag[i] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl<<8; + tmph = tmph&0x0f00; + ZigZag[i] = ZigZag[i] | tmph; + ZigZag[i] = ZigZag[i]<<4; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 1; + } else { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 0; + } + } + } + } else { /* Has Zero Table */ + /* Calculate Z-Table length */ + ZT_length = ZigZag_length/8; + tmp = ZigZag_length%8; + + if (tmp > 0) { + ZT_length = ZT_length + 1; + } + + /* Read in Zero Table */ + for (j = 0; j < ZT_length; j++) { + ZTable[j] = pIn[in_pos++]; + } + + /* Read in ZigZag[0] */ + ZigZag[0] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl<<8; + ZigZag[0] = ZigZag[0] | tmph; + ZigZag[0] = ZigZag[0]<<4; + ZigZag[0] = ZigZag[0]>>4; + + /* Decode ZigZag */ + idx = 0; + ZTable[idx] = ZTable[idx]<<1; + count = 7; + + if (Num8_Flag) { /* 8 Bits and has zero table */ + for (i = 1; i < ZigZag_length; i++) { + if ((ZTable[idx]&0x80)) { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + ZigZag[i] = ZigZag[i]>>8; + } + + ZTable[idx]=ZTable[idx]<<1; + count--; + if (count == 0) { + count = 8; + idx++; + } + } + } else { /* 12 bits and has Zero Table */ + half_byte = 0; + for (i = 1; i < ZigZag_length; i++) { + if (ZTable[idx]&0x80) { + if (half_byte == 0) { + ZigZag[i] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl <<8; + tmph = tmph & 0x0f00; + ZigZag[i] = ZigZag[i] | tmph; + ZigZag[i] = ZigZag[i]<<4; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 1; + } else { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 0; + } + } + + ZTable[idx] = ZTable[idx]<<1; + count--; + if (count == 0) { + count = 8; + idx++; + } + } + } + } + + /************* + * De-ZigZag * + *************/ + + for (j = 0; j < 64; j++) { + DeZigZag[j] = 0; + } + + if (YUVFlag == 1) { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1]<<1; + DeZigZag[2] = ZigZag[5]<<1; + DeZigZag[3] = ZigZag[6]<<2; + + DeZigZag[8] = ZigZag[2]<<1; + DeZigZag[9] = ZigZag[4]<<1; + DeZigZag[10] = ZigZag[7]<<1; + DeZigZag[11] = ZigZag[13]<<2; + + DeZigZag[16] = ZigZag[3]<<1; + DeZigZag[17] = ZigZag[8]<<1; + DeZigZag[18] = ZigZag[12]<<2; + DeZigZag[19] = ZigZag[17]<<2; + + DeZigZag[24] = ZigZag[9]<<2; + DeZigZag[25] = ZigZag[11]<<2; + DeZigZag[26] = ZigZag[18]<<2; + DeZigZag[27] = ZigZag[24]<<3; + } else { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1]<<2; + DeZigZag[2] = ZigZag[5]<<2; + DeZigZag[3] = ZigZag[6]<<3; + + DeZigZag[8] = ZigZag[2]<<2; + DeZigZag[9] = ZigZag[4]<<2; + DeZigZag[10] = ZigZag[7]<<2; + DeZigZag[11] = ZigZag[13]<<4; + + DeZigZag[16] = ZigZag[3]<<2; + DeZigZag[17] = ZigZag[8]<<2; + DeZigZag[18] = ZigZag[12]<<3; + DeZigZag[19] = ZigZag[17]<<4; + + DeZigZag[24] = ZigZag[9]<<3; + DeZigZag[25] = ZigZag[11]<<4; + DeZigZag[26] = ZigZag[18]<<4; + DeZigZag[27] = ZigZag[24]<<4; + } + + /***************** + **** IDCT 1D **** + *****************/ + +#define IDCT_1D(c0, c1, c2, c3, in) \ + do { \ + tmp1=((c0)*DeZigZag[in])+((c2)*DeZigZag[(in)+2]); \ + tmp2=(c1)*DeZigZag[(in)+1]; \ + tmp3=(c3)*DeZigZag[(in)+3]; \ + } while (0) + +#define COMPOSE_1(out1, out2) \ + do { \ + tmp=tmp1+tmp2+tmp3; \ + temp[out1] = tmp>>15; \ + tmp=tmp1-tmp2-tmp3; \ + temp[out2] = tmp>>15; \ + } while (0) + +#define COMPOSE_2(out1, out2) \ + do { \ + tmp=tmp1+tmp2-tmp3; \ + temp[out1] = tmp>>15; \ + tmp=tmp1-tmp2+tmp3; \ + temp[out2] = tmp>>15; \ + } while (0) + + /* j = 0 */ + IDCT_1D(a, b, c, d, 0); COMPOSE_1( 0, 56); + IDCT_1D(a, b, c, d, 8); COMPOSE_1( 1, 57); + IDCT_1D(a, b, c, d, 16); COMPOSE_1( 2, 58); + IDCT_1D(a, b, c, d, 24); COMPOSE_1( 3, 59); + + /* j = 1 */ + IDCT_1D(a, d, f, g, 0); COMPOSE_2( 8, 48); + IDCT_1D(a, d, f, g, 8); COMPOSE_2( 9, 49); + IDCT_1D(a, d, f, g, 16); COMPOSE_2(10, 50); + IDCT_1D(a, d, f, g, 24); COMPOSE_2(11, 51); + + /* j = 2 */ + IDCT_1D(a, e, -f, b, 0); COMPOSE_2(16, 40); + IDCT_1D(a, e, -f, b, 8); COMPOSE_2(17, 41); + IDCT_1D(a, e, -f, b, 16); COMPOSE_2(18, 42); + IDCT_1D(a, e, -f, b, 24); COMPOSE_2(19, 43); + + /* j = 3 */ + IDCT_1D(a, g, -c, e, 0); COMPOSE_2(24, 32); + IDCT_1D(a, g, -c, e, 8); COMPOSE_2(25, 33); + IDCT_1D(a, g, -c, e, 16); COMPOSE_2(26, 34); + IDCT_1D(a, g, -c, e, 24); COMPOSE_2(27, 35); + +#undef IDCT_1D +#undef COMPOSE_1 +#undef COMPOSE_2 + + /***************** + **** IDCT 2D **** + *****************/ + +#define IDCT_2D(c0, c1, c2, c3, in) \ + do { \ + tmp = temp[in]*(c0) + temp[(in)+1]*(c1) \ + + temp[(in)+2]*(c2) + temp[(in)+3]*(c3); \ + } while (0) + +#define STORE(i) \ + do { \ + tmp = tmp >> 15; \ + tmp = tmp + 128; \ + if (tmp > 255) tmp = 255; \ + if (tmp < 0) tmp = 0; \ + pOut[i] = (unsigned char) tmp; \ + } while (0) + +#define IDCT_2D_ROW(in) \ + do { \ + IDCT_2D(a, b, c, d, in); STORE(0+out_idx); \ + IDCT_2D(a, d, f, -g, in); STORE(1+out_idx); \ + IDCT_2D(a, e, -f, -b, in); STORE(2+out_idx); \ + IDCT_2D(a, g, -c, -e, in); STORE(3+out_idx); \ + IDCT_2D(a, -g, -c, e, in); STORE(4+out_idx); \ + IDCT_2D(a, -e, -f, b, in); STORE(5+out_idx); \ + IDCT_2D(a, -d, f, g, in); STORE(6+out_idx); \ + IDCT_2D(a, -b, c, -d, in); STORE(7+out_idx); \ + } while (0) + + +#define IDCT_2D_FAST(c0, c1, c2, c3, in) \ + do { \ + tmp1=((c0)*temp[in])+((c2)*temp[(in)+2]); \ + tmp2=(c1)*temp[(in)+1]; \ + tmp3=(c3)*temp[(in)+3]; \ + } while (0) + +#define STORE_FAST_1(out1, out2) \ + do { \ + tmp=tmp1+tmp2+tmp3; \ + STORE((out1)+out_idx); \ + tmp=tmp1-tmp2-tmp3; \ + STORE((out2)+out_idx); \ + } while (0) + +#define STORE_FAST_2(out1, out2) \ + do { \ + tmp=tmp1+tmp2-tmp3; \ + STORE((out1)+out_idx); \ + tmp=tmp1-tmp2+tmp3; \ + STORE((out2)+out_idx); \ + } while (0) + +#define IDCT_2D_FAST_ROW(in) \ + do { \ + IDCT_2D_FAST(a, b, c, d, in); STORE_FAST_1(0, 7); \ + IDCT_2D_FAST(a, d, f, g, in); STORE_FAST_2(1, 6); \ + IDCT_2D_FAST(a, e, -f, b, in); STORE_FAST_2(2, 5); \ + IDCT_2D_FAST(a, g, -c, e, in); STORE_FAST_2(3, 4); \ + } while (0) + + out_idx = out_pos; + + IDCT_2D_ROW(0); out_idx += w; + IDCT_2D_ROW(8); out_idx += w; + IDCT_2D_ROW(16); out_idx += w; + IDCT_2D_ROW(24); out_idx += w; + IDCT_2D_ROW(32); out_idx += w; + IDCT_2D_ROW(40); out_idx += w; + IDCT_2D_FAST_ROW(48); out_idx += w; + IDCT_2D_FAST_ROW(56); + + *iIn = in_pos; + *iOut = out_pos + 8; +} + +#define DECOMP_Y() DecompressYHI(pIn, pY, &iIn, &iY, w, 1) +#define DECOMP_U() DecompressYHI(pIn, pU, &iIn, &iU, w/2, 2) +#define DECOMP_V() DecompressYHI(pIn, pV, &iIn, &iV, w/2, 2) + +#if 0 +inline static int +Decompress400HiNoMMX(unsigned char *pIn, + unsigned char *pOut, + const int w, + const int h, + const int inSize) +{ + unsigned char *pY = pOut; + int x, y, iIn, iY; + + iIn = 0; + for (y = 0; y < h; y += 8) { + iY = w*y; + + for (x = 0; x < w; x += 8) + DECOMP_Y(); + } + + return 0; +} +#endif + +inline static int +Decompress420HiNoMMX(unsigned char *pIn, + unsigned char *pOut, + const int w, + const int h, + const int inSize) +{ + unsigned char *pY = pOut; + unsigned char *pU = pY + w*h; + unsigned char *pV = pU + w*h/4; + int xY, xUV, iY, iU, iV, iIn, count; + const int nBlocks = (w*h) / (32*8); + + iIn = 0; + iY = iU = iV = 0; + xY = xUV = 0; + + for (count = 0; count < nBlocks; count++) { + DECOMP_U(); + DECOMP_V(); xUV += 16; + if (xUV >= w) { + iU += (w*7)/2; + iV += (w*7)/2; + xUV = 0; + } + + DECOMP_Y(); xY += 8; + DECOMP_Y(); xY += 8; + if (xY >= w) { + iY += w*7; + xY = 0; + } + DECOMP_Y(); xY += 8; + DECOMP_Y(); xY += 8; + if (xY >= w) { + iY += w*7; + xY = 0; + } + } + + return 0; +} + +/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the + * image at pOut is specified by w. + */ +static inline void +make_8x8(unsigned char *pIn, unsigned char *pOut, int w) +{ + unsigned char *pOut1 = pOut; + int x, y; + + for (y = 0; y < 8; y++) { + pOut1 = pOut; + for (x = 0; x < 8; x++) { + *pOut1++ = *pIn++; + } + pOut += w; + } +} + +#if 0 +/* + * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments. + * The segments represent 4 squares of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 + * + */ +static void +yuv400raw_to_yuv400p(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + int x, y; + unsigned char *pIn, *pOut, *pOutLine; + + /* Copy Y */ + pIn = pIn0; + pOutLine = pOut0; + for (y = 0; y < frame->rawheight - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 8) { + make_8x8(pIn, pOut, frame->rawwidth); + pIn += 64; + pOut += 8; + } + pOutLine += 8 * frame->rawwidth; + } +} +#endif + +/* + * For YUV 4:2:0 images, the data show up in 384 byte segments. + * The first 64 bytes of each segment are U, the next 64 are V. The U and + * V are arranged as follows: + * + * 0 1 ... 7 + * 8 9 ... 15 + * ... + * 56 57 ... 63 + * + * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + * + * The next 256 bytes are full resolution Y data and represent 4 squares + * of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 + * + * Note that the U and V data in one segment represent a 16 x 16 pixel + * area, but the Y data represent a 32 x 8 pixel area. If the width is not an + * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the + * next horizontal stripe. + * + * If dumppix module param is set, _parse_data just dumps the incoming segments, + * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 + * this puts the data on the standard output and can be analyzed with the + * parseppm.c utility I wrote. That's a much faster way for figuring out how + * these data are scrambled. + */ + +/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0. + * + * FIXME: Currently only handles width and height that are multiples of 16 + */ +static void +yuv420raw_to_yuv420p(unsigned char *pIn0, unsigned char *pOut0, + int width, int height) +{ + int k, x, y; + unsigned char *pIn, *pOut, *pOutLine; + const unsigned int a = width * height; + const unsigned int w = width / 2; + + /* Copy U and V */ + pIn = pIn0; + pOutLine = pOut0 + a; + for (y = 0; y < height - 1; y += 16) { + pOut = pOutLine; + for (x = 0; x < width - 1; x += 16) { + make_8x8(pIn, pOut, w); + make_8x8(pIn + 64, pOut + a/4, w); + pIn += 384; + pOut += 8; + } + pOutLine += 8 * w; + } + + /* Copy Y */ + pIn = pIn0 + 128; + pOutLine = pOut0; + k = 0; + for (y = 0; y < height - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < width - 1; x += 8) { + make_8x8(pIn, pOut, width); + pIn += 64; + pOut += 8; + if ((++k) > 3) { + k = 0; + pIn += 128; + } + } + pOutLine += 8 * width; + } +} + + +/* Remove all 0 blocks from input */ +static void remove0blocks(unsigned char *pIn, int *inSize) +{ + long long *in = (long long *)pIn; + long long *out = (long long *)pIn; + int i, j; + + for (i = 0; i < *inSize; i += 32, in += 4) { + int all_zero = 1; + for (j = 0; j < 4; j++) + if (in[j]) { + all_zero = 0; + break; + } + + /* Skip 32 byte blocks of all 0 */ + if (all_zero) + continue; + + for (j = 0; j < 4; j++) + *out++ = in[j]; + } + + *inSize -= (in - out) * 8; +} + +static int v4lconvert_ov511_to_yuv420(unsigned char *src, unsigned char *dest, + int w, int h, int yvu, int src_size) +{ + int rc = 0; + + src_size -= 11; /* Remove footer */ + + remove0blocks(src, &src_size); + + /* Compressed ? */ + if (src[8] & 0x40) { + rc = Decompress420HiNoMMX(src + 9, dest, w, h, src_size); + } else { + yuv420raw_to_yuv420p(src + 9, dest, w, h); + } + + return rc; +} + +int main(int argc, char *argv[]) +{ + int width, height, yvu, src_size, dest_size; + unsigned char src_buf[500000]; + unsigned char dest_buf[500000]; + + while (1) { + if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (src_size > sizeof(src_buf)) { + fprintf(stderr, "%s: error: src_buf too small, need: %d\n", + argv[0], src_size); + return 2; + } + + if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + + dest_size = width * height * 3 / 2; + if (dest_size > sizeof(dest_buf)) { + fprintf(stderr, "%s: error: dest_buf too small, need: %d\n", + argv[0], dest_size); + dest_size = -1; + } else if (v4lconvert_ov511_to_yuv420(src_buf, dest_buf, width, height, + yvu, src_size)) + dest_size = -1; + + if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int), + argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (dest_size == -1) + continue; + + if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + } +} | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/ov518-decomp.c ^ |
@@ -0,0 +1,1477 @@ +/* We would like to embed this inside libv4l, but we cannot as I've failed + to contact Mark W. McClelland to get permission to relicense this, + so this lives in an external (GPL licensed) helper */ + +/* OV518 Decompression Support Module (No-MMX version) + * + * Copyright (c) 2002-2003 Mark W. McClelland. All rights reserved. + * http://alpha.dyndns.org/ov511/ + * + * Fast integer iDCT by Yuri van Oers <yvanoers AT xs4all.nl> + * Original OV511 decompression code Copyright 1998-2000 OmniVision Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + */ + +#include <string.h> +#include <unistd.h> +#include "helper-funcs.h" + +/****************************************************************************** + * Compile-time Options + ******************************************************************************/ + +/* Defining APPROXIMATE_MUL_BY_SHIFT increases performance by approximation + * the multiplications by shifts. I think there's no change in the + * calculated picture, but I'm not sure, so the choice is still in here. */ +#undef APPROXIMATE_MUL_BY_SHIFT + +/****************************************************************************** + * Local Data Types + ******************************************************************************/ + +/* Make sure this remains naturally aligned and 2^n bytes in size */ +struct tree_node { + short left; /* Pointer to left child node */ + short right; /* Pointer to right child node */ + signed char depth; /* Depth (starting at 1) if leaf, else -1 */ + signed char coeffbits; /* Size of coefficient data, or zero if none */ + signed char skip; /* Number of zero coefficients. Unused w/ DC */ + char padding; /* Pad out to 8 bytes */ +}; + +struct comp_info { + int bytes; /* Number of processed input bytes */ + int bits; /* Number of unprocessed input bits */ + int rawLen; /* Total number of bytes in input buffer */ + unsigned char *qt; /* Current quantization table */ +}; + +/****************************************************************************** + * Constant Data Definitions + ******************************************************************************/ + +/* Zig-Zag Table */ +static const unsigned char ZigZag518[] = { + 0x00, 0x02, 0x03, 0x09, + 0x01, 0x04, 0x08, 0x0a, + 0x05, 0x07, 0x0b, 0x11, + 0x06, 0x0c, 0x10, 0x12, + 0x0d, 0x0f, 0x13, 0x19, + 0x0e, 0x14, 0x18, 0x1a, + 0x15, 0x17, 0x1b, 0x1e, + 0x16, 0x1c, 0x1d, 0x1f +}; + +/* Huffman trees */ + +static const struct tree_node treeYAC[] = { + { 1, 4, -1, 0, -1}, { 2, 3, -1, 0, -1}, + { -1, -1, 2, 1, 0}, { -1, -1, 2, 2, 0}, + { 5, 9, -1, 0, -1}, { 6, 7, -1, 0, -1}, + { -1, -1, 3, 3, 0}, {323, 8, -1, 0, -1}, + { -1, -1, 4, 4, 0}, { 10, 13, -1, 0, -1}, + { 38, 11, -1, 0, -1}, { 12, 39, -1, 0, -1}, + { -1, -1, 5, 5, 0}, { 59, 14, -1, 0, -1}, + { 15, 18, -1, 0, -1}, { 16, 113, -1, 0, -1}, + { 17, 40, -1, 0, -1}, { -1, -1, 7, 6, 0}, + { 19, 22, -1, 0, -1}, { 20, 41, -1, 0, -1}, + { 21, 61, -1, 0, -1}, { -1, -1, 8, 7, 0}, + { 23, 27, -1, 0, -1}, {169, 24, -1, 0, -1}, + {208, 25, -1, 0, -1}, { 26, 62, -1, 0, -1}, + { -1, -1, 10, 8, 0}, { 44, 28, -1, 0, -1}, + { 63, 29, -1, 0, -1}, { 30, 191, -1, 0, -1}, + { 31, 119, -1, 0, -1}, { 32, 82, -1, 0, -1}, + { 33, 55, -1, 0, -1}, { 34, 48, -1, 0, -1}, + {171, 35, -1, 0, -1}, { 36, 37, -1, 0, -1}, + { -1, -1, 16, 9, 0}, { -1, -1, 16, 10, 0}, + { -1, -1, 4, 1, 1}, { -1, -1, 5, 2, 1}, + { -1, -1, 7, 3, 1}, {151, 42, -1, 0, -1}, + { 43, 79, -1, 0, -1}, { -1, -1, 9, 4, 1}, + { 96, 45, -1, 0, -1}, {246, 46, -1, 0, -1}, + { 47, 115, -1, 0, -1}, { -1, -1, 11, 5, 1}, + { 49, 52, -1, 0, -1}, { 50, 51, -1, 0, -1}, + { -1, -1, 16, 6, 1}, { -1, -1, 16, 7, 1}, + { 53, 54, -1, 0, -1}, { -1, -1, 16, 8, 1}, + { -1, -1, 16, 9, 1}, { 56, 71, -1, 0, -1}, + { 57, 68, -1, 0, -1}, { 58, 67, -1, 0, -1}, + { -1, -1, 16, 10, 1}, { 60, 77, -1, 0, -1}, + { -1, -1, 5, 1, 2}, { -1, -1, 8, 2, 2}, + { -1, -1, 10, 3, 2}, {265, 64, -1, 0, -1}, + { 65, 134, -1, 0, -1}, { 66, 80, -1, 0, -1}, + { -1, -1, 12, 4, 2}, { -1, -1, 16, 5, 2}, + { 69, 70, -1, 0, -1}, { -1, -1, 16, 6, 2}, + { -1, -1, 16, 7, 2}, { 72, 75, -1, 0, -1}, + { 73, 74, -1, 0, -1}, { -1, -1, 16, 8, 2}, + { -1, -1, 16, 9, 2}, { 76, 81, -1, 0, -1}, + { -1, -1, 16, 10, 2}, { 78, 95, -1, 0, -1}, + { -1, -1, 6, 1, 3}, { -1, -1, 9, 2, 3}, + { -1, -1, 12, 3, 3}, { -1, -1, 16, 4, 3}, + { 83, 101, -1, 0, -1}, { 84, 91, -1, 0, -1}, + { 85, 88, -1, 0, -1}, { 86, 87, -1, 0, -1}, + { -1, -1, 16, 5, 3}, { -1, -1, 16, 6, 3}, + { 89, 90, -1, 0, -1}, { -1, -1, 16, 7, 3}, + { -1, -1, 16, 8, 3}, { 92, 98, -1, 0, -1}, + { 93, 94, -1, 0, -1}, { -1, -1, 16, 9, 3}, + { -1, -1, 16, 10, 3}, { -1, -1, 6, 1, 4}, + { 97, 225, -1, 0, -1}, { -1, -1, 10, 2, 4}, + { 99, 100, -1, 0, -1}, { -1, -1, 16, 3, 4}, + { -1, -1, 16, 4, 4}, {102, 109, -1, 0, -1}, + {103, 106, -1, 0, -1}, {104, 105, -1, 0, -1}, + { -1, -1, 16, 5, 4}, { -1, -1, 16, 6, 4}, + {107, 108, -1, 0, -1}, { -1, -1, 16, 7, 4}, + { -1, -1, 16, 8, 4}, {110, 116, -1, 0, -1}, + {111, 112, -1, 0, -1}, { -1, -1, 16, 9, 4}, + { -1, -1, 16, 10, 4}, {114, 133, -1, 0, -1}, + { -1, -1, 7, 1, 5}, { -1, -1, 11, 2, 5}, + {117, 118, -1, 0, -1}, { -1, -1, 16, 3, 5}, + { -1, -1, 16, 4, 5}, {120, 156, -1, 0, -1}, + {121, 139, -1, 0, -1}, {122, 129, -1, 0, -1}, + {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1}, + { -1, -1, 16, 5, 5}, { -1, -1, 16, 6, 5}, + {127, 128, -1, 0, -1}, { -1, -1, 16, 7, 5}, + { -1, -1, 16, 8, 5}, {130, 136, -1, 0, -1}, + {131, 132, -1, 0, -1}, { -1, -1, 16, 9, 5}, + { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6}, + {135, 152, -1, 0, -1}, { -1, -1, 12, 2, 6}, + {137, 138, -1, 0, -1}, { -1, -1, 16, 3, 6}, + { -1, -1, 16, 4, 6}, {140, 147, -1, 0, -1}, + {141, 144, -1, 0, -1}, {142, 143, -1, 0, -1}, + { -1, -1, 16, 5, 6}, { -1, -1, 16, 6, 6}, + {145, 146, -1, 0, -1}, { -1, -1, 16, 7, 6}, + { -1, -1, 16, 8, 6}, {148, 153, -1, 0, -1}, + {149, 150, -1, 0, -1}, { -1, -1, 16, 9, 6}, + { -1, -1, 16, 10, 6}, { -1, -1, 8, 1, 7}, + { -1, -1, 12, 2, 7}, {154, 155, -1, 0, -1}, + { -1, -1, 16, 3, 7}, { -1, -1, 16, 4, 7}, + {157, 175, -1, 0, -1}, {158, 165, -1, 0, -1}, + {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1}, + { -1, -1, 16, 5, 7}, { -1, -1, 16, 6, 7}, + {163, 164, -1, 0, -1}, { -1, -1, 16, 7, 7}, + { -1, -1, 16, 8, 7}, {166, 172, -1, 0, -1}, + {167, 168, -1, 0, -1}, { -1, -1, 16, 9, 7}, + { -1, -1, 16, 10, 7}, {170, 187, -1, 0, -1}, + { -1, -1, 9, 1, 8}, { -1, -1, 15, 2, 8}, + {173, 174, -1, 0, -1}, { -1, -1, 16, 3, 8}, + { -1, -1, 16, 4, 8}, {176, 183, -1, 0, -1}, + {177, 180, -1, 0, -1}, {178, 179, -1, 0, -1}, + { -1, -1, 16, 5, 8}, { -1, -1, 16, 6, 8}, + {181, 182, -1, 0, -1}, { -1, -1, 16, 7, 8}, + { -1, -1, 16, 8, 8}, {184, 188, -1, 0, -1}, + {185, 186, -1, 0, -1}, { -1, -1, 16, 9, 8}, + { -1, -1, 16, 10, 8}, { -1, -1, 9, 1, 9}, + {189, 190, -1, 0, -1}, { -1, -1, 16, 2, 9}, + { -1, -1, 16, 3, 9}, {192, 258, -1, 0, -1}, + {193, 226, -1, 0, -1}, {194, 210, -1, 0, -1}, + {195, 202, -1, 0, -1}, {196, 199, -1, 0, -1}, + {197, 198, -1, 0, -1}, { -1, -1, 16, 4, 9}, + { -1, -1, 16, 5, 9}, {200, 201, -1, 0, -1}, + { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9}, + {203, 206, -1, 0, -1}, {204, 205, -1, 0, -1}, + { -1, -1, 16, 8, 9}, { -1, -1, 16, 9, 9}, + {207, 209, -1, 0, -1}, { -1, -1, 16, 10, 9}, + { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10}, + {211, 218, -1, 0, -1}, {212, 215, -1, 0, -1}, + {213, 214, -1, 0, -1}, { -1, -1, 16, 3, 10}, + { -1, -1, 16, 4, 10}, {216, 217, -1, 0, -1}, + { -1, -1, 16, 5, 10}, { -1, -1, 16, 6, 10}, + {219, 222, -1, 0, -1}, {220, 221, -1, 0, -1}, + { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10}, + {223, 224, -1, 0, -1}, { -1, -1, 16, 9, 10}, + { -1, -1, 16, 10, 10}, { -1, -1, 10, 1, 11}, + {227, 242, -1, 0, -1}, {228, 235, -1, 0, -1}, + {229, 232, -1, 0, -1}, {230, 231, -1, 0, -1}, + { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11}, + {233, 234, -1, 0, -1}, { -1, -1, 16, 4, 11}, + { -1, -1, 16, 5, 11}, {236, 239, -1, 0, -1}, + {237, 238, -1, 0, -1}, { -1, -1, 16, 6, 11}, + { -1, -1, 16, 7, 11}, {240, 241, -1, 0, -1}, + { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11}, + {243, 251, -1, 0, -1}, {244, 248, -1, 0, -1}, + {245, 247, -1, 0, -1}, { -1, -1, 16, 10, 11}, + { -1, -1, 10, 1, 12}, { -1, -1, 16, 2, 12}, + {249, 250, -1, 0, -1}, { -1, -1, 16, 3, 12}, + { -1, -1, 16, 4, 12}, {252, 255, -1, 0, -1}, + {253, 254, -1, 0, -1}, { -1, -1, 16, 5, 12}, + { -1, -1, 16, 6, 12}, {256, 257, -1, 0, -1}, + { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12}, + {259, 292, -1, 0, -1}, {260, 277, -1, 0, -1}, + {261, 270, -1, 0, -1}, {262, 267, -1, 0, -1}, + {263, 264, -1, 0, -1}, { -1, -1, 16, 9, 12}, + { -1, -1, 16, 10, 12}, {266, 322, -1, 0, -1}, + { -1, -1, 11, 1, 13}, {268, 269, -1, 0, -1}, + { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13}, + {271, 274, -1, 0, -1}, {272, 273, -1, 0, -1}, + { -1, -1, 16, 4, 13}, { -1, -1, 16, 5, 13}, + {275, 276, -1, 0, -1}, { -1, -1, 16, 6, 13}, + { -1, -1, 16, 7, 13}, {278, 285, -1, 0, -1}, + {279, 282, -1, 0, -1}, {280, 281, -1, 0, -1}, + { -1, -1, 16, 8, 13}, { -1, -1, 16, 9, 13}, + {283, 284, -1, 0, -1}, { -1, -1, 16, 10, 13}, + { -1, -1, 16, 1, 14}, {286, 289, -1, 0, -1}, + {287, 288, -1, 0, -1}, { -1, -1, 16, 2, 14}, + { -1, -1, 16, 3, 14}, {290, 291, -1, 0, -1}, + { -1, -1, 16, 4, 14}, { -1, -1, 16, 5, 14}, + {293, 308, -1, 0, -1}, {294, 301, -1, 0, -1}, + {295, 298, -1, 0, -1}, {296, 297, -1, 0, -1}, + { -1, -1, 16, 6, 14}, { -1, -1, 16, 7, 14}, + {299, 300, -1, 0, -1}, { -1, -1, 16, 8, 14}, + { -1, -1, 16, 9, 14}, {302, 305, -1, 0, -1}, + {303, 304, -1, 0, -1}, { -1, -1, 16, 10, 14}, + { -1, -1, 16, 1, 15}, {306, 307, -1, 0, -1}, + { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15}, + {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1}, + {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15}, + { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1}, + { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15}, + {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1}, + { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15}, + {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15}, + { -1, -1, 11, 0, 16}, { -1, -1, 4, 0, -1}, +}; + +static const struct tree_node treeUVAC[] = { + { 1, 3, -1, 0, -1}, {323, 2, -1, 0, -1}, + { -1, -1, 2, 1, 0}, { 4, 8, -1, 0, -1}, + { 5, 6, -1, 0, -1}, { -1, -1, 3, 2, 0}, + { 7, 37, -1, 0, -1}, { -1, -1, 4, 3, 0}, + { 9, 13, -1, 0, -1}, { 10, 60, -1, 0, -1}, + { 11, 12, -1, 0, -1}, { -1, -1, 5, 4, 0}, + { -1, -1, 5, 5, 0}, { 14, 17, -1, 0, -1}, + { 15, 97, -1, 0, -1}, { 16, 38, -1, 0, -1}, + { -1, -1, 6, 6, 0}, { 18, 21, -1, 0, -1}, + { 19, 39, -1, 0, -1}, { 20, 135, -1, 0, -1}, + { -1, -1, 7, 7, 0}, { 22, 26, -1, 0, -1}, + { 82, 23, -1, 0, -1}, { 24, 99, -1, 0, -1}, + { 25, 42, -1, 0, -1}, { -1, -1, 9, 8, 0}, + { 27, 31, -1, 0, -1}, {211, 28, -1, 0, -1}, + {248, 29, -1, 0, -1}, { 30, 63, -1, 0, -1}, + { -1, -1, 10, 9, 0}, { 43, 32, -1, 0, -1}, + { 33, 48, -1, 0, -1}, {153, 34, -1, 0, -1}, + { 35, 64, -1, 0, -1}, { 36, 47, -1, 0, -1}, + { -1, -1, 12, 10, 0}, { -1, -1, 4, 1, 1}, + { -1, -1, 6, 2, 1}, {152, 40, -1, 0, -1}, + { 41, 62, -1, 0, -1}, { -1, -1, 8, 3, 1}, + { -1, -1, 9, 4, 1}, { 84, 44, -1, 0, -1}, + {322, 45, -1, 0, -1}, { 46, 136, -1, 0, -1}, + { -1, -1, 11, 5, 1}, { -1, -1, 12, 6, 1}, + { 49, 189, -1, 0, -1}, { 50, 119, -1, 0, -1}, + { 51, 76, -1, 0, -1}, { 66, 52, -1, 0, -1}, + { 53, 69, -1, 0, -1}, { 54, 57, -1, 0, -1}, + { 55, 56, -1, 0, -1}, { -1, -1, 16, 7, 1}, + { -1, -1, 16, 8, 1}, { 58, 59, -1, 0, -1}, + { -1, -1, 16, 9, 1}, { -1, -1, 16, 10, 1}, + { 61, 81, -1, 0, -1}, { -1, -1, 5, 1, 2}, + { -1, -1, 8, 2, 2}, { -1, -1, 10, 3, 2}, + { 65, 86, -1, 0, -1}, { -1, -1, 12, 4, 2}, + {286, 67, -1, 0, -1}, { 68, 304, -1, 0, -1}, + { -1, -1, 15, 5, 2}, { 70, 73, -1, 0, -1}, + { 71, 72, -1, 0, -1}, { -1, -1, 16, 6, 2}, + { -1, -1, 16, 7, 2}, { 74, 75, -1, 0, -1}, + { -1, -1, 16, 8, 2}, { -1, -1, 16, 9, 2}, + { 77, 102, -1, 0, -1}, { 78, 91, -1, 0, -1}, + { 79, 88, -1, 0, -1}, { 80, 87, -1, 0, -1}, + { -1, -1, 16, 10, 2}, { -1, -1, 5, 1, 3}, + { 83, 171, -1, 0, -1}, { -1, -1, 8, 2, 3}, + { 85, 117, -1, 0, -1}, { -1, -1, 10, 3, 3}, + { -1, -1, 12, 4, 3}, { -1, -1, 16, 5, 3}, + { 89, 90, -1, 0, -1}, { -1, -1, 16, 6, 3}, + { -1, -1, 16, 7, 3}, { 92, 95, -1, 0, -1}, + { 93, 94, -1, 0, -1}, { -1, -1, 16, 8, 3}, + { -1, -1, 16, 9, 3}, { 96, 101, -1, 0, -1}, + { -1, -1, 16, 10, 3}, { 98, 116, -1, 0, -1}, + { -1, -1, 6, 1, 4}, {100, 188, -1, 0, -1}, + { -1, -1, 9, 2, 4}, { -1, -1, 16, 3, 4}, + {103, 110, -1, 0, -1}, {104, 107, -1, 0, -1}, + {105, 106, -1, 0, -1}, { -1, -1, 16, 4, 4}, + { -1, -1, 16, 5, 4}, {108, 109, -1, 0, -1}, + { -1, -1, 16, 6, 4}, { -1, -1, 16, 7, 4}, + {111, 114, -1, 0, -1}, {112, 113, -1, 0, -1}, + { -1, -1, 16, 8, 4}, { -1, -1, 16, 9, 4}, + {115, 118, -1, 0, -1}, { -1, -1, 16, 10, 4}, + { -1, -1, 6, 1, 5}, { -1, -1, 10, 2, 5}, + { -1, -1, 16, 3, 5}, {120, 156, -1, 0, -1}, + {121, 138, -1, 0, -1}, {122, 129, -1, 0, -1}, + {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1}, + { -1, -1, 16, 4, 5}, { -1, -1, 16, 5, 5}, + {127, 128, -1, 0, -1}, { -1, -1, 16, 6, 5}, + { -1, -1, 16, 7, 5}, {130, 133, -1, 0, -1}, + {131, 132, -1, 0, -1}, { -1, -1, 16, 8, 5}, + { -1, -1, 16, 9, 5}, {134, 137, -1, 0, -1}, + { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6}, + { -1, -1, 11, 2, 6}, { -1, -1, 16, 3, 6}, + {139, 146, -1, 0, -1}, {140, 143, -1, 0, -1}, + {141, 142, -1, 0, -1}, { -1, -1, 16, 4, 6}, + { -1, -1, 16, 5, 6}, {144, 145, -1, 0, -1}, + { -1, -1, 16, 6, 6}, { -1, -1, 16, 7, 6}, + {147, 150, -1, 0, -1}, {148, 149, -1, 0, -1}, + { -1, -1, 16, 8, 6}, { -1, -1, 16, 9, 6}, + {151, 155, -1, 0, -1}, { -1, -1, 16, 10, 6}, + { -1, -1, 7, 1, 7}, {154, 267, -1, 0, -1}, + { -1, -1, 11, 2, 7}, { -1, -1, 16, 3, 7}, + {157, 173, -1, 0, -1}, {158, 165, -1, 0, -1}, + {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1}, + { -1, -1, 16, 4, 7}, { -1, -1, 16, 5, 7}, + {163, 164, -1, 0, -1}, { -1, -1, 16, 6, 7}, + { -1, -1, 16, 7, 7}, {166, 169, -1, 0, -1}, + {167, 168, -1, 0, -1}, { -1, -1, 16, 8, 7}, + { -1, -1, 16, 9, 7}, {170, 172, -1, 0, -1}, + { -1, -1, 16, 10, 7}, { -1, -1, 8, 1, 8}, + { -1, -1, 16, 2, 8}, {174, 181, -1, 0, -1}, + {175, 178, -1, 0, -1}, {176, 177, -1, 0, -1}, + { -1, -1, 16, 3, 8}, { -1, -1, 16, 4, 8}, + {179, 180, -1, 0, -1}, { -1, -1, 16, 5, 8}, + { -1, -1, 16, 6, 8}, {182, 185, -1, 0, -1}, + {183, 184, -1, 0, -1}, { -1, -1, 16, 7, 8}, + { -1, -1, 16, 8, 8}, {186, 187, -1, 0, -1}, + { -1, -1, 16, 9, 8}, { -1, -1, 16, 10, 8}, + { -1, -1, 9, 1, 9}, {190, 257, -1, 0, -1}, + {191, 224, -1, 0, -1}, {192, 207, -1, 0, -1}, + {193, 200, -1, 0, -1}, {194, 197, -1, 0, -1}, + {195, 196, -1, 0, -1}, { -1, -1, 16, 2, 9}, + { -1, -1, 16, 3, 9}, {198, 199, -1, 0, -1}, + { -1, -1, 16, 4, 9}, { -1, -1, 16, 5, 9}, + {201, 204, -1, 0, -1}, {202, 203, -1, 0, -1}, + { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9}, + {205, 206, -1, 0, -1}, { -1, -1, 16, 8, 9}, + { -1, -1, 16, 9, 9}, {208, 217, -1, 0, -1}, + {209, 214, -1, 0, -1}, {210, 213, -1, 0, -1}, + { -1, -1, 16, 10, 9}, {212, 230, -1, 0, -1}, + { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10}, + {215, 216, -1, 0, -1}, { -1, -1, 16, 3, 10}, + { -1, -1, 16, 4, 10}, {218, 221, -1, 0, -1}, + {219, 220, -1, 0, -1}, { -1, -1, 16, 5, 10}, + { -1, -1, 16, 6, 10}, {222, 223, -1, 0, -1}, + { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10}, + {225, 241, -1, 0, -1}, {226, 234, -1, 0, -1}, + {227, 231, -1, 0, -1}, {228, 229, -1, 0, -1}, + { -1, -1, 16, 9, 10}, { -1, -1, 16, 10, 10}, + { -1, -1, 9, 1, 11}, {232, 233, -1, 0, -1}, + { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11}, + {235, 238, -1, 0, -1}, {236, 237, -1, 0, -1}, + { -1, -1, 16, 4, 11}, { -1, -1, 16, 5, 11}, + {239, 240, -1, 0, -1}, { -1, -1, 16, 6, 11}, + { -1, -1, 16, 7, 11}, {242, 250, -1, 0, -1}, + {243, 246, -1, 0, -1}, {244, 245, -1, 0, -1}, + { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11}, + {247, 249, -1, 0, -1}, { -1, -1, 16, 10, 11}, + { -1, -1, 9, 1, 12}, { -1, -1, 16, 2, 12}, + {251, 254, -1, 0, -1}, {252, 253, -1, 0, -1}, + { -1, -1, 16, 3, 12}, { -1, -1, 16, 4, 12}, + {255, 256, -1, 0, -1}, { -1, -1, 16, 5, 12}, + { -1, -1, 16, 6, 12}, {258, 291, -1, 0, -1}, + {259, 275, -1, 0, -1}, {260, 268, -1, 0, -1}, + {261, 264, -1, 0, -1}, {262, 263, -1, 0, -1}, + { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12}, + {265, 266, -1, 0, -1}, { -1, -1, 16, 9, 12}, + { -1, -1, 16, 10, 12}, { -1, -1, 11, 1, 13}, + {269, 272, -1, 0, -1}, {270, 271, -1, 0, -1}, + { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13}, + {273, 274, -1, 0, -1}, { -1, -1, 16, 4, 13}, + { -1, -1, 16, 5, 13}, {276, 283, -1, 0, -1}, + {277, 280, -1, 0, -1}, {278, 279, -1, 0, -1}, + { -1, -1, 16, 6, 13}, { -1, -1, 16, 7, 13}, + {281, 282, -1, 0, -1}, { -1, -1, 16, 8, 13}, + { -1, -1, 16, 9, 13}, {284, 288, -1, 0, -1}, + {285, 287, -1, 0, -1}, { -1, -1, 16, 10, 13}, + { -1, -1, 14, 1, 14}, { -1, -1, 16, 2, 14}, + {289, 290, -1, 0, -1}, { -1, -1, 16, 3, 14}, + { -1, -1, 16, 4, 14}, {292, 308, -1, 0, -1}, + {293, 300, -1, 0, -1}, {294, 297, -1, 0, -1}, + {295, 296, -1, 0, -1}, { -1, -1, 16, 5, 14}, + { -1, -1, 16, 6, 14}, {298, 299, -1, 0, -1}, + { -1, -1, 16, 7, 14}, { -1, -1, 16, 8, 14}, + {301, 305, -1, 0, -1}, {302, 303, -1, 0, -1}, + { -1, -1, 16, 9, 14}, { -1, -1, 16, 10, 14}, + { -1, -1, 15, 1, 15}, {306, 307, -1, 0, -1}, + { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15}, + {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1}, + {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15}, + { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1}, + { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15}, + {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1}, + { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15}, + {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15}, + { -1, -1, 10, 0, 16}, { -1, -1, 2, 0, -1}, +}; + +static const struct tree_node treeYDC[] = { + { 1, 6, -1, 0}, { 2, 3, -1, 0}, + { -1, -1, 2, 0}, { 4, 5, -1, 0}, + { -1, -1, 3, 1}, { -1, -1, 3, 2}, + { 7, 10, -1, 0}, { 8, 9, -1, 0}, + { -1, -1, 3, 3}, { -1, -1, 3, 4}, + { 11, 12, -1, 0}, { -1, -1, 3, 5}, + { 13, 14, -1, 0}, { -1, -1, 4, 6}, + { 15, 16, -1, 0}, { -1, -1, 5, 7}, + { 17, 18, -1, 0}, { -1, -1, 6, 8}, + { 19, 20, -1, 0}, { -1, -1, 7, 9}, + { 21, 22, -1, 0}, { -1, -1, 8, 10}, + { 23, -1, -1, 0}, { -1, -1, 9, 11}, +}; + +static const struct tree_node treeUVDC[] = { + { 1, 4, -1, 0}, { 2, 3, -1, 0}, + { -1, -1, 2, 0}, { -1, -1, 2, 1}, + { 5, 6, -1, 0}, { -1, -1, 2, 2}, + { 7, 8, -1, 0}, { -1, -1, 3, 3}, + { 9, 10, -1, 0}, { -1, -1, 4, 4}, + { 11, 12, -1, 0}, { -1, -1, 5, 5}, + { 13, 14, -1, 0}, { -1, -1, 6, 6}, + { 15, 16, -1, 0}, { -1, -1, 7, 7}, + { 17, 18, -1, 0}, { -1, -1, 8, 8}, + { 19, 20, -1, 0}, { -1, -1, 9, 9}, + { 21, 22, -1, 0}, { -1, -1, 10, 10}, + { 23, -1, -1, 0}, { -1, -1, 11, 11}, +}; + +/****************************************************************************** + * Huffman Decoder + ******************************************************************************/ + +/* Note: There is no penalty for passing the tree as an argument, since dummy + * args are passed anyway (to maintain 16-byte stack alignment), and since the + * address is loaded into a register either way. */ + +/* If no node is found, coeffbits and skip will not be modified */ +/* Return: Depth of node found, or -1 if invalid input code */ +static int +getNodeAC(unsigned int in, signed char *coeffbits, signed char *skip, + const struct tree_node *tree) +{ + int node = 0; + int i = 0; + int depth; + + do { + if ((in & 0x80000000) == 0) + node = tree[node].left; + else + node = tree[node].right; + + if (node == -1) + break; + + depth = tree[node].depth; + + /* Is it a leaf? If not, branch downward */ + if (depth != -1) { + *coeffbits = tree[node].coeffbits; + *skip = tree[node].skip; + return depth; + } + + in <<= 1; + ++i; + } while (i <= 15); + + return -1; +} + +/* If no node is found, coeffbits will not be modified */ +/* Return: Depth of node found, or -1 if invalid input code */ +static int +getNodeDC(unsigned int in, signed char *coeffbits, const struct tree_node *tree) +{ + int node = 0; + int i = 0; + int depth; + + do { + if ((in & 0x80000000) == 0) + node = tree[node].left; + else + node = tree[node].right; + + if (node == -1) + break; + + depth = tree[node].depth; + + /* Is it a leaf? If not, branch downward */ + if (depth != -1) { + *coeffbits = tree[node].coeffbits; + return depth; + } + + in <<= 1; + ++i; + } while (i <= 15); + + return -1; +} + +static inline unsigned int +getBytes(int *rawData, struct comp_info *cinfo) +{ + int bufLen = cinfo->rawLen; + int bits = cinfo->bits; + int bytes = cinfo->bytes; + unsigned char *in = bytes + (unsigned char *) rawData; + unsigned char b1, b2, b3, b4, b5; + unsigned int packedIn; + + /* Pull 5 bytes out of raw data */ + if (bytes < bufLen - 4) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + b4 = in[3]; + b5 = in[4]; + } else { + if (bytes < bufLen - 3) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + b4 = in[3]; + } else { + if (bytes < bufLen - 2) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + } else { + if (bytes < bufLen - 1) { + b1 = in[0]; + b2 = in[1]; + } else { + if (bytes <= bufLen) { + b1 = in[0]; + } else { + b1 = 0; + } + b2 = 0; + } + b3 = 0; + } + b4 = 0; + } + b5 = 0; + } + + /* Pack the bytes */ + packedIn = b1 << 24; + packedIn += b2 << 16; + packedIn += b3 << 8; + packedIn += b4; + + if (bits != 0) { + packedIn = packedIn << bits; + packedIn += b5 >> (8 - bits); + } + + return packedIn; +} + +static int +getACCoefficient(int *rawData, int *coeff, struct comp_info *cinfo, + const struct tree_node *tree) +{ + int input, bits, bytes, tmp_c; + signed char coeffbits = 0; + signed char skip = 0; + + input = getBytes(rawData, cinfo); + bits = getNodeAC(input, &coeffbits, &skip, tree); + + if (coeffbits) { + input = input << (bits - 1); + input &= 0x7fffffff; + if (! (input & 0x40000000)) + input |= 0x80000000; + + tmp_c = input >> (31 - coeffbits); + if (tmp_c < 0) + tmp_c++; + *coeff = tmp_c; + + bits += coeffbits; + } + + bytes = (bits + cinfo->bits) >> 3; + cinfo->bytes += bytes; + cinfo->bits += bits - (bytes << 3); + + return skip; +} + +static void +getDCCoefficient(int *rawData, int *coeff, struct comp_info *cinfo, + const struct tree_node *tree) +{ + int input, bits, bytes, tmp_c; + signed char coeffbits = 0; + + input = getBytes(rawData, cinfo); + bits = getNodeDC(input, &coeffbits, tree); + + if (bits == -1) { + bits = 1; /* Try to re-sync at the next bit */ + *coeff = 0; /* Indicates no change from last DC */ + } else { + + input = input << (bits - 1); + input &= 0x7fffffff; + if (! (input & 0x40000000)) + input |= 0x80000000; + + tmp_c = input >> (31 - coeffbits); + if (tmp_c < 0) + tmp_c++; + *coeff = tmp_c; + + bits += coeffbits; + } + + bytes = (bits + cinfo->bits) >> 3; + cinfo->bytes += bytes; + cinfo->bits += bits - (bytes << 3); +} + +/* For AC coefficients, here is what the "skip" value means: + * -1: Either the 8x4 block has ended, or the decoding failed. + * 0: Use the returned coeff. Don't skip anything. + * 1-15: The next <skip> coeffs are zero. The returned coeff is used. + * 16: The next 16 coeffs are zero. The returned coeff is ignored. + * + * You must ensure that the C[] array not be overrun, or stack corruption will + * result. + */ +static void +huffmanDecoderY(int *C, int *pIn, struct comp_info *cinfo) +{ + int coeff = 0; + int i = 1; + int k, skip; + + getDCCoefficient(pIn, C, cinfo, treeYDC); + + i = 1; + do { + skip = getACCoefficient(pIn, &coeff, cinfo, treeYAC); + + if (skip == -1) { + break; + } else if (skip == 0) { + C[i++] = coeff; + } else if (skip == 16) { + k = 16; + if (i > 16) + k = 32 - i; + + while (k--) + C[i++] = 0; + } else { + k = skip; + if (skip > 31 - i) + k = 31 - i; + + while (k--) + C[i++] = 0; + + C[i++] = coeff; + } + } while (i <= 31); + + if (skip == -1) + while (i <= 31) C[i++] = 0; + else + getACCoefficient(pIn, &coeff, cinfo, treeYAC); +} + +/* Same as huffmanDecoderY, except for the tables used */ +static void +huffmanDecoderUV(int *C, int *pIn, struct comp_info *cinfo) +{ + int coeff = 0; + int i = 1; + int k, skip; + + getDCCoefficient(pIn, C, cinfo, treeUVDC); + + i = 1; + do { + skip = getACCoefficient(pIn, &coeff, cinfo, treeUVAC); + + if (skip == -1) { + break; + } else if (skip == 0) { + C[i++] = coeff; + } else if (skip == 16) { + k = 16; + if (i > 16) + k = 32 - i; + + while (k--) + C[i++] = 0; + } else { + k = skip; + if (skip > 31 - i) + k = 31 - i; + + while (k--) + C[i++] = 0; + + C[i++] = coeff; + } + } while (i <= 31); + + if (skip == -1) + while (i <= 31) C[i++] = 0; + else + getACCoefficient(pIn, &coeff, cinfo, treeUVAC); +} + +/****************************************************************************** + * iDCT Functions + ******************************************************************************/ + +#ifndef APPROXIMATE_MUL_BY_SHIFT + +#define IDCT_MESSAGE "iDCT with multiply" + +#define TIMES_16382(u) ((u)? 16382 * (u):0) +#define TIMES_23168(u) ((u)? 23168 * (u):0) +#define TIMES_30270(u) ((u)? 30270 * (u):0) +#define TIMES_41986(u) ((u)? 41986 * (u):0) +#define TIMES_35594(u) ((u)? 35594 * (u):0) +#define TIMES_23783(u) ((u)? 23783 * (u):0) +#define TIMES_8351(u) ((u)? 8351 * (u):0) +#define TIMES_17391(u) ((u)? 17391 * (u):0) +#define TIMES_14743(u) ((u)? 14743 * (u):0) +#define TIMES_9851(u) ((u)? 9851 * (u):0) +#define TIMES_3459(u) ((u)? 3459 * (u):0) +#define TIMES_32134(u) ((u)? 32134 * (u):0) +#define TIMES_27242(u) ((u)? 27242 * (u):0) +#define TIMES_18202(u) ((u)? 18202 * (u):0) +#define TIMES_6392(u) ((u)? 6392 * (u):0) +#define TIMES_39550(u) ((u)? 39550 * (u):0) +#define TIMES_6785(u) ((u)? 6785 * (u):0) +#define TIMES_12538(u) ((u)? 12538 * (u):0) + +#else + +#define IDCT_MESSAGE "iDCT with shift" + +#define TIMES_16382(u) ( (u)? x=(u) , (x<<14) - (x<<1) :0 ) +#define TIMES_23168(u) ( (u)? x=(u) , (x<<14) + (x<<12) + (x<<11) + (x<<9) :0 ) +#define TIMES_30270(u) ( (u)? x=(u) , (x<<15) - (x<<11) :0 ) +#define TIMES_41986(u) ( (u)? x=(u) , (x<<15) + (x<<13) + (x<<10) :0 ) +#define TIMES_35594(u) ( (u)? x=(u) , (x<<15) + (x<<11) + (x<<9) + (x<<8) :0 ) +#define TIMES_23783(u) ( (u)? x=(u) , (x<<14) + (x<<13) - (x<<9) - (x<<8) :0 ) +#define TIMES_8351(u) ( (u)? x=(u) , (x<<13) :0 ) +#define TIMES_17391(u) ( (u)? x=(u) , (x<<14) + (x<<10) :0 ) +#define TIMES_14743(u) ( (u)? x=(u) , (x<<14) - (x<<10) - (x<<9) :0 ) +#define TIMES_9851(u) ( (u)? x=(u) , (x<<13) + (x<<10) + (x<<9) :0 ) +#define TIMES_3459(u) ( (u)? x=(u) , (x<<12) - (x<<9) :0 ) +#define TIMES_32134(u) ( (u)? x=(u) , (x<<15) - (x<<9) :0 ) +#define TIMES_27242(u) ( (u)? x=(u) , (x<<14) + (x<<13) + (x<<11) + (x<<9) :0 ) +#define TIMES_18202(u) ( (u)? x=(u) , (x<<14) + (x<<11) - (x<<8) :0 ) +#define TIMES_6392(u) ( (u)? x=(u) , (x<<13) - (x<<11) + (x<<8) :0 ) +#define TIMES_39550(u) ( (u)? x=(u) , (x<<15) + (x<<12) + (x<<11) + (x<<9) :0 ) +#define TIMES_6785(u) ( (u)? x=(u) , (x<<12) + (x<<11) + (x<<9) :0 ) +#define TIMES_12538(u) ( (u)? x=(u) , (x<<13) + (x<<12) + (x<<8) :0 ) + +/* + * The variables C0, C4, C16 and C20 can also be removed from the algorithm + * if APPROXIMATE_MUL_BY_SHIFTS is defined. They store correction values + * and can be considered insignificant. + */ + +#endif + +static void +DCT_8x4(int *coeff, unsigned char *out) +/* pre: coeff == coefficients + post: coeff != coefficients + ** DO NOT ASSUME coeff TO BE THE SAME BEFORE AND AFTER CALLING THIS FUNCTION! +*/ +{ + register int base,val1,val2,val3; + int tmp1,tmp2; + int C0,C4,C16,C20; + int C2_18,C6_22,C1_17,C3_19,C5_21,C7_23; + register int t; +#ifdef APPROXIMATE_MUL_BY_SHIFT + register int x; +#endif + + C0=coeff[0]; + C4=coeff[4]; + C16=coeff[16]; + C20=coeff[20]; + + coeff[0]=TIMES_23168(coeff[0]); + coeff[4]=TIMES_23168(coeff[4]); + coeff[16]=TIMES_23168(coeff[16]); + coeff[20]=TIMES_23168(coeff[20]); + + C2_18 = coeff[2]+coeff[18]; + C6_22 = coeff[6]+coeff[22]; + C1_17 = coeff[1]+coeff[17]; + C3_19 = coeff[3]+coeff[19]; + C5_21 = coeff[5]+coeff[21]; + C7_23 = coeff[7]+coeff[23]; + +// 0,7,25,32 + + base = 0x1000000; + base += coeff[0]+coeff[4]+coeff[16]+coeff[20]; + base += TIMES_30270(C2_18); + base += TIMES_12538(C6_22); + + val1 = TIMES_41986(coeff[9]); + val1 += TIMES_35594(coeff[11]); + val1 += TIMES_23783(coeff[13]); + val1 += TIMES_8351(coeff[15]); + val1 += TIMES_17391(coeff[25]); + val1 += TIMES_14743(coeff[27]); + val1 += TIMES_9851(coeff[29]); + val1 += TIMES_3459(coeff[31]); + + val2 = TIMES_32134(C1_17); + val2 += TIMES_27242(C3_19); + val2 += TIMES_18202(C5_21); + val2 += TIMES_6392(C7_23); + + val3 = TIMES_39550(coeff[10]); + val3 += TIMES_16382(coeff[14]+coeff[26]); + val3 += TIMES_6785(coeff[30]); + val3 += TIMES_30270(coeff[8]+coeff[12]); + val3 += TIMES_12538(coeff[24]+coeff[28]); + + t=(base + val1 + val2 + val3) >> 17; + out[0]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 - C4 - C20) >> 17; + out[7]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - C16- C20) >> 17; + out[24]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 - C4 - C16 - C20) >> 17; + out[31]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//1,6,25,30 + + base = 0x1000000; + base += coeff[0]-coeff[4]+coeff[16]-coeff[20]; + base += TIMES_12538(C2_18); + base -= TIMES_30270(C6_22); + + val1 = TIMES_35594(coeff[9]); + val1 -= TIMES_8351(coeff[11]); + val1 -= TIMES_41986(coeff[13]); + val1 -= TIMES_23783(coeff[15]); + val1 -= TIMES_14743(coeff[25]); + val1 -= TIMES_3459(coeff[27]); + val1 -= TIMES_17391(coeff[29]); + val1 -= TIMES_9851(coeff[31]); + + val2 = TIMES_27242(C1_17); + val2 -= TIMES_6392(C3_19); + val2 -= TIMES_32134(C5_21); + val2 -= TIMES_18202(C7_23); + + val3 = TIMES_16382(coeff[10]-coeff[30]); + val3 -= TIMES_39550(coeff[14]); + val3 += TIMES_6785(coeff[26]); + val3 += TIMES_12538(coeff[24]-coeff[28]); + val3 += TIMES_30270(coeff[8]-coeff[12]); + + t=(base + val1 + val2 + val3 + C4 + C20) >> 17; + out[1]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3) >> 17; + out[6]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 + C4 - C16 + C20) >> 17; + out[25]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 + C20) >> 17; + out[30]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//2,5,26,29 + + base = 0x1000000; + base += coeff[0] - coeff[4] + coeff[16] - coeff[20]; + base -= TIMES_12538(C2_18); + base += TIMES_30270(C6_22); + + val1 = TIMES_23783(coeff[9]); + val1 -= TIMES_41986(coeff[11]); + val1 += TIMES_8351(coeff[13]); + val1 += TIMES_35594(coeff[15]); + val1 += TIMES_9851(coeff[25]); + val1 -= TIMES_17391(coeff[27]); + val1 += TIMES_3459(coeff[29]); + val1 += TIMES_14743(coeff[31]); + + val2 = TIMES_18202(C1_17); + val2 -= TIMES_32134(C3_19); + val2 += TIMES_6392(C5_21); + val2 += TIMES_27242(C7_23); + + val3 = -TIMES_16382(coeff[10] - coeff[30]); + val3 += TIMES_39550(coeff[14]); + val3 -= TIMES_6785(coeff[26]); + val3 += TIMES_12538(coeff[24] - coeff[28]); + val3 += TIMES_30270(coeff[8] - coeff[12]); + + t=(base + val1 + val2 + val3) >> 17; + out[2]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3) >> 17; + out[5]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - C16) >> 17; + out[26]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 + C4 - C16 + C20) >> 17; + out[29]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//3,4,27,28 + + base = 0x1000000; + base += coeff[0] + coeff[4] + coeff[16] + coeff[20]; + base -= TIMES_30270(C2_18); + base -= TIMES_12538(C6_22); + + val1 = TIMES_8351(coeff[9]); + val1 -= TIMES_23783(coeff[11]); + val1 += TIMES_35594(coeff[13]); + val1 += TIMES_3459(coeff[25]); + val1 -= TIMES_9851(coeff[27]); + val1 += TIMES_14743(coeff[29]); + + val2 = TIMES_6392(C1_17); + val2 -= TIMES_18202(C3_19); + val2 += TIMES_27242(C5_21); + + val3 = -TIMES_39550(coeff[10]); + val3 += TIMES_16382(coeff[14] + coeff[26]); + val3 -= TIMES_6785(coeff[30]); + val3 += TIMES_30270(coeff[8] + coeff[12]); + val3 += TIMES_12538(coeff[24] + coeff[28]); + + tmp1 = TIMES_32134(C7_23); + tmp2 = TIMES_41986(coeff[15]) + TIMES_17391(coeff[31]); + + t=(base + val1 + val2 + val3 - tmp1 - tmp2 - C4 - C20) >> 17; + out[3]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3) >> 17; + out[4]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - tmp1 + tmp2) >> 17; + out[27]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 - C16 - C20) >> 17; + out[28]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +// Second half + C2_18 = coeff[2] - coeff[18]; + C6_22 = coeff[6] - coeff[22]; + C1_17 = coeff[1] - coeff[17]; + C3_19 = coeff[3] - coeff[19]; + C5_21 = coeff[5] - coeff[21]; + C7_23 = coeff[7] - coeff[23]; + +// 8,15,16,23 + + base = 0x1000000; + base += coeff[0] + coeff[4] - coeff[16] - coeff[20]; + base +=TIMES_30270(C2_18); + base +=TIMES_12538(C6_22); + + val1 = TIMES_17391(coeff[9]); + val1 += TIMES_14743(coeff[11]); + val1 += TIMES_9851(coeff[13]); + val1 += TIMES_3459(coeff[15]); + val1 -= TIMES_41986(coeff[25]); + val1 -= TIMES_35594(coeff[27]); + val1 -= TIMES_23783(coeff[29]); + val1 -= TIMES_8351(coeff[31]); + + val2 = TIMES_32134(C1_17); + val2 += TIMES_27242(C3_19); + val2 += TIMES_18202(C5_21); + val2 += TIMES_6392(C7_23); + + val3 = TIMES_16382(coeff[10] - coeff[30]); + val3 += TIMES_6785(coeff[14]); + val3 -= TIMES_39550(coeff[26]); + val3 -=TIMES_30270(coeff[24] + coeff[28]); + val3 +=TIMES_12538(coeff[8] + coeff[12]); + + t=(base + val1 + val2 + val3) >> 17; + out[8]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 - C4 + C16 + C20) >> 17; + out[15]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3) >> 17; + out[16]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 - C4 + C20) >> 17; + out[23]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//9,14,17,22 + + base = 0x1000000; + base += coeff[0] - coeff[4] - coeff[16] + coeff[20]; + base += TIMES_12538(C2_18); + base -= TIMES_30270(C6_22); + + val1 = TIMES_14743(coeff[9]); + val1 -= TIMES_3459(coeff[11]); + val1 -= TIMES_17391(coeff[13]); + val1 -= TIMES_9851(coeff[15]); + val1 -= TIMES_35594(coeff[25]); + val1 += TIMES_8351(coeff[27]); + val1 += TIMES_41986(coeff[29]); + val1 += TIMES_23783(coeff[31]); + + val2 = TIMES_27242(C1_17); + val2 -= TIMES_6392(C3_19); + val2 -= TIMES_32134(C5_21); + val2 -= TIMES_18202(C7_23); + + val3 = TIMES_6785(coeff[10]); + val3 -= TIMES_16382(coeff[14] + coeff[26]); + val3 += TIMES_39550(coeff[30]); + val3 += TIMES_12538(coeff[8] - coeff[12]); + val3 -= TIMES_30270(coeff[24] - coeff[28]); + + t=(base + val1 + val2 + val3 + C4 + C16 - C20) >> 17; + out[9]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 + C16) >> 17; + out[14]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 + C4) >> 17; + out[17]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3) >> 17; + out[22]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//10,13,18,21 + + base = 0x1000000; + base += coeff[0] - coeff[4] - coeff[16] + coeff[20]; + base -= TIMES_12538(C2_18); + base += TIMES_30270(C6_22); + + val1 = TIMES_9851(coeff[9]); + val1 -= TIMES_17391(coeff[11]); + val1 += TIMES_3459(coeff[13]); + val1 += TIMES_14743(coeff[15]); + val1 -= TIMES_23783(coeff[25]); + val1 += TIMES_41986(coeff[27]); + val1 -= TIMES_8351(coeff[29]); + val1 -= TIMES_35594(coeff[31]); + + val2 = TIMES_18202(C1_17); + val2 -= TIMES_32134(C3_19); + val2 += TIMES_6392(C5_21); + val2 += TIMES_27242(C7_23); + + val3 = -TIMES_6785(coeff[10]); + val3 += TIMES_16382(coeff[14]+coeff[26]); + val3 -= TIMES_39550(coeff[30]); + val3 += TIMES_12538(coeff[8]-coeff[12]); + val3 -= TIMES_30270(coeff[24]-coeff[28]); + + t=(base + val1 + val2 + val3) >> 17; + out[10]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 + C4 + C16 - C20) >> 17; + out[13]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3) >> 17; + out[18]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 + C4) >> 17; + out[21]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +// 11,12,19,20 + + base = 0x1000000; + base += coeff[0]+coeff[4]-coeff[16]-coeff[20]; + base -= TIMES_30270(C2_18); + base -= TIMES_12538(C6_22); + + val1 = TIMES_3459(coeff[9]); + val1 -= TIMES_9851(coeff[11]); + val1 += TIMES_14743(coeff[13]); + val1 -= TIMES_8351(coeff[25]); + val1 += TIMES_23783(coeff[27]); + val1 -= TIMES_35594(coeff[29]); + + val2 = TIMES_6392(C1_17); + val2 -= TIMES_18202(C3_19); + val2 += TIMES_27242(C5_21); + + val3 = -TIMES_16382(coeff[10] - coeff[30]); + val3 -= TIMES_6785(coeff[14]); + val3 += TIMES_39550(coeff[26]); + val3 -= TIMES_30270(coeff[24]+coeff[28]); + val3 += TIMES_12538(coeff[8]+coeff[12]); + + tmp1 = TIMES_32134(C7_23); + tmp2 = -TIMES_17391(coeff[15]) + TIMES_41986(coeff[31]); + + t=(base + val1 + val2 + val3 - tmp1 + tmp2 + C16 + C20) >> 17; + out[11]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 + C16 + C20) >> 17; + out[12]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - tmp1 - tmp2 - C4 + C20) >> 17; + out[19]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3) >> 17; + out[20]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; +} + +#undef TIMES_16382 +#undef TIMES_23168 +#undef TIMES_30270 +#undef TIMES_41986 +#undef TIMES_35594 +#undef TIMES_23783 +#undef TIMES_8351 +#undef TIMES_17391 +#undef TIMES_14743 +#undef TIMES_9851 +#undef TIMES_3459 +#undef TIMES_32134 +#undef TIMES_27242 +#undef TIMES_18202 +#undef TIMES_6392 +#undef TIMES_39550 +#undef TIMES_6785 +#undef TIMES_12538 + +/****************************************************************************** + * Main Decoder Functions + ******************************************************************************/ + +/* This function handles the decompression of a single 8x4 block. It is + * independent of the palette (YUV422, YUV420, YUV400, GBR422...). cinfo->bytes + * determines the positin in the input buffer. + */ +static int +decompress8x4(unsigned char *pOut, + unsigned char *pIn, + int *lastDC, + int uvFlag, + struct comp_info *cinfo) +{ + int i, x, y, dc; + int coeffs[32]; + int deZigZag[32]; + int *dest; + int *src; + unsigned char *qt = cinfo->qt; + + if (! uvFlag) { + huffmanDecoderY(coeffs, (int*) pIn, cinfo); + + /* iDPCM and dequantize first coefficient */ + dc = (*lastDC) + coeffs[0]; + coeffs[0] = dc * (qt[0] + 1); + *lastDC = dc; + + /* ...and the second coefficient */ + coeffs[1] = ((qt[1] + 1) * coeffs[1]) >> 1; + + /* Dequantize, starting at 3rd element */ + for (i = 2; i < 32; i++) + coeffs[i] = (qt[i] + 1) * coeffs[i]; + } else { + huffmanDecoderUV(coeffs, (int*) pIn, cinfo); + + /* iDPCM */ + dc = (*lastDC) + coeffs[0]; + coeffs[0] = dc; + *lastDC = dc; + + /* Dequantize */ + for (i = 0; i < 32; i++) + coeffs[i] = (qt[32 + i] + 1) * coeffs[i]; + } + + /* Dezigzag */ + for (i = 0; i < 32; i++) + deZigZag[i] = coeffs[ZigZag518[i]]; + + /* Transpose the dezigzagged coefficient matrix */ + src = deZigZag; + dest = coeffs; + for (y = 0; y <= 3; ++y) { + for (x = 0; x <= 7; ++x) { + dest[x] = src[x * 4]; + } + src += 1; + dest += 8; + } + + /* Do the inverse DCT transform */ + DCT_8x4(coeffs, pOut); + + return 0; /* Always returns 0 */ +} + +static inline void +copyBlock(unsigned char *src, unsigned char *dest, int destInc) +{ + int i; + unsigned int *pSrc, *pDest; + + for (i = 0; i <= 3; i++) { + pSrc = (unsigned int *) src; + pDest = (unsigned int *) dest; + pDest[0] = pSrc[0]; + pDest[1] = pSrc[1]; + src += 8; + dest += destInc; + } +} + +#if 0 +static inline int +decompress400NoMMXOV518(unsigned char *pIn, + unsigned char *pOut, + unsigned char *pTmp, + const int w, + const int h, + const int numpix, + struct comp_info *cinfo) +{ + int iOutY, x, y; + int lastYDC = 0; + + /* Start Y loop */ + y = 0; + do { + iOutY = w * y; + x = 0; + do { + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + x += 8; + } while (x < w); + y += 4; + } while (y < h); + + /* Did we decode too much? */ + if (cinfo->bytes > cinfo->rawLen + 897) + return 1; + + /* Did we decode enough? */ + if (cinfo->bytes >= cinfo->rawLen - 897) + return 0; + else + return 1; +} +#endif + +static inline int +decompress420NoMMXOV518(unsigned char *pIn, + unsigned char *pOut, + unsigned char *pTmp, + const int w, + const int h, + const int numpix, + struct comp_info *cinfo, + int yvu) +{ + unsigned char *pOutU, *pOutV; + int iOutY, iOutU, iOutV, x, y; + int lastYDC = 0; + int lastUDC = 0; + int lastVDC = 0; + + if (yvu) { + pOutV = pOut + numpix; + pOutU = pOutV + numpix / 4; + } else { + pOutU = pOut + numpix; + pOutV = pOutU + numpix / 4; + } + + /* Start Y loop */ + y = 0; + do { + iOutY = w * y; + iOutV = iOutU = iOutY / 4; + + x = 0; + do { + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + x += 8; + } while (x < w); + + + + iOutY = w * (y + 4); + x = 0; + do { + decompress8x4(pTmp, pIn, &lastUDC, 1, cinfo); + copyBlock(pTmp, pOutU + iOutU, w/2); + iOutU += 8; + + decompress8x4(pTmp, pIn, &lastVDC, 1, cinfo); + copyBlock(pTmp, pOutV + iOutV, w/2); + iOutV += 8; + + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + + x += 16; + } while (x < w); + + y += 8; + } while (y < h); + + /* Did we decode too much? */ + if (cinfo->bytes > cinfo->rawLen + 897) + return 1; + + /* Did we decode enough? */ + if (cinfo->bytes >= cinfo->rawLen - (897 + 64)) + return 0; + else + return 1; +} + +/* Get quantization tables from input + * Returns: <0 if error, or >=0 otherwise */ +static int +get_qt_dynamic(unsigned char *pIn, struct comp_info *cinfo) +{ + int rawLen = cinfo->rawLen; + + /* Make sure input is actually big enough to hold trailer */ + if (rawLen < 72) { + return -1; + } + + cinfo->qt = pIn + rawLen - 64; + + return 0; +} + +/* Remove all 0 blocks from input */ +static void remove0blocks(unsigned char *pIn, int *inSize) +{ + long long *in = (long long *)pIn; + long long *out = (long long *)pIn; + int i; + + for (i = 0; i < *inSize; i += 8, in++) + /* Skip 8 byte blocks of all 0 */ + if (*in) + *out++ = *in; + + *inSize -= (in - out) * 8; +} + +#if 0 /* not used */ +/* Input format is raw isoc. data (with intact SOF header, packet numbers + * stripped, and all-zero blocks removed). + * Output format is planar YUV400 + * Returns uncompressed data length if success, or zero if error + */ +static int +Decompress400(unsigned char *pIn, + unsigned char *pOut, + int w, + int h, + int inSize) +{ + struct comp_info cinfo; + int numpix = w * h; + unsigned char pTmp[32]; + + remove0blocks(pIn, &inSize); + + cinfo.bytes = 0; + cinfo.bits = 0; + cinfo.rawLen = inSize; + + if (get_qt_dynamic(pIn, &cinfo) < 0) + return 0; + + /* Decompress, skipping the 8-byte SOF header */ + if (decompress400NoMMXOV518(pIn + 8, pOut, pTmp, w, h, numpix, &cinfo)) +// return 0; + ; /* Don't return error yet */ + + return (numpix); +} +#endif + +/* Input format is raw isoc. data (with intact SOF header, packet numbers + * stripped, and all-zero blocks removed). + * Output format is planar YUV420 + * Returns uncompressed data length if success, or zero if error + */ +static int v4lconvert_ov518_to_yuv420(unsigned char *src, unsigned char *dst, + int w, int h, int yvu, int inSize) +{ + struct comp_info cinfo; + int numpix = w * h; + unsigned char pTmp[32]; + + remove0blocks(src, &inSize); + + cinfo.bytes = 0; + cinfo.bits = 0; + cinfo.rawLen = inSize; + + if (get_qt_dynamic(src, &cinfo) < 0) + return -1; + + /* Decompress, skipping the 8-byte SOF header */ + if (decompress420NoMMXOV518(src + 8, dst, pTmp, w, h, numpix, &cinfo, yvu)) + return -1; + + return 0; +} + +int main(int argc, char *argv[]) +{ + int width, height, yvu, src_size, dest_size; + unsigned char src_buf[200000]; + unsigned char dest_buf[500000]; + + while (1) { + if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (src_size > sizeof(src_buf)) { + fprintf(stderr, "%s: error: src_buf too small, need: %d\n", + argv[0], src_size); + return 2; + } + + if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + + dest_size = width * height * 3 / 2; + if (dest_size > sizeof(dest_buf)) { + fprintf(stderr, "%s: error: dest_buf too small, need: %d\n", + argv[0], dest_size); + dest_size = -1; + } else if (v4lconvert_ov518_to_yuv420(src_buf, dest_buf, width, height, + yvu, src_size)) + dest_size = -1; + + if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int), + argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (dest_size == -1) + continue; + + if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + } +} | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4lconvert/pac207.c ^ |
@@ -52,35 +52,35 @@ len = 2; } else if ((i & 0xC0) == 0x40) { /* code 01 */ - val = -5; + val = -1; len = 2; } else if ((i & 0xC0) == 0x80) { /* code 10 */ - val = +5; + val = +1; len = 2; } else if ((i & 0xF0) == 0xC0) { /* code 1100 */ - val = -10; + val = -2; len = 4; } else if ((i & 0xF0) == 0xD0) { /* code 1101 */ - val = +10; + val = +2; len = 4; } else if ((i & 0xF8) == 0xE0) { /* code 11100 */ - val = -15; + val = -3; len = 5; } else if ((i & 0xF8) == 0xE8) { /* code 11101 */ - val = +15; + val = +3; len = 5; } else if ((i & 0xFC) == 0xF0) { /* code 111100 */ - val = -20; + val = -4; len = 6; } else if ((i & 0xFC) == 0xF4) { /* code 111101 */ - val = +20; + val = +4; len = 6; } else if ((i & 0xF8) == 0xF8) { /* code 11111xxxxxx */ @@ -109,7 +109,8 @@ } static int -pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width) +pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width, + int step_size, int abs_bits) { int col; int val; @@ -135,11 +136,11 @@ if (table[code].is_abs) { /* absolute value: get 6 more bits */ code = getByte(inp, bitpos); - bitpos += 6; - *outp++ = code & 0xFC; + bitpos += abs_bits; + *outp++ = code & ~(0xff >> abs_bits); } else { /* relative to left pixel */ - val = outp[-2] + table[code].val; + val = outp[-2] + table[code].val * step_size; *outp++ = CLIP(val); } } @@ -148,18 +149,24 @@ return 2 * ((bitpos + 15) / 16); } -void v4lconvert_decode_pac207(const unsigned char *inp, unsigned char *outp, +int v4lconvert_decode_pac207(struct v4lconvert_data *data, + const unsigned char *inp, int src_size, unsigned char *outp, int width, int height) { /* we should received a whole frame with header and EOL marker in myframe->data and return a GBRG pattern in frame->tmpbuffer remove the header then copy line by line EOL is set with 0x0f 0xf0 marker or 0x1e 0xe1 for compressed line*/ + const unsigned char *end = inp + src_size; unsigned short word; int row; /* iterate over all rows */ for (row = 0; row < height; row++) { + if ((inp + 2) > end) { + V4LCONVERT_ERR("incomplete pac207 frame\n"); + return -1; + } word = getShort(inp); switch (word) { case 0x0FF0: @@ -167,21 +174,31 @@ inp += (2 + width); break; case 0x1EE1: - inp += pac_decompress_row(inp, outp, width); + inp += pac_decompress_row(inp, outp, width, 5, 6); + break; + + case 0x2DD2: + inp += pac_decompress_row(inp, outp, width, 9, 5); break; - case 0x2DD2: /* prefix for "stronger" compressed lines, currently the - kernel driver programs the cam so that we should not - get any of these */ + case 0x3CC3: + inp += pac_decompress_row(inp, outp, width, 17, 4); + break; + + case 0x4BB4: + /* skip or copy line? */ + memcpy(outp, outp - 2 * width, width); + inp += 2; + break; default: /* corrupt frame */ - /* FIXME add error reporting */ - return; + V4LCONVERT_ERR("unknown pac207 row header: 0x%04x\n", (int)word); + return -1; } outp += width; } - return; + return 0; } | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/processing ^ |
+(directory) | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/processing/autogain.c ^ |
@@ -0,0 +1,140 @@ +/* +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ +#include "../libv4lsyscall-priv.h" + +static int autogain_active(struct v4lprocessing_data *data) { + return v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN); +} + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html */ +static int autogain_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int x, y, target, steps, avg_lum = 0; + int gain, exposure, orig_gain, orig_exposure; + struct v4l2_control ctrl; + struct v4l2_queryctrl gainctrl, expoctrl; + const int deadzone = 8; + + ctrl.id = V4L2_CID_EXPOSURE; + expoctrl.id = V4L2_CID_EXPOSURE; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &expoctrl) || + SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) + return 0; + exposure = orig_exposure = ctrl.value; + + ctrl.id = V4L2_CID_GAIN; + gainctrl.id = V4L2_CID_GAIN; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &gainctrl) || + SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) + return 0; + gain = orig_gain = ctrl.value; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: + buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + + fmt->fmt.pix.width / 4; + + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + avg_lum += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width / 2; + } + avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width / 4; + break; + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + + fmt->fmt.pix.width * 3 / 4; + + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + avg_lum += *buf++; + avg_lum += *buf++; + avg_lum += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3 / 2; + } + avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width * 3 / 4; + break; + } + + /* If we are off a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET); + steps = abs(target - avg_lum) / deadzone; + + for (x = 0; x < steps; x++) { + if (avg_lum > target) { + if (exposure > expoctrl.default_value) + exposure--; + else if (gain > gainctrl.default_value) + gain--; + else if (exposure > expoctrl.minimum) + exposure--; + else if (gain > gainctrl.minimum) + gain--; + else + break; + } else { + if (gain < gainctrl.default_value) + gain++; + else if (exposure < expoctrl.default_value) + exposure++; + else if (gain < gainctrl.maximum) + gain++; + else if (exposure < expoctrl.maximum) + exposure++; + else + break; + } + } + + if (gain != orig_gain) { + ctrl.id = V4L2_CID_GAIN; + ctrl.value = gain; + SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); + } + if (exposure != orig_exposure) { + ctrl.id = V4L2_CID_EXPOSURE; + ctrl.value = exposure; + SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); + } + + return 0; +} + +struct v4lprocessing_filter autogain_filter = { + autogain_active, autogain_calculate_lookup_tables }; | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/processing/gamma.c ^ |
@@ -0,0 +1,57 @@ +/* +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 <math.h> +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" + +#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) + +static int gamma_active(struct v4lprocessing_data *data) { + int gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA); + + return gamma && gamma != 1000; +} + +static int gamma_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int i, x, gamma; + + gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA); + + if (gamma != data->last_gamma) { + for (i = 0; i < 256; i++) { + x = powf(i / 255.0, 1000.0 / gamma) * 255; + data->gamma_table[i] = CLIP(x); + } + data->last_gamma = gamma; + } + + for (i = 0; i < 256; i++) { + data->comp1[i] = data->gamma_table[data->comp1[i]]; + data->green[i] = data->gamma_table[data->green[i]]; + data->comp2[i] = data->gamma_table[data->comp2[i]]; + } + + return 1; +} + +struct v4lprocessing_filter gamma_filter = { + gamma_active, gamma_calculate_lookup_tables }; | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/processing/libv4lprocessing-priv.h ^ |
@@ -0,0 +1,66 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 +*/ + +#ifndef __LIBV4LPROCESSING_PRIV_H +#define __LIBV4LPROCESSING_PRIV_H + +#include "../control/libv4lcontrol.h" +#include "../libv4lsyscall-priv.h" + +#define V4L2PROCESSING_UPDATE_RATE 10 + +struct v4lprocessing_data { + struct v4lcontrol_data *control; + int fd; + int do_process; + int controls_changed; + /* True if any of the lookup tables does not contain + linear 0-255 */ + int lookup_table_active; + /* Counts the number of processed frames until a + V4L2PROCESSING_UPDATE_RATE overflow happens */ + int lookup_table_update_counter; + /* RGB/BGR lookup tables */ + unsigned char comp1[256]; + unsigned char green[256]; + unsigned char comp2[256]; + /* Filter private data for filters which need it */ + /* whitebalance.c data */ + int green_avg; + int comp1_avg; + int comp2_avg; + /* gamma.c data */ + int last_gamma; + unsigned char gamma_table[256]; +}; + +struct v4lprocessing_filter { + /* Returns 1 if the filter is active */ + int (*active)(struct v4lprocessing_data *data); + /* Returns 1 if any of the lookup tables was changed */ + int (*calculate_lookup_tables)(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt); +}; + +extern struct v4lprocessing_filter whitebalance_filter; +extern struct v4lprocessing_filter autogain_filter; +extern struct v4lprocessing_filter gamma_filter; + +#endif | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/processing/libv4lprocessing.c ^ |
@@ -0,0 +1,184 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ + +static struct v4lprocessing_filter *filters[] = { + &whitebalance_filter, + &autogain_filter, + &gamma_filter, +}; + +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data* control) +{ + struct v4lprocessing_data *data = + calloc(1, sizeof(struct v4lprocessing_data)); + + if (!data) + return NULL; + + data->fd = fd; + data->control = control; + + return data; +} + +void v4lprocessing_destroy(struct v4lprocessing_data *data) +{ + free(data); +} + +int v4lprocessing_pre_processing(struct v4lprocessing_data *data) +{ + int i; + + data->do_process = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) + data->do_process = 1; + } + + data->controls_changed |= v4lcontrol_controls_changed(data->control); + + return data->do_process; +} + +static void v4lprocessing_update_lookup_tables(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int i; + + for (i = 0; i < 256; i++) { + data->comp1[i] = i; + data->green[i] = i; + data->comp2[i] = i; + } + + data->lookup_table_active = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) { + if (filters[i]->calculate_lookup_tables(data, buf, fmt)) + data->lookup_table_active = 1; + } + } +} + +static void v4lprocessing_do_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int x, y; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp1[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp2[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + break; + + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + break; + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + for (y = 0; y < fmt->fmt.pix.height; y++) { + for (x = 0; x < fmt->fmt.pix.width; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - 3 * fmt->fmt.pix.width; + } + break; + } +} + +void v4lprocessing_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + if (!data->do_process) + return; + + /* Do we support the current pixformat? */ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + break; + default: + return; /* Non supported pix format */ + } + + if (data->controls_changed || + data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) { + v4lprocessing_update_lookup_tables(data, buf, fmt); + data->controls_changed = 0; + data->lookup_table_update_counter = 0; + } else + data->lookup_table_update_counter++; + + if (data->lookup_table_active) + v4lprocessing_do_processing(data, buf, fmt); + + data->do_process = 0; +} | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/processing/libv4lprocessing.h ^ |
@@ -0,0 +1,43 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 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 +*/ + +#ifndef __LIBV4LPROCESSING_H +#define __LIBV4LPROCESSING_H + +#include "../libv4lsyscall-priv.h" +#include <linux/videodev2.h> + +struct v4lprocessing_data; +struct v4lcontrol_data; + +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data *data); +void v4lprocessing_destroy(struct v4lprocessing_data *data); + +/* Prepare to process 1 frame, returns 1 if processing is necesary, + return 0 if no processing will be done */ +int v4lprocessing_pre_processing(struct v4lprocessing_data *data); + +/* Do the actual processing, this is a nop if v4lprocessing_pre_processing() + returned 0, or if called more then 1 time after a single + v4lprocessing_pre_processing() call. */ +void v4lprocessing_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt); + +#endif | ||
[-] [+] | Added | libv4l-0.6.0.tar.bz2/libv4lconvert/processing/whitebalance.c ^ |
@@ -0,0 +1,194 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# 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 <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ + +#define CLIP256(color) (((color)>0xff)?0xff:(((color)<0)?0:(color))) +#define CLIP(color, min, max) (((color)>(max))?(max):(((color)<(min))?(min):(color))) + +static int whitebalance_active(struct v4lprocessing_data *data) { + int wb; + + wb = v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE); + if (!wb) { + /* Reset cached color averages */ + data->green_avg = 0; + } + + return wb; +} + +static int whitebalance_calculate_lookup_tables_generic( + struct v4lprocessing_data *data, int green_avg, int comp1_avg, int comp2_avg) +{ + int i, avg_avg; + const int max_step = 64; + + /* Clip averages (restricts maximum white balance correction) */ + green_avg = CLIP(green_avg, 512, 3072); + comp1_avg = CLIP(comp1_avg, 512, 3072); + comp2_avg = CLIP(comp2_avg, 512, 3072); + + /* First frame ? */ + if (data->green_avg == 0) { + data->green_avg = green_avg; + data->comp1_avg = comp1_avg; + data->comp2_avg = comp2_avg; + } else { + /* Slowly adjust the averages used for the correction, so that we + do not get a sudden change in colors */ + if (abs(data->green_avg - green_avg) > max_step) { + if (data->green_avg < green_avg) + data->green_avg += max_step; + else + data->green_avg -= max_step; + } + else + data->green_avg = green_avg; + + if (abs(data->comp1_avg - comp1_avg) > max_step) { + if (data->comp1_avg < comp1_avg) + data->comp1_avg += max_step; + else + data->comp1_avg -= max_step; + } + else + data->comp1_avg = comp1_avg; + + if (abs(data->comp2_avg - comp2_avg) > max_step) { + if (data->comp2_avg < comp2_avg) + data->comp2_avg += max_step; + else + data->comp2_avg -= max_step; + } + else + data->comp2_avg = comp2_avg; + } + + if (abs(data->green_avg - data->comp1_avg) < max_step && + abs(data->green_avg - data->comp2_avg) < max_step && + abs(data->comp1_avg - data->comp2_avg) < max_step) + return 0; + + avg_avg = (data->green_avg + data->comp1_avg + data->comp2_avg) / 3; + + for (i = 0; i < 256; i++) { + data->comp1[i] = CLIP256(data->comp1[i] * avg_avg / data->comp1_avg); + data->green[i] = CLIP256(data->green[i] * avg_avg / data->green_avg); + data->comp2[i] = CLIP256(data->comp2[i] * avg_avg / data->comp2_avg); + } + + return 1; +} + +static int whitebalance_calculate_lookup_tables_bayer( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt, int starts_with_green) +{ + int x, y, a1 = 0, a2 = 0, b1 = 0, b2 = 0; + int green_avg, comp1_avg, comp2_avg; + + for (y = 0; y < fmt->fmt.pix.height; y += 2) { + for (x = 0; x < fmt->fmt.pix.width; x += 2) { + a1 += *buf++; + a2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width; x += 2) { + b1 += *buf++; + b2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + + if (starts_with_green) { + green_avg = a1 / 2 + b2 / 2; + comp1_avg = a2; + comp2_avg = b1; + } else { + green_avg = a2 / 2 + b1 / 2; + comp1_avg = a1; + comp2_avg = b2; + } + + /* Norm avg to ~ 0 - 4095 */ + green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + + return whitebalance_calculate_lookup_tables_generic(data, green_avg, + comp1_avg, comp2_avg); +} + +static int whitebalance_calculate_lookup_tables_rgb( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt) +{ + int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0; + + for (y = 0; y < fmt->fmt.pix.height; y++) { + for (x = 0; x < fmt->fmt.pix.width; x++) { + comp1_avg += *buf++; + green_avg += *buf++; + comp2_avg += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3; + } + + /* Norm avg to ~ 0 - 4095 */ + green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + + return whitebalance_calculate_lookup_tables_generic(data, green_avg, + comp1_avg, comp2_avg); +} + + +static int whitebalance_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 1); + + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 0); + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return whitebalance_calculate_lookup_tables_rgb(data, buf, fmt); + } + + return 0; /* Should never happen */ +} + +struct v4lprocessing_filter whitebalance_filter = { + whitebalance_active, whitebalance_calculate_lookup_tables }; | ||
[-] [+] | Changed | libv4l-0.6.0.tar.bz2/libv4lconvert/rgbyuv.c ^ |
@@ -1,9 +1,11 @@ /* # RGB <-> YUV conversion routines - # (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# RGB565 conversion routines +# (C) 2009 Mauro Carvalho Chehab <mchehab@redhat.com> + # 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 @@ -135,7 +137,7 @@ vsrc++; } /* Rewind u and v for next line */ - if (i&1) { + if (!(i&1)) { usrc -= width / 2; vsrc -= width / 2; } @@ -189,7 +191,7 @@ vsrc++; } /* Rewind u and v for next line */ - if (i&1) { + if (!(i&1)) { usrc -= width / 2; vsrc -= width / 2; } @@ -409,7 +411,6 @@ } /* copy the U and V values */ - src++; /* point to V */ src1 = src + width * 2; /* next line */ if (yvu) { vdest = dest; @@ -473,3 +474,103 @@ src += src_fmt->fmt.pix.bytesperline / 2; } } + +void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + unsigned short tmp = *(unsigned short *)src; + + /* Original format: rrrrrggg gggbbbbb */ + *dest++ = 0xf8 & (tmp >> 8); + *dest++ = 0xfc & (tmp >> 3); + *dest++ = 0xf8 & (tmp << 3); + + src += 2; + } + } +} + +void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + unsigned short tmp = *(unsigned short *)src; + + /* Original format: rrrrrggg gggbbbbb */ + *dest++ = 0xf8 & (tmp << 3); + *dest++ = 0xfc & (tmp >> 3); + *dest++ = 0xf8 & (tmp >> 8); + + src += 2; + } + } +} + +void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int yvu) +{ + int x, y; + unsigned short tmp; + unsigned char *udest, *vdest; + unsigned r[4], g[4], b[4]; + int avg_src[3]; + + /* Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + for (x = 0; x < src_fmt->fmt.pix.width; x++) { + tmp = *(unsigned short *)src; + r[0] = 0xf8 & (tmp << 3); + g[0] = 0xfc & (tmp >> 3); + b[0] = 0xf8 & (tmp >> 8); + RGB2Y(r[0], g[0], b[0], *dest++); + src += 2; + } + src += src_fmt->fmt.pix.bytesperline - 2 * 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++) { + tmp = *(unsigned short *)src; + r[0] = 0xf8 & (tmp << 3); + g[0] = 0xfc & (tmp >> 3); + b[0] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + 1); + r[1] = 0xf8 & (tmp << 3); + g[1] = 0xfc & (tmp >> 3); + b[1] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline); + r[2] = 0xf8 & (tmp << 3); + g[2] = 0xfc & (tmp >> 3); + b[2] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline + 1); + r[3] = 0xf8 & (tmp << 3); + g[3] = 0xfc & (tmp >> 3); + b[3] = 0xf8 & (tmp >> 8); + + avg_src[0] = (r[0] + r[1] + r[2] + r[3]) /4; + avg_src[1] = (g[0] + g[1] + g[2] + g[3]) /4; + avg_src[2] = (b[0] + b[1] + b[2] + b[3]) /4; + RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++); + src += 4; + } + src += 2 * src_fmt->fmt.pix.bytesperline - 2 * src_fmt->fmt.pix.width; + } +} |