diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-09-05 10:20:56 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-09-05 10:20:56 -0300 |
commit | 5f2d82cefe0ce930f3146875c88b993695c7e03a (patch) | |
tree | 2d33fe6b5949e509d85f48f5c8331adcb22151d3 /v4l2-apps/lib/libv4l | |
parent | ad789ba2904603896206a72f0445e8ba56da7134 (diff) | |
parent | 2257e70baa578d959dc4bdcce498ef07d2af00da (diff) | |
download | mediapointer-dvb-s2-5f2d82cefe0ce930f3146875c88b993695c7e03a.tar.gz mediapointer-dvb-s2-5f2d82cefe0ce930f3146875c88b993695c7e03a.tar.bz2 |
merge: http://linuxtv.org/hg/~awalls/v4l-dvb
From: Mauro Carvalho Chehab <mchehab@redhat.com>
Fixed a trivial conflict between a janitor's patch from Hans Verkuil and
Andy patch series.
Priority: normal
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'v4l2-apps/lib/libv4l')
24 files changed, 775 insertions, 179 deletions
diff --git a/v4l2-apps/lib/libv4l/ChangeLog b/v4l2-apps/lib/libv4l/ChangeLog index 3d4e40ad2..eefe0028b 100644 --- a/v4l2-apps/lib/libv4l/ChangeLog +++ b/v4l2-apps/lib/libv4l/ChangeLog @@ -1,8 +1,20 @@ +libv4l-0.4.2 +------------ +* The bayer pixel order in gspca's sonixb driver was different from that in + the sn9c102 driver from the mainline kernel, a recent gspca patch fixes + this, adjust libv4l to match (and make it work properly with the sn9c102 + driver). + libv4l-0.4.1 ------------ * When the driver supports read() and we are not converting let the driver handle read() instead of emulating it with mmap mode - +* Fix errors and warnings when compiling with ICC (Gregor Jasny) +* Add support to libv4lconvert for rotating images 90 (for Pixart 7302 cams) + or 180 (Philips SPC200NC / Philips SPC300NC) degrees +* Add support for Pixart custom JPEG format +* Hide non public symbols (Gregor Jasny) +* Fix and enable x86_64 asm jpeg decompress helper functions (Gregor Jasny) libv4l-0.4.0 ------------ diff --git a/v4l2-apps/lib/libv4l/Makefile b/v4l2-apps/lib/libv4l/Makefile index 31254a09e..92928271a 100644 --- a/v4l2-apps/lib/libv4l/Makefile +++ b/v4l2-apps/lib/libv4l/Makefile @@ -1,5 +1,5 @@ LIB_RELEASE=0 -V4L2_LIB_VERSION=$(LIB_RELEASE).4.1 +V4L2_LIB_VERSION=$(LIB_RELEASE).4.2 all clean install: $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ diff --git a/v4l2-apps/lib/libv4l/include/libv4l1.h b/v4l2-apps/lib/libv4l/include/libv4l1.h index 9036ae869..c878cc198 100644 --- a/v4l2-apps/lib/libv4l/include/libv4l1.h +++ b/v4l2-apps/lib/libv4l/include/libv4l1.h @@ -26,9 +26,15 @@ extern "C" { #include <stdio.h> #include <unistd.h> +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + /* Point this to a FILE opened for writing when you want to log error and status messages to a file, when NULL errors will get send to stderr */ -extern FILE *v4l1_log_file; +LIBV4L_PUBLIC extern FILE *v4l1_log_file; /* Just like your regular open/close/etc, except that when opening a v4l2 capture only device, full v4l1 emulation is done including emulating the @@ -51,14 +57,14 @@ extern FILE *v4l1_log_file; capture formats, like hw specific bayer compression methods). */ -int v4l1_open (const char *file, int oflag, ...); -int v4l1_close(int fd); -int v4l1_dup(int fd); -int v4l1_ioctl (int fd, unsigned long int request, ...); -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, +LIBV4L_PUBLIC int v4l1_open (const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l1_close(int fd); +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); -int v4l1_munmap(void *_start, size_t length); +LIBV4L_PUBLIC int v4l1_munmap(void *_start, size_t length); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/include/libv4l2.h b/v4l2-apps/lib/libv4l/include/libv4l2.h index 63529cf4b..b05b57cb6 100644 --- a/v4l2-apps/lib/libv4l/include/libv4l2.h +++ b/v4l2-apps/lib/libv4l/include/libv4l2.h @@ -26,9 +26,15 @@ extern "C" { #endif /* __cplusplus */ +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + /* Point this to a FILE opened for writing when you want to log error and status messages to a file, when NULL errors will get send to stderr */ -extern FILE *v4l2_log_file; +LIBV4L_PUBLIC extern FILE *v4l2_log_file; /* Just like your regular open/close/etc, except that format conversion is done if necessary when capturing. That is if you (try to) set a capture @@ -52,14 +58,14 @@ extern FILE *v4l2_log_file; fail. */ -int v4l2_open (const char *file, int oflag, ...); -int v4l2_close(int fd); -int v4l2_dup(int fd); -int v4l2_ioctl (int fd, unsigned long int request, ...); -ssize_t v4l2_read (int fd, void* buffer, size_t n); -void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC int v4l2_open (const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l2_close(int fd); +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); -int v4l2_munmap(void *_start, size_t length); +LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length); /* Misc utility functions */ @@ -70,12 +76,12 @@ int v4l2_munmap(void *_start, size_t length); Normally returns 0, even if the cid did not exist or was locked, returns non 0 when an other error occured. */ -int v4l2_set_control(int fd, int cid, int value); +LIBV4L_PUBLIC int v4l2_set_control(int fd, int cid, int value); /* This function returns a value of 0 - 65535, scaled to from the actual range of the given v4l control id. when the cid does not exist, could not be accessed for some reason, or some error occured 0 is returned. */ -int v4l2_get_control(int fd, int cid); +LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid); /* "low level" access functions, these functions allow somewhat lower level @@ -95,7 +101,7 @@ int v4l2_get_control(int fd, int cid); Returns fd on success, -1 if the fd is not suitable for use through libv4l2 (note the fd is left open in this case). */ -int v4l2_fd_open(int fd, int v4l2_flags); +LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/include/libv4lconvert.h b/v4l2-apps/lib/libv4l/include/libv4lconvert.h index d8446dac8..87e41ceaa 100644 --- a/v4l2-apps/lib/libv4l/include/libv4lconvert.h +++ b/v4l2-apps/lib/libv4l/include/libv4lconvert.h @@ -31,34 +31,45 @@ extern "C" { #endif /* __cplusplus */ +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + struct v4lconvert_data; -struct v4lconvert_data *v4lconvert_create(int fd); -void v4lconvert_destroy(struct v4lconvert_data *data); +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd); +LIBV4L_PUBLIC void v4lconvert_destroy(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. */ -int v4lconvert_try_format(struct v4lconvert_data *data, +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 */ -int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt); +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? */ +LIBV4L_PUBLIC int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt); /* in */ /* return value of -1 on error, otherwise the amount of bytes written to dest */ -int v4lconvert_convert(struct v4lconvert_data *data, +LIBV4L_PUBLIC 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); /* get a string describing the last error*/ -const char *v4lconvert_get_error_message(struct v4lconvert_data *data); +LIBV4L_PUBLIC const char *v4lconvert_get_error_message(struct v4lconvert_data *data); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/libv4l1/Makefile b/v4l2-apps/lib/libv4l/libv4l1/Makefile index c92731daa..27848477e 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l1/Makefile @@ -1,7 +1,7 @@ -CPPFLAGS = -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes LIBS = -lpthread @@ -17,7 +17,7 @@ else V4L1_LIB = libv4l1.so V4L1_OBJS += ../libv4l2/libv4l2.so TARGETS += $(V4L1COMPAT) -CPPFLAGS += -fPIC +override CPPFLAGS += -fPIC endif ifeq ($(LIB_RELEASE),) diff --git a/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c b/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c index 80d2fa09b..39f2d29e0 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c +++ b/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c @@ -59,6 +59,7 @@ #include <linux/videodev.h> #include <linux/videodev2.h> #include <libv4l2.h> +#include "libv4l1.h" #include "libv4l1-priv.h" #define V4L1_SUPPORTS_ENUMINPUT 0x01 @@ -582,7 +583,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) input2.index = chan->channel; result = v4l2_ioctl(fd, VIDIOC_ENUMINPUT, &input2); if (result == 0) { - snprintf(chan->name, sizeof(chan->name), "%s", input2.name); + snprintf(chan->name, sizeof(chan->name), "%s", (char*)input2.name); if (input2.type == V4L2_INPUT_TYPE_TUNER) { chan->tuners = 1; chan->type = VIDEO_TYPE_TV; diff --git a/v4l2-apps/lib/libv4l/libv4l1/log.c b/v4l2-apps/lib/libv4l/libv4l1/log.c index cccc3e6d0..74ce0f20b 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/log.c +++ b/v4l2-apps/lib/libv4l/libv4l1/log.c @@ -25,6 +25,7 @@ #include <asm/types.h> /* end broken header workaround includes */ #include <linux/videodev.h> +#include "libv4l1-priv.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -92,9 +93,9 @@ void v4l1_log_ioctl(unsigned long int request, void *arg, int result) break; case VIDIOCGWIN: case VIDIOCSWIN: - fprintf(v4l1_log_file,"width\t%d\n", + fprintf(v4l1_log_file,"width\t%u\n", ((struct video_window *)arg)->width); - fprintf(v4l1_log_file,"height\t%d\n", + fprintf(v4l1_log_file,"height\t%u\n", ((struct video_window *)arg)->height); break; @@ -115,7 +116,7 @@ void v4l1_log_ioctl(unsigned long int request, void *arg, int result) fprintf(v4l1_log_file,"palette %d\n",( (int)((struct video_picture*)arg)->palette) ); break; - case VIDIOCCAPTURE: fprintf(v4l1_log_file,"on/of? %d\n", *((int *)arg) ); + case VIDIOCCAPTURE: fprintf(v4l1_log_file,"on/off? %d\n", *((int *)arg) ); break; case VIDIOCSYNC: fprintf(v4l1_log_file,"sync %d\n", *((int *)arg) ); diff --git a/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c b/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c index f1134fe3b..e4293d2f9 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c +++ b/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c @@ -26,12 +26,21 @@ #include <fcntl.h> #include <libv4l1.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + /* Check that open/read/mmap is not a define */ #if defined open || defined read || defined mmap #error open/read/mmap is a prepocessor macro !! #endif -int open (const char *file, int oflag, ...) +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open (const char *file, int oflag, ...) { int fd; @@ -52,7 +61,7 @@ int open (const char *file, int oflag, ...) return fd; } -int open64 (const char *file, int oflag, ...) +LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) { int fd; @@ -73,16 +82,16 @@ int open64 (const char *file, int oflag, ...) return fd; } -int close(int fd) { +LIBV4L_PUBLIC int close(int fd) { return v4l1_close(fd); } -int dup(int fd) +LIBV4L_PUBLIC int dup(int fd) { return v4l1_dup(fd); } -int ioctl (int fd, unsigned long int request, ...) +LIBV4L_PUBLIC int ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; @@ -94,24 +103,25 @@ int ioctl (int fd, unsigned long int request, ...) return v4l1_ioctl (fd, request, arg); } -ssize_t read(int fd, void* buffer, size_t n) +LIBV4L_PUBLIC ssize_t read(int fd, void* buffer, size_t n) { return v4l1_read (fd, buffer, n); } -void *mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd, __off_t offset) { return v4l1_mmap(start, length, prot, flags, fd, offset); } -void *mmap64(void *start, size_t length, int prot, int flags, int fd, +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); } -int munmap(void *start, size_t length) +LIBV4L_PUBLIC int munmap(void *start, size_t length) { return v4l1_munmap(start, length); } + diff --git a/v4l2-apps/lib/libv4l/libv4l2/Makefile b/v4l2-apps/lib/libv4l/libv4l2/Makefile index d8ac01c34..648d27c0c 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l2/Makefile @@ -1,7 +1,7 @@ -CPPFLAGS = -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes LIBS = -lpthread @@ -17,7 +17,7 @@ else V4L2_LIB = libv4l2.so V4L2_OBJS += ../libv4lconvert/libv4lconvert.so TARGETS += $(V4L2CONVERT) -CPPFLAGS += -fPIC +override CPPFLAGS += -fPIC endif ifeq ($(LIB_RELEASE),) diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index a40ab4ffe..7029f69c3 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -252,23 +252,54 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) return 0; } -static int v4l2_dequeue_read_buffer(int index, int *bytesused) +static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, + unsigned char *dest, int dest_size) { - int result; - struct v4l2_buffer buf; + const int max_tries = 10; + int result, tries = max_tries; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - 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; + /* Make sure we have the real v4l2 buffers mapped */ + if ((result = v4l2_map_buffers(index))) 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; + return result; + } + + devices[index].frame_queued &= ~(1 << buf->index); + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].frame_pointers[buf->index], + buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf + + buf->index * V4L2_FRAME_BUF_SIZE), dest_size); + + if (result < 0) { + int saved_err = errno; + + if(errno == EAGAIN) + V4L2_LOG("warning error while converting frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + else + V4L2_LOG_ERR("converting / decoding frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + + v4l2_queue_read_buffer(index, buf->index); + errno = saved_err; + } + tries--; + } while (result < 0 && errno == EAGAIN && tries); + + if (result < 0 && errno == EAGAIN) { + V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s\n", + max_tries, v4lconvert_get_error_message(devices[index].convert)); } - devices[index].frame_queued &= ~(1 << buf.index); - *bytesused = buf.bytesused; - return buf.index; + return result; } static int v4l2_queue_read_buffers(int index) @@ -614,7 +645,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) is_capture_request = 1; break; case VIDIOC_ENUM_FMT: - if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION)) is_capture_request = 1; break; case VIDIOC_TRY_FMT: @@ -663,9 +695,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if (stream_needs_locking) pthread_mutex_lock(&devices[index].stream_lock); - converting = devices[index].src_fmt.fmt.pix.pixelformat != - devices[index].dest_fmt.fmt.pix.pixelformat; - + converting = v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt); switch (request) { case VIDIOC_QUERYCAP: @@ -815,15 +846,16 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if ((result = v4l2_deactivate_read_stream(index))) break; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); - if (result) { - V4L2_LOG_ERR("dequeing buffer: %s\n", strerror(errno)); + if (!converting) { + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); + if (result) { + int saved_err = errno; + V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); + errno = saved_err; + } break; } - if (!converting) - break; - /* An application can do a DQBUF before mmap-ing in the buffer, but we need the buffer _now_ to write our converted data to it! */ @@ -844,23 +876,9 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } } - /* Make sure we have the real v4l2 buffers mapped before trying to - read from them */ - if ((result = v4l2_map_buffers(index))) - break; - - result = v4lconvert_convert(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt, - devices[index].frame_pointers[buf->index], - buf->bytesused, - devices[index].convert_mmap_buf + - buf->index * V4L2_FRAME_BUF_SIZE, - V4L2_FRAME_BUF_SIZE); - if (result < 0) { - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + result = v4l2_dequeue_and_convert(index, buf, 0, V4L2_FRAME_BUF_SIZE); + if (result < 0) break; - } buf->bytesused = result; buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; @@ -901,22 +919,23 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } -ssize_t v4l2_read (int fd, void* buffer, size_t n) +ssize_t v4l2_read (int fd, void* dest, size_t n) { ssize_t result; - int index, bytesused = 0, frame_index; + int index; + struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_read, fd, buffer, n); + return syscall(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) && - devices[index].src_fmt.fmt.pix.pixelformat == - devices[index].dest_fmt.fmt.pix.pixelformat) { - result = syscall(SYS_read, fd, buffer, n); + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { + result = syscall(SYS_read, fd, dest, n); goto leave; } @@ -931,26 +950,12 @@ ssize_t v4l2_read (int fd, void* buffer, size_t n) goto leave; } - if ((frame_index = v4l2_dequeue_read_buffer(index, &bytesused)) < 0) { - result = -1; - goto leave; - } - - /* ensure buffers are mapped before using them (they could have been - unmapped by a s_fmt ioctl) */ - if ((result = v4l2_map_buffers(index))) - goto leave; - - result = v4lconvert_convert(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt, - devices[index].frame_pointers[frame_index], bytesused, - buffer, n); - - v4l2_queue_read_buffer(index, frame_index); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + result = v4l2_dequeue_and_convert(index, &buf, dest, n); - if (result < 0) - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + if (result >= 0) + v4l2_queue_read_buffer(index, buf.index); leave: pthread_mutex_unlock(&devices[index].stream_lock); @@ -988,8 +993,8 @@ 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 ?? */ - devices[index].src_fmt.fmt.pix.pixelformat == - devices[index].dest_fmt.fmt.pix.pixelformat) { + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { errno = EINVAL; result = MAP_FAILED; goto leave; diff --git a/v4l2-apps/lib/libv4l/libv4l2/log.c b/v4l2-apps/lib/libv4l/libv4l2/log.c index 05f6c46d7..6237d55ec 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/log.c +++ b/v4l2-apps/lib/libv4l/libv4l2/log.c @@ -25,6 +25,8 @@ #include <asm/types.h> /* end broken header workaround includes */ #include <linux/videodev2.h> +#include "libv4l2.h" +#include "libv4l2-priv.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -116,7 +118,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) int pixfmt = fmt->fmt.pix.pixelformat; if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %dx%d\n", + fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %ux%u\n", pixfmt & 0xff, (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, @@ -136,7 +138,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) struct v4l2_requestbuffers *req = arg; fprintf(v4l2_log_file, " count: %u type: %d memory: %d\n", - req->count, req->type, req->memory); + req->count, (int)req->type, (int)req->memory); } break; } diff --git a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c index f312828c6..e8756f338 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c @@ -27,6 +27,8 @@ #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> @@ -41,7 +43,13 @@ #error open/read/mmap is a prepocessor macro !! #endif -int open (const char *file, int oflag, ...) +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open (const char *file, int oflag, ...) { int fd; struct v4l2_capability cap; @@ -84,7 +92,7 @@ int open (const char *file, int oflag, ...) return fd; } -int open64(const char *file, int oflag, ...) +LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) { int fd; @@ -107,17 +115,17 @@ int open64(const char *file, int oflag, ...) return fd; } -int close(int fd) +LIBV4L_PUBLIC int close(int fd) { return v4l2_close(fd); } -int dup(int fd) +LIBV4L_PUBLIC int dup(int fd) { return v4l2_dup(fd); } -int ioctl (int fd, unsigned long int request, ...) +LIBV4L_PUBLIC int ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; @@ -129,24 +137,25 @@ int ioctl (int fd, unsigned long int request, ...) return v4l2_ioctl (fd, request, arg); } -ssize_t read (int fd, void* buffer, size_t n) +LIBV4L_PUBLIC ssize_t read (int fd, void* buffer, size_t n) { return v4l2_read (fd, buffer, n); } -void *mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd, __off_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } -void *mmap64(void *start, size_t length, int prot, int flags, int fd, +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); } -int munmap(void *start, size_t length) +LIBV4L_PUBLIC int munmap(void *start, size_t length) { return v4l2_munmap(start, length); } + diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile index c2a5be942..641d19d6e 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile @@ -1,16 +1,16 @@ -CPPFLAGS = -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes ifeq ($(LINKTYPE),static) CONVERT_LIB = libv4lconvert.a else CONVERT_LIB = libv4lconvert.so -CPPFLAGS += -fPIC +override CPPFLAGS += -fPIC endif -CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o \ +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 INCLUDES = ../include/libv4lconvert.h diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/flip.c b/v4l2-apps/lib/libv4l/libv4lconvert/flip.c new file mode 100644 index 000000000..cd3468a89 --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4lconvert/flip.c @@ -0,0 +1,107 @@ +/* + +# RGB / YUV flip/rotate 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 "libv4lconvert-priv.h" + +void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + src += 3 * width * height - 3; + + for (i = 0; i < width * height; i++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src -= 3; + } +} + +void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + /* First flip x and y of the Y plane */ + src += width * height - 1; + for (i = 0; i < width * height; i++) + *dst++ = *src--; + + /* Now flip the U plane */ + src += width * height * 5 / 4; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; + + /* Last flip the V plane */ + src += width * height / 2; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; +} + +void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight) +{ + int x, y; +#define srcwidth destheight +#define srcheight destwidth + + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = ((srcheight - x - 1) * srcwidth + y) * 3; + *dst++ = src[offset++]; + *dst++ = src[offset++]; + *dst++ = src[offset]; + } +} + +void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight) +{ + int x, y; + + /* Y-plane */ + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* U-plane */ + src += srcwidth * srcheight; + destwidth /= 2; + destheight /= 2; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* V-plane */ + src += srcwidth * srcheight; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } +} diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c b/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c index ba70309c9..532abc7ea 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c @@ -80,7 +80,7 @@ #define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) -#if defined(__GNUC__) && (defined(__i686__)) // || defined(__x86_64__)) +#if defined(__GNUC__) && (defined(__i686__) || defined(__x86_64__)) static inline unsigned char descale_and_clamp(int x, int shift) { @@ -92,7 +92,7 @@ static inline unsigned char descale_and_clamp(int x, int shift) "\tcmpl %4,%1\n" "\tcmovg %4,%1\n" : "=r"(x) - : "0"(x), "Ic"((unsigned char)shift), "ir"(1UL<<(shift-1)), "r" (0xff), "r" (0) + : "0"(x), "Ic"((unsigned char)shift), "ir"(1U<<(shift-1)), "r" (0xff), "r" (0) ); return x; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h index bdf847186..915c33283 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h @@ -43,6 +43,10 @@ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') #endif +#ifndef V4L2_PIX_FMT_PJPG +#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') +#endif + #ifndef V4L2_PIX_FMT_SGBRG8 #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G','B','R','G') #endif @@ -61,15 +65,21 @@ snprintf(data->error_msg, V4LCONVERT_ERROR_MSG_SIZE, \ "v4l-convert: error " __VA_ARGS__) +#define V4LCONVERT_UPSIDE_DOWN 0x01 struct v4lconvert_data { int fd; + int flags; /* bitfield */ int supported_src_formats; /* bitfield */ unsigned int no_formats; char error_msg[V4LCONVERT_ERROR_MSG_SIZE]; struct jdec_private *jdec; }; +struct v4lconvert_flags_info { + const char *card; + int flags; +}; void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst, int width, int height); @@ -107,4 +117,16 @@ void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv, int width, int height, unsigned int pixfmt); +void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight); + +void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight); + +void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height); + #endif diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c index 4c9e67d52..4b48bfac4 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c @@ -48,17 +48,26 @@ static const unsigned int supported_src_pixfmts[] = { V4L2_PIX_FMT_SPCA561, V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_PAC207, + V4L2_PIX_FMT_PJPG, }; static const unsigned int supported_dst_pixfmts[] = { SUPPORTED_DST_PIXFMTS }; +/* List of cams which need special flags */ +static const struct v4lconvert_flags_info v4lconvert_flags[] = { + { "USB Camera (0471:0325)", V4LCONVERT_UPSIDE_DOWN }, /* SPC200NC */ + { "USB Camera (0471:0326)", V4LCONVERT_UPSIDE_DOWN }, /* SPC300NC */ + { "SPC 200NC ", V4LCONVERT_UPSIDE_DOWN }, + { "SPC 300NC ", V4LCONVERT_UPSIDE_DOWN }, +}; struct v4lconvert_data *v4lconvert_create(int fd) { int i, j; struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data)); + struct v4l2_capability cap; if (!data) return NULL; @@ -84,6 +93,15 @@ struct v4lconvert_data *v4lconvert_create(int fd) data->no_formats = i; + /* Check if this cam has any special flags */ + if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) { + for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++) + if (!strcmp((const char *)v4lconvert_flags[i].card, (char *)cap.card)) { + data->flags = v4lconvert_flags[i].flags; + break; + } + } + return data; } @@ -214,17 +232,42 @@ int v4lconvert_try_format(struct v4lconvert_data *data, return 0; } +/* Is conversion necessary ? */ +int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt) /* in */ +{ + int i; + + if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) + return 1; /* Formats differ */ + + if (!(data->flags & V4LCONVERT_UPSIDE_DOWN)) + return 0; /* Formats identical and we don't need flip */ + + /* Formats are identical, but we need flip, do we support the dest_fmt? */ + for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++) + if (supported_dst_pixfmts[i] == dest_fmt->fmt.pix.pixelformat) + break; + + if (i == ARRAY_SIZE(supported_dst_pixfmts)) + return 0; /* Needs flip but we cannot do it :( */ + else + return 1; /* Needs flip and thus conversion */ +} + int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt, /* in */ - unsigned char *src, int src_size, unsigned char *dest, int dest_size) + unsigned char *src, int src_size, unsigned char *_dest, int dest_size) { unsigned int header_width, header_height; - int result, needed; + int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE; unsigned char *components[3]; + unsigned char *dest = _dest; /* Special case when no conversion is needed */ - if(!memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) { + if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) { int to_copy = MIN(dest_size, src_size); memcpy(dest, src, to_copy); return to_copy; @@ -251,7 +294,15 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } + if (data->flags & V4LCONVERT_UPSIDE_DOWN) { + rotate = 180; + dest = alloca(needed); + } + switch (src_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_PJPG: + jpeg_flags |= TINYJPEG_FLAGS_PIXART_JPEG; + /* Fall through */ case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: if (!data->jdec) { @@ -262,9 +313,7 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } } - tinyjpeg_set_flags(data->jdec, - (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)? - TINYJPEG_FLAGS_MJPEG_TABLE : 0); + tinyjpeg_set_flags(data->jdec, jpeg_flags); if (tinyjpeg_parse_header(data->jdec, src, src_size)) { V4LCONVERT_ERR("parsing JPEG header: %s\n", tinyjpeg_get_errorstring(data->jdec)); @@ -273,13 +322,22 @@ int v4lconvert_convert(struct v4lconvert_data *data, } tinyjpeg_get_size(data->jdec, &header_width, &header_height); - if (header_width != dest_fmt->fmt.pix.width || header_height != dest_fmt->fmt.pix.height) { - V4LCONVERT_ERR("unexpected width / height in JPEG header\n"); - V4LCONVERT_ERR("expected: %dx%d, header: %ux%u\n", - dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, - header_width, header_height); - errno = EIO; - return -1; + if (header_width != dest_fmt->fmt.pix.width || + header_height != dest_fmt->fmt.pix.height) { + /* Check for (pixart) rotated JPEG */ + if (header_width == dest_fmt->fmt.pix.height || + header_height == dest_fmt->fmt.pix.width) { + if (!rotate) + dest = alloca(needed); + rotate += 90; + } else { + V4LCONVERT_ERR("unexpected width / height in JPEG header" + "expected: %ux%u, header: %ux%u\n", + dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, + header_width, header_height); + errno = EIO; + return -1; + } } components[0] = dest; @@ -303,13 +361,24 @@ int v4lconvert_convert(struct v4lconvert_data *data, break; } - /* If the JPEG header checked out ok and we get an error during actual - decompression, log the error, but don't return an errorcode to the - application, so that the user gets what we managed to decompress */ - if (result) - fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s", - tinyjpeg_get_errorstring(data->jdec)); - + if (result) { + /* Pixart webcam's seem to regulary generate corrupt frames, which + are best thrown away to avoid flashes in the video stream. Tell + the upper layer this is an intermediate fault and it should try + again with a new buffer by setting errno to EAGAIN */ + if (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_PJPG) { + V4LCONVERT_ERR("Error decompressing JPEG: %s", + tinyjpeg_get_errorstring(data->jdec)); + errno = EAGAIN; + return -1; + } else { + /* If the JPEG header checked out ok and we get an error during actual + decompression, log the error, but don't return an errorcode to the + application, so that the user gets what we managed to decompress */ + fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s", + tinyjpeg_get_errorstring(data->jdec)); + } + } break; case V4L2_PIX_FMT_SBGGR8: @@ -387,7 +456,7 @@ int v4lconvert_convert(struct v4lconvert_data *data, case V4L2_PIX_FMT_SN9C10X: v4lconvert_decode_sn9c10x(src, tmpbuf, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height); - bayer_fmt = V4L2_PIX_FMT_SGBRG8; + bayer_fmt = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_PAC207: v4lconvert_decode_pac207(src, tmpbuf, dest_fmt->fmt.pix.width, @@ -455,6 +524,45 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } + /* Note when rotating dest is our temporary buffer to which our conversion + was done and _dest is the real dest! If the formats are identical no + conversion has been done! */ + if (rotate && dest_fmt->fmt.pix.pixelformat == src_fmt->fmt.pix.pixelformat) + dest = src; + + switch (rotate) { + case 0: + break; + case 90: + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate90_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rotate90_yuv420(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } + break; + case 180: + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate180_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rotate180_yuv420(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } + break; + default: + printf("FIXME add %d degrees rotation\n", rotate); + } + return needed; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c b/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c index 4887c25ee..97ef4ebc0 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c @@ -38,7 +38,7 @@ static struct { signed char val; } table[256]; -void init_pixart_decoder(void) +static void init_pixart_decoder(void) { int i; int is_abs, val, len; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c index 742dd06ce..883d62330 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c @@ -20,6 +20,8 @@ */ +#include "libv4lconvert-priv.h" + #define RGB2YUV(r,g,b,y,u,v) \ (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15); \ (u) = (( -4878*(r) - 9578*(g) + 14456*(b) + 4210688) >> 15); \ diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c b/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c index 98a513378..4ea526d49 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c @@ -50,7 +50,7 @@ static int sonix_unknown = 0; present at the MSB of byte x. */ -void sonix_decompress_init(void) +static void sonix_decompress_init(void) { int i; int is_abs, val, len, unk; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c b/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c index 802345af0..01eed4ec5 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c @@ -28,6 +28,7 @@ * but it might work with other spca561 cameras */ #include <string.h> +#include "libv4lconvert-priv.h" /*fixme: not reentrant */ static unsigned int bit_bucket; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c index 5c3b4e26d..fc9efbeb2 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c @@ -214,6 +214,28 @@ static const unsigned char val_ac_chrominance[] = 0xf9, 0xfa }; +const unsigned char pixart_quantization[][64] = { + { + 0x07, 0x07, 0x08, 0x0a, 0x09, 0x07, 0x0d, 0x0b, + 0x0c, 0x0d, 0x11, 0x10, 0x0f, 0x12, 0x17, 0x27, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + }, + { + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + }, +}; /* * 4 functions to manage the stream @@ -246,7 +268,7 @@ static const unsigned char val_ac_chrominance[] = unsigned char c; \ if (stream >= priv->stream_end) { \ snprintf(priv->error_string, sizeof(priv->error_string), \ - "fill_nbits error: need %d more bits\n", \ + "fill_nbits error: need %u more bits\n", \ nbits_wanted - nbits_in_reservoir); \ longjmp(priv->jump_state, -EIO); \ } \ @@ -288,6 +310,74 @@ static const unsigned char val_ac_chrominance[] = } while(0); +/* Special Pixart versions of the *_nbits functions, these remove the special + ff ff ff xx sequences pixart cams insert from the bitstream */ +#define pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) \ +do { \ + while (nbits_in_reservoir<nbits_wanted) \ + { \ + unsigned char c; \ + if (stream >= priv->stream_end) { \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "fill_nbits error: need %u more bits\n", \ + nbits_wanted - nbits_in_reservoir); \ + longjmp(priv->jump_state, -EIO); \ + } \ + c = *stream++; \ + reservoir <<= 8; \ + if (c == 0xff) { \ + switch (stream[0]) { \ + case 0x00: \ + stream++; \ + break; \ + case 0xd9: /* EOF marker */ \ + stream++; \ + if (stream != priv->stream_end) { \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "Pixart JPEG error: premature EOF\n"); \ + longjmp(priv->jump_state, -EIO); \ + } \ + break; \ + case 0xff: \ + if (stream[1] == 0xff && (stream[2] < 7 || stream[2] == 0xff)) { \ + stream += 3; \ + c = *stream++; \ + break; \ + } \ + /* Error fall through */ \ + default: \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "Pixart JPEG error: invalid JPEG marker: 0xff 0x%02x 0x%02x 0x%02x\n", \ + (unsigned int)stream[0], (unsigned int)stream[1], \ + (unsigned int)stream[2]); \ + longjmp(priv->jump_state, -EIO); \ + } \ + } \ + reservoir |= c; \ + nbits_in_reservoir+=8; \ + } \ +} while(0); + +/* Signed version !!!! */ +#define pixart_get_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) \ +do { \ + pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \ + result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \ + nbits_in_reservoir -= (nbits_wanted); \ + reservoir &= ((1U<<nbits_in_reservoir)-1); \ + if ((unsigned int)result < (1UL<<((nbits_wanted)-1))) \ + result += (0xFFFFFFFFUL<<(nbits_wanted))+1; \ +} while(0); + +#define pixart_look_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) \ +do { \ + pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \ + result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \ +} while(0); + +/* Note skip_nbits is identical for both */ + + #define be16_to_cpu(x) (((x)[0]<<8)|(x)[1]) static void resync(struct jdec_private *priv); @@ -334,9 +424,50 @@ static int get_next_huffman_code(struct jdec_private *priv, struct huffman_table slowtable+=2; } } + snprintf(priv->error_string, sizeof(priv->error_string), + "unknown huffman code: %08x\n", (unsigned int)hcode); + longjmp(priv->jump_state, -EIO); return 0; } +/* identical as above but with *_nbits replaced with pixart_*_nbits */ +static int pixart_get_next_huffman_code(struct jdec_private *priv, + struct huffman_table *huffman_table) +{ + int value, hcode; + unsigned int extra_nbits, nbits; + uint16_t *slowtable; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, HUFFMAN_HASH_NBITS, hcode); + value = huffman_table->lookup[hcode]; + if (value >= 0) + { + unsigned int code_size = huffman_table->code_size[value]; + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, code_size); + return value; + } + + /* Decode more bits each time ... */ + for (extra_nbits=0; extra_nbits<16-HUFFMAN_HASH_NBITS; extra_nbits++) + { + nbits = HUFFMAN_HASH_NBITS + 1 + extra_nbits; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits, hcode); + slowtable = huffman_table->slowtable[extra_nbits]; + /* Search if the code is in this array */ + while (slowtable[0]) { + if (slowtable[0] == hcode) { + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits); + return slowtable[1]; + } + slowtable+=2; + } + } + snprintf(priv->error_string, sizeof(priv->error_string), + "unknown huffman code: %08x\n", (unsigned int)hcode); + longjmp(priv->jump_state, -EIO); + return 0; +} @@ -388,15 +519,84 @@ static void process_Huffman_data_unit(struct jdec_private *priv, int component) else { j += count_0; /* skip count_0 zeroes */ - get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); - j++; + if (j < 64) { + get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } } } + if (j > 64) { + snprintf(priv->error_string, sizeof(priv->error_string), + "error: more then 63 AC components (%d) in huffman unit\n", (int)j); + longjmp(priv->jump_state, -EIO); + } + for (j = 0; j < 64; j++) c->DCT[j] = DCT[zigzag[j]]; } +/* identical as above both with *_nbits replaced with pixart_*_nbits */ +static void pixart_process_Huffman_data_unit(struct jdec_private *priv, int component) +{ + unsigned char j; + unsigned int huff_code; + unsigned char size_val, count_0; + + struct component *c = &priv->component_infos[component]; + short int DCT[64]; + + /* Initialize the DCT coef table */ + memset(DCT, 0, sizeof(DCT)); + + /* DC coefficient decoding */ + huff_code = pixart_get_next_huffman_code(priv, c->DC_table); + if (huff_code) { + pixart_get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]); + DCT[0] += c->previous_DC; + c->previous_DC = DCT[0]; + } else { + DCT[0] = c->previous_DC; + } + + + /* AC coefficient decoding */ + j = 1; + while (j<64) + { + huff_code = pixart_get_next_huffman_code(priv, c->AC_table); + + size_val = huff_code & 0xF; + count_0 = huff_code >> 4; + + if (size_val == 0) + { /* RLE */ + if (count_0 == 0) + break; /* EOB found, go out */ + else if (count_0 == 0xF) + j += 16; /* skip 16 zeros */ + } + else + { + j += count_0; /* skip count_0 zeroes */ + if (j < 64 ) { + pixart_get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } + } + } + + if (j > 64) { + snprintf(priv->error_string, sizeof(priv->error_string), + "error: more then 63 AC components (%d) in huffman unit\n", (int)j); + longjmp(priv->jump_state, -EIO); + } + + for (j = 0; j < 64; j++) + c->DCT[j] = DCT[zigzag[j]]; +} + + /* * Takes two array of bits, and build the huffman table for size, and code * @@ -404,12 +604,13 @@ static void process_Huffman_data_unit(struct jdec_private *priv, int component) * code_size will be used to known how many bits this symbol is encoded. * slowtable will be used when the first lookup didn't give the result. */ -static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) +static int build_huffman_table(struct jdec_private *priv, const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) { unsigned int i, j, code, code_size, val, nbits; unsigned char huffsize[257], *hz; unsigned int huffcode[257], *hc; int next_free_entry; + int slowtable_used[16-HUFFMAN_HASH_NBITS]; /* * Build a temp array @@ -425,7 +626,7 @@ static void build_huffman_table(const unsigned char *bits, const unsigned char * memset(table->lookup, 0xff, sizeof(table->lookup)); for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++) - table->slowtable[i][0] = 0; + slowtable_used[i] = 0; /* Build a temp array * huffcode[X] => code used to write vals[X] @@ -472,32 +673,43 @@ static void build_huffman_table(const unsigned char *bits, const unsigned char * else { /* Perhaps sorting the array will be an optimization */ - uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1]; - while(slowtable[0]) - slowtable+=2; - slowtable[0] = code; - slowtable[1] = val; - slowtable[2] = 0; - /* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */ - } + int slowtable_index = code_size-HUFFMAN_HASH_NBITS-1; + + if (slowtable_used[slowtable_index] == 254) + error("slow Huffman table overflow\n"); + table->slowtable[slowtable_index][slowtable_used[slowtable_index]] + = code; + table->slowtable[slowtable_index][slowtable_used[slowtable_index] + 1] + = val; + slowtable_used[slowtable_index] += 2; + } } + for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++) + table->slowtable[i][slowtable_used[i]] = 0; + + return 0; } -static void build_default_huffman_tables(struct jdec_private *priv) +static int build_default_huffman_tables(struct jdec_private *priv) { if ( (priv->flags & TINYJPEG_FLAGS_MJPEG_TABLE) && priv->default_huffman_table_initialized) - return; + return 0; - build_huffman_table(bits_dc_luminance, val_dc_luminance, &priv->HTDC[0]); - build_huffman_table(bits_ac_luminance, val_ac_luminance, &priv->HTAC[0]); + if (build_huffman_table(priv, bits_dc_luminance, val_dc_luminance, &priv->HTDC[0])) + return -1; + if (build_huffman_table(priv, bits_ac_luminance, val_ac_luminance, &priv->HTAC[0])) + return -1; - build_huffman_table(bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1]); - build_huffman_table(bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1]); + if (build_huffman_table(priv, bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1])) + return -1; + if (build_huffman_table(priv, bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1])) + return -1; priv->default_huffman_table_initialized = 1; + return 0; } @@ -1390,6 +1602,44 @@ static void decode_MCU_2x1_3planes(struct jdec_private *priv) IDCT(&priv->component_infos[cCr], priv->Cr, 8); } +static void pixart_decode_MCU_2x1_3planes(struct jdec_private *priv) +{ + unsigned char marker; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, + 8, marker); + /* I think the marker indicates which quantization table to use, iow + a Pixart JPEG may have a different quantization table per MCU, most + MCU's have 0x44 as marker for which our special Pixart quantization + tables are correct. Unfortunately with a 7302 some blocks also have 0x48, + and sometimes even other values. As 0x48 is quite common too, we really + need to find out the correct table for that, as currently the blocks + with a 0x48 marker look wrong. During normal operation the marker stays + within the range below, if it gets out of this range we're most likely + decoding garbage */ + if (marker < 0x20 || marker > 0x7f) { + snprintf(priv->error_string, sizeof(priv->error_string), + "Pixart JPEG error: invalid MCU marker: 0x%02x\n", + (unsigned int)marker); + longjmp(priv->jump_state, -EIO); + } + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, 8); + + // Y + pixart_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + pixart_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+8, 16); + + // Cb + pixart_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + pixart_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + /* * Decode a 2x1 * .-------. @@ -1676,9 +1926,9 @@ static int parse_SOS(struct jdec_private *priv, const unsigned char *stream) error("We do not support more than %d DC Huffman table\n", HUFFMAN_TABLES); if (cid != priv->component_infos[i].cid) - error("SOS cid order (%d:%d) isn't compatible with the SOF marker (%d:%d)\n", + error("SOS cid order (%u:%u) isn't compatible with the SOF marker (%u:%u)\n", i, cid, i, priv->component_infos[i].cid); - trace("ComponentId:%d tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4); + trace("ComponentId:%u tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4); #endif priv->component_infos[i].AC_table = &priv->HTAC[table&0xf]; priv->component_infos[i].DC_table = &priv->HTDC[table>>4]; @@ -1723,10 +1973,13 @@ static int parse_DHT(struct jdec_private *priv, const unsigned char *stream) trace("Length of the table: %d\n", count); #endif - if (index & 0xf0 ) - build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); - else - build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); + if (index & 0xf0 ) { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTAC[index&0xf])) + return -1; + } else { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTDC[index&0xf])) + return -1; + } length -= 1; length -= 16; @@ -1817,6 +2070,8 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) { int chuck_len; int marker; + int sof_marker_found = 0; + int dqt_marker_found = 0; int sos_marker_found = 0; int dht_marker_found = 0; const unsigned char *next_chunck; @@ -1838,10 +2093,12 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) case SOF: if (parse_SOF(priv, stream) < 0) return -1; + sof_marker_found = 1; break; case DQT: if (parse_DQT(priv, stream) < 0) return -1; + dqt_marker_found = 1; break; case SOS: if (parse_SOS(priv, stream) < 0) @@ -1865,9 +2122,24 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) stream = next_chunck; } + if ( !sof_marker_found || + (!dqt_marker_found && !(priv->flags & TINYJPEG_FLAGS_PIXART_JPEG))) + goto bogus_jpeg_format; + + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + if (!priv->default_huffman_table_initialized) { + build_quantization_table(priv->Q_tables[0], pixart_quantization[0]); + build_quantization_table(priv->Q_tables[1], pixart_quantization[1]); + } + + /* Pixart JPEG data starts with one unknown / unused byte */ + priv->stream++; + } + if (!dht_marker_found) { trace("No Huffman table loaded, using the default one\n"); - build_default_huffman_tables(priv); + if (build_default_huffman_tables(priv)) + return -1; } #ifdef SANITY_CHECK @@ -1962,6 +2234,13 @@ static const decode_MCU_fct decode_mcu_3comp_table[4] = { decode_MCU_2x2_3planes, }; +static const decode_MCU_fct pixart_decode_mcu_3comp_table[4] = { + NULL, + NULL, + pixart_decode_MCU_2x1_3planes, + NULL, +}; + static const decode_MCU_fct decode_mcu_1comp_table[4] = { decode_MCU_1x1_1plane, decode_MCU_1x2_1plane, @@ -2021,6 +2300,9 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) bytes_per_blocklines[2] = 0; decode_mcu_table = decode_mcu_3comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) + decode_mcu_table = pixart_decode_mcu_3comp_table; + switch (pixfmt) { case TINYJPEG_FMT_YUV420P: colorspace_array_conv = convert_colorspace_yuv420p; @@ -2056,6 +2338,8 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) case TINYJPEG_FMT_GREY: decode_mcu_table = decode_mcu_1comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) + error("Greyscale output not support for PIXART JPEG's\n"); colorspace_array_conv = convert_colorspace_grey; if (priv->components[0] == NULL) priv->components[0] = (uint8_t *)malloc(priv->width * priv->height); @@ -2064,8 +2348,7 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) break; default: - trace("Bad pixel format\n"); - return -1; + error("Bad pixel format\n"); } xstride_by_mcu = ystride_by_mcu = 8; @@ -2091,6 +2374,9 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) trace("Use decode 2x1 sampling\n"); } + if (decode_MCU == NULL) + error("no decode MCU function for this JPEG format (PIXART?)\n"); + resync(priv); /* Don't forget to that block can be either 8 or 16 lines */ @@ -2130,6 +2416,12 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) } } + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + /* Additional sanity check for funky Pixart format */ + if ((priv->stream_end - priv->stream) > 5) + error("Pixart JPEG error, stream does not end with EOF marker\n"); + } + return 0; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h index 42b60c1ee..b0096f0de 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h +++ b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h @@ -43,6 +43,7 @@ struct jdec_private; /* Flags that can be set by any applications */ #define TINYJPEG_FLAGS_MJPEG_TABLE (1<<1) +#define TINYJPEG_FLAGS_PIXART_JPEG (1<<2) /* Format accepted in outout */ enum tinyjpeg_fmt { |