diff options
Diffstat (limited to 'v4l2-apps')
55 files changed, 7978 insertions, 741 deletions
diff --git a/v4l2-apps/libv4l/ChangeLog b/v4l2-apps/libv4l/ChangeLog index dd53eced9..e69a8c8f4 100644 --- a/v4l2-apps/libv4l/ChangeLog +++ b/v4l2-apps/libv4l/ChangeLog @@ -1,3 +1,160 @@ +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. +* Fix a bug in the always report widths which are a multiple of 8 code added + in 0.5.97 + +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> +* Add support for sq905c decompression by Theodore Kilgore + <kilgota@auburn.edu> +* Add hm12 support for the cx2341x MPEG encoder devices by Hans Verkuil + <hverkuil@xs4all.nl> + +libv4l-0.5.8 +------------ +* Add support for UYVY (for USB Apple iSight) patch by Julien BLACHE + <jb@jblache.org> +* Remove v4lconvert_yvyu_to_yuv420 function as its functionality is + duplicate with v4lconvert_yuyv_to_yuv420 +* Use Requires.private where appropiate in .pc files (patch by Gregor Jasny) +* Switch to using USB-id's instead of USB product string, as not all devices + set a unique product string. This fixes the upside down issues with + genius e-messenger 112 cams +* Add support for sn9c20x-i420 format patch by Vasily Khoruzhick + <anarsoul@gmail.com> + +libv4l-0.5.7 +------------ +* Fix a nasty (and stupid) bug in the special try_fmt handling for UVC cams +* Add some more verbose logging of various calls when asking libv4l to log + calls to a file, to assist in (future) debugging + +libv4l-0.5.6 +------------ +* Always do a s_fmt on uvc cams even if this changes nothing, as not doing + the s_fmt triggers a bug in the uvcvideo driver in kernel <= 2.6.28 + (with certain cams) + +libv4l-0.5.5 +------------ +* Avoid the use of try_fmt as much as possible on UVC cams, instead use the + results of the enum_framesizes ioctl. This is because: + 1) try_fmt actually causes IO with UVC cams making apps which do lot of + querrying of device capabilities slow (cheese) + 2) some buggy cams don't like getting lots of UVC video probes and crash + when they do + +libv4l-0.5.4 +------------ +* Don't report DQBUF errors when errno is EAGAIN, this fixes flooding the + screen with errors when applications use non blocking mode +* Add support for downscaling to make apps which want low resolutions + (skype, spcaview) happy when used with cams which can only do high + resolutions (by Lukáš Karas <lukas.karas@centrum.cz>). +* Add support for converting to YV12 planar (next to the already supported + YU12 / I420) +* Implement RGB/BGR24 -> YU/YV12 conversion + +libv4l-0.5.3 +------------ +* When conversion requires multiple passes don't alloc the needed temporary + buffer on the stack, as some apps (ekiga) use so much stack themselves + this causes us to run out of stack space + +libv4l-0.5.2 +------------ +* Add Philips SPC210NC to list of cams with upside down sensor, reported by + Rieker Flaik +* Work around some drivers (pwc) not properly reflecting what one gets after a + s_fmt in their try_fmt answer +* Check that s_fmt atleast gives us the width, height and pixelformat try_fmt + promised us, and if not disable conversion +* Only check width, height and pixelformat when checking if we are doing + conversion, instead of doing a memcmp, as that are the only things which + the convert code checks +* Take into account that the buffers only contain half of the lines when + field is V4L2_FIELD_ALTERNATE + +libv4l-0.5.1 +------------ +* Add support for software cropping from 352x288 -> 320x240 / 176x144 -> + 160x120, so that apps which will only work with vga resolutions like + 320x240 (Skype!) will work with cams/drivers which do not support cropping + CIF resolutions to VGA resolutions in hardware. This makes all 2.6.27 gspca + supported cams, except for the pac7302 which only does 640x480 (and skype + wants 320x240), work with skype +* The v4lconvert_convert function was becoming a bit of a mess, so split the + functionailiy into separate v4lconvert_convert_pixfmt, v4lconvert_rotate and + v4lconvert_crop functions, and make v4lconvert_convert a frontend to + these +* Do not link the wrapper libs against libpthread (patch from Gregor Jasny) + libv4l-0.5.0 ------------ * Add support for enumerating framesizes and frameintervals of emulated diff --git a/v4l2-apps/libv4l/Makefile b/v4l2-apps/libv4l/Makefile index 4c99c3167..acec5c540 100644 --- a/v4l2-apps/libv4l/Makefile +++ b/v4l2-apps/libv4l/Makefile @@ -1,16 +1,17 @@ LIB_RELEASE=0 -V4L2_LIB_VERSION=$(LIB_RELEASE).5.0 +V4L2_LIB_VERSION=$(LIB_RELEASE).5.99 -all clean install: +all install: + $(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) $@ + +clean: + 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) $@ export: clean - mkdir /tmp/libv4l-$(V4L2_LIB_VERSION) - cp -a . /tmp/libv4l-$(V4L2_LIB_VERSION)/ - cd /tmp/ && \ - tar cvf /tmp/libv4l-$(V4L2_LIB_VERSION).tar\ - libv4l-$(V4L2_LIB_VERSION) - gzip /tmp/libv4l-$(V4L2_LIB_VERSION).tar - rm -rf /tmp/libv4l-$(V4L2_LIB_VERSION) + tar --transform s/^\./libv4l-$(V4L2_LIB_VERSION)/g -zcvf \ + /tmp/libv4l-$(V4L2_LIB_VERSION).tar.gz . diff --git a/v4l2-apps/libv4l/README b/v4l2-apps/libv4l/README index 3a2059224..481d5b92e 100644 --- a/v4l2-apps/libv4l/README +++ b/v4l2-apps/libv4l/README @@ -15,14 +15,26 @@ libv4l consists of 3 different libraries: libv4lconvert ------------- -libv4lconvert offers functions to convert from any (known) pixelformat -to V4l2_PIX_FMT_BGR24 or V4l2_PIX_FMT_YUV420. - -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 . +libv4lconvert started as a library to convert from any (known) pixelformat to +V4l2_PIX_FMT_BGR24, RGB24, YUV420 or YVU420. + +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 diff --git a/v4l2-apps/libv4l/TODO b/v4l2-apps/libv4l/TODO index f3f9ff527..8f7744337 100644 --- a/v4l2-apps/libv4l/TODO +++ b/v4l2-apps/libv4l/TODO @@ -7,6 +7,9 @@ impossible for overlays) can be done, so that it will no longer be necessary to implement CGMBUF in the kernel for each driver. --check v4l2_field during conversion +-take the possibility of pitch != width into account everywhere --add conversion from bgr24 to yuv420 +-make updating of parameters happen based on time elapsed rather then + frames + +-get standardized CID for AUTOGAIN_TARGET upstream and switch to that diff --git a/v4l2-apps/libv4l/include/libv4l1.h b/v4l2-apps/libv4l/include/libv4l1.h index c878cc198..4ddf8efdb 100644 --- a/v4l2-apps/libv4l/include/libv4l1.h +++ b/v4l2-apps/libv4l/include/libv4l1.h @@ -63,7 +63,7 @@ LIBV4L_PUBLIC int v4l1_dup(int fd); 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 diff --git a/v4l2-apps/libv4l/include/libv4l2.h b/v4l2-apps/libv4l/include/libv4l2.h index b05b57cb6..7ecbb2cee 100644 --- a/v4l2-apps/libv4l/include/libv4l2.h +++ b/v4l2-apps/libv4l/include/libv4l2.h @@ -21,6 +21,7 @@ #include <stdio.h> #include <unistd.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -41,18 +42,12 @@ LIBV4L_PUBLIC extern FILE *v4l2_log_file; 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_dup(int fd); 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 @@ LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid); /* 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 diff --git a/v4l2-apps/libv4l/include/libv4lconvert.h b/v4l2-apps/libv4l/include/libv4lconvert.h index 626c43473..b274c938b 100644 --- a/v4l2-apps/libv4l/include/libv4lconvert.h +++ b/v4l2-apps/libv4l/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 @@ struct v4lconvert_data; 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_framesizes(struct v4lconvert_data *data, 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 */ diff --git a/v4l2-apps/libv4l/libv4l1/Makefile b/v4l2-apps/libv4l/libv4l1/Makefile index 27848477e..e6d306055 100644 --- a/v4l2-apps/libv4l/libv4l1/Makefile +++ b/v4l2-apps/libv4l/libv4l1/Makefile @@ -3,7 +3,7 @@ override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -LIBS = -lpthread +LIBS_libv4l1 = -lpthread V4L1_OBJS = libv4l1.o log.o V4L1COMPAT = v4l1compat.so @@ -13,9 +13,10 @@ INCLUDES = ../include/libv4l1.h 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 @@ endif all: $(TARGETS) +-include $(V4L1_OBJS:.o=.d) -$(V4L1_LIB): $(V4L1_OBJS) +$(V4L1_LIB): $(V4L1_DEPS) $(V4L1COMPAT): $(V4L1COMPAT_O) $(V4L1_LIB) @@ -46,7 +48,7 @@ libv4l1.pc: @echo 'Name: libv4l1' >> libv4l1.pc @echo 'Description: v4l1 compatibility library' >> libv4l1.pc @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4l1.pc - @echo 'Requires: libv4l2' >> libv4l1.pc + @echo 'Requires.private: libv4l2' >> libv4l1.pc @echo 'Libs: -L$${libdir} -lv4l1' >> libv4l1.pc @echo 'Libs.private: -lpthread' >> libv4l1.pc @echo 'Cflags: -I$${prefix}/include' >> libv4l1.pc @@ -69,13 +71,13 @@ endif install -m 644 libv4l1.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4l1.pc log *~ + 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) + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) ln -f -s $@.$(LIB_RELEASE) $@ %.a: diff --git a/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h b/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h index 651599255..370686b3a 100644 --- a/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h +++ b/v4l2-apps/libv4l/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 diff --git a/v4l2-apps/libv4l/libv4l1/libv4l1.c b/v4l2-apps/libv4l/libv4l1/libv4l1.c index 797c8768a..9b23926be 100644 --- a/v4l2-apps/libv4l/libv4l1/libv4l1.c +++ b/v4l2-apps/libv4l/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 @@ static int v4l1_set_format(int index, unsigned int width, /* 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 @@ static void v4l1_find_min_and_max_size(int index, struct v4l2_format *fmt2) 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 @@ static void v4l1_find_min_and_max_size(int index, struct v4l2_format *fmt2) 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 @@ int v4l1_open (const char *file, int oflag, ...) 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 @@ int v4l1_open (const char *file, int oflag, ...) /* 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 @@ int v4l1_open (const char *file, int oflag, ...) 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 v4l1_close(int fd) { 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 @@ int v4l1_close(int fd) { 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 @@ int v4l1_dup(int fd) return v4l2_dup(fd); } - int v4l1_ioctl (int fd, unsigned long int request, ...) { void *arg; @@ -458,7 +453,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) 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 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) { 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 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) 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 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) 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 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) 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 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) } 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 v4l1_read(int fd, void* buffer, size_t n) 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 @@ ssize_t v4l1_read(int fd, void* buffer, size_t n) void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset) + int64_t offset) { int index; void *result; diff --git a/v4l2-apps/libv4l/libv4l1/log.c b/v4l2-apps/libv4l/libv4l1/log.c index 9ff0cea46..fd095817f 100644 --- a/v4l2-apps/libv4l/libv4l1/log.c +++ b/v4l2-apps/libv4l/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" diff --git a/v4l2-apps/libv4l/libv4l1/v4l1compat.c b/v4l2-apps/libv4l/libv4l1/v4l1compat.c index e4293d2f9..704ec22dd 100644 --- a/v4l2-apps/libv4l/libv4l1/v4l1compat.c +++ b/v4l2-apps/libv4l/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 @@ LIBV4L_PUBLIC int open (const char *file, int oflag, ...) return fd; } +#ifdef linux LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) { int fd; @@ -81,6 +83,7 @@ LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) return fd; } +#endif LIBV4L_PUBLIC int close(int fd) { return v4l1_close(fd); @@ -114,11 +117,13 @@ LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd 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) { diff --git a/v4l2-apps/libv4l/libv4l2/Makefile b/v4l2-apps/libv4l/libv4l2/Makefile index 648d27c0c..06c010f3c 100644 --- a/v4l2-apps/libv4l/libv4l2/Makefile +++ b/v4l2-apps/libv4l/libv4l2/Makefile @@ -3,7 +3,7 @@ override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -LIBS = -lpthread +LIBS_libv4l2 = -lpthread V4L2_OBJS = libv4l2.o log.o V4L2CONVERT = v4l2convert.so @@ -13,9 +13,10 @@ INCLUDES = ../include/libv4l2.h 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 @@ endif all: $(TARGETS) -$(V4L2_LIB): $(V4L2_OBJS) +-include $(V4L2_OBJS:.o=.d) + +$(V4L2_LIB): $(V4L2_DEPS) $(V4L2CONVERT): $(V4L2CONVERT_O) $(V4L2_LIB) @@ -45,7 +48,7 @@ libv4l2.pc: @echo 'Name: libv4l2' >> libv4l2.pc @echo 'Description: v4l2 device access library' >> libv4l2.pc @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4l2.pc - @echo 'Requires: libv4lconvert' >> libv4l2.pc + @echo 'Requires.private: libv4lconvert' >> libv4l2.pc @echo 'Libs: -L$${libdir} -lv4l2' >> libv4l2.pc @echo 'Libs.private: -lpthread' >> libv4l2.pc @echo 'Cflags: -I$${prefix}/include' >> libv4l2.pc @@ -68,15 +71,14 @@ endif install -m 644 libv4l2.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4l2.pc log *~ + 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) + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) ln -f -s $@.$(LIB_RELEASE) $@ %.a: $(AR) cqs $@ $^ - diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h index 8724832e1..0bfd53e2a 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h +++ b/v4l2-apps/libv4l/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 diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index b4a10afac..b71c9b4c6 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/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> @@ -76,6 +75,8 @@ #define V4L2_BUFFERS_REQUESTED_BY_READ 0x0200 #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 @@ -98,7 +99,7 @@ static int v4l2_request_read_buffers(int index) 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; @@ -125,7 +126,7 @@ static void v4l2_unrequest_read_buffers(int index) 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); @@ -146,7 +147,7 @@ static int v4l2_map_buffers(int index) 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)); @@ -154,9 +155,9 @@ static int v4l2_map_buffers(int index) 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)); @@ -180,7 +181,7 @@ static void v4l2_unmap_buffers(int index) /* 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); @@ -194,7 +195,7 @@ static int v4l2_streamon(int index) 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)); @@ -213,7 +214,7 @@ static int v4l2_streamoff(int index) 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)); @@ -241,7 +242,7 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) 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; @@ -263,10 +264,12 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, return result; do { - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf))) { - int saved_err = errno; - V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); - errno = saved_err; + 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)); + errno = saved_err; + } return result; } @@ -361,12 +364,20 @@ static int v4l2_deactivate_read_stream(int index) 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; @@ -374,7 +385,7 @@ static int v4l2_buffers_mapped(int index) 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; @@ -410,12 +421,12 @@ int v4l2_open (const char *file, int oflag, ...) 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) @@ -423,7 +434,7 @@ int v4l2_open (const char *file, int oflag, ...) if (v4l2_fd_open(fd, 0) == -1) { int saved_err = errno; - syscall(SYS_close, fd); + SYS_CLOSE(fd); errno = saved_err; return -1; } @@ -445,7 +456,7 @@ int v4l2_fd_open(int fd, int v4l2_flags) 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; @@ -460,7 +471,7 @@ int v4l2_fd_open(int fd, int v4l2_flags) /* 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; @@ -490,10 +501,22 @@ int v4l2_fd_open(int fd, int v4l2_flags) devices[index].flags = v4l2_flags; if (cap.capabilities & V4L2_CAP_READWRITE) devices[index].flags |= V4L2_SUPPORTS_READ; + if (!strcmp((char *)cap.driver, "uvcvideo")) + devices[index].flags |= V4L2_IS_UVC; devices[index].open_count = 1; 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; @@ -539,7 +562,7 @@ int v4l2_close(int fd) 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 */ @@ -553,15 +576,15 @@ int v4l2_close(int fd) /* 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 @@ -571,7 +594,7 @@ int v4l2_close(int fd) /* 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); @@ -608,7 +631,7 @@ static int v4l2_check_buffer_change_ok(int index) /* 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; @@ -620,11 +643,41 @@ static int v4l2_check_buffer_change_ok(int index) return 0; } +static int v4l2_pix_fmt_identical(struct v4l2_format *a, struct v4l2_format *b) +{ + if (a->fmt.pix.width == b->fmt.pix.width && + a->fmt.pix.height == b->fmt.pix.height && + a->fmt.pix.pixelformat == b->fmt.pix.pixelformat && + a->fmt.pix.field == b->fmt.pix.field) + return 1; + + return 0; +} + +static void v4l2_set_src_and_dest_format(int index, + struct v4l2_format *src_fmt, struct v4l2_format *dest_fmt) +{ + /* Sigh some drivers (pwc) do not properly reflect what one really gets + after a s_fmt in their try_fmt answer. So update dest format (which we + report as result from s_fmt / g_fmt to the app) with all info from the src + format not changed by conversion */ + dest_fmt->fmt.pix.field = src_fmt->fmt.pix.field; + dest_fmt->fmt.pix.colorspace = src_fmt->fmt.pix.colorspace; + /* When we're not converting use bytesperline and imagesize from src_fmt */ + if (v4l2_pix_fmt_identical(src_fmt, dest_fmt)) { + dest_fmt->fmt.pix.bytesperline = src_fmt->fmt.pix.bytesperline; + dest_fmt->fmt.pix.sizeimage = src_fmt->fmt.pix.sizeimage; + } + + devices[index].src_fmt = *src_fmt; + devices[index].dest_fmt = *dest_fmt; +} + int v4l2_ioctl (int fd, unsigned long int request, ...) { 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); @@ -632,7 +685,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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 @@ -641,21 +694,28 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) /* 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: @@ -689,7 +749,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } 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; @@ -697,18 +757,48 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } - 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; @@ -725,6 +815,9 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) case VIDIOC_ENUM_FRAMEINTERVALS: result = v4lconvert_enum_frameintervals(devices[index].convert, arg); + if (result) + V4L2_LOG("ENUM_FRAMEINTERVALS Error: %s", + v4lconvert_get_error_message(devices[index].convert)); break; case VIDIOC_TRY_FMT: @@ -734,14 +827,29 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) case VIDIOC_S_FMT: { struct v4l2_format src_fmt, *dest_fmt = arg; + struct v4l2_pix_format req_pix_fmt; - if (!memcmp(&devices[index].dest_fmt, dest_fmt, sizeof(*dest_fmt))) { + /* Don't be lazy on uvc cams, as this triggers a bug in the uvcvideo + driver in kernel <= 2.6.28 (with certain cams) */ + if (!(devices[index].flags & V4L2_IS_UVC) && + v4l2_pix_fmt_identical(&devices[index].dest_fmt, dest_fmt)) { + *dest_fmt = devices[index].dest_fmt; result = 0; break; } + if (v4l2_log_file) { + int pixfmt = dest_fmt->fmt.pix.pixelformat; + + fprintf(v4l2_log_file, "VIDIOC_S_FMT app requesting: %c%c%c%c\n", + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24); + } + 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 { @@ -749,8 +857,12 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) &src_fmt); } - if (result) + if (result) { + saved_err = errno; + V4L2_LOG("S_FMT error trying format: %s\n", strerror(errno)); + errno = saved_err; break; + } if (src_fmt.fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat && v4l2_log_file) { @@ -765,8 +877,10 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) /* Maybe after try format has adjusted width/height etc, to whats available nothing has changed (on the cam side) ? */ - if (!memcmp(&devices[index].src_fmt, &src_fmt, sizeof(src_fmt))) { - devices[index].dest_fmt = *dest_fmt; + if (!(devices[index].flags & V4L2_IS_UVC) && + v4l2_pix_fmt_identical(&devices[index].src_fmt, &src_fmt)) { + v4l2_set_src_and_dest_format(index, &devices[index].src_fmt, + dest_fmt); result = 0; break; } @@ -774,7 +888,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if ((result = v4l2_check_buffer_change_ok(index))) break; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT, &src_fmt); + req_pix_fmt = src_fmt.fmt.pix; + 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)); @@ -783,9 +898,17 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) errno = saved_err; break; } + /* See if we've gotten what try_fmt promised us + (this check should never fail) */ + if (src_fmt.fmt.pix.width != req_pix_fmt.width || + src_fmt.fmt.pix.height != req_pix_fmt.height || + src_fmt.fmt.pix.pixelformat != req_pix_fmt.pixelformat) { + V4L2_LOG_ERR("set_fmt gave us a different result then try_fmt!\n"); + /* Not what we expected / wanted, disable conversion */ + *dest_fmt = src_fmt; + } - devices[index].src_fmt = src_fmt; - devices[index].dest_fmt = *dest_fmt; + v4l2_set_src_and_dest_format(index, &src_fmt, dest_fmt); } break; @@ -816,7 +939,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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 */ @@ -836,8 +959,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) /* 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; @@ -855,11 +978,11 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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: @@ -870,8 +993,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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)); @@ -884,7 +1007,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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, + devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL, (size_t)( devices[index].no_frames * V4L2_FRAME_BUF_SIZE), @@ -929,7 +1052,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; default: - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); } if (stream_needs_locking) @@ -950,16 +1073,15 @@ ssize_t v4l2_read (int fd, void* dest, size_t n) 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; } @@ -988,7 +1110,7 @@ leave: } 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; @@ -1008,8 +1130,7 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, 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); @@ -1017,15 +1138,14 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, 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), @@ -1098,7 +1218,7 @@ int v4l2_munmap(void *_start, size_t length) 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 */ @@ -1108,7 +1228,7 @@ int v4l2_set_control(int fd, int cid, int value) 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) && @@ -1119,7 +1239,7 @@ int v4l2_set_control(int fd, int cid, int value) 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; @@ -1130,13 +1250,13 @@ int v4l2_get_control(int fd, int cid) 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 + diff --git a/v4l2-apps/libv4l/libv4l2/log.c b/v4l2-apps/libv4l/libv4l2/log.c index 6237d55ec..86d71b64e 100644 --- a/v4l2-apps/libv4l/libv4l2/log.c +++ b/v4l2-apps/libv4l/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 @@ -18,12 +20,9 @@ #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 <string.h> +#include <errno.h> +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev2.h> #include "libv4l2.h" #include "libv4l2-priv.h" @@ -89,12 +88,17 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS", [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS", [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS", +#ifdef VIDIOC_ENUM_FRAMESIZES + [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", + [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", +#endif }; void v4l2_log_ioctl(unsigned long int request, void *arg, int result) { const char *ioctl_str; char buf[40]; + int saved_errno = errno; if (!v4l2_log_file) return; @@ -102,7 +106,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) if (_IOC_TYPE(request) == 'V' && _IOC_NR(request) < ARRAY_SIZE(v4l2_ioctls)) ioctl_str = v4l2_ioctls[_IOC_NR(request)]; else { - snprintf(buf, sizeof(buf), "unknown request: %c %d\n", + snprintf(buf, sizeof(buf), "unknown request: %c %d", (int)_IOC_TYPE(request), (int)_IOC_NR(request)); ioctl_str = buf; } @@ -110,11 +114,18 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) fprintf(v4l2_log_file, "request == %s\n", ioctl_str); switch (request) { + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *fmt = arg; + fprintf(v4l2_log_file, " index: %u, description: %s\n", + fmt->index, (result < 0) ? "" : (const char *)fmt->description); + } + break; case VIDIOC_G_FMT: case VIDIOC_S_FMT: case VIDIOC_TRY_FMT: { - struct v4l2_format* fmt = arg; + struct v4l2_format *fmt = arg; int pixfmt = fmt->fmt.pix.pixelformat; if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -141,8 +152,68 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) req->count, (int)req->type, (int)req->memory); } break; +#ifdef VIDIOC_ENUM_FRAMESIZES + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *frmsize = arg; + int pixfmt = frmsize->pixel_format; + + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c\n", + frmsize->index, + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24); + switch (frmsize->type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + 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", + frmsize->stepwise.min_width, frmsize->stepwise.min_height, + frmsize->stepwise.max_width, frmsize->stepwise.max_height); + break; + } + } + break; + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *frmival = arg; + int pixfmt = frmival->pixel_format; + + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c %ux%u:\n", + frmival->index, + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24, + frmival->width, + frmival->height); + switch (frmival->type) { + case V4L2_FRMIVAL_TYPE_DISCRETE: + 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", + frmival->stepwise.min.numerator, + frmival->stepwise.min.denominator, + frmival->stepwise.max.numerator, + frmival->stepwise.max.denominator); + break; + } + } + break; +#endif } - fprintf(v4l2_log_file, "result == %d\n", result); + if (result < 0) + fprintf(v4l2_log_file, "result == %d (%s)\n", result, strerror(saved_errno)); + else + fprintf(v4l2_log_file, "result == %d\n", result); + fflush(v4l2_log_file); } diff --git a/v4l2-apps/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/libv4l/libv4l2/v4l2convert.c index 307a03ce5..4b75a5ebf 100644 --- a/v4l2-apps/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/libv4l/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 @@ LIBV4L_PUBLIC int open (const char *file, int oflag, ...) 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 @@ LIBV4L_PUBLIC int open (const char *file, int oflag, ...) /* 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 @@ LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) return fd; } +#endif LIBV4L_PUBLIC int close(int fd) { @@ -153,11 +149,13 @@ LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd 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) { diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile index 641d19d6e..036649e86 100644 --- a/v4l2-apps/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/libv4l/libv4lconvert/Makefile @@ -3,6 +3,8 @@ override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden 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 @@ -10,9 +12,13 @@ CONVERT_LIB = libv4lconvert.so override CPPFLAGS += -fPIC endif -CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o flip.o \ - jidctflt.o spca561-decompress.o rgbyuv.o spca501.o bayer.o -TARGETS = $(CONVERT_LIB) libv4lconvert.pc +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 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),) @@ -27,8 +33,12 @@ ifeq ($(LIBDIR),) LIBDIR = $(PREFIX)/lib endif +override CPPFLAGS += -DLIBDIR=\"$(LIBDIR)\" + all: $(TARGETS) +-include $(CONVERT_OBJS:.o=.d) + $(CONVERT_LIB): $(CONVERT_OBJS) libv4lconvert.pc: @@ -39,32 +49,37 @@ libv4lconvert.pc: @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 *~ + 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 diff --git a/v4l2-apps/libv4l/libv4lconvert/bayer.c b/v4l2-apps/libv4l/libv4lconvert/bayer.c index ca7bb486f..033ee2724 100644 --- a/v4l2-apps/libv4l/libv4lconvert/bayer.c +++ b/v4l2-apps/libv4l/libv4lconvert/bayer.c @@ -433,16 +433,23 @@ static void v4lconvert_border_bayer_line_to_y( } } -void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, - unsigned char *yuv, int width, int height, unsigned int pixfmt) +void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv, + int width, int height, unsigned int src_pixfmt, int yvu) { int blue_line = 0, start_with_green = 0, x, y; unsigned char *ydst = yuv; - unsigned char *udst = yuv + width * height; - unsigned char *vdst = udst + width * height / 4; + unsigned char *udst, *vdst; + + if (yvu) { + vdst = yuv + width * height; + udst = vdst + width * height / 4; + } else { + udst = yuv + width * height; + vdst = udst + width * height / 4; + } /* First calculate the u and v planes 2x2 pixels at a time */ - switch (pixfmt) { + switch (src_pixfmt) { case V4L2_PIX_FMT_SBGGR8: for (y = 0; y < height; y += 2) { for (x = 0; x < width; x += 2) { diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h new file mode 100644 index 000000000..6211ee22a --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h @@ -0,0 +1,54 @@ +/* +# (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; +}; + +#endif diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c new file mode 100644 index 000000000..4e600351b --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c @@ -0,0 +1,504 @@ +/* +# (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 }, + /* Asus N50Vn laptop */ + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vn ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, +/* 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]= ""; + 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); + } + + 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))) { + 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; +} diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h new file mode 100644 index 000000000..e29fde3d4 --- /dev/null +++ b/v4l2-apps/libv4l/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 diff --git a/v4l2-apps/libv4l/libv4lconvert/crop.c b/v4l2-apps/libv4l/libv4lconvert/crop.c new file mode 100644 index 000000000..46c87f0ce --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/crop.c @@ -0,0 +1,288 @@ +/* + +# RGB and YUV crop routines + +# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include <string.h> +#include "libv4lconvert-priv.h" + + +static void v4lconvert_reduceandcrop_rgbbgr24( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int x, y; + int startx = src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width; + int starty = src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height; + + src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx; + + for (y = 0; y < dest_fmt->fmt.pix.height; y++) { + unsigned char *mysrc = src; + for (x = 0; x < dest_fmt->fmt.pix.width; x++) { + *(dest++) = *(mysrc++); + *(dest++) = *(mysrc++); + *(dest++) = *(mysrc++); + mysrc += 3; /* skip one pixel */ + } + src += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */ + } +} + +static void v4lconvert_crop_rgbbgr24(unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int x; + int startx = (src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2; + int starty = (src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2; + + src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx; + + for (x = 0; x < dest_fmt->fmt.pix.height; x++) { + memcpy(dest, src, dest_fmt->fmt.pix.width * 3); + src += src_fmt->fmt.pix.bytesperline; + dest += dest_fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_reduceandcrop_yuv420( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int x,y; + int dest_height_half = dest_fmt->fmt.pix.height / 2; + int dest_width_half = dest_fmt->fmt.pix.width / 2; + int startx = (src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width) & ~1; + int starty = (src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height) & ~1; + unsigned char *mysrc, *mysrc2; + + /* Y */ + mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx; + for (y = 0; y < dest_fmt->fmt.pix.height; y++){ + mysrc2 = mysrc; + for (x = 0; x < dest_fmt->fmt.pix.width; x++){ + *(dest++) = *mysrc2; + mysrc2 += 2; /* skip one pixel */ + } + mysrc += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */ + } + + /* U */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (y = 0; y < dest_height_half; y++){ + mysrc2 = mysrc; + for (x = 0; x < dest_width_half; x++){ + *(dest++) = *mysrc2; + mysrc2 += 2; /* skip one pixel */ + } + mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */ + } + + /* V */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4 + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (y = 0; y < dest_height_half; y++){ + mysrc2 = mysrc; + for (x = 0; x < dest_width_half; x++){ + *(dest++) = *mysrc2; + mysrc2 += 2; /* skip one pixel */ + } + mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */ + } +} + +static void v4lconvert_crop_yuv420(unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int x; + int startx = ((src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2) & ~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 */ + for (x = 0; x < dest_fmt->fmt.pix.height; x++) { + memcpy(dest, mysrc, dest_fmt->fmt.pix.width); + mysrc += src_fmt->fmt.pix.bytesperline; + dest += dest_fmt->fmt.pix.bytesperline; + } + + /* U */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) { + memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2); + mysrc += src_fmt->fmt.pix.bytesperline / 2; + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + /* V */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4 + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) { + memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2); + mysrc += src_fmt->fmt.pix.bytesperline / 2; + dest += dest_fmt->fmt.pix.bytesperline / 2; + } +} + +/* 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 <= 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 <= 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; + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/flip.c b/v4l2-apps/libv4l/libv4lconvert/flip.c index cd3468a89..8c4d8233c 100644 --- a/v4l2-apps/libv4l/libv4lconvert/flip.c +++ b/v4l2-apps/libv4l/libv4lconvert/flip.c @@ -20,10 +20,102 @@ */ +#include <string.h> #include "libv4lconvert-priv.h" -void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, - int width, int height) +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) { int i; @@ -38,8 +130,8 @@ void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, } } -void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height) +static void v4lconvert_rotate180_yuv420(const unsigned char *src, + unsigned char *dst, int width, int height) { int i; @@ -59,8 +151,8 @@ void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, *dst++ = *src--; } -void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, - int destwidth, int destheight) +static void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, + unsigned char *dst, int destwidth, int destheight) { int x, y; #define srcwidth destheight @@ -75,8 +167,8 @@ void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, } } -void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, - int destwidth, int destheight) +static void v4lconvert_rotate90_yuv420(const unsigned char *src, + unsigned char *dst, int destwidth, int destheight) { int x, y; @@ -105,3 +197,70 @@ void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, *dst++ = src[offset]; } } + +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) +{ + 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_hflip_rgbbgr24(src, dest, fmt); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_hflip_yuv420(src, dest, fmt); + break; + } + } else if (vflip) { + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_vflip_rgbbgr24(src, dest, fmt); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_vflip_yuv420(src, dest, fmt); + break; + } + } + + /* Our newly written data has no padding */ + v4lconvert_fixup_fmt(fmt); +} diff --git a/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h b/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h new file mode 100644 index 000000000..8ce12343c --- /dev/null +++ b/v4l2-apps/libv4l/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; +} diff --git a/v4l2-apps/libv4l/libv4lconvert/helper.c b/v4l2-apps/libv4l/libv4lconvert/helper.c new file mode 100644 index 000000000..c1d55c2e5 --- /dev/null +++ b/v4l2-apps/libv4l/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; + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/hm12.c b/v4l2-apps/libv4l/libv4lconvert/hm12.c new file mode 100644 index 000000000..f711627b4 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/hm12.c @@ -0,0 +1,159 @@ +/* + +cx2341x HM12 conversion routines + +(C) 2009 Hans Verkuil <hverkuil@xs4all.nl> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +#include "libv4lconvert-priv.h" +#include <string.h> + +/* The HM12 format is used in the Conexant cx23415/6/8 MPEG encoder devices. + It is a macroblock format with separate Y and UV planes, each plane + consisting of 16x16 values. All lines are always 720 bytes long. If the + width of the image is less than 720, then the remainder is padding. + + The height has to be a multiple of 32 in order to get correct chroma + values. + + It is basically a by-product of the MPEG encoding inside the device, + which is available for raw video as a 'bonus feature'. + */ + +#define CLIP(color) \ + (unsigned char)(((color) > 0xff) ? 0xff : (((color) < 0) ? 0 : (color))) + +static const int stride = 720; + +static void v4lconvert_hm12_to_rgb(const unsigned char *src, unsigned char *dest, + int width, int height, int rgb) +{ + unsigned int y, x, i, j; + const unsigned char *y_base = src; + const unsigned char *uv_base = src + stride * height; + const unsigned char *src_y; + const unsigned char *src_uv; + int mb_size = 256; + int r = rgb ? 0 : 2; + int b = 2 - r; + + for (y = 0; y < height; y += 16) { + int mb_y = (y / 16) * (stride / 16); + int mb_uv = (y / 32) * (stride / 16); + int maxy = (height - y < 16 ? height - y : 16); + + for (x = 0; x < width; x += 16, mb_y++, mb_uv++) { + int maxx = (width - x < 16 ? width - x : 16); + + src_y = y_base + mb_y * mb_size; + src_uv = uv_base + mb_uv * mb_size; + + if (y & 0x10) + src_uv += mb_size / 2; + + for (i = 0; i < maxy; i++) { + int idx = (x + (y + i) * width) * 3; + + for (j = 0; j < maxx; j++) { + int y = src_y[j]; + int u = src_uv[j & ~1]; + int v = src_uv[j | 1]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + dest[idx+r] = CLIP(y + v1); + dest[idx+1] = CLIP(y - rg); + dest[idx+b] = CLIP(y + u1); + idx += 3; + } + src_y += 16; + if (i & 1) + src_uv += 16; + } + } + } +} + +void v4lconvert_hm12_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + v4lconvert_hm12_to_rgb(src, dest, width, height, 1); +} + +void v4lconvert_hm12_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + v4lconvert_hm12_to_rgb(src, dest, width, height, 0); +} + +static void de_macro_uv(unsigned char *dstu, unsigned char *dstv, + const unsigned char *src, int w, int h) +{ + unsigned int y, x, i, j; + + for (y = 0; y < h; y += 16) { + for (x = 0; x < w; x += 8) { + const unsigned char *src_uv = src + y * stride + x * 32; + int maxy = (h - y < 16 ? h - y : 16); + int maxx = (w - x < 8 ? w - x : 8); + + for (i = 0; i < maxy; i++) { + int idx = x + (y + i) * w; + + for (j = 0; j < maxx; j++) { + dstu[idx+j] = src_uv[2 * j]; + dstv[idx+j] = src_uv[2 * j + 1]; + } + src_uv += 16; + } + } + } +} + +static void de_macro_y(unsigned char *dst, const unsigned char *src, + int w, int h) +{ + unsigned int y, x, i; + + for (y = 0; y < h; y += 16) { + for (x = 0; x < w; x += 16) { + const unsigned char *src_y = src + y * stride + x * 16; + int maxy = (h - y < 16 ? h - y : 16); + int maxx = (w - x < 16 ? w - x : 16); + + for (i = 0; i < maxy; i++) { + memcpy(dst + x + (y + i) * w, src_y, maxx); + src_y += 16; + } + } + } +} + +void v4lconvert_hm12_to_yuv420(const unsigned char *src, unsigned char *dest, + int width, int height, int yvu) +{ + de_macro_y(dest, src, width, height); + dest += width * height; + src += stride * height; + if (yvu) + de_macro_uv(dest + width * height / 4, dest, src, width / 2, height / 2); + else + de_macro_uv(dest, dest + width * height / 4, src, width / 2, height / 2); +} diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h index 0c4eff6ce..2cf8f6ba4 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/libv4l/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 @@ -43,6 +48,14 @@ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') #endif +#ifndef V4L2_PIX_FMT_MR97310A +#define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M','3','1','0') +#endif + +#ifndef V4L2_PIX_FMT_SQ905C +#define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') +#endif + #ifndef V4L2_PIX_FMT_PJPG #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') #endif @@ -63,6 +76,24 @@ #define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') #endif +#ifndef V4L2_PIX_FMT_HM12 +#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') +#endif + +#ifndef V4L2_PIX_FMT_SN9C20X_I420 +#define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') +#endif + +#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 @@ -71,25 +102,40 @@ "v4l-convert: error " __VA_ARGS__) /* Card flags */ -#define V4LCONVERT_UPSIDE_DOWN 0x01 +#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; -}; - -struct v4lconvert_flags_info { - const char *card; - int flags; + int convert1_buf_size; + int convert2_buf_size; + int rotate90_buf_size; + int flip_buf_size; + int convert_pixfmt_buf_size; + 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; + + /* 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 { @@ -97,11 +143,16 @@ struct v4lconvert_pixfmt { 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); + void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst, - int width, int height); + int width, int height, int yvu); void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dst, - int width, int height); + int width, int height, int yvu); void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dst, int width, int height); @@ -110,7 +161,7 @@ void v4lconvert_yuyv_to_bgr24(const unsigned char *src, unsigned char *dst, int width, int height); void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height); + int width, int height, int yvu); void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst, int width, int height); @@ -118,20 +169,32 @@ void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst, void v4lconvert_yvyu_to_bgr24(const unsigned char *src, unsigned char *dst, int width, int height); -void v4lconvert_yvyu_to_yuv420(const unsigned char *src, unsigned char *dst, +void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dst, int width, int height); +void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); + void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst, int width, int height); +void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dst, + const struct v4l2_format *src_fmt); + void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height); + int width, int height, int yvu); void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height); + int width, int height, int yvu); void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height); + int width, int height, int yvu); + +void v4lconvert_sn9c20x_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); void v4lconvert_decode_spca561(const unsigned char *src, unsigned char *dst, int width, int height); @@ -142,25 +205,43 @@ void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst, void v4lconvert_decode_pac207(const unsigned char *src, unsigned char *dst, int width, int height); +void v4lconvert_decode_mr97310a(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst, + int width, int height); + void v4lconvert_bayer_to_rgb24(const unsigned char *bayer, unsigned char *rgb, int width, int height, unsigned int pixfmt); void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, unsigned char *rgb, int width, int height, unsigned int pixfmt); -void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, - unsigned char *yuv, int width, int height, unsigned int pixfmt); +void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv, + int width, int height, unsigned int src_pixfmt, int yvu); -void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, - int destwidth, int destheight); +void v4lconvert_hm12_to_rgb24(const unsigned char *src, + unsigned char *dst, int width, int height); -void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, - int destwidth, int destheight); +void v4lconvert_hm12_to_bgr24(const unsigned char *src, + unsigned char *dst, int width, int height); -void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, - int width, int height); +void v4lconvert_hm12_to_yuv420(const unsigned char *src, + unsigned char *dst, int width, int height, int yvu); -void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height); +void v4lconvert_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 diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c index 93bc67c7e..1389c58bc 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c @@ -19,56 +19,69 @@ #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 */ #define SUPPORTED_DST_PIXFMTS \ { V4L2_PIX_FMT_RGB24, 0 }, \ { V4L2_PIX_FMT_BGR24, 0 }, \ - { V4L2_PIX_FMT_YUV420, 0 } + { V4L2_PIX_FMT_YUV420, 0 }, \ + { V4L2_PIX_FMT_YVU420, 0 } static void v4lconvert_get_framesizes(struct v4lconvert_data *data, - unsigned int pixelformat); + unsigned int pixelformat, int index); /* Note uncompressed formats must go first so that they are prefered by v4lconvert_try_format for low resolutions */ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = { SUPPORTED_DST_PIXFMTS, - { V4L2_PIX_FMT_YUYV, 0 }, - { V4L2_PIX_FMT_YVYU, 0 }, - { V4L2_PIX_FMT_SBGGR8, 0 }, - { V4L2_PIX_FMT_SGBRG8, 0 }, - { V4L2_PIX_FMT_SGRBG8, 0 }, - { V4L2_PIX_FMT_SRGGB8, 0 }, - { V4L2_PIX_FMT_SPCA501, 0 }, - { V4L2_PIX_FMT_SPCA505, 0 }, - { V4L2_PIX_FMT_SPCA508, 0 }, - { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED }, - { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED }, - { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED }, - { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED }, - { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED }, - { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_YUYV, 0 }, + { V4L2_PIX_FMT_YVYU, 0 }, + { V4L2_PIX_FMT_UYVY, 0 }, + { V4L2_PIX_FMT_SN9C20X_I420, 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 }, + { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_MR97310A, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_SQ905C, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED }, + { 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[] = { - { "SPC 200NC ", V4LCONVERT_UPSIDE_DOWN }, - { "SPC 300NC ", V4LCONVERT_UPSIDE_DOWN }, - { "USB Camera (0471:0325)", V4LCONVERT_UPSIDE_DOWN }, /* SPC200NC */ - { "USB Camera (0471:0326)", V4LCONVERT_UPSIDE_DOWN }, /* SPC300NC */ - { "USB Camera (093a:2476)", V4LCONVERT_UPSIDE_DOWN }, /* Genius E-M 112 */ +/* List of well known resolutions which we can get by cropping somewhat larger + resolutions */ +static const int v4lconvert_crop_res[][2] = { + /* low res VGA resolutions, can be made by software cropping SIF resolutions + for cam/drivers which do not support this in hardware */ + { 320, 240 }, + { 160, 120 }, + /* Some CIF cams (with vv6410 sensor) have slightly larger then usual CIF + resolutions, make regular CIF resolutions available on these by sw crop */ + { 352, 288 }, + { 176, 144 }, }; struct v4lconvert_data *v4lconvert_create(int fd) @@ -76,12 +89,16 @@ 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->jdec = NULL; + data->decompress_pid = -1; /* Check supported formats */ for (i = 0; ; i++) { @@ -89,27 +106,47 @@ struct v4lconvert_data *v4lconvert_create(int fd) 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; } - v4lconvert_get_framesizes(data, fmt.pixelformat); + if (j == ARRAY_SIZE(supported_src_pixfmts)) + always_needs_conversion = 0; } data->no_formats = i; /* Check if this cam has any special flags */ - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) { - for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++) - if (!strcmp((const char *)v4lconvert_flags[i].card, (char *)cap.card)) { - data->flags = v4lconvert_flags[i].flags; - break; - } + 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; @@ -117,15 +154,23 @@ struct v4lconvert_data *v4lconvert_create(int fd) 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); } + 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; @@ -136,6 +181,12 @@ static int v4lconvert_supported_dst_format(unsigned int pixelformat) 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) { @@ -143,17 +194,22 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt) 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; @@ -171,8 +227,65 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt) return 0; } -/* See libv4lconvert.h for description of in / out parameters */ -int v4lconvert_try_format(struct v4lconvert_data *data, +/* Find out what format to use based on the (cached) results of enum + framesizes instead of doing a zillion try_fmt calls. This function + currently is intended for use with UVC cams only. This is esp. + important for UVC based cams as doing try_fmt there actually causes I/O */ +static int v4lconvert_do_try_format_uvc(struct v4lconvert_data *data, + struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt) +{ + int i; + unsigned int closest_fmt_size_diff = -1; + int best_framesize = 0;/* Just use the first format if no small enough one */ + int best_format = 0; + + for (i = 0; i < data->no_framesizes; i++) { + if (data->framesizes[i].discrete.width <= dest_fmt->fmt.pix.width && + data->framesizes[i].discrete.height <= dest_fmt->fmt.pix.height) { + int size_x_diff = dest_fmt->fmt.pix.width - + data->framesizes[i].discrete.width; + int size_y_diff = dest_fmt->fmt.pix.height - + data->framesizes[i].discrete.height; + unsigned int size_diff = size_x_diff * size_x_diff + + size_y_diff * size_y_diff; + + if (size_diff < closest_fmt_size_diff) { + closest_fmt_size_diff = size_diff; + best_framesize = i; + } + } + } + + for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) { + /* is this format supported? */ + if (!(data->framesizes[best_framesize].pixel_format & (1 << i))) + continue; + + if (!best_format || + supported_src_pixfmts[i].fmt == dest_fmt->fmt.pix.pixelformat || + ((data->framesizes[best_framesize].discrete.width > 180 || + data->framesizes[best_framesize].discrete.height > 148) && + (supported_src_pixfmts[i].flags & V4LCONVERT_COMPRESSED))) + best_format = supported_src_pixfmts[i].fmt; + } + + dest_fmt->fmt.pix.width = data->framesizes[best_framesize].discrete.width; + dest_fmt->fmt.pix.height = data->framesizes[best_framesize].discrete.height; + dest_fmt->fmt.pix.field = V4L2_FIELD_NONE; /* UVC has no fields */ + /* Not pretty, the pwc driver doesn't fill these in try_fmt either though, + so we should be able to get away with this. */ + dest_fmt->fmt.pix.bytesperline = 0; + dest_fmt->fmt.pix.sizeimage = 0; + dest_fmt->fmt.pix.colorspace = 0; + dest_fmt->fmt.pix.priv = 0; + + *src_fmt = *dest_fmt; + src_fmt->fmt.pix.pixelformat = best_format; + + return 0; +} + +static int v4lconvert_do_try_format(struct v4lconvert_data *data, struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt) { int i; @@ -180,14 +293,8 @@ int v4lconvert_try_format(struct v4lconvert_data *data, unsigned int desired_pixfmt = dest_fmt->fmt.pix.pixelformat; struct v4l2_format try_fmt, closest_fmt = { .type = 0 }; - /* Can we do conversion to the requested format & type? */ - if (!v4lconvert_supported_dst_format(desired_pixfmt) || - dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - int ret = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt); - if (src_fmt) - *src_fmt = *dest_fmt; - return ret; - } + if (data->flags & V4LCONVERT_IS_UVC) + return v4lconvert_do_try_format_uvc(data, dest_fmt, src_fmt); for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) { /* is this format supported? */ @@ -197,7 +304,7 @@ int v4lconvert_try_format(struct v4lconvert_data *data, 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 - @@ -209,7 +316,7 @@ int v4lconvert_try_format(struct v4lconvert_data *data, if (size_diff < closest_fmt_size_diff || (size_diff == closest_fmt_size_diff && (supported_src_pixfmts[i].fmt == desired_pixfmt || - ((try_fmt.fmt.pix.width > 176 || try_fmt.fmt.pix.height > 144) && + ((try_fmt.fmt.pix.width > 180 || try_fmt.fmt.pix.height > 148) && (supported_src_pixfmts[i].flags & V4LCONVERT_COMPRESSED))))) { closest_fmt_size_diff = size_diff; closest_fmt = try_fmt; @@ -218,35 +325,142 @@ int v4lconvert_try_format(struct v4lconvert_data *data, } } - if (closest_fmt.type == 0) { - int ret = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt); + if (closest_fmt.type == 0) + return -1; + + *dest_fmt = closest_fmt; + if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt) + dest_fmt->fmt.pix.pixelformat = desired_pixfmt; + *src_fmt = closest_fmt; + + return 0; +} + +void v4lconvert_fixup_fmt(struct v4l2_format *fmt) +{ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * 3; + fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + fmt->fmt.pix.bytesperline = fmt->fmt.pix.width; + fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3 / 2; + break; + } +} + +/* See libv4lconvert.h for description of in / out parameters */ +int v4lconvert_try_format(struct v4lconvert_data *data, + struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt) +{ + int i, result; + unsigned int desired_width = dest_fmt->fmt.pix.width; + unsigned int desired_height = dest_fmt->fmt.pix.height; + struct v4l2_format try_src, try_dest, 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 = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt); if (src_fmt) *src_fmt = *dest_fmt; - return ret; + return result; } - *dest_fmt = closest_fmt; + /* 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; + } + } - /* Are we converting? */ - if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt) { - dest_fmt->fmt.pix.pixelformat = desired_pixfmt; - switch (dest_fmt->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - dest_fmt->fmt.pix.bytesperline = dest_fmt->fmt.pix.width * 3; - dest_fmt->fmt.pix.sizeimage = dest_fmt->fmt.pix.width * - dest_fmt->fmt.pix.height * 3; - break; - case V4L2_PIX_FMT_YUV420: - dest_fmt->fmt.pix.bytesperline = dest_fmt->fmt.pix.width; - dest_fmt->fmt.pix.sizeimage = (dest_fmt->fmt.pix.width * - dest_fmt->fmt.pix.height * 3) / 2; + /* In case of a non exact resolution match, see if this is a well known + resolution some apps are hardcoded too and try to give the app what it + asked for by cropping a slightly larger resolution 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) { + try2_dest = *dest_fmt; + + /* Note these are chosen so that cropping to vga res just works for + vv6410 sensor cams, which have 356x292 and 180x148 */ + try2_dest.fmt.pix.width = desired_width * 113 / 100; + try2_dest.fmt.pix.height = desired_height * 124 / 100; + result = v4lconvert_do_try_format(data, &try2_dest, &try2_src); + if (result == 0 && + (/* 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 * 2 && + try2_dest.fmt.pix.height <= desired_height * 5 / 2))) { + /* Success! */ + try2_dest.fmt.pix.width = desired_width; + try2_dest.fmt.pix.height = desired_height; + try_dest = try2_dest; + try_src = try2_src; + } break; + } } } + /* 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) + v4lconvert_fixup_fmt(&try_dest); + + *dest_fmt = try_dest; if (src_fmt) - *src_fmt = closest_fmt; + *src_fmt = try_src; return 0; } @@ -256,63 +470,71 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt) /* in */ { - if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) - return 1; /* Formats differ */ - - if (!(data->flags & V4LCONVERT_UPSIDE_DOWN)) - return 0; /* Formats identical and we don't need flip */ + 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; - /* 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 */ + return 0; } -int v4lconvert_convert(struct v4lconvert_data *data, - const struct v4l2_format *src_fmt, /* in */ - const struct v4l2_format *dest_fmt, /* in */ - unsigned char *src, int src_size, unsigned char *_dest, int dest_size) +static int v4lconvert_processing_needs_double_conversion( + unsigned int src_pix_fmt, unsigned int dest_pix_fmt) { - unsigned int header_width, header_height; - int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE; - unsigned char *components[3]; - unsigned char *dest = _dest; - - /* Special case when no conversion is needed */ - if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) { - int to_copy = MIN(dest_size, src_size); - memcpy(dest, src, to_copy); - return to_copy; + 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; } - - /* sanity check, is the dest buffer large enough? */ - switch (dest_fmt->fmt.pix.pixelformat) { + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: - needed = dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3; - break; - case V4L2_PIX_FMT_YUV420: - needed = (dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3) / 2; - break; - default: - V4LCONVERT_ERR("Unknown dest format in conversion\n"); - errno = EINVAL; - return -1; + return 0; } - if (dest_size < needed) { - V4LCONVERT_ERR("destination buffer too small\n"); - errno = EFAULT; - return -1; - } + return 1; +} - if (data->flags & V4LCONVERT_UPSIDE_DOWN) { - rotate = 180; - dest = alloca(needed); +static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data, + int needed, unsigned char **buf, int *buf_size) +{ + if (*buf_size < needed) { + free(*buf); + *buf = malloc(needed); + if (*buf == NULL) { + *buf_size = 0; + V4LCONVERT_ERR("could not allocate memory\n"); + errno = ENOMEM; + return NULL; + } + *buf_size = needed; } + return *buf; +} - switch (src_fmt->fmt.pix.pixelformat) { +static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, + 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 = 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: jpeg_flags |= TINYJPEG_FLAGS_PIXART_JPEG; /* Fall through */ @@ -335,31 +557,36 @@ int v4lconvert_convert(struct v4lconvert_data *data, } tinyjpeg_get_size(data->jdec, &header_width, &header_height); - if (header_width != dest_fmt->fmt.pix.width || - header_height != dest_fmt->fmt.pix.height) { + if (header_width != width || header_height != height) { /* Check for (pixart) rotated JPEG */ - if (header_width == dest_fmt->fmt.pix.height || - header_height == dest_fmt->fmt.pix.width) { - if (!rotate) - dest = alloca(needed); - rotate += 90; + if (header_width == height && header_height == width) { + if (!(data->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", - dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, - header_width, header_height); + "expected: %ux%u, header: %ux%u\n", width, height, + header_width, header_height); errno = EIO; return -1; } + } 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; - components[1] = components[0] + dest_fmt->fmt.pix.width * - dest_fmt->fmt.pix.height; - components[2] = components[1] + (dest_fmt->fmt.pix.width * - dest_fmt->fmt.pix.height) / 4; - switch (dest_fmt->fmt.pix.pixelformat) { + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: tinyjpeg_set_components(data->jdec, components, 1); result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_RGB24); @@ -368,7 +595,15 @@ int v4lconvert_convert(struct v4lconvert_data *data, tinyjpeg_set_components(data->jdec, components, 1); result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_BGR24); break; - default: + case V4L2_PIX_FMT_YUV420: + components[1] = components[0] + width * height; + components[2] = components[1] + width * height / 4; + tinyjpeg_set_components(data->jdec, components, 3); + result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P); + break; + case V4L2_PIX_FMT_YVU420: + components[2] = components[0] + width * height; + components[1] = components[2] + width * height / 4; tinyjpeg_set_components(data->jdec, components, 3); result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P); break; @@ -379,8 +614,9 @@ int v4lconvert_convert(struct v4lconvert_data *data, are best thrown away to avoid flashes in the video stream. Tell the upper layer this is an intermediate fault and it should try again with a new buffer by setting errno to EAGAIN */ - if (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_PJPG) { - V4LCONVERT_ERR("Error decompressing JPEG: %s", + if (src_pix_fmt == V4L2_PIX_FMT_PJPG || + data->flags & V4LCONVERT_IS_SN9C20X) { + V4LCONVERT_ERR("decompressing JPEG: %s", tinyjpeg_get_errorstring(data->jdec)); errno = EAGAIN; return -1; @@ -394,223 +630,451 @@ int v4lconvert_convert(struct v4lconvert_data *data, } break; - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - switch (dest_fmt->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_RGB24: - v4lconvert_bayer_to_rgb24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat); - break; - case V4L2_PIX_FMT_BGR24: - v4lconvert_bayer_to_bgr24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat); - break; - default: - v4lconvert_bayer_to_yuv420(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat); - break; - } - break; - - /* YUYV line by line formats */ + /* Custom cam specific YUV formats */ case V4L2_PIX_FMT_SPCA501: case V4L2_PIX_FMT_SPCA505: case V4L2_PIX_FMT_SPCA508: + case V4L2_PIX_FMT_SN9C20X_I420: + case V4L2_PIX_FMT_OV511: + case V4L2_PIX_FMT_OV518: { - unsigned char tmpbuf[dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * - 3 / 2]; - unsigned char *my_dst = (dest_fmt->fmt.pix.pixelformat != - V4L2_PIX_FMT_YUV420) ? tmpbuf : dest; + unsigned char *d; + int d_size; + int yvu = 0; + + if (dest_pix_fmt != V4L2_PIX_FMT_YUV420 && + dest_pix_fmt != V4L2_PIX_FMT_YVU420) { + d = v4lconvert_alloc_buffer(data, width * height * 3 / 2, + &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); + if (!d) + return -1; + d_size = width * height * 3 / 2; + } else { + d = dest; + d_size = dest_size; + } + + if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) + yvu = 1; - switch (src_fmt->fmt.pix.pixelformat) { + switch (src_pix_fmt) { case V4L2_PIX_FMT_SPCA501: - v4lconvert_spca501_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_spca501_to_yuv420(src, d, width, height, yvu); break; case V4L2_PIX_FMT_SPCA505: - v4lconvert_spca505_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_spca505_to_yuv420(src, d, width, height, yvu); break; case V4L2_PIX_FMT_SPCA508: - v4lconvert_spca508_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_spca508_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_SN9C20X_I420: + v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu); + break; + 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_fmt->fmt.pix.pixelformat) { + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: - v4lconvert_yuv420_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuv420_to_rgb24(data->convert_pixfmt_buf, dest, width, + height, yvu); break; case V4L2_PIX_FMT_BGR24: - v4lconvert_yuv420_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuv420_to_bgr24(data->convert_pixfmt_buf, dest, width, + height, yvu); break; } break; } + /* Conexant cx2341x raw video macroblock format */ + case V4L2_PIX_FMT_HM12: + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_hm12_to_rgb24(src, dest, width, height); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_hm12_to_bgr24(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_hm12_to_yuv420(src, dest, width, height, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_hm12_to_yuv420(src, dest, width, height, 1); + break; + } + break; + /* compressed bayer formats */ case V4L2_PIX_FMT_SPCA561: case V4L2_PIX_FMT_SN9C10X: case V4L2_PIX_FMT_PAC207: + case V4L2_PIX_FMT_MR97310A: + case V4L2_PIX_FMT_SQ905C: { - unsigned char tmpbuf[dest_fmt->fmt.pix.width*dest_fmt->fmt.pix.height]; - unsigned int bayer_fmt = 0; + unsigned char *tmpbuf; + struct v4l2_format tmpfmt = *fmt; - switch (src_fmt->fmt.pix.pixelformat) { + tmpbuf = v4lconvert_alloc_buffer(data, width * height, + &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); + if (!tmpbuf) + return -1; + + switch (src_pix_fmt) { case V4L2_PIX_FMT_SPCA561: - v4lconvert_decode_spca561(src, tmpbuf, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); - bayer_fmt = V4L2_PIX_FMT_SGBRG8; + v4lconvert_decode_spca561(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG8; break; case V4L2_PIX_FMT_SN9C10X: - v4lconvert_decode_sn9c10x(src, tmpbuf, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); - bayer_fmt = V4L2_PIX_FMT_SBGGR8; + v4lconvert_decode_sn9c10x(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_PAC207: - v4lconvert_decode_pac207(src, tmpbuf, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); - bayer_fmt = V4L2_PIX_FMT_SBGGR8; + v4lconvert_decode_pac207(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + break; + case V4L2_PIX_FMT_MR97310A: + v4lconvert_decode_mr97310a(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + break; + case V4L2_PIX_FMT_SQ905C: + v4lconvert_decode_sq905c(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; break; } + /* 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; + } - switch (dest_fmt->fmt.pix.pixelformat) { + /* Raw bayer formats */ + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: - v4lconvert_bayer_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height, bayer_fmt); + v4lconvert_bayer_to_rgb24(src, dest, width, height, src_pix_fmt); break; case V4L2_PIX_FMT_BGR24: - v4lconvert_bayer_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height, bayer_fmt); + v4lconvert_bayer_to_bgr24(src, dest, width, height, src_pix_fmt); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_bayer_to_yuv420(src, dest, width, height, src_pix_fmt, 0); break; - default: - v4lconvert_bayer_to_yuv420(tmpbuf, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height, bayer_fmt); + case V4L2_PIX_FMT_YVU420: + v4lconvert_bayer_to_yuv420(src, dest, width, height, src_pix_fmt, 1); break; } break; - } case V4L2_PIX_FMT_RGB24: - switch (dest_fmt->fmt.pix.pixelformat) { + switch (dest_pix_fmt) { case V4L2_PIX_FMT_BGR24: - v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_swap_rgb(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - printf("FIXME add rgb24 -> yuv420 conversion\n"); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1); break; } break; + case V4L2_PIX_FMT_BGR24: - switch (dest_fmt->fmt.pix.pixelformat) { + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: - v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_swap_rgb(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - printf("FIXME add bgr24 -> yuv420 conversion\n"); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1); break; } break; case V4L2_PIX_FMT_YUV420: - switch (dest_fmt->fmt.pix.pixelformat) { + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: - v4lconvert_yuv420_to_rgb24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuv420_to_rgb24(src, dest, width, + height, 0); break; case V4L2_PIX_FMT_BGR24: - v4lconvert_yuv420_to_bgr24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuv420_to_bgr24(src, dest, width, + height, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_swap_uv(src, dest, fmt); break; } break; - case V4L2_PIX_FMT_YUYV: - switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YVU420: + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: - v4lconvert_yuyv_to_rgb24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuv420_to_rgb24(src, dest, width, + height, 1); break; case V4L2_PIX_FMT_BGR24: - v4lconvert_yuyv_to_bgr24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuv420_to_bgr24(src, dest, width, + height, 1); break; - default: - v4lconvert_yuyv_to_yuv420(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + case V4L2_PIX_FMT_YUV420: + v4lconvert_swap_uv(src, dest, fmt); break; } break; - case V4L2_PIX_FMT_YVYU: - switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: - v4lconvert_yvyu_to_rgb24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuyv_to_rgb24(src, dest, width, height); break; case V4L2_PIX_FMT_BGR24: - v4lconvert_yvyu_to_bgr24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yuyv_to_bgr24(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_yuyv_to_yuv420(src, dest, width, height, 0); break; - default: - v4lconvert_yvyu_to_yuv420(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + case V4L2_PIX_FMT_YVU420: + v4lconvert_yuyv_to_yuv420(src, dest, width, height, 1); break; } break; - default: - V4LCONVERT_ERR("Unknown src format in conversion\n"); - errno = EINVAL; - return -1; - } - - /* Note when rotating dest is our temporary buffer to which our conversion - was done and _dest is the real dest! If the formats are identical no - conversion has been done! */ - if (rotate && dest_fmt->fmt.pix.pixelformat == src_fmt->fmt.pix.pixelformat) - dest = src; - - switch (rotate) { - case 0: - break; - case 90: - switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YVYU: + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: + v4lconvert_yvyu_to_rgb24(src, dest, width, height); + break; case V4L2_PIX_FMT_BGR24: - v4lconvert_rotate90_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_yvyu_to_bgr24(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_rotate90_yuv420(dest, _dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + /* Note we use yuyv_to_yuv420 not v4lconvert_yvyu_to_yuv420, + with the last argument reversed to make it have as we want */ + v4lconvert_yuyv_to_yuv420(src, dest, width, height, 1); break; - } - break; - case 180: - switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YVU420: + v4lconvert_yuyv_to_yuv420(src, dest, width, height, 0); + break; + } + break; + + case V4L2_PIX_FMT_UYVY: + switch (dest_pix_fmt) { case V4L2_PIX_FMT_RGB24: + v4lconvert_uyvy_to_rgb24(src, dest, width, height); + break; case V4L2_PIX_FMT_BGR24: - v4lconvert_rotate180_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_uyvy_to_bgr24(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_rotate180_yuv420(dest, _dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + v4lconvert_uyvy_to_yuv420(src, dest, width, height, 0); break; - } - break; - default: - printf("FIXME add %d degrees rotation\n", rotate); + case V4L2_PIX_FMT_YVU420: + v4lconvert_uyvy_to_yuv420(src, dest, width, height, 1); + break; + } + break; + + default: + V4LCONVERT_ERR("Unknown src format in conversion\n"); + errno = EINVAL; + return -1; } - return needed; + fmt->fmt.pix.pixelformat = dest_pix_fmt; + v4lconvert_fixup_fmt(fmt); + + return 0; +} + +int v4lconvert_convert(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt, /* in */ + unsigned char *src, int src_size, unsigned char *dest, int dest_size) +{ + int res, dest_needed, temp_needed, 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; + + 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; + } + + /* When field is V4L2_FIELD_ALTERNATE, each buffer only contains half the + lines */ + if (my_src_fmt.fmt.pix.field == V4L2_FIELD_ALTERNATE) { + my_src_fmt.fmt.pix.height /= 2; + my_dest_fmt.fmt.pix.height /= 2; + } + + /* sanity check, is the dest buffer large enough? */ + switch (my_dest_fmt.fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3; + temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + dest_needed = + my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3 / 2; + temp_needed = + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3 / 2; + break; + default: + V4LCONVERT_ERR("Unknown dest format in conversion\n"); + errno = EINVAL; + return -1; + } + + if (dest_size < dest_needed) { + V4LCONVERT_ERR("destination buffer too small\n"); + errno = EFAULT; + return -1; + } + + + /* 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; + + /* 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; + + flip_src = crop_src = rotate90_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 = 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, convert2_src, src_size, + convert2_dest, convert2_dest_size, + &my_src_fmt, + my_dest_fmt.fmt.pix.pixelformat); + if (res) + return res; + + 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 (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); + + return dest_needed; } const char *v4lconvert_get_error_message(struct v4lconvert_data *data) @@ -619,19 +1083,19 @@ const char *v4lconvert_get_error_message(struct v4lconvert_data *data) } static void v4lconvert_get_framesizes(struct v4lconvert_data *data, - unsigned int pixelformat) + unsigned int pixelformat, int index) { int i, j, match; struct v4l2_frmsizeenum frmsize = { .pixel_format = pixelformat }; 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 */ match = 0; - for (j = 0; j < data->no_framesizes && !match; j++) { + for (j = 0; j < data->no_framesizes; j++) { if (frmsize.type != data->framesizes[j].type) continue; @@ -648,6 +1112,8 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data, match = 1; break; } + if (match) + break; } /* Add this framesize if it is not already in our list */ if (!match) { @@ -657,6 +1123,9 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data, return; } data->framesizes[data->no_framesizes].type = frmsize.type; + /* We use the pixel_format member to store a bitmask of all + supported src_formats which can do this size */ + data->framesizes[data->no_framesizes].pixel_format = 1 << index; switch(frmsize.type) { case V4L2_FRMSIZE_TYPE_DISCRETE: data->framesizes[data->no_framesizes].discrete = frmsize.discrete; @@ -668,14 +1137,21 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data, } data->no_framesizes++; } + else + data->framesizes[j].pixel_format |= 1 << index; } } 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; @@ -686,6 +1162,9 @@ int v4lconvert_enum_framesizes(struct v4lconvert_data *data, 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: @@ -702,8 +1181,16 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, int res; struct v4l2_format src_fmt, dest_fmt; - if (!v4lconvert_supported_dst_format(frmival->pixel_format)) - return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); + if (!v4lconvert_supported_dst_format(frmival->pixel_format)) { + 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; + } /* Check which format we will be using to convert to frmival->pixel_format */ memset(&dest_fmt, 0, sizeof(dest_fmt)); @@ -711,13 +1198,30 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, dest_fmt.fmt.pix.pixelformat = frmival->pixel_format; dest_fmt.fmt.pix.width = frmival->width; dest_fmt.fmt.pix.height = frmival->height; - if ((res = v4lconvert_try_format(data, &dest_fmt, &src_fmt))) + if ((res = v4lconvert_try_format(data, &dest_fmt, &src_fmt))) { + if (res) + V4LCONVERT_ERR("trying format: %s\n", strerror(errno)); return res; + } /* Check the requested format is supported exactly as requested */ if (dest_fmt.fmt.pix.pixelformat != frmival->pixel_format || dest_fmt.fmt.pix.width != frmival->width || dest_fmt.fmt.pix.height != frmival->height) { + int frmival_pixformat = frmival->pixel_format; + int dest_pixformat = dest_fmt.fmt.pix.pixelformat; + V4LCONVERT_ERR("Could not find matching framesize for: %c%c%c%c %dx%d " + "closest match: %c%c%c%c %dx%d\n", + frmival_pixformat & 0xff, + (frmival_pixformat >> 8) & 0xff, + (frmival_pixformat >> 16) & 0xff, + frmival_pixformat >> 24, + frmival->width, frmival->height, + dest_pixformat & 0xff, + (dest_pixformat >> 8) & 0xff, + (dest_pixformat >> 16) & 0xff, + dest_pixformat >> 24, + dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height); errno = EINVAL; return -1; } @@ -726,7 +1230,24 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, 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; + V4LCONVERT_ERR("Could not enum frameival index: %d for: %c%c%c%c %dx%d " + "using src: %c%c%c%c %dx%d, error: %s\n", + frmival->index, + dest_pixfmt & 0xff, + (dest_pixfmt >> 8) & 0xff, + (dest_pixfmt >> 16) & 0xff, + dest_pixfmt >> 24, + dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height, + src_pixfmt & 0xff, + (src_pixfmt >> 8) & 0xff, + (src_pixfmt >> 16) & 0xff, + src_pixfmt >> 24, + src_fmt.fmt.pix.width, src_fmt.fmt.pix.height, strerror(errno)); + } /* Restore the requested format in the frmival struct */ frmival->pixel_format = dest_fmt.fmt.pix.pixelformat; @@ -735,3 +1256,15 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, 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); +} diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h new file mode 100644 index 000000000..a456ceea3 --- /dev/null +++ b/v4l2-apps/libv4l/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_ */ diff --git a/v4l2-apps/libv4l/libv4lconvert/mr97310a.c b/v4l2-apps/libv4l/libv4lconvert/mr97310a.c new file mode 100644 index 000000000..e6ce94b29 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/mr97310a.c @@ -0,0 +1,172 @@ +/* + * MR97310A decoder + * + * Copyright (C) 2004 Theodore Kilgore <kilgota@auburn.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "libv4lconvert-priv.h" + +#define CLIP(x) ((x)<0?0:((x)>0xff)?0xff:(x)) + +/* FIXME not threadsafe */ +static int decoder_initialized = 0; + +static struct { + unsigned char is_abs; + unsigned char len; + signed char val; +} table[256]; + +static void init_mr97310a_decoder(void) +{ + int i; + int is_abs, val, len; + + for (i = 0; i < 256; ++i) { + is_abs = 0; + val = 0; + len = 0; + if ((i & 0x80) == 0) { + /* code 0 */ + val = 0; + len = 1; + } else if ((i & 0xe0) == 0xc0) { + /* code 110 */ + val = -3; + len = 3; + } else if ((i & 0xe0) == 0xa0) { + /* code 101 */ + val = +3; + len = 3; + } else if ((i & 0xf0) == 0x80) { + /* code 1000 */ + val = +7; + len = 4; + } else if ((i & 0xf0) == 0x90) { + /* code 1001 */ + val = -7; + len = 4; + } else if ((i & 0xf0) == 0xf0) { + /* code 1111 */ + val = -15; + len = 4; + } else if ((i & 0xf8) == 0xe0) { + /* code 11100 */ + val = +15; + len = 5; + } else if ((i & 0xf8) == 0xe8) { + /* code 11101xxxxx */ + is_abs = 1; + val = 0; /* value is calculated later */ + len = 5; + } + table[i].is_abs = is_abs; + table[i].val = val; + table[i].len = len; + } + decoder_initialized = 1; +} + +static inline unsigned char get_byte(const unsigned char *inp, + unsigned int bitpos) +{ + const unsigned char *addr; + addr = inp + (bitpos >> 3); + return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); +} + +void v4lconvert_decode_mr97310a(const unsigned char *inp, unsigned char *outp, + int width, int height) +{ + int row, col; + int val; + int bitpos; + unsigned char code; + unsigned char lp, tp, tlp, trp; + + if (!decoder_initialized) + init_mr97310a_decoder(); + + /* remove the header */ + inp += 12; + + bitpos = 0; + + /* main decoding loop */ + for (row = 0; row < height; ++row) { + col = 0; + + /* first two pixels in first two rows are stored as raw 8-bit */ + if (row < 2) { + code = get_byte(inp, bitpos); + bitpos += 8; + *outp++ = code; + + code = get_byte(inp, bitpos); + bitpos += 8; + *outp++ = code; + + col += 2; + } + + while (col < width) { + /* get bitcode */ + code = get_byte(inp, bitpos); + /* update bit position */ + bitpos += table[code].len; + + /* calculate pixel value */ + if (table[code].is_abs) { + /* get 5 more bits and use them as absolute value */ + code = get_byte(inp, bitpos); + val = (code & 0xf8); + bitpos += 5; + + } else { + /* value is relative to top or left pixel */ + val = table[code].val; + lp = outp[-2]; + if (row > 1) { + tlp = outp[-2*width-2]; + tp = outp[-2*width]; + trp = outp[-2*width+2]; + } + if (row < 2) { + /* top row: relative to left pixel */ + val += lp; + } else if (col < 2) { + /* left column: relative to top pixel */ + /* initial estimate */ + val += (2*tp + 2*trp + 1)/4; + } else if (col > width - 3) { + /* left column: relative to top pixel */ + val += (2*tp + 2*tlp + 1)/4; + /* main area: average of left and top pixel */ + } else { + /* initial estimate for predictor */ + val += (2*lp + tp + trp + 1)/4; + } + } + /* store pixel */ + *outp++ = CLIP(val); + ++col; + } + } + + return; +} diff --git a/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c b/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c new file mode 100644 index 000000000..11ed2a601 --- /dev/null +++ b/v4l2-apps/libv4l/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 */ + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c b/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c new file mode 100644 index 000000000..51b8d8c60 --- /dev/null +++ b/v4l2-apps/libv4l/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 */ + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c b/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c new file mode 100644 index 000000000..358264c68 --- /dev/null +++ b/v4l2-apps/libv4l/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 }; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c b/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c new file mode 100644 index 000000000..fcb5bb0cf --- /dev/null +++ b/v4l2-apps/libv4l/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 }; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h new file mode 100644 index 000000000..b73c73b53 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h @@ -0,0 +1,60 @@ +/* +# (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; + /* 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 */ + 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 diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c new file mode 100644 index 000000000..cbbcca73c --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c @@ -0,0 +1,181 @@ +/* +# (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; + } + + 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 (v4lcontrol_controls_changed(data->control) || + data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) { + v4lprocessing_update_lookup_tables(data, buf, fmt); + 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; +} diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h new file mode 100644 index 000000000..69d8865b2 --- /dev/null +++ b/v4l2-apps/libv4l/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 diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c new file mode 100644 index 000000000..64d20b3ff --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c @@ -0,0 +1,142 @@ +/* +# (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 CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) + +static int whitebalance_active(struct v4lprocessing_data *data) { + return v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE); +} + +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, avg_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 + b2) / 512; + comp1_avg = a2 / 256; + comp2_avg = b1 / 256; + } else { + green_avg = (a2 + b1) / 512; + comp1_avg = a1 / 256; + comp2_avg = b2 / 256; + } + + x = fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + if (abs(green_avg - comp1_avg) < x && + abs(green_avg - comp2_avg) < x && + abs(comp1_avg - comp2_avg) < x) + return 0; + + avg_avg = (green_avg + comp1_avg + comp2_avg) / 3; + + for (x = 0; x < 256; x++) { + data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg); + data->green[x] = CLIP(data->green[x] * avg_avg / green_avg); + data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg); + } + + return 1; +} + +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, avg_avg; + + 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; + } + + x = fmt->fmt.pix.width * fmt->fmt.pix.height * 4; + if (abs(green_avg - comp1_avg) < x && + abs(green_avg - comp2_avg) < x && + abs(comp1_avg - comp2_avg) < x) + return 0; + + /* scale to avoid integer overflows */ + green_avg /= 256; + comp1_avg /= 256; + comp2_avg /= 256; + avg_avg = (green_avg + comp1_avg + comp2_avg) / 3; + + for (x = 0; x < 256; x++) { + data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg); + data->green[x] = CLIP(data->green[x] * avg_avg / green_avg); + data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg); + } + + return 1; +} + + +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 }; diff --git a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c index 0f26b227a..31cbc3c68 100644 --- a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c +++ b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c @@ -20,13 +20,65 @@ */ +#include <string.h> #include "libv4lconvert-priv.h" -#define RGB2YUV(r,g,b,y,u,v) \ - (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15); \ +#define RGB2Y(r,g,b,y) \ + (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15) + +#define RGB2UV(r,g,b,u,v) \ (u) = (( -4878*(r) - 9578*(g) + 14456*(b) + 4210688) >> 15); \ (v) = (( 14456*(r) - 12105*(g) - 2351*(b) + 4210688) >> 15) +void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int bgr, int yvu) +{ + int x, y; + unsigned char *udest, *vdest; + + /* Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + for (x = 0; x < src_fmt->fmt.pix.width; x++) { + if (bgr) { + RGB2Y(src[2], src[1], src[0], *dest++); + } else { + RGB2Y(src[0], src[1], src[2], *dest++); + } + src += 3; + } + src += src_fmt->fmt.pix.bytesperline - 3 * src_fmt->fmt.pix.width; + } + src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline; + + /* U + V */ + if (yvu) { + vdest = dest; + udest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } else { + udest = dest; + vdest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < src_fmt->fmt.pix.width / 2; x++) { + int avg_src[3]; + avg_src[0] = (src[0] + src[3] + src[src_fmt->fmt.pix.bytesperline] + + src[src_fmt->fmt.pix.bytesperline + 3]) / 4; + avg_src[1] = (src[1] + src[4] + src[src_fmt->fmt.pix.bytesperline + 1] + + src[src_fmt->fmt.pix.bytesperline + 4]) / 4; + avg_src[2] = (src[2] + src[5] + src[src_fmt->fmt.pix.bytesperline + 2] + + src[src_fmt->fmt.pix.bytesperline + 5]) / 4; + if (bgr) { + RGB2UV(avg_src[2], avg_src[1], avg_src[0], *udest++, *vdest++); + } else { + RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++); + } + src += 6; + } + src += 2 * src_fmt->fmt.pix.bytesperline - 3 * src_fmt->fmt.pix.width; + } +} + #define YUV2R(y, u, v) ({ \ int r = (y) + ((((v)-128)*1436) >> 10); r > 255 ? 255 : r < 0 ? 0 : r; }) #define YUV2G(y, u, v) ({ \ @@ -37,13 +89,20 @@ #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color))) void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest, - int width, int height) + int width, int height, int yvu) { int i,j; const unsigned char *ysrc = src; - const unsigned char *usrc = src + width * height; - const unsigned char *vsrc = usrc + (width * height) / 4; + const unsigned char *usrc, *vsrc; + + if (yvu) { + vsrc = src + width * height; + usrc = vsrc + (width * height) / 4; + } else { + usrc = src + width * height; + vsrc = usrc + (width * height) / 4; + } for (i = 0; i < height; i++) { for (j = 0; j < width; j += 2) { @@ -76,7 +135,7 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest, vsrc++; } /* Rewind u and v for next line */ - if (i&1) { + if (!(i&1)) { usrc -= width / 2; vsrc -= width / 2; } @@ -84,13 +143,20 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest, } void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest, - int width, int height) + int width, int height, int yvu) { int i,j; const unsigned char *ysrc = src; - const unsigned char *usrc = src + width * height; - const unsigned char *vsrc = usrc + (width * height) / 4; + const unsigned char *usrc, *vsrc; + + if (yvu) { + vsrc = src + width * height; + usrc = vsrc + (width * height) / 4; + } else { + usrc = src + width * height; + vsrc = usrc + (width * height) / 4; + } for (i = 0; i < height; i++) { for (j = 0; j < width; j += 2) { @@ -123,7 +189,7 @@ void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest, vsrc++; } /* Rewind u and v for next line */ - if (i&1) { + if (!(i&1)) { usrc -= width / 2; vsrc -= width / 2; } @@ -183,11 +249,11 @@ void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dest, } void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest, - int width, int height) + int width, int height, int yvu) { int i, j; const unsigned char *src1; - unsigned char *vdest; + unsigned char *udest, *vdest; /* copy the Y values */ src1 = src; @@ -202,10 +268,16 @@ void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest, /* copy the U and V values */ src++; /* point to V */ src1 = src + width * 2; /* next line */ - vdest = dest + width * height / 4; + if (yvu) { + vdest = dest; + udest = dest + width * height / 4; + } else { + udest = dest; + vdest = dest + width * height / 4; + } for (i = 0; i < height; i += 2) { for (j = 0; j < width; j += 2) { - *dest++ = ((int) src[0] + src1[0]) / 2; /* U */ + *udest++ = ((int) src[0] + src1[0]) / 2; /* U */ *vdest++ = ((int) src[2] + src1[2]) / 2; /* V */ src += 4; src1 += 4; @@ -267,31 +339,88 @@ void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dest, } } -void v4lconvert_yvyu_to_yuv420(const unsigned char *src, unsigned char *dest, +void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dest, int width, int height) { + int j; + + while (--height >= 0) { + for (j = 0; j < width; j += 2) { + int u = src[0]; + int v = src[2]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[1] + u1); + *dest++ = CLIP(src[1] - rg); + *dest++ = CLIP(src[1] + v1); + + *dest++ = CLIP(src[3] + u1); + *dest++ = CLIP(src[3] - rg); + *dest++ = CLIP(src[3] + v1); + src += 4; + } + } +} + +void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + + while (--height >= 0) { + for (j = 0; j < width; j += 2) { + int u = src[0]; + int v = src[2]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[1] + v1); + *dest++ = CLIP(src[1] - rg); + *dest++ = CLIP(src[1] + u1); + + *dest++ = CLIP(src[3] + v1); + *dest++ = CLIP(src[3] - rg); + *dest++ = CLIP(src[3] + u1); + src += 4; + } + } +} + +void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dest, + int width, int height, int yvu) +{ int i, j; const unsigned char *src1; - unsigned char *vdest; + unsigned char *udest, *vdest; /* copy the Y values */ src1 = src; for (i = 0; i < height; i++) { for (j = 0; j < width; j += 2) { - *dest++ = src1[0]; - *dest++ = src1[2]; + *dest++ = src1[1]; + *dest++ = src1[3]; src1 += 4; } } /* copy the U and V values */ - src++; /* point to V */ src1 = src + width * 2; /* next line */ - vdest = dest + width * height / 4; + if (yvu) { + vdest = dest; + udest = dest + width * height / 4; + } else { + udest = dest; + vdest = dest + width * height / 4; + } for (i = 0; i < height; i += 2) { for (j = 0; j < width; j += 2) { - *dest++ = ((int) src[2] + src1[2]) / 2; /* U */ - *vdest++ = ((int) src[0] + src1[0]) / 2; /* V */ + *udest++ = ((int) src[0] + src1[0]) / 2; /* U */ + *vdest++ = ((int) src[2] + src1[2]) / 2; /* V */ src += 4; src1 += 4; } @@ -314,3 +443,32 @@ void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst, *dst++ = tmp0; } } + +void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt) +{ + int y; + + /* Copy Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + memcpy(dest, src, src_fmt->fmt.pix.width); + dest += src_fmt->fmt.pix.width; + src += src_fmt->fmt.pix.bytesperline; + } + + /* Copy component 2 */ + src += src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 4; + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + dest += src_fmt->fmt.pix.width / 2; + src += src_fmt->fmt.pix.bytesperline / 2; + } + + /* Copy component 1 */ + src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 2; + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + dest += src_fmt->fmt.pix.width / 2; + src += src_fmt->fmt.pix.bytesperline / 2; + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c b/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c new file mode 100644 index 000000000..19951300f --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c @@ -0,0 +1,137 @@ +/* + * Sonix SN9C20X decoder + * Vasily Khoruzhick, (C) 2008-2009 + * Algorithm based on Java code written by Jens on microdia google group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Note this code was originally licensed under the GNU GPL instead of the + * GNU LGPL, its license has been changed by its author. + */ + +#include "libv4lconvert-priv.h" + +#define DO_SANITY_CHECKS 0 + +static const int UVTranslate[32] = {0, 1, 2, 3, + 8, 9, 10, 11, + 16, 17, 18, 19, + 24, 25, 26, 27, + 4, 5, 6, 7, + 12, 13, 14, 15, + 20, 21, 22, 23, + 28, 29, 30, 31}; + +static const int Y_coords_624x[128][2] = { +{ 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 4, 0}, { 5, 0}, { 6, 0}, { 7, 0}, +{ 0, 1}, { 1, 1}, { 2, 1}, { 3, 1}, { 4, 1}, { 5, 1}, { 6, 1}, { 7, 1}, +{ 0, 2}, { 1, 2}, { 2, 2}, { 3, 2}, { 4, 2}, { 5, 2}, { 6, 2}, { 7, 2}, +{ 0, 3}, { 1, 3}, { 2, 3}, { 3, 3}, { 4, 3}, { 5, 3}, { 6, 3}, { 7, 3}, + +{ 0, 4}, { 1, 4}, { 2, 4}, { 3, 4}, { 4, 4}, { 5, 4}, { 6, 4}, { 7, 4}, +{ 0, 5}, { 1, 5}, { 2, 5}, { 3, 5}, { 4, 5}, { 5, 5}, { 6, 5}, { 7, 5}, +{ 0, 6}, { 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6}, { 7, 6}, +{ 0, 7}, { 1, 7}, { 2, 7}, { 3, 7}, { 4, 7}, { 5, 7}, { 6, 7}, { 7, 7}, + +{ 8, 0}, { 9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0}, +{ 8, 1}, { 9, 1}, {10, 1}, {11, 1}, {12, 1}, {13, 1}, {14, 1}, {15, 1}, +{ 8, 2}, { 9, 2}, {10, 2}, {11, 2}, {12, 2}, {13, 2}, {14, 2}, {15, 2}, +{ 8, 3}, { 9, 3}, {10, 3}, {11, 3}, {12, 3}, {13, 3}, {14, 3}, {15, 3}, + +{ 8, 4}, { 9, 4}, {10, 4}, {11, 4}, {12, 4}, {13, 4}, {14, 4}, {15, 4}, +{ 8, 5}, { 9, 5}, {10, 5}, {11, 5}, {12, 5}, {13, 5}, {14, 5}, {15, 5}, +{ 8, 6}, { 9, 6}, {10, 6}, {11, 6}, {12, 6}, {13, 6}, {14, 6}, {15, 6}, +{ 8, 7}, { 9, 7}, {10, 7}, {11, 7}, {12, 7}, {13, 7}, {14, 7}, {15, 7} +}; + +static void do_write_u(const unsigned char *buf, unsigned char *ptr, + int i, int j) +{ + *ptr = buf[i + 128 + j]; +} + +static void do_write_v(const unsigned char *buf, unsigned char *ptr, + int i, int j) +{ + *ptr = buf[i + 160 + j]; +} + +void v4lconvert_sn9c20x_to_yuv420(const unsigned char *raw, unsigned char *i420, + int width, int height, int yvu) +{ + int i = 0, x = 0, y = 0, j, relX, relY, x_div2, y_div2; + const unsigned char *buf = raw; + unsigned char *ptr; + int frame_size = width * height; + int frame_size_div2 = frame_size >> 1; + int frame_size_div4 = frame_size >> 2; + int width_div2 = width >> 1; + int height_div2 = height >> 1; + void (*do_write_uv1)(const unsigned char *buf, unsigned char *ptr, int i, + int j) = NULL; + void (*do_write_uv2)(const unsigned char *buf, unsigned char *ptr, int i, + int j) = NULL; + + if (yvu) { + do_write_uv1 = do_write_v; + do_write_uv2 = do_write_u; + } + else { + do_write_uv1 = do_write_u; + do_write_uv2 = do_write_v; + } + + while (i < (frame_size + frame_size_div2)) { + for (j = 0; j < 128; j++) { + relX = x + Y_coords_624x[j][0]; + relY = y + Y_coords_624x[j][1]; + +#if (DO_SANITY_CHECKS==1) + if ((relX < width) && (relY < height)) { +#endif + ptr = i420 + relY * width + relX; + *ptr = buf[i + j]; +#if (DO_SANITY_CHECKS==1) + } +#endif + + } + x_div2 = x >> 1; + y_div2 = y >> 1; + for (j = 0; j < 32; j++) { + relX = (x_div2) + (j & 0x07); + relY = (y_div2) + (j >> 3); + +#if (DO_SANITY_CHECKS==1) + if ((relX < width_div2) && (relY < height_div2)) { +#endif + ptr = i420 + frame_size + + relY * width_div2 + relX; + do_write_uv1(buf, ptr, i, j); + ptr += frame_size_div4; + do_write_uv2(buf, ptr, i, j); +#if (DO_SANITY_CHECKS==1) + } +#endif + } + + i += 192; + x += 16; + if (x >= width) { + x = 0; + y += 8; + } + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/spca501.c b/v4l2-apps/libv4l/libv4lconvert/spca501.c index 9157629e3..f491512e3 100644 --- a/v4l2-apps/libv4l/libv4lconvert/spca501.c +++ b/v4l2-apps/libv4l/libv4lconvert/spca501.c @@ -20,7 +20,7 @@ /* YUYV per line */ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height) + int width, int height, int yvu) { int i,j; unsigned long *lsrc = (unsigned long *)src; @@ -34,7 +34,10 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, } /* -128 - 127 --> 0 - 255 and copy 1 line U */ - ldst = (unsigned long *)(dst + width * height + i * width / 4); + if (yvu) + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + else + ldst = (unsigned long *)(dst + width * height + i * width / 4); for (j = 0; j < width/2; j += sizeof(long)) { *ldst = *lsrc++; *ldst++ ^= 0x8080808080808080ULL; @@ -48,7 +51,10 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, } /* -128 - 127 --> 0 - 255 and copy 1 line V */ - ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + if (yvu) + ldst = (unsigned long *)(dst + width * height + i * width / 4); + else + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); for (j = 0; j < width/2; j += sizeof(long)) { *ldst = *lsrc++; *ldst++ ^= 0x8080808080808080ULL; @@ -58,7 +64,7 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, /* YYUV per line */ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height) + int width, int height, int yvu) { int i,j; unsigned long *lsrc = (unsigned long *)src; @@ -72,14 +78,20 @@ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, } /* -128 - 127 --> 0 - 255 and copy 1 line U */ - ldst = (unsigned long *)(dst + width * height + i * width / 4); + if (yvu) + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + else + ldst = (unsigned long *)(dst + width * height + i * width / 4); for (j = 0; j < width/2; j += sizeof(long)) { *ldst = *lsrc++; *ldst++ ^= 0x8080808080808080ULL; } /* -128 - 127 --> 0 - 255 and copy 1 line V */ - ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + if (yvu) + ldst = (unsigned long *)(dst + width * height + i * width / 4); + else + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); for (j = 0; j < width/2; j += sizeof(long)) { *ldst = *lsrc++; *ldst++ ^= 0x8080808080808080ULL; @@ -89,7 +101,7 @@ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, /* YUVY per line */ void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst, - int width, int height) + int width, int height, int yvu) { int i,j; unsigned long *lsrc = (unsigned long *)src; @@ -103,14 +115,20 @@ void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst, } /* -128 - 127 --> 0 - 255 and copy 1 line U */ - ldst = (unsigned long *)(dst + width * height + i * width / 4); + if (yvu) + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + else + ldst = (unsigned long *)(dst + width * height + i * width / 4); for (j = 0; j < width/2; j += sizeof(long)) { *ldst = *lsrc++; *ldst++ ^= 0x8080808080808080ULL; } /* -128 - 127 --> 0 - 255 and copy 1 line V */ - ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + if (yvu) + ldst = (unsigned long *)(dst + width * height + i * width / 4); + else + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); for (j = 0; j < width/2; j += sizeof(long)) { *ldst = *lsrc++; *ldst++ ^= 0x8080808080808080ULL; diff --git a/v4l2-apps/libv4l/libv4lconvert/sq905c.c b/v4l2-apps/libv4l/libv4lconvert/sq905c.c new file mode 100644 index 000000000..a73b4da93 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/sq905c.c @@ -0,0 +1,217 @@ +/* + * sq905c.c + * + * Here is the decompression function for the SQ905C cameras. The functions + * used are adapted from the libgphoto2 functions for the same cameras, + * which was + * Copyright (c) 2005 and 2007 Theodore Kilgore <kilgota@auburn.edu> + * This version for libv4lconvert is + * Copyright (c) 2009 Theodore Kilgore <kilgota@auburn.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> + +#include "libv4lconvert-priv.h" + + +#define CLIP(x) ((x) < 0 ? 0 : ((x) > 0xff) ? 0xff : (x)) + + +static int +sq905c_first_decompress(unsigned char *output, const unsigned char *input, + unsigned int outputsize) +{ + unsigned char parity = 0; + unsigned char nibble_to_keep[2]; + unsigned char temp1 = 0, temp2 = 0; + unsigned char input_byte; + unsigned char lookup = 0; + unsigned int i = 0; + unsigned int bytes_used = 0; + unsigned int bytes_done = 0; + unsigned int bit_counter = 8; + unsigned int cycles = 0; + int table[9] = { -1, 0, 2, 6, 0x0e, 0x0e, 0x0e, 0x0e, 0xfb}; + unsigned char lookup_table[16] + = {0, 2, 6, 0x0e, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb}; + unsigned char translator[16] = {8, 7, 9, 6, 10, 11, 12, 13, + 14, 15, 5, 4, 3, 2, 1, 0}; + + nibble_to_keep[0] = 0; + nibble_to_keep[1] = 0; + + while (bytes_done < outputsize) { + while (parity < 2) { + while (lookup > table[cycles]) { + if (bit_counter == 8) { + input_byte = input[bytes_used]; + bytes_used++; + temp1 = input_byte; + bit_counter = 0; + } + input_byte = temp1; + temp2 = (temp2 << 1) & 0xFF; + input_byte = input_byte >> 7; + temp2 = temp2 | input_byte; + temp1 = (temp1 << 1) & 0xFF; + bit_counter++; + cycles++; + if (cycles > 9) + return -1; + lookup = temp2 & 0xff; + } + temp2 = 0; + for (i = 0; i < 17; i++) { + if (lookup == lookup_table[i]) { + nibble_to_keep[parity] = translator[i]; + break; + } + if (i == 16) + return -1; + } + cycles = 0; + parity++; + } + output[bytes_done] = (nibble_to_keep[0]<<4)|nibble_to_keep[1]; + bytes_done++; + parity = 0; + } + return 0; +} + +static int +sq905c_second_decompress(unsigned char *uncomp, unsigned char *in, + int width, int height) +{ + int diff = 0; + int tempval = 0; + int i, m, parity; + unsigned char delta_left = 0; + unsigned char delta_right = 0; + int input_counter = 0; + int delta_table[] = {-144, -110, -77, -53, -35, -21, -11, -3, + 2, 10, 20, 34, 52, 76, 110, 144}; + unsigned char *templine_red; + unsigned char *templine_green; + unsigned char *templine_blue; + templine_red = malloc(width); + if (!templine_red) { + free(templine_red); + return -1; + } + for (i = 0; i < width; i++) + templine_red[i] = 0x80; + templine_green = malloc(width); + if (!templine_green) { + free(templine_green); + return -1; + } + for (i = 0; i < width; i++) + templine_green[i] = 0x80; + templine_blue = malloc(width); + if (!templine_blue) { + free(templine_blue); + return -1; + } + for (i = 0; i < width; i++) + templine_blue[i] = 0x80; + for (m = 0; m < height/2; m++) { + /* First we do an even-numbered line */ + for (i = 0; i < width/2; i++) { + parity = i&1; + delta_right = in[input_counter] & 0x0f; + delta_left = (in[input_counter]>>4)&0xff; + input_counter++; + /* left pixel (red) */ + diff = delta_table[delta_left]; + if (!i) + tempval = templine_red[0] + diff; + else + tempval = (templine_red[i] + + uncomp[2*m*width+2*i-2])/2 + diff; + tempval = CLIP(tempval); + uncomp[2*m*width+2*i] = tempval; + templine_red[i] = tempval; + /* right pixel (green) */ + diff = delta_table[delta_right]; + if (!i) + tempval = templine_green[1] + diff; + else if (2*i == width - 2) + tempval = (templine_green[i] + + uncomp[2*m*width+2*i-1])/2 + + diff; + else + tempval = (templine_green[i+1] + + uncomp[2*m*width+2*i-1])/2 + + diff; + tempval = CLIP(tempval); + uncomp[2*m*width+2*i+1] = tempval; + templine_green[i] = tempval; + } + /* then an odd-numbered line */ + for (i = 0; i < width/2; i++) { + delta_right = in[input_counter] & 0x0f; + delta_left = (in[input_counter]>>4) & 0xff; + input_counter++; + /* left pixel (green) */ + diff = delta_table[delta_left]; + if (!i) + tempval = templine_green[0] + diff; + else + tempval = (templine_green[i] + + uncomp[(2*m+1)*width+2*i-2])/2 + + diff; + tempval = CLIP(tempval); + uncomp[(2*m+1)*width+2*i] = tempval; + templine_green[i] = tempval; + /* right pixel (blue) */ + diff = delta_table[delta_right]; + if (!i) + tempval = templine_blue[0] + diff; + else + tempval = (templine_blue[i] + + uncomp[(2*m+1)*width+2*i-1])/2 + + diff; + tempval = CLIP(tempval); + uncomp[(2*m+1)*width+2*i+1] = tempval; + templine_blue[i] = tempval; + } + } + free(templine_green); + free(templine_red); + free(templine_blue); + return 0; +} + +void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int size; + unsigned char *temp_data; + const unsigned char *raw; + /* here we get rid of the 0x50 bytes of header in src. */ + raw = src + 0x50; + size = width*height/2; + temp_data = malloc(size); + if (!temp_data) + goto out; + sq905c_first_decompress(temp_data, raw, size); + sq905c_second_decompress(dst, temp_data, width, height); +out: + free(temp_data); +} diff --git a/v4l2-apps/test/Makefile b/v4l2-apps/test/Makefile index d118b9210..13eebf377 100644 --- a/v4l2-apps/test/Makefile +++ b/v4l2-apps/test/Makefile @@ -7,6 +7,7 @@ binaries = ioctl-test \ sliced-vbi-detect \ vbi-test \ v4lgrab \ + v4l2grab \ driver-test \ pixfmt-test \ stress-buffer \ @@ -24,9 +25,15 @@ install: ../libv4l2util/libv4l2util.a: ../libv4l2util/v4l2_driver.c ../libv4l2util/frequencies.c make -C ../libv4l2util libv4l2util.a +../libv4l/libv4l2/libv4l2.so: + make -C ../libv4l/ + driver-test: driver-test.o ../libv4l2util/libv4l2util.a pixfmt-test: pixfmt-test.o $(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ -lX11 +v4l2grab: v4l2grab.o + $(CC) $(LDFLAGS) $^ -o $@ -L ../libv4l/libv4l2 -L ../libv4l/libv4lconvert -lv4l2 -lv4lconvert + include ../Make.rules diff --git a/v4l2-apps/test/ioctl-test.c b/v4l2-apps/test/ioctl-test.c index f9ac1cad5..883282106 100644 --- a/v4l2-apps/test/ioctl-test.c +++ b/v4l2-apps/test/ioctl-test.c @@ -90,7 +90,6 @@ union v4l_parms { struct v4l2_encoder_cmd p_v4l2_encoder_cmd; struct v4l2_dbg_register p_v4l2_dbg_register; struct v4l2_dbg_chip_ident p_v4l2_dbg_chip_ident; - struct v4l2_chip_ident_old p_v4l2_chip_ident_old; struct v4l2_hw_freq_seek p_v4l2_hw_freq_seek; }; @@ -197,7 +196,6 @@ static const struct { ioc(VIDIOC_DBG_S_REGISTER), /* struct v4l2_register */ ioc(VIDIOC_DBG_G_REGISTER), /* struct v4l2_register */ ioc(VIDIOC_DBG_G_CHIP_IDENT), /* struct v4l2_dbg_chip_ident */ - ioc(VIDIOC_G_CHIP_IDENT_OLD), /* struct v4l2_chip_ident_old */ ioc(VIDIOC_S_HW_FREQ_SEEK), /* struct v4l2_hw_freq_seek */ #ifdef __OLD_VIDIOC_ ioc(VIDIOC_OVERLAY_OLD), /* int */ diff --git a/v4l2-apps/test/v4l2grab.c b/v4l2-apps/test/v4l2grab.c new file mode 100644 index 000000000..de921874b --- /dev/null +++ b/v4l2-apps/test/v4l2grab.c @@ -0,0 +1,162 @@ +/* V4L2 video picture grabber + Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org> + + 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. + + 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <linux/videodev2.h> +#include "../libv4l/include/libv4l2.h" + +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + +struct buffer { + void *start; + size_t length; +}; + +static void xioctl(int fh, int request, void *arg) +{ + int r; + + do { + r = v4l2_ioctl(fh, request, arg); + } while (r == -1 && ((errno == EINTR) || (errno == EAGAIN))); + + if (r == -1) { + fprintf(stderr, "error %d, %s\n", errno, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + struct v4l2_format fmt; + struct v4l2_buffer buf; + struct v4l2_requestbuffers req; + enum v4l2_buf_type type; + fd_set fds; + struct timeval tv; + int r, fd = -1; + unsigned int i, n_buffers; + char *dev_name = "/dev/video0"; + char out_name[256]; + FILE *fout; + struct buffer *buffers; + + fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0); + if (fd < 0) { + perror("Cannot open device"); + exit(EXIT_FAILURE); + } + + CLEAR(fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = 640; + fmt.fmt.pix.height = 480; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + xioctl(fd, VIDIOC_S_FMT, &fmt); + if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24) { + printf("Libv4l didn't accept RGB24 format. Can't proceed.\n"); + exit(EXIT_FAILURE); + } + if ((fmt.fmt.pix.width != 640) || (fmt.fmt.pix.height != 480)) + printf("Warning: driver is sending image at %dx%d\n", + fmt.fmt.pix.width, fmt.fmt.pix.height); + + CLEAR(req); + req.count = 2; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + xioctl(fd, VIDIOC_REQBUFS, &req); + + buffers = calloc(req.count, sizeof(*buffers)); + for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + + xioctl(fd, VIDIOC_QUERYBUF, &buf); + + buffers[n_buffers].length = buf.length; + buffers[n_buffers].start = v4l2_mmap(NULL, buf.length, + PROT_READ | PROT_WRITE, MAP_SHARED, + fd, buf.m.offset); + + if (MAP_FAILED == buffers[n_buffers].start) { + perror("mmap"); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < n_buffers; ++i) { + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + xioctl(fd, VIDIOC_QBUF, &buf); + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + xioctl(fd, VIDIOC_STREAMON, &type); + for (i = 0; i < 20; i++) { + do { + FD_ZERO(&fds); + FD_SET(fd, &fds); + + /* Timeout. */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + r = select(fd + 1, &fds, NULL, NULL, &tv); + } while ((r == -1 && (errno = EINTR))); + if (r == -1) { + perror("select"); + return errno; + } + + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + xioctl(fd, VIDIOC_DQBUF, &buf); + + sprintf(out_name, "out%03d.ppm", i); + fout = fopen(out_name, "w"); + if (!fout) { + perror("Cannot open image"); + exit(EXIT_FAILURE); + } + fprintf(fout, "P6\n%d %d 255\n", + fmt.fmt.pix.width, fmt.fmt.pix.height); + fwrite(buffers[buf.index].start, buf.bytesused, 1, fout); + fclose(fout); + + xioctl(fd, VIDIOC_QBUF, &buf); + } + + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + xioctl(fd, VIDIOC_STREAMOFF, &type); + for (i = 0; i < n_buffers; ++i) + v4l2_munmap(buffers[i].start, buffers[i].length); + v4l2_close(fd); + + return 0; +} diff --git a/v4l2-apps/util/cx18-ctl.c b/v4l2-apps/util/cx18-ctl.c index 3d8ff385d..138f3c26a 100644 --- a/v4l2-apps/util/cx18-ctl.c +++ b/v4l2-apps/util/cx18-ctl.c @@ -52,12 +52,6 @@ #define CX18_DBGFLG_HIGHVOL (1 << 8) /* Internals copied from media/v4l2-common.h */ -struct v4l2_routing { - __u32 input; - __u32 output; -}; -#define VIDIOC_INT_S_AUDIO_ROUTING _IOW('d', 109, struct v4l2_routing) - #define VIDIOC_INT_RESET _IOW('d', 102, __u32) #define __stringify_1(x) #x @@ -80,7 +74,6 @@ enum Option { OptHelp = 'h', OptSetGPIO = 'i', OptListGPIO = 'I', - OptSetAudioRoute = 'v', OptReset = 128, OptVersion, OptLast = 256 @@ -97,7 +90,6 @@ static struct option long_options[] = { {"help", no_argument, 0, OptHelp}, {"set-gpio", required_argument, 0, OptSetGPIO}, {"list-gpio", no_argument, 0, OptListGPIO}, - {"audio-route", required_argument, 0, OptSetAudioRoute}, {"reset", required_argument, 0, OptReset}, {"version", no_argument, 0, OptVersion}, {0, 0, 0, 0} @@ -129,8 +121,6 @@ static void usage(void) printf(" show GPIO input/direction/output bits\n"); printf(" -i, --set-gpio [dir=<dir>,]val=<val>\n"); printf(" set GPIO direction bits to <dir> and set output to <val>\n"); - printf(" -v, --audio-route=input=<in>,output=<out>\n"); - printf(" set the audio input/output routing [VIDIOC_INT_S_AUDIO_ROUTING]\n"); exit(0); } @@ -250,14 +240,8 @@ int main(int argc, char **argv) char *subopts[] = { #define SUB_VAL 0 "val", -#define SUB_YUV_MODE 1 - "mode", -#define SUB_DIR 2 +#define SUB_DIR 1 "dir", -#define SUB_INPUT 3 - "input", -#define SUB_OUTPUT 4 - "output", NULL }; @@ -270,7 +254,6 @@ int main(int argc, char **argv) const char *device = "/dev/video0"; /* -d device */ int ch; int yuv_mode = 0; - struct v4l2_routing route; /* audio_route */ unsigned int gpio_out = 0x0; /* GPIO output data */ unsigned int gpio_dir = 0x0; /* GPIO direction bits */ int gpio_set_dir = 0; @@ -321,34 +304,6 @@ int main(int argc, char **argv) device = newdev; } break; - case OptSetAudioRoute: - subs = optarg; - while (*subs != '\0') { - switch (getsubopt(&subs, subopts, &value)) { - case SUB_INPUT: - if (value == NULL) { - printf - ("No value given to suboption <input>\n"); - usage(); - } - route.input = strtol(value, 0L, 0); - break; - case SUB_OUTPUT: - if (value == NULL) { - printf - ("No value given to suboption <output>\n"); - usage(); - } - route.output = strtol(value, 0L, 0); - break; - default: - printf - ("Invalid suboptions specified\n"); - usage(); - break; - } - } - break; case OptReset: reset = strtol(optarg, 0L, 0); break; @@ -414,10 +369,6 @@ int main(int argc, char **argv) } /* Setting Opts */ - - if (options[OptSetAudioRoute]) - doioctl(fd, VIDIOC_INT_S_AUDIO_ROUTING, &route, "VIDIOC_INT_S_AUDIO_ROUTING"); - if (options[OptSetGPIO]) { struct v4l2_dbg_register reg; diff --git a/v4l2-apps/util/ivtv-ctl.c b/v4l2-apps/util/ivtv-ctl.c index a05dcbba2..6af8fc663 100644 --- a/v4l2-apps/util/ivtv-ctl.c +++ b/v4l2-apps/util/ivtv-ctl.c @@ -56,12 +56,6 @@ #define IVTV_DBGFLG_HIGHVOL (1 << 10) /* Internals copied from media/v4l2-common.h */ -struct v4l2_routing { - __u32 input; - __u32 output; -}; -#define VIDIOC_INT_S_AUDIO_ROUTING _IOW('d', 109, struct v4l2_routing) - #define VIDIOC_INT_RESET _IOW('d', 102, __u32) #include <linux/ivtv.h> @@ -81,7 +75,6 @@ enum Option { OptListGPIO = 'I', OptPassThrough = 'K', OptFrameSync = 'k', - OptSetAudioRoute = 'v', OptReset = 128, OptSetYuvMode, OptGetYuvMode, @@ -106,7 +99,6 @@ static struct option long_options[] = { {"list-gpio", no_argument, 0, OptListGPIO}, {"passthrough", required_argument, 0, OptPassThrough}, {"sync", no_argument, 0, OptFrameSync}, - {"audio-route", required_argument, 0, OptSetAudioRoute}, {"reset", required_argument, 0, OptReset}, {"get-yuv-mode", no_argument, 0, OptGetYuvMode}, {"set-yuv-mode", required_argument, 0, OptSetYuvMode}, @@ -167,8 +159,6 @@ static void usage(void) printf(" -i, --set-gpio [dir=<dir>,]val=<val>\n"); printf(" set GPIO direction bits to <dir> and set output to <val>\n"); printf(" -k, --sync test vsync's capabilities [VIDEO_GET_EVENT]\n"); - printf(" -v, --audio-route=input=<in>,output=<out>\n"); - printf(" set the audio input/output routing [VIDIOC_INT_S_AUDIO_ROUTING]\n"); exit(0); } @@ -330,10 +320,6 @@ int main(int argc, char **argv) "mode", #define SUB_DIR 2 "dir", -#define SUB_INPUT 3 - "input", -#define SUB_OUTPUT 4 - "output", NULL }; @@ -346,7 +332,6 @@ int main(int argc, char **argv) const char *device = "/dev/video0"; /* -d device */ int ch; int yuv_mode = 0; - struct v4l2_routing route; /* audio_route */ unsigned short gpio_out = 0x0; /* GPIO output data */ unsigned short gpio_dir = 0x0; /* GPIO direction bits */ int gpio_set_dir = 0; @@ -424,34 +409,6 @@ int main(int argc, char **argv) device = newdev; } break; - case OptSetAudioRoute: - subs = optarg; - while (*subs != '\0') { - switch (getsubopt(&subs, subopts, &value)) { - case SUB_INPUT: - if (value == NULL) { - printf - ("No value given to suboption <input>\n"); - usage(); - } - route.input = strtol(value, 0L, 0); - break; - case SUB_OUTPUT: - if (value == NULL) { - printf - ("No value given to suboption <output>\n"); - usage(); - } - route.output = strtol(value, 0L, 0); - break; - default: - printf - ("Invalid suboptions specified\n"); - usage(); - break; - } - } - break; case OptReset: reset = strtol(optarg, 0L, 0); break; @@ -529,10 +486,6 @@ int main(int argc, char **argv) /* Setting Opts */ - if (options[OptSetAudioRoute]) - doioctl(fd, VIDIOC_INT_S_AUDIO_ROUTING, &route, - "VIDIOC_INT_S_AUDIO_ROUTING"); - if (options[OptFrameSync]) { printf("ioctl: VIDEO_GET_EVENT\n"); diff --git a/v4l2-apps/util/keytable.c b/v4l2-apps/util/keytable.c index 3b922f12b..4b39a43f0 100644 --- a/v4l2-apps/util/keytable.c +++ b/v4l2-apps/util/keytable.c @@ -1,6 +1,6 @@ /* keytable.c - This program allows checking/replacing keys at IR - Copyright (C) 2006 Mauro Carvalho Chehab <mchehab@infradead.org> + Copyright (C) 2006-2009 Mauro Carvalho Chehab <mchehab@infradead.org> 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 @@ -29,7 +29,7 @@ void prtcode (int *codes) for (p=keynames;p->name!=NULL;p++) { if (p->value == (unsigned)codes[1]) { - printf("scancode %d = %s (0x%02x)\n", codes[0], p->name, codes[1]); + printf("scancode 0x%04x = %s (0x%02x)\n", codes[0], p->name, codes[1]); return; } } @@ -55,7 +55,7 @@ int parse_code(char *string) int main (int argc, char *argv[]) { int fd; - unsigned int i; + unsigned int i, j; int codes[2]; if (argc<2 || argc>4) { @@ -104,10 +104,12 @@ int main (int argc, char *argv[]) } /* Clears old table */ - for (i=0;i<256;i++) { - codes[0] = i; - codes[1] = 0; - ioctl(fd, EVIOCSKEYCODE, codes); + for (j = 0; j < 256; j++) { + for (i = 0; i < 256; i++) { + codes[0] = (j << 8) | i; + codes[1] = KEY_UNKNOWN; + ioctl(fd, EVIOCSKEYCODE, codes); + } } while (fgets(s,sizeof(s),fin)) { @@ -116,14 +118,21 @@ int main (int argc, char *argv[]) perror ("parsing input file scancode"); return -1; } + if (!strcasecmp(scancode, "scancode")) { + scancode = strtok(NULL,"\n\t =:"); + if (!scancode) { + perror ("parsing input file scancode"); + return -1; + } + } - keycode=strtok(NULL,"\n\t "); + keycode=strtok(NULL,"\n\t =:("); if (!keycode) { perror ("parsing input file keycode"); return -1; } - // printf ("parsing %s=%s:",scancode,keycode); + // printf ("parsing %s=%s:", scancode, keycode); value=parse_code(keycode); // printf ("\tvalue=%d\n",value); @@ -136,6 +145,7 @@ int main (int argc, char *argv[]) codes [0] = (unsigned) strtol(scancode, NULL, 0); codes [1] = (unsigned) value; + // printf("\t%04x=%04x\n",codes[0], codes[1]); if(ioctl(fd, EVIOCSKEYCODE, codes)) perror ("EVIOCSKEYCODE"); @@ -146,12 +156,13 @@ int main (int argc, char *argv[]) } /* Get scancode table */ - for (i=0;i<256;i++) { - codes[0] = i; - if(ioctl(fd, EVIOCGKEYCODE, codes)==0) - prtcode(codes); + for (j = 0; j < 256; j++) { + for (i = 0; i < 256; i++) { + codes[0] = (j << 8) | i; + if(ioctl(fd, EVIOCGKEYCODE, codes)==0) + prtcode(codes); + } } - return 0; } diff --git a/v4l2-apps/util/rewrite_eeprom.pl b/v4l2-apps/util/rewrite_eeprom.pl new file mode 100644 index 000000000..495c4717c --- /dev/null +++ b/v4l2-apps/util/rewrite_eeprom.pl @@ -0,0 +1,336 @@ +#!/usr/bin/perl -w +# +################################################################################ +# Copyright (C) 2009 +# +# Mauro Carvalho Chehab <mchehab@redhat.com> +# Douglas Schilling Landgraf <dougsland@redhat.com> +# +# 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. +# +# 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. +# +# Although not very common, on a few devices, the eeprom may be erased, due to a +# bug on a *few eeprom* chipsets that sometimes considers i2c messages to other +# devices as being for it. +# +# The solution for it is to reprogram the eeprom with their original contents. +# +# Modules this script is known to work with: em28xx and saa7134 +# * Not tested against newer em28xx chipsets like the em2874 and em2884 +# +######################################################## +# NOTE # +################################################################################ +# Since the script will try to detect the proper i2c bus address, it will only # +# work well if you have just one V4L device connected. # +################################################################################ +# +######################################## +# What do you need to run this script? # +######################################## +# +# * eeprom - A dump file with the older eeprom. +# +# As example this is a dump from EMPIRE TV DUAL (310U): +# ^^^^^^^^^^^^^^ +# shell> dmesg +# +# [11196.181543] em28xx #0: i2c eeprom 00: 1a eb 67 95 1a eb 10 e3 d0 12 5c 03 6a 22 00 00 +# [11196.181559] em28xx #0: i2c eeprom 10: 00 00 04 57 4e 07 00 00 00 00 00 00 00 00 00 00 +# [11196.181572] em28xx #0: i2c eeprom 20: 46 00 01 00 f0 10 01 00 00 00 00 00 5b 1e 00 00 +# [11196.181585] em28xx #0: i2c eeprom 30: 00 00 20 40 20 80 02 20 01 01 00 00 00 00 00 00 +# [11196.181598] em28xx #0: i2c eeprom 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181610] em28xx #0: i2c eeprom 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181622] em28xx #0: i2c eeprom 60: 00 00 00 00 00 00 00 00 00 00 22 03 55 00 53 00 +# [11196.181635] em28xx #0: i2c eeprom 70: 42 00 20 00 32 00 38 00 38 00 31 00 20 00 44 00 +# [11196.181648] em28xx #0: i2c eeprom 80: 65 00 76 00 69 00 63 00 65 00 00 00 00 00 00 00 +# [11196.181660] em28xx #0: i2c eeprom 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181673] em28xx #0: i2c eeprom a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181685] em28xx #0: i2c eeprom b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181698] em28xx #0: i2c eeprom c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181710] em28xx #0: i2c eeprom d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181722] em28xx #0: i2c eeprom e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# [11196.181735] em28xx #0: i2c eeprom f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# +############################################# +# Where can I find my original eeprom? # +############################################# +# +# e.g: Old dmesg output +# +######################################## +# How this script works? # +######################################## +# +# To use it, you'll need to run the script, passing, as a parameter, the original dmesg with +# your original eeprom (before the corruption). Something like: +# +# shell> perl ./rewrite_eeprom my_old_dmesg_with_right_eeprom.txt > eeprom_script +# +# It will generate the eeprom_script file, with a script capable of recovering +# your eeprom. +# +# After having the proper eeprom_script generated, you'll need to run it, as root: +# +# shell> sh ./eeprom_script +# +# +# After running the script, your original contents should be restored. Try to +# remove it and reinsert to see if it will be properly detected again. +# +use Switch; + +$argv = $ARGV[0]; + +# Files +$file_eeprom = "/tmp/eeprom-original.txt"; +$file_bus = "/tmp/i2c-detect-bus.txt"; +$file_addr = "/tmp/i2c-detect-addr.txt"; +$file_i2c_tmp = "/tmp/i2ctmp.txt"; + +# Bus +$businfo = ""; +$addrinfo = ""; + +# Modules +$modules = "em28xx|saa713"; +$_ = $modules; +tr/|/ /d; +s/saa713/saa7134/g; +$modules_str = $_; + +# eeprom stuff +$eeprom_backup_dir = "~/.eeprom"; +$NR_EEPROM_REGS = 0; + +########################## +# Subroutines # +########################## + +sub build_script +{ + my $script_prevent; + + # Building script + + $script_prevent = "#!/bin/bash\n"; + $script_prevent .= "#\n"; + $script_prevent .= "# Notes:\n"; + $script_prevent .= "# - i2c_dev module should be loaded\n"; + $script_prevent .= "# - v4l driver should be loaded\n\n"; + + + $script_prevent .= "if [ ! \"\$UID\" = \"0\" ]; then\n\techo \"You must run this script as root\";\n\texit;\nfi\n\n"; + + $script_prevent .= "i2cset &> /dev/null\n"; + $script_prevent .= "if [ ! \"\$\?\" = \"1\" ]; then\n"; + $script_prevent .= "\techo \"Please install i2c-tools package before continue.\";\n"; + $script_prevent .= "\texit;\n"; + $script_prevent .= "fi\n\n"; + + $script_prevent .= "modprobe i2c-dev\n"; + $script_prevent .= "if [ ! \"\$\?\" = \"0\" ]; then\n"; + $script_prevent .= "\techo \"Can't load i2c-dev module.\";\n"; + $script_prevent .= "\texit;\n"; + $script_prevent .= "fi\n\n"; + + $script_prevent .= "clear;\n"; + $script_prevent .= "echo \""; + $script_prevent .= "\n\033[1;31m# # # ###### # # ### # # #####\n"; + $script_prevent .= "# # # # # # # ## # # ## # # #\n"; + $script_prevent .= "# # # # # # # # # # # # # # # \n"; + $script_prevent .= "# # # # # ###### # # # # # # # # ####\n"; + $script_prevent .= "# # # ####### # # # # # # # # # # #\n"; + $script_prevent .= "# # # # # # # # ## # # ## # #\n"; + $script_prevent .= " ## ## # # # # # # ### # # #####\033[0m\n\n"; + + + $script_prevent .= "This tool is *ONLY RECOMMENDED* in cases of: LOST or CORRUPTED EEPROM data.\n"; + $script_prevent .= "Otherwise:\n\nYOU MAY *LOST* THE CURRENT EEPROM FROM YOUR DEVICE AND IT WILL MAKE YOUR BOARD "; + $script_prevent .= "*DO NOT WORK* UNTIL YOU SET THE RIGHT EEPROM AGAIN!\n\n"; + $script_prevent .= "If you have *any doubt*, BEFORE run it contact people from: linux-media\@vger.kernel.org\n"; + + $script_prevent .= "Are you \033[1;31mABSOLUTELY SURE\033[0m to continue? (yes or not)\";\n\n"; + $script_prevent .= "read confirmation;\n"; + $script_prevent .= "if \[ ! \"\$confirmation\" = \"yes\" \]; then\n"; + $script_prevent .= "\techo \"process aborted\";\n"; + $script_prevent .= "\texit;\n"; + $script_prevent .= "fi\n\n"; + + $script_prevent .= "lsmod | egrep -e \"$modules\" &> /dev/null\n"; + $script_prevent .= "if [ ! \"\$\?\" = \"0\" ]; then\n"; + $script_prevent .= "\techo \"Aborting script.. None of the supported driver $modules_str are loaded. Did you forget to connect the device?\";\n"; + $script_prevent .= "\texit;"; + $script_prevent .= "\nfi\n\n"; + + print $script_prevent; +} + +sub check_user +{ + if ($>) { + die "You must run this program as root\n"; + } + +} + +sub get_bus_and_addr +{ + + my $lines = 0; + my $output = "#!/bin/bash\n"; + + system("\ni2cdetect -l | egrep -e \"$modules\" 2> /dev/null | cut -b 5 > $file_bus\n"); + + # Checking number if lines + open(FILE, $file_bus) or die "Can't open `$file_bus': $!"; + while (sysread FILE, $buffer, 1) { + $lines += ($buffer =~ tr/\n//); + } + close FILE; + + switch ($lines) { + case 0 { + $output .= "echo \"Could not detect i2c bus from any device, run again $0. Did you forget to connect the device?\";\n"; + $output .= "echo \"Modules supported: $modules_str\";\n"; + $output .= "exit;"; + print $output; + } + case 1 { + # Starting script + &build_script; + } + else { + $output .= "humm, I got too many busses, please connect or plug just a hardware peer time!\n"; + $output .= "Read a note inside the script!\n"; + $output .= "exit;"; + print $output; + } + } + + # Reading BUS from temporary file + open (FILE, $file_bus); + while (<FILE>) { + $businfo = "$_"; + chomp($businfo); + } + close FILE; + + system("i2cdetect -y $businfo > $file_i2c_tmp 2> /dev/null"); + system("awk \'NR==7\' $file_i2c_tmp | cut -d \' \' -f 2 > $file_addr"); + + # Reading BUS from temporary file + open (FILE, $file_addr); + while (<FILE>) { + $addrinfo = "$_"; + chomp($addrinfo); + } + + if($addrinfo eq "--") { + print "\necho \"**** Failed to recognize bus address!\n**** Please connect your device *with eeprom* and try to run $0 tool again, aborted!\";\n"; + print "exit;"; + } + + # Double check + $bkp_eeprom = "\n\ni2cdetect -y $businfo > $file_i2c_tmp 2> /dev/null;\n"; + $bkp_eeprom .= "BUSCHECK=\`awk \'NR==7\' $file_i2c_tmp | cut -d \' \' -f 2\`;\n"; + $bkp_eeprom .= "rm -f $file_i2c_tmp;\n"; + $bkp_eeprom .= "if [ \"\$BUSCHECK\" == \"--\" ]; then\n"; + $bkp_eeprom .= "\t echo \"Aborting script.. I cannot make backup of your current eeprom.. It's not safe to continue!\";\n"; + $bkp_eeprom .= "\t exit;\n"; + $bkp_eeprom .= "fi\n\n"; + + # Backup + $bkp_eeprom .= "\nDATE=\`/bin/date +%Y%m%d_%I%M%S\`\n"; + $bkp_eeprom .= "echo \"\nMaking backup of current eeprom - dir [$eeprom_backup_dir/eeprom-\$DATE]\";\n"; + $bkp_eeprom .= "mkdir -p $eeprom_backup_dir\n"; + $bkp_eeprom .= "\necho \"\n--EEPROM DUMP START HERE--\n\" > $eeprom_backup_dir/eeprom-\$DATE\n"; + $bkp_eeprom .= "i2cdump -y $businfo 0x$addrinfo >> $eeprom_backup_dir/eeprom-\$DATE 2> /dev/null\n"; + $bkp_eeprom .= "if [ ! \"\$\?\" = \"0\" ]; then\n"; + $bkp_eeprom .= "\t echo \"Aborting script.. I cannot make backup of your current eeprom.. It's not safe to continue!\";\n"; + $bkp_eeprom .= "\t exit;"; + $bkp_eeprom .= "\nfi\n"; + $bkp_eeprom .= "\necho \"\n--DMESG START HERE--\n\" >> $eeprom_backup_dir/eeprom-\$DATE\n"; + $bkp_eeprom .= "\ndmesg >> $eeprom_backup_dir/eeprom-\$DATE\n"; + + print $bkp_eeprom; + + close FILE; +} + +sub print_i2c +{ + $cmd = "cat $argv | egrep \"eeprom [0-9a-f]\"| sed -e \"s/.*eeprom/eeprom/\" | cut -d ' ' -f 3-22 > $file_eeprom"; + system($cmd); + + open (INPUT, "$file_eeprom") or die "Can't open data file: $!"; + + # Reading dump + @eeprom = ""; + my $eeprom_pos = 0; + while (!eof(INPUT)) { + read(INPUT, $fc, 2); + $eeprom[$eeprom_pos] = "0x$fc"; + seek(INPUT, tell(INPUT) + 1, 0); + $eeprom_pos++; + $NR_EEPROM_REGS++; + } + close INPUT; + + if ($NR_EEPROM_REGS == 0) { + print "\necho \"**** Failed to recognize any dump in: $argv! Make sure that you have the right dump file before run again $0 tool, aborted!\";\n"; + print "exit;"; + } + + print "\n\necho \"\033[1;31m\n[DO NOT REMOVE YOUR DEVICE UNTIL THE UPDATE IS FINISHED]\033[0m\";\n"; + print "echo \"Press ENTER to start\";\n"; + print "read\n"; + + for ($i=0; $i < $NR_EEPROM_REGS; $i++) { + printf("i2cset -y $businfo 0x$addrinfo 0x%02x $eeprom[$i] b\n", $i); + } + + printf("\necho \"\nDone! Remove and re-insert your device to see if it will be properly detected again. :-)\n\";\n"); +} + +################ +# Main # +################ + +&check_user; + +if (@ARGV <= 0) { + + my $em28xx_note = "\n\033[1;31m\nNOTES\033[0m:\n\t Not tested against newer em28xx chipsets like the em2874 and em2884\n"; + + print "\033[1;31m\nWARNING\033[0m:\n \t This script can *\033[1;31mDAMAGE\033[0m* your board, if you are not sure how to use it, *DO NOT* run it\n"; + print "\t Current modules supported: $modules_str *\033[1;31mONLY\033[0m*"; + print $em28xx_note; + print "\t If you have *any doubt*, \033[1;31mBEFORE run it\033[0m contact people from: linux-media\@vger.kernel.org\n"; + + print "\nUsage:\n"; + print "\tshell>perl $0 ./dmesg-dump-eeprom > eeprom_script.sh\n"; + print "\tshell>sh ./eeprom_script.sh\n\n"; + exit(); +} + +if (! -e $argv) { + printf("No such file: $argv\n"); + exit(); +} + +# Calling sub routines +&get_bus_and_addr; +&print_i2c; + +# Removing tmp files +system("rm -f $file_bus"); +system("rm -f $file_addr"); +system("rm -f $file_i2c_tmp"); +system("rm -f $file_eeprom"); diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index dec9edd61..5b38e37b3 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -77,6 +77,8 @@ enum Option { OptSetTuner = 't', OptGetVideoFormat = 'V', OptSetVideoFormat = 'v', + OptGetParm = 'P', + OptSetParm = 'p', OptGetSlicedVbiOutFormat = 128, OptGetOverlayFormat, @@ -103,6 +105,9 @@ enum Option { OptStreamOn, OptListStandards, OptListFormats, + OptListFormatsExt, + OptListFrameSizes, + OptListFrameIntervals, OptLogStatus, OptVerbose, OptSilent, @@ -131,7 +136,11 @@ enum Option { OptOverlay, OptGetJpegComp, OptSetJpegComp, + OptGetModulator, + OptSetModulator, OptListDevices, + OptGetOutputParm, + OptSetOutputParm, OptLast = 256 }; @@ -142,19 +151,17 @@ static int verbose; static unsigned capabilities; -typedef std::vector<struct v4l2_ext_control> ctrl_list; -static ctrl_list user_ctrls; -static ctrl_list mpeg_ctrls; +typedef std::map<unsigned, std::vector<struct v4l2_ext_control> > class2ctrls_map; -typedef std::map<std::string, unsigned> ctrl_strmap; -static ctrl_strmap ctrl_str2id; +typedef std::map<std::string, struct v4l2_queryctrl> ctrl_qmap; +static ctrl_qmap ctrl_str2q; typedef std::map<unsigned, std::string> ctrl_idmap; static ctrl_idmap ctrl_id2str; typedef std::list<std::string> ctrl_get_list; static ctrl_get_list get_ctrls; -typedef std::map<std::string,std::string> ctrl_set_map; +typedef std::map<std::string, std::string> ctrl_set_map; static ctrl_set_map set_ctrls; typedef std::vector<std::string> dev_vec; @@ -217,8 +224,15 @@ static struct option long_options[] = { {"streamon", no_argument, 0, OptStreamOn}, {"list-standards", no_argument, 0, OptListStandards}, {"list-formats", no_argument, 0, OptListFormats}, + {"list-formats-ext", no_argument, 0, OptListFormatsExt}, + {"list-framesizes", required_argument, 0, OptListFrameSizes}, + {"list-frameintervals", required_argument, 0, OptListFrameIntervals}, {"get-standard", no_argument, 0, OptGetStandard}, {"set-standard", required_argument, 0, OptSetStandard}, + {"get-parm", no_argument, 0, OptGetParm}, + {"set-parm", required_argument, 0, OptSetParm}, + {"get-output-parm", no_argument, 0, OptGetOutputParm}, + {"set-output-parm", required_argument, 0, OptSetOutputParm}, {"info", no_argument, 0, OptGetDriverInfo}, {"list-ctrls", no_argument, 0, OptListCtrls}, {"list-ctrls-menus", no_argument, 0, OptListCtrlsMenus}, @@ -260,6 +274,8 @@ static struct option long_options[] = { {"set-crop-output-overlay", required_argument, 0, OptSetOutputOverlayCrop}, {"get-jpeg-comp", no_argument, 0, OptGetJpegComp}, {"set-jpeg-comp", required_argument, 0, OptSetJpegComp}, + {"get-modulator", no_argument, 0, OptGetModulator}, + {"set-modulator", required_argument, 0, OptSetModulator}, {"overlay", required_argument, 0, OptOverlay}, {"list-devices", no_argument, 0, OptListDevices}, {0, 0, 0, 0} @@ -301,11 +317,26 @@ static void usage(void) " ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n" " secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n" " --list-standards display supported video standards [VIDIOC_ENUMSTD]\n" + " -P, --get-parm display video parameters [VIDIOC_G_PARM]\n" + " -p, --set-parm=<fps>\n" + " set video framerate in <fps> [VIDIOC_S_PARM]\n" " -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n" " -t, --set-tuner=<mode>\n" " set the audio mode of the tuner [VIDIOC_S_TUNER]\n" " Possible values: mono, stereo, lang2, lang1, bilingual\n" " --list-formats display supported video formats [VIDIOC_ENUM_FMT]\n" + " --list-formats-ext display supported video formats including frame sizes\n" + " and intervals\n" + " --list-framesizes=<f>\n" + " list supported framesizes for pixelformat <f>\n" + " [VIDIOC_ENUM_FRAMESIZES]\n" + " pixelformat is either the format index as reported by\n" + " --list-formats, or the fourcc value as a string\n" + " --list-frameintervals=width=<w>,height=<h>,pixelformat=<f>\n" + " list supported frame intervals for pixelformat <f> and\n" + " the given width and height [VIDIOC_ENUM_FRAMEINTERVALS]\n" + " pixelformat is either the format index as reported by\n" + " --list-formats, or the fourcc value as a string\n" " -V, --get-fmt-video\n" " query the video capture format [VIDIOC_G_FMT]\n" " -v, --set-fmt-video=width=<w>,height=<h>,pixelformat=<f>\n" @@ -404,6 +435,20 @@ static void usage(void) " display audio outputs [VIDIOC_ENUMAUDOUT]\n" " --list-audio-inputs\n" " display audio inputs [VIDIOC_ENUMAUDIO]\n" + " --get-modulator query the modulator settings [VIDIOC_G_MODULATOR]\n" + " --set-modulator=<txsubchans>\n" + " set the sub-carrier modulation [VIDIOC_S_MODULATOR]\n" + " <txsubchans> is one of:\n" + " mono: Modulate as mono\n" + " mono-rds: Modulate as mono with RDS (radio only)\n" + " stereo: Modulate as stereo\n" + " stereo-rds: Modulate as stereo with RDS (radio only)\n" + " bilingual: Modulate as bilingual\n" + " mono-sap: Modulate as mono with Second Audio Program\n" + " stereo-sap: Modulate as stereo with Second Audio Program\n" + " --get-output-parm display output video parameters [VIDIOC_G_PARM]\n" + " --set-output-parm=<fps>\n" + " set output video framerate in <fps> [VIDIOC_S_PARM]\n" "\n"); printf("Expert options:\n" " --streamoff turn the stream off [VIDIOC_STREAMOFF]\n" @@ -553,6 +598,43 @@ static std::string name2var(unsigned char *name) return s; } +static std::string safename(const unsigned char *name) +{ + std::string s; + + while (*name) { + if (*name == '\n') { + s += "\\n"; + } + else if (*name == '\r') { + s += "\\r"; + } + else if (*name == '\f') { + s += "\\f"; + } + else if (*name == '\\') { + s += "\\\\"; + } + else if ((*name & 0x7f) < 0x20) { + char buf[3]; + + sprintf(buf, "%02x", *name); + s += "\\x"; + s += buf; + } + else { + s += *name; + } + name++; + } + return s; +} + +static std::string safename(const char *name) +{ + return safename((const unsigned char *)name); +} + static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, struct v4l2_ext_control *ctrl, int show_menus) { @@ -572,6 +654,12 @@ static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, case V4L2_CTRL_TYPE_INTEGER64: printf("%31s (int64): value=%lld", s.c_str(), ctrl->value64); break; + case V4L2_CTRL_TYPE_STRING: + printf("%31s (str) : min=%d max=%d step=%d value='%s'", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, safename(ctrl->string).c_str()); + break; case V4L2_CTRL_TYPE_BOOLEAN: printf("%31s (bool) : default=%d value=%d", s.c_str(), @@ -629,6 +717,11 @@ static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) ctrls.controls = &ext_ctrl; if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER && qctrl.id < V4L2_CID_PRIVATE_BASE) { + if (qctrl.type == V4L2_CTRL_TYPE_STRING) { + ext_ctrl.size = qctrl.maximum + 1; + ext_ctrl.string = (char *)malloc(ext_ctrl.size); + ext_ctrl.string[0] = 0; + } if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) { printf("error %d getting ext_ctrl %s\n", errno, qctrl.name); @@ -645,6 +738,8 @@ static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) ext_ctrl.value = ctrl.value; } print_qctrl(fd, &qctrl, &ext_ctrl, show_menus); + if (qctrl.type == V4L2_CTRL_TYPE_STRING) + free(ext_ctrl.string); return 1; } @@ -678,7 +773,7 @@ static void find_controls(int fd) while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS && !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_str2q[name2var(qctrl.name)] = qctrl; ctrl_id2str[qctrl.id] = name2var(qctrl.name); } qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; @@ -689,14 +784,14 @@ static void find_controls(int fd) qctrl.id = id; if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 && !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_str2q[name2var(qctrl.name)] = qctrl; ctrl_id2str[qctrl.id] = name2var(qctrl.name); } } for (qctrl.id = V4L2_CID_PRIVATE_BASE; ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_str2q[name2var(qctrl.name)] = qctrl; ctrl_id2str[qctrl.id] = name2var(qctrl.name); } } @@ -810,7 +905,7 @@ static void printcropcap(const struct v4l2_cropcap &cropcap) printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator); } -static void printfmt(struct v4l2_format vfmt) +static void printfmt(const struct v4l2_format &vfmt) { const flag_def vbi_def[] = { { V4L2_VBI_UNSYNC, "unsynchronized" }, @@ -874,6 +969,72 @@ static void printfmt(struct v4l2_format vfmt) } } +static std::string frmtype2s(unsigned type) +{ + static const char *types[] = { + "Unknown", + "Discrete", + "Continuous", + "Stepwise" + }; + + if (type > 3) + type = 0; + return types[type]; +} + +static std::string fract2sec(const struct v4l2_fract &f) +{ + char buf[100]; + + sprintf(buf, "%.3f s", (1.0 * f.numerator) / f.denominator); + return buf; +} + +static std::string fract2fps(const struct v4l2_fract &f) +{ + char buf[100]; + + sprintf(buf, "%.3f fps", (1.0 * f.denominator) / f.numerator); + return buf; +} + +static void print_frmsize(const struct v4l2_frmsizeenum &frmsize, const char *prefix) +{ + printf("%s\tSize: %s ", prefix, frmtype2s(frmsize.type).c_str()); + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + printf("%dx%d", frmsize.discrete.width, frmsize.discrete.height); + } else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) { + printf("%dx%d - %dx%d with step %d/%d", + frmsize.stepwise.min_width, + frmsize.stepwise.min_height, + frmsize.stepwise.max_width, + frmsize.stepwise.max_height, + frmsize.stepwise.step_width, + frmsize.stepwise.step_height); + } + printf("\n"); +} + +static void print_frmival(const struct v4l2_frmivalenum &frmival, const char *prefix) +{ + printf("%s\tInterval: %s ", prefix, frmtype2s(frmival.type).c_str()); + if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { + printf("%s (%s)\n", fract2sec(frmival.discrete).c_str(), + fract2fps(frmival.discrete).c_str()); + } else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) { + printf("%s - %s with step %s\n", + fract2sec(frmival.stepwise.min).c_str(), + fract2sec(frmival.stepwise.max).c_str(), + fract2sec(frmival.stepwise.step).c_str()); + printf("%s\t : ", prefix); + printf("(%s - %s with step %s)\n", + fract2fps(frmival.stepwise.min).c_str(), + fract2fps(frmival.stepwise.max).c_str(), + fract2fps(frmival.stepwise.step).c_str()); + } +} + static void print_video_formats(int fd, enum v4l2_buf_type type) { struct v4l2_fmtdesc fmt; @@ -893,6 +1054,43 @@ static void print_video_formats(int fd, enum v4l2_buf_type type) } } +static void print_video_formats_ext(int fd, enum v4l2_buf_type type) +{ + struct v4l2_fmtdesc fmt; + struct v4l2_frmsizeenum frmsize; + struct v4l2_frmivalenum frmival; + + fmt.index = 0; + fmt.type = type; + while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { + printf("\tIndex : %d\n", fmt.index); + printf("\tType : %s\n", buftype2s(type).c_str()); + printf("\tPixel Format: '%s'", fcc2s(fmt.pixelformat).c_str()); + if (fmt.flags) + printf(" (compressed)"); + printf("\n"); + printf("\tName : %s\n", fmt.description); + frmsize.pixel_format = fmt.pixelformat; + frmsize.index = 0; + while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) { + print_frmsize(frmsize, "\t"); + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + frmival.index = 0; + frmival.pixel_format = fmt.pixelformat; + frmival.width = frmsize.discrete.width; + frmival.height = frmsize.discrete.height; + while (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) { + print_frmival(frmival, "\t\t"); + frmival.index++; + } + } + frmsize.index++; + } + printf("\n"); + fmt.index++; + } +} + static char *pts_to_string(char *str, unsigned long pts) { static char buf[256]; @@ -943,6 +1141,25 @@ static std::string rxsubchans2s(int rxsubchans) s += "lang1 "; if (rxsubchans & V4L2_TUNER_SUB_LANG2) s += "lang2 "; + if (rxsubchans & V4L2_TUNER_SUB_RDS) + s += "rds "; + return s; +} + +static std::string txsubchans2s(int txsubchans) +{ + std::string s; + + if (txsubchans & V4L2_TUNER_SUB_MONO) + s += "mono"; + if (txsubchans & V4L2_TUNER_SUB_STEREO) + s += "stereo"; + if (txsubchans & V4L2_TUNER_SUB_LANG1) + s += "bilingual"; + if (txsubchans & V4L2_TUNER_SUB_SAP) + s += "+sap"; + if (txsubchans & V4L2_TUNER_SUB_RDS) + s += "+rds"; return s; } @@ -962,6 +1179,8 @@ static std::string tcap2s(unsigned cap) s += "lang1 "; if (cap & V4L2_TUNER_CAP_LANG2) s += "lang2 "; + if (cap & V4L2_TUNER_CAP_RDS) + s += "rds "; return s; } @@ -987,8 +1206,12 @@ static std::string cap2s(unsigned cap) s += "\t\tSliced VBI Output\n"; if (cap & V4L2_CAP_RDS_CAPTURE) s += "\t\tRDS Capture\n"; + if (cap & V4L2_CAP_RDS_OUTPUT) + s += "\t\tRDS Output\n"; if (cap & V4L2_CAP_TUNER) s += "\t\tTuner\n"; + if (cap & V4L2_CAP_MODULATOR) + s += "\t\tModulator\n"; if (cap & V4L2_CAP_AUDIO) s += "\t\tAudio\n"; if (cap & V4L2_CAP_RADIO) @@ -1339,6 +1562,17 @@ static enum v4l2_field parse_field(const char *s) return V4L2_FIELD_ANY; } +static __u32 find_pixel_format(int fd, unsigned index) +{ + struct v4l2_fmtdesc fmt; + + fmt.index = index; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (doioctl(fd, VIDIOC_ENUM_FMT, &fmt, "VIDIOC_ENUM_FMT")) + return 0; + return fmt.pixelformat; +} + int main(int argc, char **argv) { char *value, *subs; @@ -1370,22 +1604,29 @@ int main(int argc, char **argv) struct v4l2_format raw_fmt_out; /* set_format/get_format for VBI output */ struct v4l2_format overlay_fmt; /* set_format/get_format video overlay */ struct v4l2_format overlay_fmt_out; /* set_format/get_format video overlay output */ - struct v4l2_tuner tuner; /* set_tuner/get_tuner */ + struct v4l2_tuner tuner; /* set_freq/get_freq */ + struct v4l2_modulator modulator;/* set_freq/get_freq */ struct v4l2_capability vcap; /* list_cap */ struct v4l2_input vin; /* list_inputs */ struct v4l2_output vout; /* list_outputs */ struct v4l2_audio vaudio; /* list audio inputs */ struct v4l2_audioout vaudout; /* audio outputs */ + struct v4l2_frmsizeenum frmsize;/* list frame sizes */ + struct v4l2_frmivalenum frmival;/* list frame intervals */ struct v4l2_rect vcrop; /* crop rect */ struct v4l2_rect vcrop_out; /* crop rect */ struct v4l2_rect vcrop_overlay; /* crop rect */ struct v4l2_rect vcrop_out_overlay; /* crop rect */ struct v4l2_framebuffer fbuf; /* fbuf */ struct v4l2_jpegcompression jpegcomp; /* jpeg compression */ + struct v4l2_streamparm parm; /* get/set parm */ int input; /* set_input/get_input */ int output; /* set_output/get_output */ + int txsubchans; /* set_modulator */ v4l2_std_id std; /* get_std/set_std */ double freq = 0; /* get/set frequency */ + double fps = 0; /* set framerate speed, in fps */ + double output_fps = 0; /* set framerate speed, in fps */ struct v4l2_frequency vf; /* get_freq/set_freq */ struct v4l2_standard vs; /* list_std */ int overlay; /* overlay */ @@ -1404,11 +1645,14 @@ int main(int argc, char **argv) memset(&overlay_fmt_out, 0, sizeof(overlay_fmt_out)); memset(&raw_fmt_out, 0, sizeof(raw_fmt_out)); memset(&tuner, 0, sizeof(tuner)); + memset(&modulator, 0, sizeof(modulator)); memset(&vcap, 0, sizeof(vcap)); memset(&vin, 0, sizeof(vin)); memset(&vout, 0, sizeof(vout)); memset(&vaudio, 0, sizeof(vaudio)); memset(&vaudout, 0, sizeof(vaudout)); + memset(&frmsize, 0, sizeof(frmsize)); + memset(&frmival, 0, sizeof(frmival)); memset(&vcrop, 0, sizeof(vcrop)); memset(&vcrop_out, 0, sizeof(vcrop_out)); memset(&vcrop_overlay, 0, sizeof(vcrop_overlay)); @@ -1602,6 +1846,41 @@ int main(int argc, char **argv) case OptOverlay: overlay = strtol(optarg, 0L, 0); break; + case OptListFrameSizes: + if (strlen(optarg) == 4) + frmsize.pixel_format = v4l2_fourcc(optarg[0], optarg[1], + optarg[2], optarg[3]); + else + frmsize.pixel_format = strtol(optarg, 0L, 0); + break; + case OptListFrameIntervals: + subs = optarg; + while (*subs != '\0') { + static const char *const subopts[] = { + "width", + "height", + "pixelformat", + NULL + }; + + switch (parse_subopt(&subs, subopts, &value)) { + case 0: + frmival.width = strtol(value, 0L, 0); + break; + case 1: + frmival.height = strtol(value, 0L, 0); + break; + case 2: + if (strlen(value) == 4) + frmival.pixel_format = + v4l2_fourcc(value[0], value[1], + value[2], value[3]); + else + frmival.pixel_format = strtol(value, 0L, 0); + break; + } + } + break; case OptSetCrop: parse_crop(optarg, set_crop, vcrop); break; @@ -1652,6 +1931,12 @@ int main(int argc, char **argv) std = strtol(optarg, 0L, 0) | (1ULL << 63); } break; + case OptSetParm: + fps = strtod(optarg, NULL); + break; + case OptSetOutputParm: + output_fps = strtod(optarg, NULL); + break; case OptGetCtrl: subs = optarg; while (*subs != '\0') { @@ -1695,6 +1980,28 @@ int main(int argc, char **argv) return 1; } break; + case OptSetModulator: + txsubchans = strtol(optarg, 0L, 0); + if (!strcmp(optarg, "stereo")) + txsubchans = V4L2_TUNER_SUB_STEREO; + else if (!strcmp(optarg, "stereo-sap")) + txsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP; + else if (!strcmp(optarg, "bilingual")) + txsubchans = V4L2_TUNER_SUB_LANG1; + else if (!strcmp(optarg, "mono")) + txsubchans = V4L2_TUNER_SUB_MONO; + else if (!strcmp(optarg, "mono-sap")) + txsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP; + else if (!strcmp(optarg, "stereo-rds")) + txsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS; + else if (!strcmp(optarg, "mono-rds")) + txsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_RDS; + else { + fprintf(stderr, "Unknown txsubchans value\n"); + usage(); + return 1; + } + break; case OptSetSlicedVbiFormat: case OptSetSlicedVbiOutFormat: case OptTrySlicedVbiFormat: @@ -1837,13 +2144,13 @@ int main(int argc, char **argv) capabilities = vcap.capabilities; find_controls(fd); for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { - if (ctrl_str2id.find(*iter) == ctrl_str2id.end()) { + if (ctrl_str2q.find(*iter) == ctrl_str2q.end()) { fprintf(stderr, "unknown control '%s'\n", (*iter).c_str()); exit(1); } } for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) { - if (ctrl_str2id.find(iter->first) == ctrl_str2id.end()) { + if (ctrl_str2q.find(iter->first) == ctrl_str2q.end()) { fprintf(stderr, "unknown control '%s'\n", iter->first.c_str()); exit(1); } @@ -1859,8 +2166,11 @@ int main(int argc, char **argv) options[OptGetAudioInput] = 1; options[OptGetAudioOutput] = 1; options[OptGetStandard] = 1; + options[OptGetParm] = 1; + options[OptGetOutputParm] = 1; options[OptGetFreq] = 1; options[OptGetTuner] = 1; + options[OptGetModulator] = 1; options[OptGetOverlayFormat] = 1; options[OptGetOutputOverlayFormat] = 1; options[OptGetVbiFormat] = 1; @@ -1896,11 +2206,17 @@ int main(int argc, char **argv) if (options[OptSetFreq]) { double fac = 16; - if (doioctl(fd, VIDIOC_G_TUNER, &tuner, "VIDIOC_G_TUNER") == 0) { - fac = (tuner.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + if (capabilities & V4L2_CAP_MODULATOR) { + if (doioctl(fd, VIDIOC_G_MODULATOR, &modulator, "VIDIOC_G_MODULATOR") == 0) + fac = (modulator.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + } else { + vf.type = V4L2_TUNER_ANALOG_TV; + if (doioctl(fd, VIDIOC_G_TUNER, &tuner, "VIDIOC_G_TUNER") == 0) { + fac = (tuner.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + vf.type = tuner.type; + } } vf.tuner = 0; - vf.type = tuner.type; vf.frequency = __u32(freq * fac); if (doioctl(fd, VIDIOC_S_FREQUENCY, &vf, "VIDIOC_S_FREQUENCY") == 0) @@ -1919,6 +2235,43 @@ int main(int argc, char **argv) printf("Standard set to %08llx\n", (unsigned long long)std); } + + if (options[OptSetParm]) { + memset(&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm.parm.capture.timeperframe.numerator = 1000; + parm.parm.capture.timeperframe.denominator = + fps * parm.parm.capture.timeperframe.numerator; + + if (doioctl(fd, VIDIOC_S_PARM, &parm, "VIDIOC_S_PARM") == 0) { + struct v4l2_fract *tf = &parm.parm.capture.timeperframe; + + if (!tf->denominator || !tf->numerator) + printf("Invalid frame rate\n"); + else + printf("Frame rate set to %.3f fps\n", + 1.0 * tf->denominator / tf->numerator); + } + } + + if (options[OptSetOutputParm]) { + memset(&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + parm.parm.output.timeperframe.numerator = 1000; + parm.parm.output.timeperframe.denominator = + fps * parm.parm.output.timeperframe.numerator; + + if (doioctl(fd, VIDIOC_S_PARM, &parm, "VIDIOC_S_PARM") == 0) { + struct v4l2_fract *tf = &parm.parm.output.timeperframe; + + if (!tf->denominator || !tf->numerator) + printf("Invalid frame rate\n"); + else + printf("Frame rate set to %.3f fps\n", + 1.0 * tf->denominator / tf->numerator); + } + } + if (options[OptSetInput]) { if (doioctl(fd, VIDIOC_S_INPUT, &input, "VIDIOC_S_INPUT") == 0) { printf("Video input set to %d", input); @@ -1954,6 +2307,16 @@ int main(int argc, char **argv) } } + if (options[OptSetModulator]) { + struct v4l2_modulator mt; + + memset(&mt, 0, sizeof(struct v4l2_modulator)); + if (doioctl(fd, VIDIOC_G_MODULATOR, &mt, "VIDIOC_G_MODULATOR") == 0) { + mt.txsubchans = txsubchans; + doioctl(fd, VIDIOC_S_MODULATOR, &mt, "VIDIOC_S_MODULATOR"); + } + } + if (options[OptSetVideoFormat] || options[OptTryVideoFormat]) { struct v4l2_format in_vfmt; @@ -1966,13 +2329,8 @@ int main(int argc, char **argv) if (set_fmts & FmtPixelFormat) { in_vfmt.fmt.pix.pixelformat = vfmt.fmt.pix.pixelformat; if (in_vfmt.fmt.pix.pixelformat < 256) { - struct v4l2_fmtdesc fmt; - - fmt.index = in_vfmt.fmt.pix.pixelformat; - fmt.type = in_vfmt.type; - if (doioctl(fd, VIDIOC_ENUM_FMT, &fmt, "VIDIOC_ENUM_FMT")) - goto set_vid_fmt_error; - in_vfmt.fmt.pix.pixelformat = fmt.pixelformat; + in_vfmt.fmt.pix.pixelformat = + find_pixel_format(fd, in_vfmt.fmt.pix.pixelformat); } } if (options[OptSetVideoFormat]) @@ -2116,42 +2474,61 @@ set_vid_fmt_error: if (options[OptSetCtrl] && !set_ctrls.empty()) { struct v4l2_ext_controls ctrls = { 0 }; + class2ctrls_map class2ctrls; for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) { struct v4l2_ext_control ctrl = { 0 }; - ctrl.id = ctrl_str2id[iter->first]; - ctrl.value = strtol(iter->second.c_str(), NULL, 0); - if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_MPEG) - mpeg_ctrls.push_back(ctrl); - else - user_ctrls.push_back(ctrl); - } - for (unsigned i = 0; i < user_ctrls.size(); i++) { - struct v4l2_control ctrl; - - ctrl.id = user_ctrls[i].id; - ctrl.value = user_ctrls[i].value; - if (doioctl(fd, VIDIOC_S_CTRL, &ctrl, "VIDIOC_S_CTRL")) { - fprintf(stderr, "%s: %s\n", - ctrl_id2str[ctrl.id].c_str(), - strerror(errno)); + ctrl.id = ctrl_str2q[iter->first].id; + if (ctrl_str2q[iter->first].type == V4L2_CTRL_TYPE_STRING) { + unsigned len = iter->second.length(); + unsigned maxlen = ctrl_str2q[iter->first].maximum; + + ctrl.size = maxlen + 1; + ctrl.string = (char *)malloc(ctrl.size); + if (len > maxlen) { + memcpy(ctrl.string, iter->second.c_str(), maxlen); + ctrl.string[maxlen] = 0; + } + else { + strcpy(ctrl.string, iter->second.c_str()); + } + } else { + ctrl.value = strtol(iter->second.c_str(), NULL, 0); } + class2ctrls[V4L2_CTRL_ID2CLASS(ctrl.id)].push_back(ctrl); } - if (mpeg_ctrls.size()) { - ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; - ctrls.count = mpeg_ctrls.size(); - ctrls.controls = &mpeg_ctrls[0]; - if (doioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls, "VIDIOC_S_EXT_CTRLS")) { - if (ctrls.error_idx >= ctrls.count) { - fprintf(stderr, "Error setting MPEG controls: %s\n", - strerror(errno)); + for (class2ctrls_map::iterator iter = class2ctrls.begin(); + iter != class2ctrls.end(); ++iter) { + if (iter->first == V4L2_CTRL_CLASS_USER) { + for (unsigned i = 0; i < iter->second.size(); i++) { + struct v4l2_control ctrl; + + ctrl.id = iter->second[i].id; + ctrl.value = iter->second[i].value; + if (doioctl(fd, VIDIOC_S_CTRL, &ctrl, "VIDIOC_S_CTRL")) { + fprintf(stderr, "%s: %s\n", + ctrl_id2str[ctrl.id].c_str(), + strerror(errno)); + } } - else { - fprintf(stderr, "%s: %s\n", - ctrl_id2str[mpeg_ctrls[ctrls.error_idx].id].c_str(), - strerror(errno)); + continue; + } + if (iter->second.size()) { + ctrls.ctrl_class = iter->first; + ctrls.count = iter->second.size(); + ctrls.controls = &iter->second[0]; + if (doioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls, "VIDIOC_S_EXT_CTRLS")) { + if (ctrls.error_idx >= ctrls.count) { + fprintf(stderr, "Error setting MPEG controls: %s\n", + strerror(errno)); + } + else { + fprintf(stderr, "%s: %s\n", + ctrl_id2str[iter->second[ctrls.error_idx].id].c_str(), + strerror(errno)); + } } } } @@ -2321,6 +2698,16 @@ set_vid_fmt_error: if (options[OptGetFreq]) { double fac = 16; + if (capabilities & V4L2_CAP_MODULATOR) { + if (doioctl(fd, VIDIOC_G_MODULATOR, &modulator, "VIDIOC_G_MODULATOR") == 0) + fac = (modulator.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + } else { + vf.type = V4L2_TUNER_ANALOG_TV; + if (doioctl(fd, VIDIOC_G_TUNER, &tuner, "VIDIOC_G_TUNER") == 0) { + fac = (tuner.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + vf.type = tuner.type; + } + } if (doioctl(fd, VIDIOC_G_TUNER, &tuner, "VIDIOC_G_TUNER") == 0) { fac = (tuner.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; } @@ -2366,43 +2753,99 @@ set_vid_fmt_error: } } + if (options[OptGetParm]) { + memset(&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (doioctl(fd, VIDIOC_G_PARM, &parm, "VIDIOC_G_PARM") == 0) { + const struct v4l2_fract &tf = parm.parm.capture.timeperframe; + + printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str()); + if (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) + printf("\tCapabilities : timeperframe\n"); + if (parm.parm.capture.capturemode & V4L2_MODE_HIGHQUALITY) + printf("\tCapture mode : high quality\n"); + if (!tf.denominator || !tf.numerator) + printf("\tFrames per second: invalid (%d/%d)\n", + tf.denominator, tf.numerator); + else + printf("\tFrames per second: %.3f (%d/%d)\n", + (1.0 * tf.denominator) / tf.numerator, + tf.denominator, tf.numerator); + printf("\tRead buffers : %d\n", parm.parm.output.writebuffers); + } + } + + if (options[OptGetOutputParm]) { + memset(&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (doioctl(fd, VIDIOC_G_PARM, &parm, "VIDIOC_G_PARM") == 0) { + const struct v4l2_fract &tf = parm.parm.output.timeperframe; + + printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str()); + if (parm.parm.output.capability & V4L2_CAP_TIMEPERFRAME) + printf("\tCapabilities : timeperframe\n"); + if (parm.parm.output.outputmode & V4L2_MODE_HIGHQUALITY) + printf("\tOutput mode : high quality\n"); + if (!tf.denominator || !tf.numerator) + printf("\tFrames per second: invalid (%d/%d)\n", + tf.denominator, tf.numerator); + else + printf("\tFrames per second: %.3f (%d/%d)\n", + (1.0 * tf.denominator) / tf.numerator, + tf.denominator, tf.numerator); + printf("\tWrite buffers : %d\n", parm.parm.output.writebuffers); + } + } + if (options[OptGetCtrl] && !get_ctrls.empty()) { struct v4l2_ext_controls ctrls = { 0 }; + class2ctrls_map class2ctrls; - mpeg_ctrls.clear(); - user_ctrls.clear(); for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { struct v4l2_ext_control ctrl = { 0 }; - ctrl.id = ctrl_str2id[*iter]; - if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_MPEG) - mpeg_ctrls.push_back(ctrl); - else - user_ctrls.push_back(ctrl); - } - for (unsigned i = 0; i < user_ctrls.size(); i++) { - struct v4l2_control ctrl; - - ctrl.id = user_ctrls[i].id; - doioctl(fd, VIDIOC_G_CTRL, &ctrl, "VIDIOC_G_CTRL"); - printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + ctrl.id = ctrl_str2q[*iter].id; + if (ctrl_str2q[*iter].type == V4L2_CTRL_TYPE_STRING) { + ctrl.size = ctrl_str2q[*iter].maximum + 1; + ctrl.string = (char *)malloc(ctrl.size); + ctrl.string[0] = 0; + } + class2ctrls[V4L2_CTRL_ID2CLASS(ctrl.id)].push_back(ctrl); } - if (mpeg_ctrls.size()) { - ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; - ctrls.count = mpeg_ctrls.size(); - ctrls.controls = &mpeg_ctrls[0]; - doioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls, "VIDIOC_G_EXT_CTRLS"); - for (unsigned i = 0; i < mpeg_ctrls.size(); i++) { - struct v4l2_ext_control ctrl = mpeg_ctrls[i]; - - printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + for (class2ctrls_map::iterator iter = class2ctrls.begin(); + iter != class2ctrls.end(); ++iter) { + if (iter->first == V4L2_CTRL_CLASS_USER) { + for (unsigned i = 0; i < iter->second.size(); i++) { + struct v4l2_control ctrl; + + ctrl.id = iter->second[i].id; + doioctl(fd, VIDIOC_G_CTRL, &ctrl, "VIDIOC_G_CTRL"); + printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + } + continue; + } + if (iter->second.size()) { + ctrls.ctrl_class = iter->first; + ctrls.count = iter->second.size(); + ctrls.controls = &iter->second[0]; + doioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls, "VIDIOC_G_EXT_CTRLS"); + for (unsigned i = 0; i < iter->second.size(); i++) { + struct v4l2_ext_control ctrl = iter->second[i]; + + if (ctrl_str2q[ctrl_id2str[ctrl.id]].type == V4L2_CTRL_TYPE_STRING) + printf("%s: '%s'\n", ctrl_id2str[ctrl.id].c_str(), + safename(ctrl.string).c_str()); + else + printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + } } } } if (options[OptGetTuner]) { struct v4l2_tuner vt; + memset(&vt, 0, sizeof(struct v4l2_tuner)); if (doioctl(fd, VIDIOC_G_TUNER, &vt, "VIDIOC_G_TUNER") == 0) { printf("Tuner:\n"); @@ -2421,6 +2864,25 @@ set_vid_fmt_error: } } + if (options[OptGetModulator]) { + struct v4l2_modulator mt; + + memset(&mt, 0, sizeof(struct v4l2_modulator)); + if (doioctl(fd, VIDIOC_G_MODULATOR, &mt, "VIDIOC_G_MODULATOR") == 0) { + printf("Modulator:\n"); + printf("\tName : %s\n", mt.name); + printf("\tCapabilities : %s\n", tcap2s(mt.capability).c_str()); + if (mt.capability & V4L2_TUNER_CAP_LOW) + printf("\tFrequency range : %.1f MHz - %.1f MHz\n", + mt.rangelow / 16000.0, mt.rangehigh / 16000.0); + else + printf("\tFrequency range : %.1f MHz - %.1f MHz\n", + mt.rangelow / 16.0, mt.rangehigh / 16.0); + printf("\tSubchannel modulation: %s\n", + txsubchans2s(mt.txsubchans).c_str()); + } + } + if (options[OptLogStatus]) { static char buf[40960]; int len; @@ -2546,6 +3008,35 @@ set_vid_fmt_error: print_video_formats(fd, V4L2_BUF_TYPE_VIDEO_OVERLAY); } + if (options[OptListFormatsExt]) { + printf("ioctl: VIDIOC_ENUM_FMT\n"); + print_video_formats_ext(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE); + print_video_formats(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT); + print_video_formats(fd, V4L2_BUF_TYPE_VIDEO_OVERLAY); + } + + if (options[OptListFrameSizes]) { + printf("ioctl: VIDIOC_ENUM_FRAMESIZES\n"); + if (frmsize.pixel_format < 256) + frmsize.pixel_format = find_pixel_format(fd, frmsize.pixel_format); + frmsize.index = 0; + while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) { + print_frmsize(frmsize, ""); + frmsize.index++; + } + } + + if (options[OptListFrameIntervals]) { + printf("ioctl: VIDIOC_ENUM_FRAMEINTERVALS\n"); + if (frmival.pixel_format < 256) + frmival.pixel_format = find_pixel_format(fd, frmival.pixel_format); + frmival.index = 0; + while (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) { + print_frmival(frmival, ""); + frmival.index++; + } + } + if (options[OptGetSlicedVbiCap]) { struct v4l2_sliced_vbi_cap cap; diff --git a/v4l2-apps/util/v4l2-dbg-micron.h b/v4l2-apps/util/v4l2-dbg-micron.h new file mode 100644 index 000000000..8466322bf --- /dev/null +++ b/v4l2-apps/util/v4l2-dbg-micron.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org> + 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. + + 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 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 "v4l2-dbg.h" + +#define MT9V011_IDENT "mt9v011" + +/* Register name prefix */ +#define MT9V011_PREFIX "MT9V011_" + +static struct board_regs mt9v011_regs[] = { + {0x00, MT9V011_PREFIX "CHIP_VERSION"}, + {0x01, MT9V011_PREFIX "ROWSTART"}, + {0x02, MT9V011_PREFIX "COLSTART"}, + {0x03, MT9V011_PREFIX "HEIGHT"}, + {0x04, MT9V011_PREFIX "WIDTH"}, + {0x05, MT9V011_PREFIX "HBLANK"}, + {0x06, MT9V011_PREFIX "VBLANK"}, + {0x07, MT9V011_PREFIX "OUT_CTRL"}, + {0x09, MT9V011_PREFIX "SHUTTER_WIDTH"}, + {0x0a, MT9V011_PREFIX "CLK_SPEED"}, + {0x0b, MT9V011_PREFIX "RESTART"}, + {0x0c, MT9V011_PREFIX "SHUTTER_DELAY"}, + {0x0d, MT9V011_PREFIX "RESET"}, + {0x1e, MT9V011_PREFIX "DIGITAL_ZOOM"}, + {0x20, MT9V011_PREFIX "READ_MODE"}, + {0x2b, MT9V011_PREFIX "GREEN_1_GAIN"}, + {0x2c, MT9V011_PREFIX "BLUE_GAIN"}, + {0x2d, MT9V011_PREFIX "RED_GAIN"}, + {0x2e, MT9V011_PREFIX "GREEN_2_GAIN"}, + {0x35, MT9V011_PREFIX "GLOBAL_GAIN"}, + {0xf1, MT9V011_PREFIX "CHIP_ENABLE"}, +}; diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp index f427d4442..1a481c04c 100644 --- a/v4l2-apps/util/v4l2-dbg.cpp +++ b/v4l2-apps/util/v4l2-dbg.cpp @@ -47,6 +47,7 @@ #include "v4l2-dbg-em28xx.h" #include "v4l2-dbg-ac97.h" #include "v4l2-dbg-tvp5150.h" +#include "v4l2-dbg-micron.h" #define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) @@ -61,7 +62,7 @@ struct board_list { static const struct board_list boards[] = { #define AC97_BOARD 0 - { /* From ac97-dbg.h */ + { /* From v4l2-dbg-ac97.h */ AC97_IDENT, sizeof(AC97_PREFIX) - 1, ac97_regs, @@ -69,7 +70,7 @@ static const struct board_list boards[] = { NULL, 0, }, - { /* From bttv-dbg.h */ + { /* From v4l2-dbg-bttv.h */ BTTV_IDENT, sizeof(BTTV_PREFIX) - 1, bt8xx_regs, @@ -77,7 +78,7 @@ static const struct board_list boards[] = { bt8xx_regs_other, ARRAY_SIZE(bt8xx_regs_other), }, - { /* From saa7134-dbg.h */ + { /* From v4l2-dbg-saa7134.h */ SAA7134_IDENT, sizeof(SAA7134_PREFIX) - 1, saa7134_regs, @@ -85,7 +86,7 @@ static const struct board_list boards[] = { NULL, 0, }, - { /* From em28xx-dbg.h */ + { /* From v4l2-dbg-em28xx.h */ EM28XX_IDENT, sizeof(EM28XX_PREFIX) - 1, em28xx_regs, @@ -93,7 +94,7 @@ static const struct board_list boards[] = { em28xx_alt_regs, ARRAY_SIZE(em28xx_alt_regs), }, - { /* From tvp5150-dbg.h */ + { /* From v4l2-dbg-tvp5150.h */ TVP5150_IDENT, sizeof(TVP5150_PREFIX) - 1, tvp5150_regs, @@ -101,6 +102,14 @@ static const struct board_list boards[] = { NULL, 0, }, + { /* From v4l2-dbg-micron.h */ + MT9V011_IDENT, + sizeof(MT9V011_PREFIX) - 1, + mt9v011_regs, + ARRAY_SIZE(mt9v011_regs), + NULL, + 0, + }, }; struct chipid { @@ -713,6 +722,9 @@ int main(int argc, char **argv) case V4L2_IDENT_CX23418: name = "cx23418"; break; + case V4L2_IDENT_CAFE: + name = "cafe"; + break; default: if (get_reg.match.type == V4L2_CHIP_MATCH_I2C_DRIVER) name = get_reg.match.name; @@ -726,6 +738,8 @@ int main(int argc, char **argv) print_regs(fd, &get_reg, 0, 0xff, stride); } else if (name == "saa7127") { print_regs(fd, &get_reg, 0, 0x7f, stride); + } else if (name == "ov7670") { + print_regs(fd, &get_reg, 0, 0x89, stride); } else if (name == "cx25840") { print_regs(fd, &get_reg, 0, 2, stride); print_regs(fd, &get_reg, 0x100, 0x15f, stride); @@ -738,6 +752,11 @@ int main(int argc, char **argv) print_regs(fd, &get_reg, 0x02000000, 0x020000ff, stride); } else if (name == "cx23418") { print_regs(fd, &get_reg, 0x02c40000, 0x02c409c7, stride); + } else if (name == "cafe") { + print_regs(fd, &get_reg, 0, 0x43, stride); + print_regs(fd, &get_reg, 0x88, 0x8f, stride); + print_regs(fd, &get_reg, 0xb4, 0xbb, stride); + print_regs(fd, &get_reg, 0x3000, 0x300c, stride); } else { /* unknown chip, dump 0-0xff by default */ print_regs(fd, &get_reg, 0, 0xff, stride); diff --git a/v4l2-apps/util/v4l2-sysfs-path.c b/v4l2-apps/util/v4l2-sysfs-path.c index e3f3e63e8..4f0817017 100644 --- a/v4l2-apps/util/v4l2-sysfs-path.c +++ b/v4l2-apps/util/v4l2-sysfs-path.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <string.h> #include <dirent.h> +#include <sys/stat.h> #define USB_ID "usb-" #define PCI_ID "PCI:" @@ -135,6 +136,131 @@ err: return NULL; } +char *seek_name(char *path, char *match) +{ + DIR *dir; + struct dirent *entry; + struct stat st; + char *p; + static char name[1024]; + int major, minor; + + dir = opendir(path); + if (!dir) + return NULL; + + strcpy(name, path); + strcat(name, "/"); + p = name + strlen(name); + + entry = readdir(dir); + while (entry) { + if (!strncmp(entry->d_name, match, strlen(match))) { + + strcpy(name, entry->d_name); + closedir(dir); + return name; + } + entry = readdir(dir); + } + closedir(dir); + return NULL; +} + +int get_dev(char *class, int *major, int *minor, char *extra) +{ + char path[1024]; + char *name; + FILE *fp; + + name = strchr(class,':'); + if (!name) + return -1; + *name = 0; + name++; + + *extra = 0; + + if (!strcmp(class, "input")) { + char *event; + + sprintf(path, "/sys/class/%s/%s/", class, name); + event = seek_name(path, "event"); + if (!event) + return -1; + + strcpy(extra, event); + + sprintf(path, "/sys/class/%s/%s/%s/dev", class, name, event); + + } else + sprintf(path, "/sys/class/%s/%s/dev", class, name); + + fp = fopen(path, "r"); + if (!fp) + return -1; + + fscanf(fp, "%d:%d", major, minor); + + return 0; +} + +/* + Examples of subdevs: + sound:audio1 + sound:controlC1 + sound:dsp1 + sound:mixer1 + sound:pcmC1D0c + dvb:dvb0.demux0 + dvb:dvb0.dvr0 + dvb:dvb0.frontend0 + dvb:dvb0.net0 + i2c-adapter:i2c-4 + input:input8 +*/ + +void get_subdevs(char *path) +{ + DIR *dir; + struct dirent *entry; + struct stat st; + char *p, name[1024], extra[20]; + int major, minor; + + dir = opendir(path); + if (!dir) + return; + + strcpy(name, path); + strcat(name, "/"); + p = name + strlen(name); + + printf("Associated devices:\n"); + entry = readdir(dir); + while (entry) { + strcpy(p, entry->d_name); + if ((lstat(name, &st) == 0) && + !S_ISDIR(st.st_mode)) { + char *s = strchr(entry->d_name, ':'); + if (s) { + printf("\t%s", entry->d_name); + if (!get_dev(entry->d_name, &major, &minor, extra)) { + if (*extra) + printf(":%s (dev %d,%d)", + extra, major, minor); + else + printf(" (dev %d,%d)", + major, minor); + } + printf("\n"); + } + } + entry = readdir(dir); + } + closedir(dir); +} + void get_sysfs(char *fname) { struct v4l2_driver drv; @@ -148,9 +274,11 @@ void get_sysfs(char *fname) printf("bus info = %s\n", drv.cap.bus_info); path = obtain_bus_sysfs_path((char *)drv.cap.bus_info); if (path) { - printf("sysfs path = %s\n\n", path); + printf("sysfs path = %s\n", path); + get_subdevs(path); free(path); } + printf("\n"); v4l2_close(&drv); } diff --git a/v4l2-apps/util/xc3028-firmware/firmware-tool.c b/v4l2-apps/util/xc3028-firmware/firmware-tool.c index de1262ceb..43b78df7e 100644 --- a/v4l2-apps/util/xc3028-firmware/firmware-tool.c +++ b/v4l2-apps/util/xc3028-firmware/firmware-tool.c @@ -33,8 +33,8 @@ #include <asm/byteorder.h> #include <asm/types.h> -#include "../../../linux/drivers/media/video/tuner-xc2028-types.h" -#include "../../../linux/include/linux/videodev2.h" +#include "../../../linux/drivers/media/common/tuners/tuner-xc2028-types.h" +#include "linux/videodev2.h" #include "extract_head.h" #include "standards.h" |