diff options
Diffstat (limited to 'v4l2-apps')
48 files changed, 2134 insertions, 186 deletions
diff --git a/v4l2-apps/libv4l/ChangeLog b/v4l2-apps/libv4l/ChangeLog index e69a8c8f4..e2e7ac36f 100644 --- a/v4l2-apps/libv4l/ChangeLog +++ b/v4l2-apps/libv4l/ChangeLog @@ -1,3 +1,21 @@ +libv4l-0.6.2 +------------ +* Add more laptop models to the upside down devices table + +libv4l-0.6.1 +------------ +* Add more laptop models to the upside down devices table +* Makefile changes to make life easier for the Debian package (Gregor Jasny) +* Bugfix: fixup 320x240 output for pac7302 cameras +* README improvements / clarifications (Bifferos) +* Bugfix: fix reqbuf Device or Resource busy error when using v4l2_read() +* Some applications want to use jpg format if possible, so do not hide + it from the apps (do not assume it always needs conversion) +* Change controls shm segment name to include the username, as it is only + writable by the user (this means libv4l controls are per user) (Gregor Jasny) +* Add support for decompressing sn9c2028 compressed bayer (Theodore Kilgore) +* Report V4L2_FMT_FLAG_EMULATED in v4l2_fmtdesc flags for emulated formats + libv4l-0.6.0 ------------ * Recognize disabled controls and replace with fake equivalents where @@ -6,8 +24,12 @@ libv4l-0.6.0 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 +* Add tons of laptop models to the upside down devices table +* Support for rgb565 source format by Mauro Carvalho Chehab +* Many bug fixes (see the mercurial tree for details) +* Improved pac207 decompression code to also support higher compression + modes of the pac207, which enables us to use higher framerates. + Many many thanks to Bertrik Sikken for figuring the decompression out! libv4l-0.5.99 ------------- diff --git a/v4l2-apps/libv4l/Makefile b/v4l2-apps/libv4l/Makefile index acec5c540..1afca883c 100644 --- a/v4l2-apps/libv4l/Makefile +++ b/v4l2-apps/libv4l/Makefile @@ -1,5 +1,5 @@ LIB_RELEASE=0 -V4L2_LIB_VERSION=$(LIB_RELEASE).5.99 +V4L2_LIB_VERSION=$(LIB_RELEASE).6.2-test all install: $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ diff --git a/v4l2-apps/libv4l/README b/v4l2-apps/libv4l/README index 481d5b92e..20eee5858 100644 --- a/v4l2-apps/libv4l/README +++ b/v4l2-apps/libv4l/README @@ -25,13 +25,13 @@ 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 +processing functions to 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 +These controls are stored application wide (until reboot) by using a persistent shared memory object. libv4lconvert/processing offers the actual video processing functionality. @@ -80,6 +80,12 @@ $ export LD_PRELOAD=/usr/local/lib/libv4l/v4l1compat.so $ camorama +Prerequisites +------------- + +libv4l requires shmem file system support in the kernel (CONFIG_SHMEM). + + Installation Instructions ------------------------- @@ -88,9 +94,15 @@ Simple type the following commands from the libv4l-x.y.z directory make make install PREFIX=/usr/local -Note: make install also supports the DESTDIR=... paramter for installation +Note: make install also supports the DESTDIR=... parameter for installation into chroots. +If you require static libraries to also be built, these can be compiled +along with the dynamic equivalents by defining LINKTYPE to 'static', e.g.: + +make LINKTYPE=static +make install LINKTYPE=static + FAQ --- @@ -117,10 +129,13 @@ So clearly this belongs in a library, and in a library with a license which allows this code to be used from as many different applications as possible. Hence libv4l was born. + Q: Under which license may I use and distribute libv4l? -A: All libv4l components are licensed under the GNU Library General Publishing +A: The libv4l libraries are licensed under the GNU Library General Publishing License version 2 or (at your option) any later version. See the included -COPYING.LIB file. +COPYING.LIB file. The decompression helpers are licensed under the GNU +Library Publishing License version 2 (as they are derived from kernel code) + Q: Okay so I get the use of having a libv4lconvert, but why libv4l1 ? A: Many v4l2 drivers do not offer full v4l1 compatibility. They often do not @@ -138,9 +153,8 @@ applications. Q: Why should I use libv4l2 in my app instead of direct device access -combined with libv4lconvert? - -libv4l2 is mainly meant for quickly and easily adding support for more + combined with libv4lconvert? +A: libv4l2 is mainly meant for quickly and easily adding support for more pixelformats to existing v4l2 applications. So if you feel better directly accessing the device in combination with libv4lconvert thats fine too. @@ -151,3 +165,11 @@ zero-copy access to the captured frame, and then it can write the converted data directly to the buffer the application provided to v4l2_read(). Thus another reason to use liv4l2 is to get the no memcpy advantage of the mmap capture method combined with the simplicity of making a simple read() call. + + +Q: Where to send bugreports / questions? +A: Please send libv4l questions / bugreports to the: + Linux Media Mailing List <linux-media@vger.kernel.org> + Subscription is not necessary to send mail to this list. If you're not + subscribed please put yourself in the CC of your original mail so you + will receive replies. diff --git a/v4l2-apps/libv4l/include/libv4l1.h b/v4l2-apps/libv4l/include/libv4l1.h index 4ddf8efdb..a2f8bd8d4 100644 --- a/v4l2-apps/libv4l/include/libv4l1.h +++ b/v4l2-apps/libv4l/include/libv4l1.h @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/include/libv4l2.h b/v4l2-apps/libv4l/include/libv4l2.h index 7ecbb2cee..dfae21471 100644 --- a/v4l2-apps/libv4l/include/libv4l2.h +++ b/v4l2-apps/libv4l/include/libv4l2.h @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 @@ -51,6 +51,13 @@ LIBV4L_PUBLIC extern FILE *v4l2_log_file; 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. + + Note that the argument to v4l2_ioctl after the request must be a valid + memory address of structure of the appropriate type for the request (for + v4l2 requests which expect a structure address). Passing in NULL or an + invalid memory address will not lead to failure with errno being EFAULT, + as it would with a real ioctl, but will cause libv4l2 to break, and you + get to keep both pieces. */ LIBV4L_PUBLIC int v4l2_open (const char *file, int oflag, ...); diff --git a/v4l2-apps/libv4l/include/libv4lconvert.h b/v4l2-apps/libv4l/include/libv4lconvert.h index b274c938b..09ce17cae 100644 --- a/v4l2-apps/libv4l/include/libv4lconvert.h +++ b/v4l2-apps/libv4l/include/libv4lconvert.h @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/libv4l1/Makefile b/v4l2-apps/libv4l/libv4l1/Makefile index e6d306055..3403f3710 100644 --- a/v4l2-apps/libv4l/libv4l1/Makefile +++ b/v4l2-apps/libv4l/libv4l1/Makefile @@ -33,6 +33,10 @@ ifeq ($(LIBDIR),) LIBDIR = $(PREFIX)/lib endif +ifeq ($(LIBSUBDIR),) +LIBSUBDIR = libv4l +endif + all: $(TARGETS) -include $(V4L1_OBJS:.o=.d) @@ -60,12 +64,12 @@ ifeq ($(LINKTYPE),static) mkdir -p $(DESTDIR)$(LIBDIR) install -m 644 $(V4L1_LIB) $(DESTDIR)$(LIBDIR) else - mkdir -p $(DESTDIR)$(LIBDIR)/libv4l + mkdir -p $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR) install -m 755 $(V4L1_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR) cd $(DESTDIR)$(LIBDIR) && \ ln -f -s $(V4L1_LIB).$(LIB_RELEASE) $(V4L1_LIB) install -m 755 $(V4L1COMPAT).$(LIB_RELEASE) \ - $(DESTDIR)$(LIBDIR)/libv4l/$(V4L1COMPAT) + $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR)/$(V4L1COMPAT) endif mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig install -m 644 libv4l1.pc $(DESTDIR)$(LIBDIR)/pkgconfig diff --git a/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h b/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h index 370686b3a..33abcdcb0 100644 --- a/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h +++ b/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/libv4l1/libv4l1.c b/v4l2-apps/libv4l/libv4l1/libv4l1.c index 9b23926be..29df658b1 100644 --- a/v4l2-apps/libv4l/libv4l1/libv4l1.c +++ b/v4l2-apps/libv4l/libv4l1/libv4l1.c @@ -1,7 +1,7 @@ /* # libv4l1 userspace v4l1 api emulation for v4l2 devices -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/libv4l1/log.c b/v4l2-apps/libv4l/libv4l1/log.c index fd095817f..68060e65a 100644 --- a/v4l2-apps/libv4l/libv4l1/log.c +++ b/v4l2-apps/libv4l/libv4l1/log.c @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/libv4l1/v4l1compat.c b/v4l2-apps/libv4l/libv4l1/v4l1compat.c index 704ec22dd..50fc2bab1 100644 --- a/v4l2-apps/libv4l/libv4l1/v4l1compat.c +++ b/v4l2-apps/libv4l/libv4l1/v4l1compat.c @@ -2,7 +2,7 @@ # open/close/ioctl/mmap/munmap library call wrapper doing v4l1 api emulation # for v4l2 devices -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/libv4l2/Makefile b/v4l2-apps/libv4l/libv4l2/Makefile index 06c010f3c..c13d7315d 100644 --- a/v4l2-apps/libv4l/libv4l2/Makefile +++ b/v4l2-apps/libv4l/libv4l2/Makefile @@ -33,6 +33,10 @@ ifeq ($(LIBDIR),) LIBDIR = $(PREFIX)/lib endif +ifeq ($(LIBSUBDIR),) +LIBSUBDIR = libv4l +endif + all: $(TARGETS) -include $(V4L2_OBJS:.o=.d) @@ -60,12 +64,12 @@ ifeq ($(LINKTYPE),static) mkdir -p $(DESTDIR)$(LIBDIR) install -m 644 $(V4L2_LIB) $(DESTDIR)$(LIBDIR) else - mkdir -p $(DESTDIR)$(LIBDIR)/libv4l + mkdir -p $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR) install -m 755 $(V4L2_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR) cd $(DESTDIR)$(LIBDIR) && \ ln -f -s $(V4L2_LIB).$(LIB_RELEASE) $(V4L2_LIB) install -m 755 $(V4L2CONVERT).$(LIB_RELEASE) \ - $(DESTDIR)$(LIBDIR)/libv4l/$(V4L2CONVERT) + $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR)/$(V4L2CONVERT) endif mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig install -m 644 libv4l2.pc $(DESTDIR)$(LIBDIR)/pkgconfig diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h index 0bfd53e2a..8256cfdb6 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h +++ b/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 @@ -80,6 +80,9 @@ struct v4l2_dev_info { int frame_queued; /* 1 status bit per frame */ /* mapping tracking of our fake (converting mmap) frame buffers */ unsigned char frame_map_count[V4L2_MAX_NO_FRAMES]; + /* buffer when doing conversion and using read() for read() */ + int readbuf_size; + unsigned char *readbuf; }; /* From log.c */ diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index b71c9b4c6..3366538a7 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/libv4l2/libv4l2.c @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 @@ -77,6 +77,7 @@ #define V4L2_SUPPORTS_READ 0x0800 #define V4L2_IS_UVC 0x1000 #define V4L2_STREAM_TOUCHED 0x2000 +#define V4L2_USE_READ_FOR_READ 0x4000 #define V4L2_MMAP_OFFSET_MAGIC 0xABCDEF00u @@ -101,7 +102,7 @@ static int v4l2_request_read_buffers(int index) req.memory = V4L2_MEMORY_MMAP; 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)); + V4L2_LOG("warning reqbuf (%u) failed: %s\n", req.count, strerror(errno)); errno = saved_err; return result; } @@ -305,6 +306,62 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, return result; } +static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size) +{ + const int max_tries = 10; + int result, buf_size, tries = max_tries; + + buf_size = devices[index].dest_fmt.fmt.pix.sizeimage; + + if (devices[index].readbuf_size < buf_size) { + unsigned char *new_buf; + + new_buf = realloc(devices[index].readbuf, buf_size); + if (!new_buf) + return -1; + + devices[index].readbuf = new_buf; + devices[index].readbuf_size = buf_size; + } + + do { + result = SYS_READ(devices[index].fd, devices[index].readbuf, buf_size); + if (result <= 0) { + if (result && errno != EAGAIN) { + int saved_err = errno; + V4L2_LOG_ERR("reading: %s\n", strerror(errno)); + errno = saved_err; + } + return result; + } + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].readbuf, result, dest, 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)); + + 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)); + } + + return result; +} + static int v4l2_queue_read_buffers(int index) { unsigned int i; @@ -332,6 +389,11 @@ static int v4l2_activate_read_stream(int index) { int result; + if ((devices[index].flags & V4L2_STREAMON) || devices[index].frame_queued) { + errno = EBUSY; + return -1; + } + if ((result = v4l2_request_read_buffers(index))) return result; @@ -463,10 +525,9 @@ int v4l2_fd_open(int fd, int v4l2_flags) return -1; } - /* we only add functionality for video capture devices, and we do not - handle devices which don't do mmap */ + /* we only add functionality for video capture devices */ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || - !(cap.capabilities & V4L2_CAP_STREAMING)) + !(cap.capabilities & (V4L2_CAP_STREAMING|V4L2_CAP_READWRITE))) return fd; /* Get current cam format */ @@ -501,6 +562,8 @@ 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 (!(cap.capabilities & V4L2_CAP_STREAMING)) + devices[index].flags |= V4L2_USE_READ_FOR_READ; if (!strcmp((char *)cap.driver, "uvcvideo")) devices[index].flags |= V4L2_IS_UVC; devices[index].open_count = 1; @@ -508,7 +571,7 @@ int v4l2_fd_open(int fd, int v4l2_flags) 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). + is a supported one we will align the resolution (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( @@ -528,6 +591,8 @@ int v4l2_fd_open(int fd, int v4l2_flags) devices[index].frame_map_count[i] = 0; } devices[index].frame_queued = 0; + devices[index].readbuf = NULL; + devices[index].readbuf_size = 0; if (index >= devices_used) devices_used = index + 1; @@ -585,6 +650,9 @@ int v4l2_close(int fd) devices[index].convert_mmap_buf = MAP_FAILED; } v4lconvert_destroy(devices[index].convert); + free(devices[index].readbuf); + devices[index].readbuf = NULL; + devices[index].readbuf_size = 0; /* 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 @@ -1070,7 +1138,6 @@ ssize_t v4l2_read (int fd, void* dest, size_t n) { ssize_t result; int index; - struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) return SYS_READ(fd, dest, n); @@ -1085,23 +1152,33 @@ ssize_t v4l2_read (int fd, void* dest, size_t n) goto leave; } - if (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ)) { - if ((devices[index].flags & V4L2_STREAMON) || - devices[index].frame_queued) { - errno = EBUSY; - result = -1; - goto leave; - } + /* Since we need to do conversion try to use mmap (streaming) mode under + the hood as that safes a memcpy for each frame read. + + Note sometimes this will fail as some drivers (atleast gspca) do not allow + switching from read mode to mmap mode and they assume read() mode if a + select or poll() is done before any buffers are requested. So using mmap + mode under the hood will fail if a select() or poll() is done before the + first emulated read() call. */ + if (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) && + !(devices[index].flags & V4L2_USE_READ_FOR_READ)) { if ((result = v4l2_activate_read_stream(index))) - goto leave; + /* Activating mmap mode failed, use read() instead */ + devices[index].flags |= V4L2_USE_READ_FOR_READ; } - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - result = v4l2_dequeue_and_convert(index, &buf, dest, n); + if (devices[index].flags & V4L2_USE_READ_FOR_READ) { + result = v4l2_read_and_convert(index, dest, n); + } else { + struct v4l2_buffer buf; - if (result >= 0) - v4l2_queue_read_buffer(index, buf.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_queue_read_buffer(index, buf.index); + } leave: pthread_mutex_unlock(&devices[index].stream_lock); @@ -1226,9 +1303,15 @@ int v4l2_set_control(int fd, int cid, int value) { struct v4l2_queryctrl qctrl = { .id = cid }; struct v4l2_control ctrl = { .id = cid }; - int result; + int index, result; + + if ((index = v4l2_get_index(fd)) == -1) { + V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd); + errno = EBADF; + return -1; + } - if ((result = SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl))) + if ((result = v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl))) return result; if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) && @@ -1239,7 +1322,7 @@ int v4l2_set_control(int fd, int cid, int value) ctrl.value = (value * (qctrl.maximum - qctrl.minimum) + 32767) / 65535 + qctrl.minimum; - result = SYS_IOCTL(fd, VIDIOC_S_CTRL, &ctrl); + result = v4lconvert_vidioc_s_ctrl(devices[index].convert, &ctrl); } return result; @@ -1249,14 +1332,21 @@ int v4l2_get_control(int fd, int cid) { struct v4l2_queryctrl qctrl = { .id = cid }; struct v4l2_control ctrl = { .id = cid }; + int index; + + if ((index = v4l2_get_index(fd)) == -1) { + V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd); + errno = EBADF; + return -1; + } - if (SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl)) + if (v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl)) return 0; if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) return 0; - if (SYS_IOCTL(fd, VIDIOC_G_CTRL, &ctrl)) + if (v4lconvert_vidioc_g_ctrl(devices[index].convert, &ctrl)) return 0; return ((ctrl.value - qctrl.minimum) * 65535 + diff --git a/v4l2-apps/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/libv4l/libv4l2/v4l2convert.c index 4b75a5ebf..e402f78d8 100644 --- a/v4l2-apps/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/libv4l/libv4l2/v4l2convert.c @@ -3,7 +3,7 @@ # for v4l2 applications which want to be able to simply capture bgr24 / yuv420 # from v4l2 devices with more exotic frame formats. -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile index 036649e86..cc3941b87 100644 --- a/v4l2-apps/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/libv4l/libv4lconvert/Makefile @@ -14,10 +14,10 @@ endif 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 \ + rgbyuv.o sn9c2028-decomp.o spca501.o sq905c.o bayer.o hm12.o \ control/libv4lcontrol.o processing/libv4lprocessing.o \ processing/whitebalance.o processing/autogain.o \ - processing/gamma.o + processing/gamma.o helper.o TARGETS = $(CONVERT_LIB) libv4lconvert.pc ov511-decomp ov518-decomp INCLUDES = ../include/libv4lconvert.h @@ -33,7 +33,11 @@ ifeq ($(LIBDIR),) LIBDIR = $(PREFIX)/lib endif -override CPPFLAGS += -DLIBDIR=\"$(LIBDIR)\" +ifeq ($(LIBSUBDIR),) +LIBSUBDIR = libv4l +endif + +override CPPFLAGS += -DLIBDIR=\"$(LIBDIR)\" -DLIBSUBDIR=\"$(LIBSUBDIR)\" all: $(TARGETS) @@ -55,7 +59,7 @@ libv4lconvert.pc: install: all mkdir -p $(DESTDIR)$(PREFIX)/include install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include - mkdir -p $(DESTDIR)$(LIBDIR)/libv4l + mkdir -p $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR) ifeq ($(LINKTYPE),static) install -m 644 $(CONVERT_LIB) $(DESTDIR)$(LIBDIR) else @@ -63,7 +67,7 @@ else cd $(DESTDIR)$(LIBDIR) && \ ln -f -s $(CONVERT_LIB).$(LIB_RELEASE) $(CONVERT_LIB) endif - install -m 755 *-decomp $(DESTDIR)$(LIBDIR)/libv4l + install -m 755 *-decomp $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR) mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig diff --git a/v4l2-apps/libv4l/libv4lconvert/bayer.c b/v4l2-apps/libv4l/libv4lconvert/bayer.c index 033ee2724..d299629a6 100644 --- a/v4l2-apps/libv4l/libv4lconvert/bayer.c +++ b/v4l2-apps/libv4l/libv4lconvert/bayer.c @@ -1,6 +1,6 @@ /* * lib4lconvert, video4linux2 format conversion lib - * (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + * (C) 2008 Hans de Goede <hdegoede@redhat.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h index 6211ee22a..632e9338a 100644 --- a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h @@ -25,6 +25,7 @@ #define V4LCONTROL_SHM_SIZE 4096 #define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01 +#define V4LCONTROL_MEMORY_IS_MALLOCED 0x02 struct v4lcontrol_flags_info; @@ -49,6 +50,15 @@ struct v4lcontrol_flags_info { const char *product; */ int flags; int default_gamma; +/* Some seldom used dmi strings (for notebooks with bogus info in the board + entries, but usefull info elsewhere). We keep this at the end as to not + polute the initalizers for the normal case. */ + /* System (product) vendor / name */ + const char *dmi_system_vendor; + const char *dmi_system_name; + /* Board and System versions */ + const char *dmi_board_version; + const char *dmi_system_version; }; #endif diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c index 4e600351b..243f35dae 100644 --- a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c @@ -3,6 +3,7 @@ # (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> +# (C) 2009 Paul Sladen <ubuntu@paul.sladen.org> # 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 @@ -28,6 +29,7 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <pwd.h> #include "libv4lcontrol.h" #include "libv4lcontrol-priv.h" #include "../libv4lsyscall-priv.h" @@ -57,9 +59,111 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { /* 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 */ + /* Laptops */ + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "F7L ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "W7Sg ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "F7SR ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "G50VT ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "W7S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "X55SR ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "X55SV ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* note different white space: http://launchpad.net/bugs/413752 */ + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "X71SL ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* These 3 PACKARD BELL's seem to be Asus notebook in disguise */ + { 0x04f2, 0xb012, 0, "Packard Bell BV", "T32A ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG45", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG46", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc. ", "U6S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc. ", "U6V ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "K40IJ ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "K50IJ ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "K50IN ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "K70AB ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "K70IO ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "N10J ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc. ", "N20A ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Note no whitespace padding for these 2 models, this is not a typo */ + { 0x04f2, 0xb071, 0, "ASUSTeK Computer INC.", "K50AB", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "ASUSTeK Computer INC.", "N5051Tp", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vc ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vn ", V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N51Vf ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N51Vg ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N51Vn ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb16b, 0, "ASUSTeK Computer Inc. ", "U80A ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Note no whitespace padding for board vendor, this is not a typo */ + { 0x064e, 0xa111, 0, "ASUSTeK Computer Inc.", "F5RL ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Another Asus laptop in disguise */ + { 0x064e, 0xa111, 0, "PEGATRON CORPORATION ", "F5SR ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc. ", "N10Jb ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc. ", "N10Jc ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc. ", "N20A ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc. ", "X58LE ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x090c, 0xe370, 0, "ASUSTeK Computer Inc. ", "U6S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Ke ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Q ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Sa ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Sg ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F3Sr ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F5N ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "F5SL ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "G1S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "G1Sn ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc. ", "G2S ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x174f, 0xa311, 0, "ASUSTeK Computer Inc. ", "A3F ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + { 0x5986, 0x0200, 0, "LENOVO", "SPEEDY ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "Lenovo IdeaPad Y510" }, + { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "Lenovo IdeaPad U330" }, + /* Second: devices which should use some software processing by default */ /* Pac207 based devices */ { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, @@ -69,6 +173,8 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { /* Pac7302 based devices */ { 0x093a, 0x2620, 0x0f, NULL, NULL, V4LCONTROL_ROTATED_90_JPEG|V4LCONTROL_WANTS_WB }, + { 0x06f8, 0x3009, 0, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG|V4LCONTROL_WANTS_WB }, /* Pac7311 based devices */ { 0x093a, 0x2600, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB }, /* sq905 devices */ @@ -87,6 +193,33 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { static const struct v4l2_queryctrl fake_controls[]; +static void v4lcontrol_get_dmi_string(const char *string, char *buf, int size) +{ + FILE *f; + char *s, sysfs_name[512]; + + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/dmi/id/%s", string); + f = fopen(sysfs_name, "r"); + if (!f) { + /* Try again with a different sysfs path, not sure if this is needed + but we used to look under /sys/devices/virtual/dmi/id in older + libv4l versions, but this did not work with some kernels */ + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/devices/virtual/dmi/id/%s", string); + f = fopen(sysfs_name, "r"); + if (!f) { + buf[0] = 0; + return; + } + } + + s = fgets(buf, size, f); + if (s) + s[strlen(s) - 1] = 0; + fclose(f); +} + static void v4lcontrol_init_flags(struct v4lcontrol_data *data) { struct stat st; @@ -94,8 +227,8 @@ static void v4lcontrol_init_flags(struct v4lcontrol_data *data) char sysfs_name[512]; unsigned short vendor_id = 0; unsigned short product_id = 0; - char dmi_board_vendor[512] = ""; - char dmi_board_name[512]= ""; + char dmi_system_vendor[512], dmi_system_name[512], dmi_system_version[512]; + char dmi_board_vendor[512], dmi_board_name[512], dmi_board_version[512]; int i, minor; char c, *s, buf[32]; struct v4l2_input input; @@ -174,31 +307,39 @@ static void v4lcontrol_init_flags(struct v4lcontrol_data *data) 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); - } + /* Get DMI board and system strings */ + v4lcontrol_get_dmi_string("sys_vendor", dmi_system_vendor, + sizeof(dmi_system_vendor)); + v4lcontrol_get_dmi_string("product_name", dmi_system_name, + sizeof(dmi_system_name)); + v4lcontrol_get_dmi_string("product_version", dmi_system_version, + sizeof(dmi_system_version)); + + v4lcontrol_get_dmi_string("board_vendor", dmi_board_vendor, + sizeof(dmi_board_vendor)); + v4lcontrol_get_dmi_string("board_name", dmi_board_name, + sizeof(dmi_board_name)); + v4lcontrol_get_dmi_string("board_version", dmi_board_version, + sizeof(dmi_board_version)); 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_system_vendor == NULL || + !strcmp(v4lcontrol_flags[i].dmi_system_vendor, dmi_system_vendor)) && + (v4lcontrol_flags[i].dmi_system_name == NULL || + !strcmp(v4lcontrol_flags[i].dmi_system_name, dmi_system_name)) && + (v4lcontrol_flags[i].dmi_system_version == NULL || + !strcmp(v4lcontrol_flags[i].dmi_system_version, dmi_system_version)) && + (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))) { + !strcmp(v4lcontrol_flags[i].dmi_board_name, dmi_board_name)) && + (v4lcontrol_flags[i].dmi_board_version == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_version, dmi_board_version))) { data->flags |= v4lcontrol_flags[i].flags; data->flags_info = &v4lcontrol_flags[i]; break; @@ -209,14 +350,17 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) { int shm_fd; int i, rc, init = 0; - char *s, shm_name[256]; + char *s, shm_name[256], pwd_buf[1024]; struct v4l2_capability cap; struct v4l2_queryctrl ctrl; + struct passwd pwd, *pwd_p; struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data)); - if (!data) + if (!data) { + fprintf(stderr, "libv4lcontrol: error: out of memory!\n"); return NULL; + } data->fd = fd; @@ -252,8 +396,19 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) 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); + if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) { + perror("libv4lcontrol: error querying device capabilities"); + goto error; + } + + if (getpwuid_r(geteuid(), &pwd, pwd_buf, sizeof(pwd_buf), &pwd_p) == 0) { + snprintf(shm_name, 256, "/libv4l-%s:%s:%s", pwd.pw_name, + cap.bus_info, cap.card); + } else { + perror("libv4lcontrol: error getting username using uid instead"); + snprintf(shm_name, 256, "/libv4l-%lu:%s:%s", (unsigned long)geteuid(), + cap.bus_info, cap.card); + } /* / is not allowed inside shm names */ for (i = 1; shm_name[i]; i++) @@ -264,23 +419,41 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) 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; + else + shm_fd = shm_open(shm_name, O_RDWR, (S_IREAD | S_IWRITE)); - /* Set the shared memory size */ - ftruncate(shm_fd, V4LCONTROL_SHM_SIZE); + if (shm_fd >= 0) { + /* 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); + /* 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 (data->shm_values == MAP_FAILED) { + perror("libv4lcontrol: error shm mmap failed"); + data->shm_values = NULL; + } + } else + perror("libv4lcontrol: error creating shm segment failed"); + + /* Fall back to malloc */ + if (data->shm_values == NULL) { + fprintf(stderr, + "libv4lcontrol: falling back to malloc-ed memory for controls\n"); + data->shm_values = malloc(V4LCONTROL_SHM_SIZE); + if (!data->shm_values) { + fprintf(stderr, "libv4lcontrol: error: out of memory!\n"); + goto error; + } + init = 1; + data->priv_flags |= V4LCONTROL_MEMORY_IS_MALLOCED; + } if (init) { /* Initialize the new shm object we created */ - memset(data->shm_values, 0, sizeof(V4LCONTROL_SHM_SIZE)); + memset(data->shm_values, 0, V4LCONTROL_SHM_SIZE); for (i = 0; i < V4LCONTROL_COUNT; i++) data->shm_values[i] = fake_controls[i].default_value; @@ -301,12 +474,15 @@ error: void v4lcontrol_destroy(struct v4lcontrol_data *data) { - if (data->controls) - munmap(data->shm_values, V4LCONTROL_SHM_SIZE); + if (data->controls) { + if (data->priv_flags & V4LCONTROL_MEMORY_IS_MALLOCED) + free(data->shm_values); + else + 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, diff --git a/v4l2-apps/libv4l/libv4lconvert/crop.c b/v4l2-apps/libv4l/libv4lconvert/crop.c index 46c87f0ce..b2d878963 100644 --- a/v4l2-apps/libv4l/libv4lconvert/crop.c +++ b/v4l2-apps/libv4l/libv4lconvert/crop.c @@ -2,7 +2,7 @@ # RGB and YUV crop routines -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l/libv4lconvert/flip.c b/v4l2-apps/libv4l/libv4lconvert/flip.c index 8c4d8233c..dc9b858c9 100644 --- a/v4l2-apps/libv4l/libv4lconvert/flip.c +++ b/v4l2-apps/libv4l/libv4lconvert/flip.c @@ -2,7 +2,7 @@ # RGB / YUV flip/rotate routines -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 @@ -219,6 +219,7 @@ void v4lconvert_rotate90(unsigned char *src, unsigned char *dest, fmt->fmt.pix.height); break; } + v4lconvert_fixup_fmt(fmt); } void v4lconvert_flip(unsigned char *src, unsigned char *dest, diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h index 2cf8f6ba4..f16d05d02 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 @@ -52,6 +52,10 @@ #define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M','3','1','0') #endif +#ifndef V4L2_PIX_FMT_SN9C2028 +#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') +#endif + #ifndef V4L2_PIX_FMT_SQ905C #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') #endif @@ -92,6 +96,10 @@ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ #endif +#ifndef V4L2_FMT_FLAG_EMULATED +#define V4L2_FMT_FLAG_EMULATED 0x0002 +#endif + #define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) #define V4LCONVERT_ERROR_MSG_SIZE 256 @@ -108,6 +116,7 @@ /* Pixformat flags */ #define V4LCONVERT_COMPRESSED 0x01 /* Compressed format */ #define V4LCONVERT_NEEDS_CONVERSION 0x02 /* Apps likely wont know this */ +#define V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION 0x03 struct v4lconvert_data { int fd; @@ -184,6 +193,15 @@ void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst, void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dst, const struct v4l2_format *src_fmt); +void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height); + +void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height); + +void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int yvu); + void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height, int yvu); @@ -202,12 +220,16 @@ void v4lconvert_decode_spca561(const unsigned char *src, unsigned char *dst, void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst, int width, int height); -void v4lconvert_decode_pac207(const unsigned char *src, unsigned char *dst, +int v4lconvert_decode_pac207(struct v4lconvert_data *data, + const unsigned char *inp, int src_size, unsigned char *outp, int width, int height); void v4lconvert_decode_mr97310a(const unsigned char *src, unsigned char *dst, int width, int height); +void v4lconvert_decode_sn9c2028(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); diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c index 1389c58bc..32b9a51bb 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 @@ -46,6 +46,7 @@ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = { { V4L2_PIX_FMT_YUYV, 0 }, { V4L2_PIX_FMT_YVYU, 0 }, { V4L2_PIX_FMT_UYVY, 0 }, + { V4L2_PIX_FMT_RGB565, 0 }, { V4L2_PIX_FMT_SN9C20X_I420, V4LCONVERT_NEEDS_CONVERSION }, { V4L2_PIX_FMT_SBGGR8, V4LCONVERT_NEEDS_CONVERSION }, { V4L2_PIX_FMT_SGBRG8, V4LCONVERT_NEEDS_CONVERSION }, @@ -57,14 +58,15 @@ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = { { 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 }, + { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SN9C2028, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_MR97310A, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SQ905C, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_OV511, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_OV518, V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION }, }; static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = { @@ -94,8 +96,10 @@ struct v4lconvert_data *v4lconvert_create(int fd) processing controls without a performance impact. */ int always_needs_conversion = 1; - if (!data) + if (!data) { + fprintf(stderr, "libv4lconvert: error: out of memory!\n"); return NULL; + } data->fd = fd; data->decompress_pid = -1; @@ -113,7 +117,7 @@ struct v4lconvert_data *v4lconvert_create(int fd) 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) + if (!(supported_src_pixfmts[j].flags & V4LCONVERT_NEEDS_CONVERSION)) always_needs_conversion = 0; break; } @@ -215,7 +219,7 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt) return -1; } - fmt->flags = 0; + fmt->flags = V4L2_FMT_FLAG_EMULATED; fmt->pixelformat = faked_fmts[i]; fmt->description[0] = faked_fmts[i] & 0xff; fmt->description[1] = (faked_fmts[i] >> 8) & 0xff; @@ -490,6 +494,7 @@ static int v4lconvert_processing_needs_double_conversion( case V4L2_PIX_FMT_SN9C10X: case V4L2_PIX_FMT_PAC207: case V4L2_PIX_FMT_MR97310A: + case V4L2_PIX_FMT_SN9C2028: case V4L2_PIX_FMT_SQ905C: case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: @@ -671,7 +676,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu); break; case V4L2_PIX_FMT_OV511: - if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov511-decomp", + if (v4lconvert_helper_decompress(data, LIBDIR "/" LIBSUBDIR "/ov511-decomp", src, src_size, d, d_size, width, height, yvu)) { /* Corrupt frame, better get another one */ errno = EAGAIN; @@ -679,7 +684,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, } break; case V4L2_PIX_FMT_OV518: - if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov518-decomp", + if (v4lconvert_helper_decompress(data, LIBDIR "/" LIBSUBDIR "/ov518-decomp", src, src_size, d, d_size, width, height, yvu)) { /* Corrupt frame, better get another one */ errno = EAGAIN; @@ -724,6 +729,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, case V4L2_PIX_FMT_SN9C10X: case V4L2_PIX_FMT_PAC207: case V4L2_PIX_FMT_MR97310A: + case V4L2_PIX_FMT_SN9C2028: case V4L2_PIX_FMT_SQ905C: { unsigned char *tmpbuf; @@ -744,13 +750,22 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_PAC207: - v4lconvert_decode_pac207(src, tmpbuf, width, height); + if (v4lconvert_decode_pac207(data, src, src_size, tmpbuf, + width, height)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_MR97310A: v4lconvert_decode_mr97310a(src, tmpbuf, width, height); tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; + case V4L2_PIX_FMT_SN9C2028: + v4lconvert_decode_sn9c2028(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; @@ -787,6 +802,23 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, } break; + case V4L2_PIX_FMT_RGB565: + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_rgb565_to_rgb24(src, dest, width, height); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_rgb565_to_bgr24(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb565_to_yuv420(src, dest, fmt, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb565_to_yuv420(src, dest, fmt, 1); + break; + } + break; + case V4L2_PIX_FMT_RGB24: switch (dest_pix_fmt) { case V4L2_PIX_FMT_BGR24: diff --git a/v4l2-apps/libv4l/libv4lconvert/pac207.c b/v4l2-apps/libv4l/libv4lconvert/pac207.c index 97ef4ebc0..a81d5d511 100644 --- a/v4l2-apps/libv4l/libv4lconvert/pac207.c +++ b/v4l2-apps/libv4l/libv4lconvert/pac207.c @@ -52,35 +52,35 @@ static void init_pixart_decoder(void) len = 2; } else if ((i & 0xC0) == 0x40) { /* code 01 */ - val = -5; + val = -1; len = 2; } else if ((i & 0xC0) == 0x80) { /* code 10 */ - val = +5; + val = +1; len = 2; } else if ((i & 0xF0) == 0xC0) { /* code 1100 */ - val = -10; + val = -2; len = 4; } else if ((i & 0xF0) == 0xD0) { /* code 1101 */ - val = +10; + val = +2; len = 4; } else if ((i & 0xF8) == 0xE0) { /* code 11100 */ - val = -15; + val = -3; len = 5; } else if ((i & 0xF8) == 0xE8) { /* code 11101 */ - val = +15; + val = +3; len = 5; } else if ((i & 0xFC) == 0xF0) { /* code 111100 */ - val = -20; + val = -4; len = 6; } else if ((i & 0xFC) == 0xF4) { /* code 111101 */ - val = +20; + val = +4; len = 6; } else if ((i & 0xF8) == 0xF8) { /* code 11111xxxxxx */ @@ -109,7 +109,8 @@ static inline unsigned short getShort(const unsigned char *pt) } static int -pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width) +pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width, + int step_size, int abs_bits) { int col; int val; @@ -135,11 +136,11 @@ pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width) if (table[code].is_abs) { /* absolute value: get 6 more bits */ code = getByte(inp, bitpos); - bitpos += 6; - *outp++ = code & 0xFC; + bitpos += abs_bits; + *outp++ = code & ~(0xff >> abs_bits); } else { /* relative to left pixel */ - val = outp[-2] + table[code].val; + val = outp[-2] + table[code].val * step_size; *outp++ = CLIP(val); } } @@ -148,18 +149,24 @@ pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width) return 2 * ((bitpos + 15) / 16); } -void v4lconvert_decode_pac207(const unsigned char *inp, unsigned char *outp, +int v4lconvert_decode_pac207(struct v4lconvert_data *data, + const unsigned char *inp, int src_size, unsigned char *outp, int width, int height) { /* we should received a whole frame with header and EOL marker in myframe->data and return a GBRG pattern in frame->tmpbuffer remove the header then copy line by line EOL is set with 0x0f 0xf0 marker or 0x1e 0xe1 for compressed line*/ + const unsigned char *end = inp + src_size; unsigned short word; int row; /* iterate over all rows */ for (row = 0; row < height; row++) { + if ((inp + 2) > end) { + V4LCONVERT_ERR("incomplete pac207 frame\n"); + return -1; + } word = getShort(inp); switch (word) { case 0x0FF0: @@ -167,21 +174,31 @@ or 0x1e 0xe1 for compressed line*/ inp += (2 + width); break; case 0x1EE1: - inp += pac_decompress_row(inp, outp, width); + inp += pac_decompress_row(inp, outp, width, 5, 6); + break; + + case 0x2DD2: + inp += pac_decompress_row(inp, outp, width, 9, 5); break; - case 0x2DD2: /* prefix for "stronger" compressed lines, currently the - kernel driver programs the cam so that we should not - get any of these */ + case 0x3CC3: + inp += pac_decompress_row(inp, outp, width, 17, 4); + break; + + case 0x4BB4: + /* skip or copy line? */ + memcpy(outp, outp - 2 * width, width); + inp += 2; + break; default: /* corrupt frame */ - /* FIXME add error reporting */ - return; + V4LCONVERT_ERR("unknown pac207 row header: 0x%04x\n", (int)word); + return -1; } outp += width; } - return; + return 0; } diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h index b73c73b53..c314231cd 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h @@ -30,6 +30,7 @@ struct v4lprocessing_data { struct v4lcontrol_data *control; int fd; int do_process; + int controls_changed; /* True if any of the lookup tables does not contain linear 0-255 */ int lookup_table_active; @@ -41,6 +42,11 @@ struct v4lprocessing_data { unsigned char green[256]; unsigned char comp2[256]; /* Filter private data for filters which need it */ + /* whitebalance.c data */ + int green_avg; + int comp1_avg; + int comp2_avg; + /* gamma.c data */ int last_gamma; unsigned char gamma_table[256]; }; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c index cbbcca73c..424173ec5 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c @@ -38,8 +38,10 @@ struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data* struct v4lprocessing_data *data = calloc(1, sizeof(struct v4lprocessing_data)); - if (!data) + if (!data) { + fprintf(stderr, "libv4lprocessing: error: out of memory!\n"); return NULL; + } data->fd = fd; data->control = control; @@ -62,6 +64,8 @@ int v4lprocessing_pre_processing(struct v4lprocessing_data *data) data->do_process = 1; } + data->controls_changed |= v4lcontrol_controls_changed(data->control); + return data->do_process; } @@ -167,9 +171,10 @@ void v4lprocessing_processing(struct v4lprocessing_data *data, return; /* Non supported pix format */ } - if (v4lcontrol_controls_changed(data->control) || + if (data->controls_changed || data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) { v4lprocessing_update_lookup_tables(data, buf, fmt); + data->controls_changed = 0; data->lookup_table_update_counter = 0; } else data->lookup_table_update_counter++; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c index 64d20b3ff..5ee0cfaa8 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c +++ b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c @@ -27,10 +27,82 @@ #include "libv4lprocessing-priv.h" #include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ -#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) +#define CLIP256(color) (((color)>0xff)?0xff:(((color)<0)?0:(color))) +#define CLIP(color, min, max) (((color)>(max))?(max):(((color)<(min))?(min):(color))) static int whitebalance_active(struct v4lprocessing_data *data) { - return v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE); + int wb; + + wb = v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE); + if (!wb) { + /* Reset cached color averages */ + data->green_avg = 0; + } + + return wb; +} + +static int whitebalance_calculate_lookup_tables_generic( + struct v4lprocessing_data *data, int green_avg, int comp1_avg, int comp2_avg) +{ + int i, avg_avg; + const int max_step = 64; + + /* Clip averages (restricts maximum white balance correction) */ + green_avg = CLIP(green_avg, 512, 3072); + comp1_avg = CLIP(comp1_avg, 512, 3072); + comp2_avg = CLIP(comp2_avg, 512, 3072); + + /* First frame ? */ + if (data->green_avg == 0) { + data->green_avg = green_avg; + data->comp1_avg = comp1_avg; + data->comp2_avg = comp2_avg; + } else { + /* Slowly adjust the averages used for the correction, so that we + do not get a sudden change in colors */ + if (abs(data->green_avg - green_avg) > max_step) { + if (data->green_avg < green_avg) + data->green_avg += max_step; + else + data->green_avg -= max_step; + } + else + data->green_avg = green_avg; + + if (abs(data->comp1_avg - comp1_avg) > max_step) { + if (data->comp1_avg < comp1_avg) + data->comp1_avg += max_step; + else + data->comp1_avg -= max_step; + } + else + data->comp1_avg = comp1_avg; + + if (abs(data->comp2_avg - comp2_avg) > max_step) { + if (data->comp2_avg < comp2_avg) + data->comp2_avg += max_step; + else + data->comp2_avg -= max_step; + } + else + data->comp2_avg = comp2_avg; + } + + if (abs(data->green_avg - data->comp1_avg) < max_step && + abs(data->green_avg - data->comp2_avg) < max_step && + abs(data->comp1_avg - data->comp2_avg) < max_step) + return 0; + + avg_avg = (data->green_avg + data->comp1_avg + data->comp2_avg) / 3; + + for (i = 0; i < 256; i++) { + data->comp1[i] = CLIP256(data->comp1[i] * avg_avg / data->comp1_avg); + data->green[i] = CLIP256(data->green[i] * avg_avg / data->green_avg); + data->comp2[i] = CLIP256(data->comp2[i] * avg_avg / data->comp2_avg); + } + + return 1; } static int whitebalance_calculate_lookup_tables_bayer( @@ -38,7 +110,7 @@ static int whitebalance_calculate_lookup_tables_bayer( 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; + int green_avg, comp1_avg, comp2_avg; for (y = 0; y < fmt->fmt.pix.height; y += 2) { for (x = 0; x < fmt->fmt.pix.width; x += 2) { @@ -54,37 +126,29 @@ static int whitebalance_calculate_lookup_tables_bayer( } if (starts_with_green) { - green_avg = (a1 + b2) / 512; - comp1_avg = a2 / 256; - comp2_avg = b1 / 256; + green_avg = a1 / 2 + b2 / 2; + comp1_avg = a2; + comp2_avg = b1; } else { - green_avg = (a2 + b1) / 512; - comp1_avg = a1 / 256; - comp2_avg = b2 / 256; + green_avg = a2 / 2 + b1 / 2; + comp1_avg = a1; + comp2_avg = b2; } - 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); - } + /* Norm avg to ~ 0 - 4095 */ + green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; - return 1; + return whitebalance_calculate_lookup_tables_generic(data, green_avg, + comp1_avg, comp2_avg); } static int whitebalance_calculate_lookup_tables_rgb( struct v4lprocessing_data *data, unsigned char *buf, const struct v4l2_format *fmt) { - int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0, avg_avg; + int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0; for (y = 0; y < fmt->fmt.pix.height; y++) { for (x = 0; x < fmt->fmt.pix.width; x++) { @@ -95,25 +159,13 @@ static int whitebalance_calculate_lookup_tables_rgb( 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; + /* Norm avg to ~ 0 - 4095 */ + green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; - 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; + return whitebalance_calculate_lookup_tables_generic(data, green_avg, + comp1_avg, comp2_avg); } diff --git a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c index 31cbc3c68..5c43ce07f 100644 --- a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c +++ b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c @@ -1,8 +1,10 @@ /* # RGB <-> YUV conversion routines +# (C) 2008 Hans de Goede <hdegoede@redhat.com> -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# RGB565 conversion routines +# (C) 2009 Mauro Carvalho Chehab <mchehab@redhat.com> # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by @@ -472,3 +474,103 @@ void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dest, src += src_fmt->fmt.pix.bytesperline / 2; } } + +void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + unsigned short tmp = *(unsigned short *)src; + + /* Original format: rrrrrggg gggbbbbb */ + *dest++ = 0xf8 & (tmp >> 8); + *dest++ = 0xfc & (tmp >> 3); + *dest++ = 0xf8 & (tmp << 3); + + src += 2; + } + } +} + +void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + unsigned short tmp = *(unsigned short *)src; + + /* Original format: rrrrrggg gggbbbbb */ + *dest++ = 0xf8 & (tmp << 3); + *dest++ = 0xfc & (tmp >> 3); + *dest++ = 0xf8 & (tmp >> 8); + + src += 2; + } + } +} + +void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int yvu) +{ + int x, y; + unsigned short tmp; + unsigned char *udest, *vdest; + unsigned r[4], g[4], b[4]; + int avg_src[3]; + + /* Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + for (x = 0; x < src_fmt->fmt.pix.width; x++) { + tmp = *(unsigned short *)src; + r[0] = 0xf8 & (tmp << 3); + g[0] = 0xfc & (tmp >> 3); + b[0] = 0xf8 & (tmp >> 8); + RGB2Y(r[0], g[0], b[0], *dest++); + src += 2; + } + src += src_fmt->fmt.pix.bytesperline - 2 * src_fmt->fmt.pix.width; + } + src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline; + + /* U + V */ + if (yvu) { + vdest = dest; + udest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } else { + udest = dest; + vdest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < src_fmt->fmt.pix.width / 2; x++) { + tmp = *(unsigned short *)src; + r[0] = 0xf8 & (tmp << 3); + g[0] = 0xfc & (tmp >> 3); + b[0] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + 1); + r[1] = 0xf8 & (tmp << 3); + g[1] = 0xfc & (tmp >> 3); + b[1] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline); + r[2] = 0xf8 & (tmp << 3); + g[2] = 0xfc & (tmp >> 3); + b[2] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline + 1); + r[3] = 0xf8 & (tmp << 3); + g[3] = 0xfc & (tmp >> 3); + b[3] = 0xf8 & (tmp >> 8); + + avg_src[0] = (r[0] + r[1] + r[2] + r[3]) /4; + avg_src[1] = (g[0] + g[1] + g[2] + g[3]) /4; + avg_src[2] = (b[0] + b[1] + b[2] + b[3]) /4; + RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++); + src += 4; + } + src += 2 * src_fmt->fmt.pix.bytesperline - 2 * src_fmt->fmt.pix.width; + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/sn9c2028-decomp.c b/v4l2-apps/libv4l/libv4lconvert/sn9c2028-decomp.c new file mode 100644 index 000000000..c21696752 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/sn9c2028-decomp.c @@ -0,0 +1,157 @@ +/* + * sn9c2028-decomp.c + * + * Decompression function for the Sonix SN9C2028 dual-mode cameras. + * + * Code adapted from libgphoto2/camlibs/sonix, original version of which was + * Copyright (c) 2005 Theodore Kilgore <kilgota@auburn.edu> + * + * History: + * + * This decoding algorithm originates from the work of Bertrik Sikken for the + * SN9C102 cameras. This version is an adaptation of work done by Mattias + * Krauss for the webcam-osx (macam) project. There, it was further adapted + * for use with the Vivitar Vivicam 3350B (an SN9C2028 camera) by + * Harald Ruda <hrx@users.sourceforge.net>. Harald brought to my attention the + * work done in the macam project and suggested that I use it. One improvement + * of my own was to notice that the even and odd columns of the image have been + * reversed by the decompression algorithm, and this needs to be corrected + * during the decompression. + * + * + * 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" + +/* Four defines for bitstream operations, used in the decode function */ + +#define PEEK_BITS(num,to) {\ + if (bitBufCount < num) {\ + do {\ + bitBuf = (bitBuf << 8)|(*(src++));\ + bitBufCount += 8; \ + } \ + while\ + (bitBufCount < 24);\ + } \ + to = bitBuf >> (bitBufCount-num);\ +} + +/* + * PEEK_BITS puts the next <num> bits into the low bits of <to>. + * when the buffer is empty, it is completely refilled. + * This strategy tries to reduce memory access. Note that the high bits + * are NOT set to zero! + */ + +#define EAT_BITS(num) { bitBufCount -= num; bits_eaten += num; } + +/* + * EAT_BITS consumes <num> bits (PEEK_BITS does not consume anything, + * it just peeks) + */ + +#define PARSE_PIXEL(val) {\ + PEEK_BITS(10, bits);\ + if ((bits&0x200) == 0) {\ + EAT_BITS(1);\ + } \ + else if ((bits&0x380) == 0x280) {\ + EAT_BITS(3);\ + val += 3;\ + if (val > 255)\ + val = 255;\ + } \ + else if ((bits&0x380) == 0x300) {\ + EAT_BITS(3);\ + val -= 3;\ + if (val < 0)\ + val = 0;\ + } \ + else if ((bits&0x3c0) == 0x200) {\ + EAT_BITS(4);\ + val += 8;\ + if (val > 255)\ + val = 255;\ + } \ + else if ((bits&0x3c0) == 0x240) {\ + EAT_BITS(4);\ + val -= 8;\ + if (val < 0)\ + val = 0;\ + } \ + else if ((bits&0x3c0) == 0x3c0) {\ + EAT_BITS(4);\ + val -= 20;\ + if (val < 0)\ + val = 0;\ + } \ + else if ((bits&0x3e0) == 0x380) {\ + EAT_BITS(5);\ + val += 20;\ + if (val > 255)\ + val = 255;\ + } \ + else {\ + EAT_BITS(10);\ + val = 8*(bits&0x1f)+0;\ + } \ +} + + +#define PUT_PIXEL_PAIR {\ + long pp;\ + pp = (c1val<<8)+c2val;\ + *((unsigned short *) (dst+dst_index)) = pp;\ + dst_index += 2;\ +} + +/* Now the decode function itself */ + +void v4lconvert_decode_sn9c2028(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + long dst_index = 0; + int starting_row = 0; + unsigned short bits; + short c1val, c2val; + int x, y; + unsigned long bitBuf = 0; + unsigned long bitBufCount = 0; + unsigned long bits_eaten = 0; + + src += 12; /* Remove the header */ + + for (y = starting_row; y < height; y++) { + PEEK_BITS(8, bits); + EAT_BITS(8); + c2val = (bits & 0xff); + PEEK_BITS(8, bits); + EAT_BITS(8); + c1val = (bits & 0xff); + + PUT_PIXEL_PAIR; + + for (x = 2; x < width ; x += 2) { + /* The compression reversed the even and odd columns.*/ + PARSE_PIXEL(c2val); + PARSE_PIXEL(c1val); + PUT_PIXEL_PAIR; + } + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/spca501.c b/v4l2-apps/libv4l/libv4lconvert/spca501.c index f491512e3..f4fbc594a 100644 --- a/v4l2-apps/libv4l/libv4lconvert/spca501.c +++ b/v4l2-apps/libv4l/libv4lconvert/spca501.c @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (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 diff --git a/v4l2-apps/libv4l2util/v4l2_driver.c b/v4l2-apps/libv4l2util/v4l2_driver.c index 94f826968..919119b6a 100644 --- a/v4l2-apps/libv4l2util/v4l2_driver.c +++ b/v4l2-apps/libv4l2util/v4l2_driver.c @@ -646,6 +646,9 @@ int v4l2_mmap_bufs(struct v4l2_driver *drv, unsigned int num_buffers) return 0; } +/* Returns <0, if error, 0 if nothing to read and <size>, if something + read + */ int v4l2_rcvbuf(struct v4l2_driver *drv, v4l2_recebe_buffer *rec_buf) { int ret; @@ -668,7 +671,7 @@ int v4l2_rcvbuf(struct v4l2_driver *drv, v4l2_recebe_buffer *rec_buf) default: perror ("dqbuf"); - return errno; + return -errno; } } prt_buf_info("DQBUF",&buf); @@ -677,19 +680,18 @@ int v4l2_rcvbuf(struct v4l2_driver *drv, v4l2_recebe_buffer *rec_buf) ret = rec_buf (&buf,&drv->bufs[buf.index]); - if (ret) { + if (ret<0) { v4l2_free_bufs(drv); return ret; } if (-1 == xioctl (drv->fd, VIDIOC_QBUF, &buf)) { perror ("qbuf"); - return errno; + return -errno; } - return 0; + return ret; } - int v4l2_start_streaming(struct v4l2_driver *drv) { uint32_t i; diff --git a/v4l2-apps/test/capture-example.c b/v4l2-apps/test/capture-example.c index 9d5b0d6ce..0ae49c4ed 100644 --- a/v4l2-apps/test/capture-example.c +++ b/v4l2-apps/test/capture-example.c @@ -3,8 +3,8 @@ * * This program can be used and distributed without restrictions. * - * This program were got from V4L2 API, Draft 0.20 - * available at: http://v4l2spec.bytesex.org/ + * This program is provided with the V4L2 API + * see http://linuxtv.org/docs.php for more information */ #include <stdio.h> diff --git a/v4l2-apps/util/Makefile b/v4l2-apps/util/Makefile index 29f3f4852..0bee48571 100644 --- a/v4l2-apps/util/Makefile +++ b/v4l2-apps/util/Makefile @@ -28,7 +28,7 @@ endif CPPFLAGS += -I../include -D_GNU_SOURCE LDFLAGS += -lm -binaries = v4l2-ctl v4l2-dbg v4l2-compliance ivtv-ctl cx18-ctl v4l2-sysfs-path +binaries = v4l2-ctl v4l2-dbg v4l2-compliance ivtv-ctl cx18-ctl v4l2-sysfs-path decode_tm6000 ifeq ($(prefix),) prefix = /usr @@ -53,6 +53,12 @@ qv4l2: v4l2-dbg: v4l2-dbg.o v4l2-chipids.o $(CXX) $^ -o $@ +../lib/libv4l2.a: ../lib/v4l2_driver.c ../lib/frequencies.c + make -C ../lib libv4l2.a + +decode_tm6000: decode_tm6000.o ../lib/libv4l2.a + + v4l2-dbg.o: v4l2-dbg.h v4l2-dbg-bttv.h v4l2-dbg-em28xx.h v4l2-dbg-saa7134.h v4l2-sysfs-path.o: v4l2-sysfs-path.c ../libv4l2util/v4l2_driver.h diff --git a/v4l2-apps/util/cx25821/audioplayback b/v4l2-apps/util/cx25821/audioplayback new file mode 100644 index 000000000..1b09da915 --- /dev/null +++ b/v4l2-apps/util/cx25821/audioplayback @@ -0,0 +1 @@ +aplay -c 2 -r 48000 -f S16_LE $1 diff --git a/v4l2-apps/util/cx25821/audiorecord b/v4l2-apps/util/cx25821/audiorecord new file mode 100644 index 000000000..ee2c46650 --- /dev/null +++ b/v4l2-apps/util/cx25821/audiorecord @@ -0,0 +1 @@ +arecord -D hw:1,0 -d $2 -c 2 -r 48000 -f S16_LE > $1 diff --git a/v4l2-apps/util/cx25821/cx25821-medusa-decoder.c b/v4l2-apps/util/cx25821/cx25821-medusa-decoder.c new file mode 100644 index 000000000..7bd35ed43 --- /dev/null +++ b/v4l2-apps/util/cx25821/cx25821-medusa-decoder.c @@ -0,0 +1,106 @@ +/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.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; either version 2 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ +#include "cx25821.h" + + +// Array for GPIO selected bits for a specific decoder. + +enum GPIO_DEF { DECA_SEL = 0, + DECB_SEL, + DECC_SEL, + DECD_SEL, + MON_SEL = 4}; + +enum GPIO_OCTAL_DEF { OCTAL_DECA_SEL = 0, + OCTAL_DECB_SEL, + OCTAL_DECC_SEL, + OCTAL_DECD_SEL, + OCTAL_DECE_SEL, + OCTAL_DECF_SEL, + OCTAL_DECG_SEL, + OCTAL_DECH_SEL, + OCTAL_MON_SEL = 8 }; + + +// Direction bits are at offset 23:16 +#define Set_GPIO_Direction_Bit_To_OUTPUT(Bit) ((1 << Bit) << 16) + +struct medusa_decoder { + int _cur_dec; + unsigned short _num_vdec; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +// Selects Medusa Decoder Channel. +//////////////////////////////////////////////////////////////////////////////////////// +void medusa_decoder_select(struct cx25821_dev *dev, struct medusa_decoder *dec, int decoder) +{ + + if (decoder < DECA_SEL && decoder > MON_SEL ) + return; + + dec->_cur_dec = decoder; + + u32 gpioRegister = cx_read(GP0_IO); + u32 value = (gpioRegister & 0xFFFFFFF0) | dec->_cur_dec; + cx_write( GP0_IO, value ); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Sets the GPIO pin directions +// Parameters: +// +//////////////////////////////////////////////////////////////////////////////////////// +void medusa_video_set_gpiopin_directions_to_output(struct cx25821_dev *dev, struct medusa_decoder *dec ) +{ + // Here we will make sure that the GPIOs 0-3 are output. keep the rest as is + u32 gpioRegister = cx_read( GP0_IO ); + + // This operation will set the GPIO bits below. + if (dec->_num_vdec == 4) + { + cx_write( GP0_IO, + gpioRegister + | (Set_GPIO_Direction_Bit_To_OUTPUT(DECA_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(DECB_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(DECC_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(DECD_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(MON_SEL)) ); + medusa_decoder_select(dev, dec, MON_SEL); + } + else + { + cx_write( GP0_IO, + gpioRegister + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECA_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECB_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECC_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECD_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECE_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECF_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECG_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_DECH_SEL)) + | (Set_GPIO_Direction_Bit_To_OUTPUT(OCTAL_MON_SEL)) ); + medusa_decoder_select(dev, dec, OCTAL_MON_SEL); + } +} diff --git a/v4l2-apps/util/cx25821/medusaReadWrite.c b/v4l2-apps/util/cx25821/medusaReadWrite.c new file mode 100644 index 000000000..cf4a1a5e6 --- /dev/null +++ b/v4l2-apps/util/cx25821/medusaReadWrite.c @@ -0,0 +1,158 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.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; either version 2 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/errno.h> +#include <linux/ioctl.h> + + +#define MEDUSA_READ 910 +#define MEDUSA_WRITE 911 + +typedef struct{ + char *vid_stdname; + int pixel_format; + int cif_resolution_enable; + int cif_width; + int decoder_select; + int command; + int reg_address; + int reg_data; +}downstream_user_struct; + + +void print_usage() +{ + printf("\n*********************************\n"); + printf("Sample Usage: ./medusa d 1 r 0x01\n"); + printf("Sample Usage: ./medusa d 1 w 0x0 0x2\n"); + printf(" d device_id Device ID (1 and above) for MultiCard\n"); + printf(" r Read Medusa register\n"); + printf(" w Write Medusa register\n"); +} + +int main(int argc, char** argv) +{ + unsigned int cmd= 0 ; + int fp; + int i = 1, j = 0, k = 0, len = 0; + int mode = 0; + + int register_addr = 0; + int write_value = 0; + + int device_id = 0, video_id = 11; + char * temp2; + char *device_str[4] = {"/dev/video11", "/dev/video23", "/dev/video35", "/dev/video47"}; + + char mode_temp = 's'; + char *param_temp; + + + if(argc < 5 || (tolower(*(argv[1])) != 'd') ) + { + print_usage(); + return -EINVAL; + } + + sscanf(argv[2], "%d", &device_id ); + i += 2; + + if( device_id <= 0 || device_id > 4 ) + { + print_usage(); + return -EINVAL; + } + + printf("\n********************************* \n"); + + if((fp = open(device_str[device_id-1], O_RDWR)) == -1) + { + printf("Error: cannot open device file %s !\n", device_str[device_id-1]); + return -EINVAL; + } + + printf("Device %s open for IOCTL successfully!\n", device_str[device_id-1]); + + + for( ; i < argc; i++) + { + temp2 = argv[i]; + mode_temp = tolower(temp2[0]); + param_temp = argv[i+1]; + + + switch(mode_temp) + { + case 'r': + { + downstream_user_struct arguments; + + sscanf(param_temp, "%x", ®ister_addr ); + + arguments.reg_address = register_addr; + arguments.command = MEDUSA_READ; + + printf("Read parameters: read register = 0x%x, command = %d \n", arguments.reg_address, arguments.command); + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + printf("Reg 0x%x = 0x%x\n", arguments.reg_address, arguments.reg_data); + } + break; + + case 'w': + { + downstream_user_struct arguments; + + sscanf(param_temp, "%x", ®ister_addr ); + sscanf(argv[i+2], "%x", &write_value ); + i++; + + arguments.command = MEDUSA_WRITE; + arguments.reg_address = register_addr; + arguments.reg_data = write_value; + + printf("Write parameters: write register = 0x%x, write_value = 0x%x, command = %d \n", arguments.reg_address, arguments.reg_data, arguments.command); + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + } + break; + + default: + printf("Please verify the options are correct!\n"); + break; + } + + i++; + } + + + printf("********************************* \n\n"); + close(fp); + return 0; +} + diff --git a/v4l2-apps/util/cx25821/mencode_av b/v4l2-apps/util/cx25821/mencode_av new file mode 100644 index 000000000..6533cb4d8 --- /dev/null +++ b/v4l2-apps/util/cx25821/mencode_av @@ -0,0 +1 @@ +mencoder tv:// -tv driver=v4l2:input=0:norm=ntsc:width=720:height=480:outfmt=yuy2:device=/dev/video$1:forceaudio:alsa=1:adevice=hw.1:audiorate=48000:immediatemode=0:amode=1 buffersize=64 -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=5000:keyint=30 -oac mp3lame -lameopts br=128:cbr:mode=3 -ffourcc divx -o $2
\ No newline at end of file diff --git a/v4l2-apps/util/cx25821/mencode_video b/v4l2-apps/util/cx25821/mencode_video new file mode 100644 index 000000000..9f0c29ebd --- /dev/null +++ b/v4l2-apps/util/cx25821/mencode_video @@ -0,0 +1 @@ +mencoder tv:// -tv driver=v4l2:input=0:norm=ntsc:width=720:height=480:outfmt=yuy2:device=/dev/video$1 -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=5000:keyint=30 -nosound -ffourcc divx -o $2 diff --git a/v4l2-apps/util/cx25821/mplay_av b/v4l2-apps/util/cx25821/mplay_av new file mode 100644 index 000000000..0b11e7af6 --- /dev/null +++ b/v4l2-apps/util/cx25821/mplay_av @@ -0,0 +1 @@ +mplayer -vo x11 -tv driver=v4l2:device=/dev/video$1:input=1:norm=NTSC-M:width=720:height=480:outfmt=yuy2:alsa=1:adevice=hw.1:audiorate=48000:immediatemode=0:amode=1 tv:///0 diff --git a/v4l2-apps/util/cx25821/mplay_cifNTSC b/v4l2-apps/util/cx25821/mplay_cifNTSC new file mode 100644 index 000000000..ba3c34ec2 --- /dev/null +++ b/v4l2-apps/util/cx25821/mplay_cifNTSC @@ -0,0 +1 @@ +mplayer -vo x11 -tv driver=v4l2:device=/dev/video$1:input=1:norm=NTSC-M:width=320:height=240:outfmt=yuy2: tv:///0 diff --git a/v4l2-apps/util/cx25821/mplay_cifPAL b/v4l2-apps/util/cx25821/mplay_cifPAL new file mode 100644 index 000000000..2139ffb0a --- /dev/null +++ b/v4l2-apps/util/cx25821/mplay_cifPAL @@ -0,0 +1 @@ +mplayer -vo x11 -tv driver=v4l2:device=/dev/video$1:input=1:norm=PAL-BG:width=352:height=288:outfmt=yuy2: tv:///0 diff --git a/v4l2-apps/util/cx25821/mplay_video b/v4l2-apps/util/cx25821/mplay_video new file mode 100644 index 000000000..7d9567662 --- /dev/null +++ b/v4l2-apps/util/cx25821/mplay_video @@ -0,0 +1 @@ +mplayer -vo x11 -tv driver=v4l2:device=/dev/video$1:input=1:norm=NTSC-M:width=720:height=480:outfmt=yuy2: tv:///0 diff --git a/v4l2-apps/util/cx25821/regReadWrite.c b/v4l2-apps/util/cx25821/regReadWrite.c new file mode 100644 index 000000000..2d506183d --- /dev/null +++ b/v4l2-apps/util/cx25821/regReadWrite.c @@ -0,0 +1,158 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.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; either version 2 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/errno.h> +#include <linux/ioctl.h> + + +#define REG_READ 900 +#define REG_WRITE 901 + +typedef struct{ + char *vid_stdname; + int pixel_format; + int cif_resolution_enable; + int cif_width; + int decoder_select; + int command; + int reg_address; + int reg_data; +}downstream_user_struct; + + +void print_usage() +{ + printf("\n*********************************\n"); + printf("Sample Usage: ./reg d 1 r 0x01\n"); + printf("Sample Usage: ./reg d 1 w 0x0 0x2\n"); + printf(" d device_id Device ID (1 and above) for MultiCard\n"); + printf(" r Read Athena register\n"); + printf(" w Write Athena register\n"); +} + +int main(int argc, char** argv) +{ + unsigned int cmd= 0 ; + int fp; + int i = 1, j = 0, k = 0, len = 0; + int mode = 0; + + int register_addr = 0; + int write_value = 0; + + int device_id = 0, video_id = 11; + char * temp2; + char *device_str[4] = {"/dev/video11", "/dev/video23", "/dev/video35", "/dev/video47"}; + + char mode_temp = 's'; + char *param_temp; + + + if(argc < 5 || (tolower(*(argv[1])) != 'd') ) + { + print_usage(); + return -EINVAL; + } + + sscanf(argv[2], "%d", &device_id ); + i += 2; + + if( device_id <= 0 || device_id > 4 ) + { + print_usage(); + return -EINVAL; + } + + printf("\n********************************* \n"); + + if((fp = open(device_str[device_id-1], O_RDWR)) == -1) + { + printf("Error: cannot open device file %s !\n", device_str[device_id-1]); + return -EINVAL; + } + + printf("Device %s open for IOCTL successfully!\n", device_str[device_id-1]); + + + for( ; i < argc; i++) + { + temp2 = argv[i]; + mode_temp = tolower(temp2[0]); + param_temp = argv[i+1]; + + + switch(mode_temp) + { + case 'r': + { + downstream_user_struct arguments; + + sscanf(param_temp, "%x", ®ister_addr ); + + arguments.reg_address = register_addr; + arguments.command = REG_READ; + + printf("Read parameters: read register = 0x%x, command = %d \n", arguments.reg_address, arguments.command); + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + printf("Reg 0x%x = 0x%x\n", arguments.reg_address, arguments.reg_data); + } + break; + + case 'w': + { + downstream_user_struct arguments; + + sscanf(param_temp, "%x", ®ister_addr ); + sscanf(argv[i+2], "%x", &write_value ); + i++; + + arguments.command = REG_WRITE; + arguments.reg_address = register_addr; + arguments.reg_data = write_value; + + printf("Write parameters: write register = 0x%x, write_value = 0x%x, command = %d \n", arguments.reg_address, arguments.reg_data, arguments.command); + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + } + break; + + default: + printf("Please verify the options are correct!\n"); + break; + } + + i++; + } + + + printf("********************************* \n\n"); + close(fp); + return 0; +} + diff --git a/v4l2-apps/util/cx25821/setAlsaVolume b/v4l2-apps/util/cx25821/setAlsaVolume new file mode 100644 index 000000000..55dd3dc89 --- /dev/null +++ b/v4l2-apps/util/cx25821/setAlsaVolume @@ -0,0 +1,2 @@ +amixer set Master,0 100% unmute +amixer set Capture,0 90% captur diff --git a/v4l2-apps/util/cx25821/setvideosetting.c b/v4l2-apps/util/cx25821/setvideosetting.c new file mode 100644 index 000000000..87e36045d --- /dev/null +++ b/v4l2-apps/util/cx25821/setvideosetting.c @@ -0,0 +1,187 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.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; either version 2 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/errno.h> +#include <linux/ioctl.h> + + +#define SET_VIDEO_STD 800 +#define SET_PIXEL_FORMAT 1000 +#define ENABLE_CIF_RESOLUTION 1001 + + +#define PIXEL_FRMT_422 4 +#define PIXEL_FRMT_411 5 +#define PIXEL_FRMT_Y8 6 +#define ALL_DECODERS 752 + +typedef struct { + char *vid_stdname; + int pixel_format; + int cif_resolution_enable; + int cif_width; + int decoder_select; + int command; +} downstream_user_struct; + + +void print_usage() +{ + printf("\n*********************************\n"); + printf("Sample Usage: ./set d 1 standard NTSC format 422 7 resolution 320 7\n"); + printf(" device_id Device ID (1 and above) for MultiCard\n"); + printf(" standard Video Standard (PAL/NTSC)\n"); + printf(" format Output Pixel Format (422 or 411) for specific decoder (e.g. 7)\n"); + printf(" resolution 352/320 for CIF and 720 for D1 resolution for specific decoder (e.g. 7)\n"); +} + +int main(int argc, char** argv) +{ + unsigned int cmd= 0 ; + int fp; + int i = 1, j = 0, k = 0, len = 0; + int mode = 0; + int pixel_format = 0; + int width_input = 0; + int decoder_input = 0; + int device_id = 0, video_id = 11; + char * temp2; + char *device_str[4] = {"/dev/video11", "/dev/video23", "/dev/video35", "/dev/video47"}; + + char mode_temp = 's'; + char *param_temp; + + + if(argc < 3 || (tolower(*(argv[1])) != 'd') ) + { + print_usage(); + return -EINVAL; + } + + sscanf(argv[2], "%d", &device_id ); + i += 2; + + if( device_id <= 0 || device_id > 4 ) + { + print_usage(); + return -EINVAL; + } + + printf("\n********************************* \n"); + + if((fp = open(device_str[device_id-1], O_RDWR)) == -1) + { + printf("Error: cannot open device file %s !\n", device_str[device_id-1]); + return -EINVAL; + } + + printf("Device %s open for IOCTL successfully!\n", device_str[device_id-1]); + + + for( ; i < argc; i++) + { + temp2 = argv[i]; + mode_temp = tolower(temp2[0]); + param_temp = argv[i+1]; + + + switch(mode_temp) + { + case 's': + { + downstream_user_struct arguments; + char * temp = param_temp; + arguments.vid_stdname = (tolower(temp[0]) == 'p') ? "PAL" : "NTSC"; + arguments.command = SET_VIDEO_STD; + + printf("User parameters: vid_stdname = %s, command = %d \n", arguments.vid_stdname, arguments.command); + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + } + break; + + case 'f': + { + sscanf(param_temp, "%d", &pixel_format ); + sscanf(argv[i+2], "%d", &decoder_input ); i++; + + if( pixel_format < 411 || pixel_format > 422) + pixel_format = 422; + + if( decoder_input < 0 ) + decoder_input = ALL_DECODERS; + + downstream_user_struct arguments; + arguments.pixel_format = (pixel_format == 411) ? PIXEL_FRMT_411 : PIXEL_FRMT_422; + arguments.decoder_select = decoder_input; + arguments.command = SET_PIXEL_FORMAT; + + printf("User parameters: pixel_format = %d, decoder_input = %d, command = %d \n", arguments.pixel_format, arguments.decoder_select, arguments.command); + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + } + break; + + case 'r': + { + sscanf(param_temp, "%d", &width_input ); + sscanf(argv[i+2], "%d", &decoder_input ); i++; + + if( width_input < 320 || width_input > 352 ) + width_input = 720; + + if( decoder_input < 0 ) + decoder_input = ALL_DECODERS; + + downstream_user_struct arguments; + arguments.cif_resolution_enable = (width_input == 320 || width_input == 352) ? 1 : 0; + arguments.cif_width = width_input; + arguments.decoder_select = decoder_input; + arguments.command = ENABLE_CIF_RESOLUTION; + + printf("User parameters: cif_resolution_enable = %d, decoder_input = %d, command = %d \n", arguments.cif_resolution_enable, arguments.decoder_select, arguments.command); + + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + } + break; + + default: + printf("Please verify the options are correct!\n"); + break; + } + + i++; + } + + + printf("********************************* \n\n"); + close(fp); + return 0; +} diff --git a/v4l2-apps/util/cx25821/upstream_app.c b/v4l2-apps/util/cx25821/upstream_app.c new file mode 100644 index 000000000..97f7b84aa --- /dev/null +++ b/v4l2-apps/util/cx25821/upstream_app.c @@ -0,0 +1,221 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.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; either version 2 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <linux/errno.h> +#include <linux/ioctl.h> + +#define VID_UPSTREAM_SRAM_CHANNEL_I 9 +#define VID_UPSTREAM_SRAM_CHANNEL_J 10 +#define AUDIO_UPSTREAM_SRAM_CHANNEL_B 11 +#define PIXEL_FRMT_422 4 +#define PIXEL_FRMT_411 5 +#define UPSTREAM_START_VIDEO 700 +#define UPSTREAM_STOP_VIDEO 701 +#define UPSTREAM_START_AUDIO 702 +#define UPSTREAM_STOP_AUDIO 703 +#define UPSTREAM_DUMP_REGISTERS 702 + + +typedef struct { + char *input_filename; + char *vid_stdname; + int pixel_format; + int channel_select; + int command; +} upstream_user_struct; + +void print_video_usage() +{ + printf("\n************************************************************************ \n"); + printf("Video Sample Usage: ./upstream_app d 1 v /root/filename.yuv NTSC 422 1 start \n"); + printf("Argument 0: ./upstream_app \n"); + printf("Argument 1: Device ID (1 and above)\n"); + printf("Argument 2: v for video\n"); + printf("Argument 3: input file name \n"); + printf("Argument 4: Video Standard \n"); + printf("Argument 5: Pixel Format (Y422 or Y411) \n"); + printf("Argument 6: Upstream Channel Number\n"); + printf("Argument 7: start/stop command\n\n"); +} + +void print_audio_usage() +{ + printf("\n************************************************************************ \n"); + printf("Audio Sample Usage: ./upstream_app d 1 a /root/audio.wav start\n"); + printf("Argument 0: ./upstream_app \n"); + printf("Argument 1: Device ID (1 and above)\n"); + printf("Argument 2: a for audio \n"); + printf("Argument 3: input file name \n"); + printf("Argument 4: start/stop command\n\n"); +} + +int main(int argc, char** argv) +{ + int fp; + unsigned int cmd= 0 ; + int i = 1; + int mode = 0; + int pixel_format = 422; + int channel_select = 1; + int device_id = 0, video_id = 11; + char * temp2; + char *video_device_str[4][2] = {{"/dev/video8", "/dev/video9"}, {"/dev/video20", "/dev/video21"}, + {"/dev/video32", "/dev/video33"}, {"/dev/video44", "/dev/video45"} }; + char *audio_device_str[4] = {"/dev/video10", "/dev/video22", "/dev/video34", "/dev/video46"}; + char mode_temp = 'v'; + + if(argc < 2 || (tolower(*(argv[1])) != 'd') ) + { + print_video_usage(); + print_audio_usage(); + return -EINVAL; + } + else + { + sscanf(argv[2], "%d", &device_id ); + i += 2; + + if( device_id <= 0 || device_id > 4 ) + { + print_video_usage(); + print_audio_usage(); + return -EINVAL; + } + + temp2 = argv[i]; + mode_temp = tolower(temp2[0]); + switch(mode_temp) + { + case 'v': + if( argc < 9 ) + { + print_video_usage(); + return -EINVAL; + } + break; + + case 'a': + if( argc < 6 ) + { + print_audio_usage(); + return -EINVAL; + } + break; + } + } + + if( mode_temp == 'v' || mode_temp == 'a' ) + { + FILE* file_ptr = fopen(argv[4], "r"); + + if( !file_ptr ) + { + printf("\nERROR: %s file does NOT exist!!! \n\n", argv[4]); + return -EINVAL; + } + + fclose(file_ptr); + } + else + { + print_video_usage(); + print_audio_usage(); + return -EINVAL; + } + + printf("\n*************************************************************** \n"); + + switch(mode_temp) + { + case 'v': + { + char * temp = argv[5]; + upstream_user_struct arguments; + arguments.input_filename = argv[4]; + arguments.vid_stdname = (tolower(temp[0]) == 'p') ? "PAL" : "NTSC"; + sscanf(argv[6], "%d", &pixel_format); + sscanf(argv[7], "%d", &channel_select); + arguments.pixel_format = (pixel_format == 422) ? PIXEL_FRMT_422 : PIXEL_FRMT_411; + arguments.channel_select = (channel_select==1) ? VID_UPSTREAM_SRAM_CHANNEL_I : VID_UPSTREAM_SRAM_CHANNEL_J; + temp = argv[8]; + arguments.command = (strcasecmp("STOP", temp) == 0) ? UPSTREAM_STOP_VIDEO : UPSTREAM_START_VIDEO; + + + if( channel_select >= 1 && channel_select <= 2 ) + { + if((fp = open(video_device_str[device_id-1][channel_select-1], O_RDWR)) == -1) + { + printf("Error: cannot open device file %s !\n", video_device_str[device_id-1][channel_select-1]); + return -EINVAL; + } + + printf("Device %s open for IOCTL successfully!\n", video_device_str[device_id-1][channel_select-1]); + printf("UPSTREAM parameters: filename = %s, channel_select(I=1) = %d \n", arguments.input_filename, channel_select); + + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + { + printf("Error: ioctl FAILED!\n"); + } + } + } + break; + + case 'a': + { + char * temp = argv[5]; + upstream_user_struct arguments; + arguments.input_filename = argv[4]; + arguments.vid_stdname = "NTSC"; + arguments.pixel_format = PIXEL_FRMT_422; + arguments.channel_select = AUDIO_UPSTREAM_SRAM_CHANNEL_B; + arguments.command = (strcasecmp("STOP", temp) == 0) ? UPSTREAM_STOP_AUDIO : UPSTREAM_START_AUDIO; + + + if((fp = open(audio_device_str[device_id-1], O_RDWR)) == -1) + { + printf("Error: cannot open device file %s !\n", audio_device_str[device_id-1]); + return -EINVAL; + } + + printf("Device %s open for IOCTL successfully!\n", audio_device_str[device_id-1]); + printf("UPSTREAM parameters: filename = %s, audio channel = %d \n", arguments.input_filename, arguments.channel_select); + + + if((ioctl(fp, arguments.command, (char *) &arguments)) == -1) + printf("Error: ioctl FAILED!\n"); + } + break; + + default: + printf("ERROR: INVALID ioctl command!\n"); + return -EINVAL; + } + + printf("*************************************************************** \n\n"); + close(fp); + return 0; +} diff --git a/v4l2-apps/util/decode_tm6000.c b/v4l2-apps/util/decode_tm6000.c new file mode 100644 index 000000000..d622360d2 --- /dev/null +++ b/v4l2-apps/util/decode_tm6000.c @@ -0,0 +1,364 @@ +/* + decode_tm6000.c - decode multiplexed format from TM5600/TM6000 USB + + Copyright (C) 2007 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. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "../lib/v4l2_driver.h" +#include <stdio.h> +#include <string.h> +#include <argp.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +const char *argp_program_version="decode_tm6000 version 0.0.1"; +const char *argp_program_bug_address="Mauro Carvalho Chehab <mchehab@infradead.org>"; +const char doc[]="Decodes tm6000 proprietary format streams"; +const struct argp_option options[] = { + {"verbose", 'v', 0, 0, "enables debug messages", 0}, + {"device", 'd', "DEV", 0, "uses device for reading", 0}, + {"output", 'o', "FILE", 0, "outputs raw stream to a file", 0}, + {"input", 'i', "FILE", 0, "parses a file, instead of a device", 0}, + {"freq", 'f', "Freq", 0, "station frequency, in MHz (default is 193.25)", 0}, + {"nbufs", 'n', "quant",0, "number of video buffers", 0}, + {"audio", 'a', 0, 0, "outputs audio on stdout", 0}, + {"read", 'r', 0, 0, "use read() instead of mmap method", 0}, + { 0, 0, 0, 0, 0, 0 } +}; + +static char outbuf[692224]; +static int debug=0, audio=0, use_mmap=1, nbufs=4; +static float freq_mhz=193.25; +static char *devname="/dev/video0"; +static char *filename=NULL; +static enum { + NORMAL, + INPUT, + OUTPUT +} mode = NORMAL; + +static FILE *fout; + +//const char args_doc[]="ARG1 ARG2"; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'a': + audio++; + break; + case 'r': + use_mmap=0; + break; + case 'v': + debug++; + break; + case 'd': + devname=arg; + break; + case 'i': + case 'o': + if (mode!=NORMAL) { + argp_error(state,"You can't use input/output options simultaneously.\n"); + break; + } + if (key=='i') + mode=INPUT; + else + mode=OUTPUT; + + filename=arg; + break; + case 'f': + freq_mhz=atof(arg); + break; + case 'n': + nbufs=atoi(arg); + if (nbufs<2) + nbufs=2; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + .options=options, + .parser=parse_opt, + .args_doc=NULL, + .doc=doc, +}; + +#define TM6000_URB_MSG_LEN 180 +enum { + TM6000_URB_MSG_VIDEO=1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; + +const char *tm6000_msg_type[]= { + "unknown(0)", //0 + "video", //1 + "audio", //2 + "vbi", //3 + "pts", //4 + "err", //5 + "unknown(6)", //6 + "unknown(7)", //7 +}; + +#define dprintf(fmt,arg...) \ + if (debug) fprintf(stderr, fmt, ##arg) + +int recebe_buffer (struct v4l2_buffer *v4l2_buf, struct v4l2_t_buf *buf) +{ + dprintf("Received %d bytes\n",buf->length); +fflush(stdout); + memcpy (outbuf,buf->start,buf->length); + return buf->length; +} + + +int prepare_read (struct v4l2_driver *drv) +{ + struct v4l2_format fmt; + double freq; + int rc; + + memset (drv,0,sizeof(*drv)); + + if (v4l2_open (devname, 1,drv)<0) { + perror ("Error opening dev"); + return -1; + } + + memset (&fmt,0,sizeof(fmt)); + + uint32_t pixelformat=V4L2_PIX_FMT_TM6000; + + if (v4l2_gettryset_fmt_cap (drv,V4L2_SET,&fmt, 720, 480, + pixelformat,V4L2_FIELD_ANY)) { + perror("set_input to tm6000 raw format"); + return -1; + } + + if (freq_mhz) { + freq=freq_mhz * 1000 * 1000; + rc=v4l2_getset_freq (drv,V4L2_SET, &freq); + if (rc<0) + printf ("Cannot set freq to %.3f MHz\n",freq_mhz); + } + + if (use_mmap) { + printf("Preparing for receiving frames on %d buffers...\n",nbufs); + fflush (stdout); + + rc=v4l2_mmap_bufs(drv, nbufs); + if (rc<0) { + printf ("Cannot mmap %d buffers\n",nbufs); + return -1; + } + +// v4l2_stop_streaming(&drv); + rc=v4l2_start_streaming(drv); + if (rc<0) { + printf ("Cannot start streaming\n"); + return -1; + } + } + printf("Waiting for frames...\n"); + + return 0; +} + +int read_stream (struct v4l2_driver *drv, int fd) +{ + if (use_mmap) { + fd_set fds; + struct timeval tv; + int r; + + FD_ZERO (&fds); + FD_SET (fd, &fds); + + /* Timeout. */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + r = select (fd + 1, &fds, NULL, NULL, &tv); + if (-1 == r) { + if (EINTR == errno) { + perror ("select"); + return -errno; + } + } + + if (0 == r) { + fprintf (stderr, "select timeout\n"); + return -errno; + } + + return v4l2_rcvbuf(drv, recebe_buffer); + } else { + int size=read(fd, outbuf, sizeof(outbuf)); + return size; + } + + return 0; +} + +int read_char (struct v4l2_driver *drv, int fd) +{ + static int sizebuf=0; + static unsigned char *p=NULL; + unsigned char c; + + if (sizebuf<=0) { + sizebuf=read_stream(drv,fd); + if (sizebuf<=0) + return -1; + p=(unsigned char *)outbuf; + } + c=*p; + p++; + sizebuf--; + + return c; +} + + +int main (int argc, char*argv[]) +{ + int fd; + unsigned int i; + unsigned char buf[TM6000_URB_MSG_LEN], img[720*2*480]; + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos=0; + unsigned long header=0; + int linesize=720*2,skip=0; + struct v4l2_driver drv; + + argp_parse (&argp, argc, argv, 0, 0, 0); + + if (mode!=INPUT) { + if (prepare_read (&drv)<0) + return -1; + fd=drv.fd; + } else { + /*mode == INPUT */ + + fd=open(filename,O_RDONLY); + if (fd<0) { + perror ("error opening a file for parsing"); + return -1; + } + dprintf("file %s opened for parsing\n",filename); + use_mmap=0; + } + + if (mode==OUTPUT) { + fout=fopen(filename,"w"); + if (!fout) { + perror ("error opening a file to write"); + return -1; + } + dprintf("file %s opened for output\n",filename); + + do { + size=read_stream (&drv,fd); + + if (size<=0) { + close (fd); + return -1; + } + dprintf("writing %d bytes\n",size); + fwrite(outbuf,1, size,fout); +// fflush(fout); + } while (1); + } + + + while (1) { + skip=0; + header=0; + do { + int c; + c=read_char (&drv,fd); + if (c<0) { + perror("read"); + return -1; + } + + header=(header>>8)&0xffffff; + header=header|(c<<24); + skip++; + } while ( (((header>>24)&0xff) != 0x47) ); + + /* split the header fields */ + size = (((header & 0x7e)<<1) -1) * 4; + block = (header>>7) & 0xf; + field = (header>>11) & 0x1; + line = (header>>12) & 0x1ff; + cmd = (header>>21) & 0x7; + + /* Read the remaining buffer */ + for (i=0;i<sizeof(buf);i++) { + int c; + c=read_char (&drv,fd); + if (c<0) { + perror("read"); + return -1; + } + buf[i]=c; + } + + /* FIXME: Mounts the image as field0+field1 + * It should, instead, check if the user selected + * entrelaced or non-entrelaced mode + */ + pos=((line<<1)+field)*linesize+ + block*TM6000_URB_MSG_LEN; + + /* Prints debug info */ + dprintf("0x%08x (skip %d), %s size=%d, line=%d, field=%d, block=%d\n", + (unsigned int)header, skip, + tm6000_msg_type[cmd], + size, line, field, block); + + /* Don't allow to write out of the buffer */ + if (pos+sizeof(buf) > sizeof(img)) + cmd = TM6000_URB_MSG_ERR; + + /* handles each different URB message */ + switch(cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + memcpy(buf,&img[pos],sizeof(buf)); + case TM6000_URB_MSG_AUDIO: + if (audio) + fwrite(buf,sizeof(buf),1,stdout); +// case TM6000_URB_MSG_VBI: +// case TM6000_URB_MSG_PTS: + break; + } + } + close(fd); + return 0; +} |